@sanity/cross-dataset-duplicator 0.4.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +53 -57
  3. package/lib/index.esm.js +1 -0
  4. package/lib/index.esm.js.map +1 -0
  5. package/lib/index.js +1 -21
  6. package/lib/index.js.map +1 -1
  7. package/lib/src/index.d.ts +548 -0
  8. package/package.json +64 -38
  9. package/sanity.json +2 -10
  10. package/src/actions/DuplicateToAction.tsx +20 -8
  11. package/src/components/CrossDatasetDuplicator.tsx +34 -49
  12. package/src/components/CrossDatasetDuplicatorAction.tsx +14 -0
  13. package/src/components/CrossDatasetDuplicatorTool.tsx +18 -0
  14. package/src/components/{DuplicatorTool.tsx → Duplicator.tsx} +179 -181
  15. package/src/components/DuplicatorQuery.tsx +36 -13
  16. package/src/components/DuplicatorWrapper.tsx +66 -0
  17. package/src/components/ResetSecret.tsx +9 -9
  18. package/src/components/SelectButtons.tsx +13 -9
  19. package/src/components/StatusBadge.tsx +13 -9
  20. package/src/context/ConfigProvider.tsx +30 -0
  21. package/src/helpers/clientConfig.ts +1 -1
  22. package/src/helpers/constants.ts +10 -1
  23. package/src/helpers/getDocumentsInArray.ts +28 -21
  24. package/src/helpers/index.ts +6 -10
  25. package/src/index.ts +5 -0
  26. package/src/plugin.tsx +31 -0
  27. package/src/tool/index.ts +11 -10
  28. package/src/types/index.ts +17 -7
  29. package/v2-incompatible.js +11 -0
  30. package/.babelrc +0 -3
  31. package/.eslintignore +0 -1
  32. package/config.dist.json +0 -5
  33. package/lib/actions/DuplicateToAction.js +0 -44
  34. package/lib/actions/DuplicateToAction.js.map +0 -1
  35. package/lib/actions/index.js +0 -29
  36. package/lib/actions/index.js.map +0 -1
  37. package/lib/components/CrossDatasetDuplicator.js +0 -81
  38. package/lib/components/CrossDatasetDuplicator.js.map +0 -1
  39. package/lib/components/DuplicatorQuery.js +0 -105
  40. package/lib/components/DuplicatorQuery.js.map +0 -1
  41. package/lib/components/DuplicatorTool.js +0 -556
  42. package/lib/components/DuplicatorTool.js.map +0 -1
  43. package/lib/components/Feedback.js +0 -23
  44. package/lib/components/Feedback.js.map +0 -1
  45. package/lib/components/ResetSecret.js +0 -34
  46. package/lib/components/ResetSecret.js.map +0 -1
  47. package/lib/components/SelectButtons.js +0 -84
  48. package/lib/components/SelectButtons.js.map +0 -1
  49. package/lib/components/StatusBadge.js +0 -85
  50. package/lib/components/StatusBadge.js.map +0 -1
  51. package/lib/helpers/clientConfig.js +0 -11
  52. package/lib/helpers/clientConfig.js.map +0 -1
  53. package/lib/helpers/constants.js +0 -9
  54. package/lib/helpers/constants.js.map +0 -1
  55. package/lib/helpers/getDocumentsInArray.js +0 -74
  56. package/lib/helpers/getDocumentsInArray.js.map +0 -1
  57. package/lib/helpers/index.js +0 -27
  58. package/lib/helpers/index.js.map +0 -1
  59. package/lib/tool/index.js +0 -18
  60. package/lib/tool/index.js.map +0 -1
  61. package/lib/types/index.js +0 -2
  62. package/lib/types/index.js.map +0 -1
  63. package/src/actions/index.ts +0 -22
  64. package/src/index.js +0 -7
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021 Simeon Griggs
3
+ Copyright (c) 2023 Sanity.io
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,13 +1,16 @@
1
1
  # Cross Dataset Duplicator
2
2
 
3
- Sanity Studio Tool and Document Action for empowering content editors to migrate Documents and Assets between Sanity Datasets and Projects from inside the Studio.
3
+ Sanity Studio v3 Tool and Document Action for empowering content editors to migrate Documents and Assets between Sanity Datasets and Projects from inside the Studio.
4
+ ## Installation
4
5
 
5
- ## Install
6
+ ```
7
+ npm install --save @sanity/cross-dataset-duplicator
8
+ ```
6
9
 
7
- From the root directory of your studio
10
+ or
8
11
 
9
12
  ```
10
- sanity install @sanity/cross-dataset-duplicator
13
+ yarn add @sanity/cross-dataset-duplicator
11
14
  ```
12
15
 
13
16
  ### Important Notes
@@ -16,54 +19,61 @@ This plugin is designed as a convenience for Authors to make small, infrequent c
16
19
 
17
20
  - This plugin should be used in conjunction with a reliable backup strategy.
18
21
  - Proceed with caution as this plugin can instantly write changes to Datasets.
19
- - Larger migrations may take more time, especially with Assets. The plugin tries to mitigate this with rate limiting asset uploads to 3 at a time.
22
+ - Larger migrations may take more time, especially with Assets. Trying to upload them all at once could result in a rate-limiting issue, so the plugin mitigates this by limiting simultaneous asset uploads to 3.
20
23
  - If an Asset is already present at the destination, there's no need to duplicate it again.
21
- - Before starting a Duplication you can select which Documents and Assets to include. Migrations will fail if every Referenced Document or Asset is not included in the transaction or already present at the destination Dataset.
24
+ - Before starting a Duplication you can select which Documents and Assets to include. Migrations will fail if every Referenced Document or Asset is not included in the transaction or is already present at the destination Dataset.
22
25
 
23
26
  ## Tool
24
27
 
25
28
  The **Duplicate** Tool allows you to migrate Documents that are returned from any GROQ query.
26
29
 
27
- ![2022-04-04 13 23 57](https://user-images.githubusercontent.com/9684022/161548068-80f2552a-3cb6-47fb-ac13-b4e24a98bd05.gif)
30
+ ![Cross Dataset Duplicator Tool in Sanity Studio v3](./img/cdd-tool.png)
28
31
 
29
32
  ## Document Action
30
33
 
31
34
  The **Duplicate to...** Document Action allows you to migrate an individual Document.
32
35
 
33
- ![2022-04-04 13 52 14](https://user-images.githubusercontent.com/9684022/161548033-216f5de1-5617-4f2c-a201-3ab9efbf0803.gif)
34
-
35
- **Note:** If your Studio registered its own Document Actions, the plugin config will be overruled. See "Importing the Document Action" below.
36
+ ![Cross Dataset Duplicator Action in Sanity Studio v3](./img/cdd-action.png)
36
37
 
37
38
  ## Required Setup
38
39
 
39
- ### 1. Spaces
40
+ ### 1. Workspaces
40
41
 
41
- You must have [Spaces configured](https://www.sanity.io/docs/spaces) to use this plugin. Spaces are still listed as an experimental feature but have been supported for some time.
42
+ You must have more than one [Workspace configured](https://www.sanity.io/docs/config-api-reference#37c85e3072b2) to use this plugin.
42
43
 
43
- All Datasets setup in Spaces will become selectable "destinations" for Migrations.
44
+ All Datasets and Project IDs set up as Workspaces will become selectable "destinations" for Migrations.
44
45
 
45
- Once setup, you will see a dropdown menu next to the Search bar in the Studio with the Datasets you have configured in Spaces.
46
+ Once set up, you will see a dropdown menu next to the Search bar in the Studio with the Datasets you have configured.
46
47
 
47
48
  ### 2. Configuration
48
49
 
49
50
  The plugin has some configuration options. These can be set by adding a config file to your Studio
50
51
 
51
- ```js
52
- // ./config/@sanity/cross-dataset-duplicator.json
53
- ```
54
-
55
- ```json
56
- {
57
- "tool": true,
58
- "types": ["article", "page"],
59
- "filter": "_type != 'product'",
60
- "follow" []
61
- }
52
+ ```ts
53
+ // ./sanity.config.ts
54
+
55
+ import {defineConfig} from 'sanity'
56
+ import {crossDatasetDuplicator} from '@sanity/cross-dataset-duplicator'
57
+
58
+ export const defineConfig({
59
+ // all other settings...
60
+ plugins: [
61
+ // all other plugins...
62
+ crossDatasetDuplicator({
63
+ // Required settings to show document action
64
+ types: ['article', 'page'],
65
+ // Optional settings
66
+ tool: true,
67
+ filter: '_type != "product"',
68
+ follow: []
69
+ })
70
+ ]
71
+ })
62
72
  ```
63
73
 
64
74
  Options:
65
75
 
66
- - `tool` (boolean, default: true) – Set whether the Migration Tool is enabled.
76
+ - `tool` (boolean, default: true) – Set whether the Migration **Tool** is enabled.
67
77
  - `types` (Array[String], default: []) – Set which Schema Types the Migration Action should be enabled in.
68
78
  - `filter` (String, default: undefined) - Set a predicate for documents when gathering dependencies.
69
79
  - `follow` (("inbound" | "outbound")[], default: []) – Add buttons to allow the user to begin with just the existing document or first fetch all inbound references.
@@ -74,47 +84,33 @@ To Duplicate the original files of Assets, an API Token with Viewer permissions
74
84
 
75
85
  This plugin uses [Sanity Secrets](https://github.com/sanity-io/sanity-studio-secrets/) to store the token in the Dataset itself.
76
86
 
77
- You can [create API tokens in manage](https://sanity.io/manage)
87
+ You can [create API tokens in Manage](https://sanity.io/manage)
78
88
 
79
89
  ### 4. CORS origins
80
90
 
81
- If you want to duplicate data across different projects, you need to enable CORS for the different hosts. This allows different projects to connect to each other through the project API. CORS origins configuration can be found in your project page, under the API tab.
82
-
83
- ## Importing the Document Action
91
+ If you want to duplicate data across different projects, you need to enable CORS for the different hosts. This allows different projects to connect through the project API. CORS origins configuration can be found on your project page, under the API tab.
84
92
 
85
- In your Studio's `sanity.json` file, look for the `document-actions/resolver` part, it will look like this:
93
+ ## Future feature ideas
86
94
 
87
- ```json
88
- {
89
- "implements": "part:@sanity/base/document-actions/resolver",
90
- "path": "./src/document-actions"
91
- }
92
- ```
95
+ - Save predefined GROQ queries in the Tool to make bulk repeated Migrations simpler
96
+ - Config options for allowed migrations (eg Dev -> Staging but not Dev -> Live)
97
+ - Config options for permissions/user role checks
93
98
 
94
- Now update your Studio's Document Actions resolver to be something like this
99
+ ## License
95
100
 
96
- ```js
97
- import defaultResolve from 'part:@sanity/base/document-actions'
98
- import {DuplicateToAction} from '@sanity/cross-dataset-duplicator'
99
- import config from 'config:@sanity/cross-dataset-duplicator'
101
+ MIT-licensed. See LICENSE.
100
102
 
101
- export default function resolveDocumentActions(props) {
102
- const defaultActions = defaultResolve(props)
103
+ ## Develop & test
103
104
 
104
- // This will look through the "types" array in your migration.json config file
105
- // If the type of this document is found in that array, the Migrate Action will show
106
- if (config?.types?.length && config.types.includes(props.type)) {
107
- return [...defaultActions, DuplicateToAction]
108
- }
105
+ This plugin uses [@sanity/plugin-kit](https://github.com/sanity-io/plugin-kit)
106
+ with default configuration for build & watch scripts.
109
107
 
110
- // ...all your other document action code
108
+ See [Testing a plugin in Sanity Studio](https://github.com/sanity-io/plugin-kit#testing-a-plugin-in-sanity-studio)
109
+ on how to run this plugin with hotreload in the studio.
111
110
 
112
- return defaultActions
113
- }
114
- ```
111
+ ### Release new version
115
112
 
116
- ## Future feature ideas
113
+ Run ["CI & Release" workflow](https://github.com/sanity-io/cross-dataset-duplicator/actions/workflows/main.yml).
114
+ Make sure to select the main branch and check "Release new version".
117
115
 
118
- - Save predefined GROQ queries in the Tool to make bulk repeated Migrations simpler
119
- - Config options for allowed migrations (eg Dev -> Staging but not Dev -> Live)
120
- - Config options for permissions/user role checks
116
+ Semantic release will only release on configured branches, so it is safe to run release on any branch.
@@ -0,0 +1 @@
1
+ const e=["pluginConfig"];function t(e,t){if(null==e)return{};var n,i,o=function(e,t){if(null==e)return{};var n,i,o={},r=Object.keys(e);for(i=0;i<r.length;i++)n=r[i],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i<r.length;i++)n=r[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function n(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function i(e){for(var t=1;t<arguments.length;t++){var i=null!=arguments[t]?arguments[t]:{};t%2?n(Object(i),!0).forEach((function(t){o(e,t,i[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(i)):n(Object(i)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(i,t))}))}return e}function o(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var i=n.call(e,t||"default");if("object"!=typeof i)return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}import{useClient as r,useSchema as a,useWorkspaces as l,Preview as d,definePlugin as c}from"sanity";import{jsx as s,jsxs as u,Fragment as h}from"react/jsx-runtime";import p,{useState as f,useEffect as g,createContext as m,useContext as b}from"react";import{ArrowRightIcon as y,SearchIcon as v,LaunchIcon as w}from"@sanity/icons";import{useSecrets as D,SettingsView as E}from"@sanity/studio-secrets";import{Card as T,Flex as S,Button as A,Badge as I,Tooltip as O,Box as _,Text as C,useTheme as x,Container as j,Stack as k,Label as P,Select as R,Checkbox as U,Grid as z,TextInput as B,Spinner as N}from"@sanity/ui";import L from"async/mapLimit";import X from"async/asyncify";import{extractWithPath as V}from"@sanity/mutator";import{dset as W}from"dset";import{isAssetId as q,isSanityFileAsset as H}from"@sanity/asset-utils";function G(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return[1===e?"This Document contains":"These ".concat(e," Documents contain"),1===t?"1 Reference.":"".concat(t," References."),1===t?"That Document":"Those Documents","may have References too. If referenced Documents do not exist at the target Destination, this transaction will fail."].join(" ")}const F=function(){return{position:"sticky",top:0,zIndex:100,backgroundColor:!(arguments.length>0&&void 0!==arguments[0])||arguments[0]?"rgba(10,10,10,0.95)":"rgba(255,255,255,0.95)"}};async function Q(e){const{fetchIds:t,client:n,pluginConfig:i,currentIds:o,projection:r}=e,a=[],l=["_id in $fetchIds",i.filter].filter(Boolean).join(" && "),d="*[".concat(l,"]").concat(null!=r?r:""),c=await n.fetch(d,{fetchIds:null!=t?t:[]});if(!(null==c?void 0:c.length))return[];const s=null!=o?o:new Set,u=new Set(c.map((e=>e._id)).filter((e=>(null==o?void 0:o.size)?!s.has(e):Boolean(e))));u.size&&(a.push(...c),s.add(...u),await Promise.all(c.map((async e=>{const t=V(".._ref",e).map((e=>e.value));if(t.length){const e=new Set(t.filter((e=>!s.has(e))));if(e.size){const t=await Q({fetchIds:Array.from(e),currentIds:s,client:n,pluginConfig:i});(null==t?void 0:t.length)&&a.push(...t)}}}))));return a.filter(Boolean).reduce(((e,t)=>e.some((e=>e._id===t._id))?e:[...e,t]),[])}const $=["All","None",null,"New","Existing","Older",null,"Documents","Assets"];function M(e){const{payload:t,setPayload:n}=e,[i,o]=f([]);return g((()=>{!(null==i?void 0:i.length)&&t.every((e=>e.include))&&o(["ALL"])}),[null==i?void 0:i.length,t]),s(T,{padding:1,radius:3,shadow:1,children:s(S,{gap:2,children:$.map(((e,r)=>e?s(A,{fontSize:1,mode:"bleed",padding:2,text:e,disabled:i.includes(e.toUpperCase()),onClick:()=>function(e){if(!e||!t.length)return;const i=[...t];switch(e){case"ALL":i.map((e=>e.include=!0));break;case"NONE":i.map((e=>e.include=!1));break;case"NEW":i.map((e=>e.include=Boolean("CREATE"===e.status)));break;case"EXISTING":i.map((e=>e.include=Boolean("EXISTS"===e.status)));break;case"OLDER":i.map((e=>e.include=Boolean("OVERWRITE"===e.status)));break;case"ASSETS":i.map((e=>e.include=q(e.doc._id)));break;case"DOCUMENTS":i.map((e=>e.include=!q(e.doc._id)))}o([e]),n(i)}(e.toUpperCase())},e):s(T,{borderLeft:!0},"divider-".concat(r))))})})}const Y={EXISTS:"primary",OVERWRITE:"critical",UPDATE:"caution",CREATE:"positive",UNPUBLISHED:"caution"},J={EXISTS:"critical",OVERWRITE:"critical",UPDATE:"critical",CREATE:"positive",UNPUBLISHED:"default"},K={EXISTS:"This document already exists at the Destination with the same ID with the same Updated time.",OVERWRITE:"A newer version of this document exists at the Destination, and it will be overwritten with this version.",UPDATE:"An older version of this document exists at the Destination, and it will be overwritten with this version.",CREATE:"This document will be created at the destination.",UNPUBLISHED:"A Draft version of this Document exists in this Dataset, but only the Published version will be duplicated to the destination."},Z={EXISTS:"This Asset already exists at the Destination",OVERWRITE:"This Asset already exists at the Destination",UPDATE:"This Asset already exists at the Destination",CREATE:"This Asset does not yet exist at the Destination",UNPUBLISHED:""},ee={EXISTS:"RE-UPLOAD",OVERWRITE:"RE-UPLOAD",UPDATE:"RE-UPLOAD",CREATE:"UPLOAD",UNPUBLISHED:""};function te(e){const{status:t,isAsset:n}=e;if(!t)return null;const i=n?J[t]:Y[t];if(!i)return s(I,{muted:!0,padding:2,fontSize:1,mode:"outline",children:"Checking..."});const o=n?Z[t]:K[t],r=n?ee[t]:t;return s(O,{content:s(_,{padding:3,style:{maxWidth:200},children:s(C,{size:1,children:o})}),fallbackPlacements:["right","left"],placement:"top",portal:!0,children:s(I,{muted:!0,padding:2,fontSize:1,tone:i,mode:"outline",children:r})})}function ne(e){const{children:t,tone:n="caution"}=e;return s(T,{padding:3,radius:2,shadow:1,tone:n,children:s(C,{size:1,children:t})})}const ie={apiVersion:"2021-05-19"};function oe(e){var t,n,o;const{docs:c,token:m,pluginConfig:b}=e,D=x().sanity.color.dark,E=r(ie),I=a(),O=l(),z=O.map((e=>i(i({},e),{},{disabled:e.dataset===E.config().dataset}))),[B,N]=f(O.length&&null!=(t=z.find((e=>!e.disabled)))?t:null),[$,Y]=f(null),[J,K]=f([]),[Z,ee]=f(!1),[oe,re]=f(!1),[ae,le]=f(!1),[de,ce]=f([0,0]);async function se(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];const t=e.length?e:J;if(!t.length||!(null==B?void 0:B.name))return;const n=t.map((e=>{let{doc:t}=e;return t._id})),o=E.withConfig(i(i({},ie),{},{dataset:B.dataset,projectId:B.projectId})),r=await o.fetch("*[_id in $payloadIds]{ _id, _updatedAt }",{payloadIds:n}),a=t.map((e=>{var t;const n=r.find((t=>t._id===e.doc._id));return(null==n?void 0:n._updatedAt)&&(null==(t=null==e?void 0:e.doc)?void 0:t._updatedAt)?n._updatedAt===e.doc._updatedAt?e.status="EXISTS":n._updatedAt&&e.doc._updatedAt&&(e.status=new Date(n._updatedAt)>new Date(e.doc._updatedAt)?"OVERWRITE":"UPDATE"):e.status="CREATE",e}));K(a)}g((()=>{const e=[],t=[];c.forEach((n=>{const i=V(".._ref",n).map((e=>e.value));e.push(...i),t.push({include:!0,doc:n})})),K(t);const n=c.length,i=e.length;e.length&&(ee(!0),Y({tone:"caution",text:G(n,i)}))}),[c]),g((()=>{se()}),[B]);const ue=J.length,he=J.findIndex((e=>{let{doc:t}=e;return"svg"===t.extension})),pe=J.filter((e=>e.include&&!q(e.doc._id))).length,fe=J.filter((e=>e.include&&q(e.doc._id))).length,ge=pe+fe,me=null!=(n=null==B?void 0:B.title)?n:null==B?void 0:B.name,be=new Set(z.map((e=>null==e?void 0:e.projectId)).filter(Boolean)).size>1,ye=[ge,"/",ue,"Documents and Assets selected"].join(" "),ve=p.useMemo((()=>{const e=["Duplicate"];return pe>1&&e.push(String(pe),1===pe?"Document":"Documents"),fe>1&&e.push("and",String(fe),1===fe?"Asset":"Assets"),E.config().projectId!==(null==B?void 0:B.projectId)&&e.push("between Projects"),e.push("to",String(me)),e.join(" ")}),[pe,fe,E,null==B?void 0:B.projectId,me]);return z.length<2?u(ne,{tone:"critical",children:[s("code",{children:"sanity.config.ts"})," must contain at least two Workspaces to use this plugin."]}):s(j,{width:1,children:s(T,{border:!0,children:s(k,{children:u(h,{children:[s(T,{borderBottom:!0,padding:4,style:F(D),children:u(k,{space:4,children:[u(S,{gap:3,children:[u(k,{style:{flex:1},space:3,children:[s(P,{children:"Duplicate from"}),s(R,{readOnly:!0,value:null==(o=z.find((e=>e.disabled)))?void 0:o.name,children:z.filter((e=>e.disabled)).map((e=>{var t;return u("option",{value:e.name,disabled:e.disabled,children:[null!=(t=e.title)?t:e.name,be?" (".concat(e.projectId,")"):""]},e.name)}))})]}),s(_,{padding:4,paddingTop:5,paddingBottom:0,children:s(C,{size:3,children:s(y,{})})}),u(k,{style:{flex:1},space:3,children:[s(P,{children:"To Destination"}),s(R,{onChange:function(e){if(!z.length)return;const t=z.find((t=>t.name===e.currentTarget.value));t&&N(t)},children:z.map((e=>{var t;return u("option",{value:e.name,disabled:e.disabled,children:[null!=(t=e.title)?t:e.name,be?" (".concat(e.projectId,")"):"",e.disabled?" (Current)":""]},e.name)}))})]})]}),oe&&s(T,{border:!0,radius:2,children:s(T,{style:{width:"100%",transform:"scaleX(".concat(de[0]/de[1],")"),transformOrigin:"left",transition:"transform .2s ease",boxSizing:"border-box"},padding:1,tone:"positive"})}),J.length>0&&u(h,{children:[s(P,{children:ye}),s(M,{payload:J,setPayload:K})]})]})}),$&&s(_,{paddingX:4,paddingTop:4,children:s(T,{padding:3,radius:2,shadow:1,tone:$.tone,children:s(C,{size:1,children:$.text})})}),J.length>0&&s(k,{padding:4,space:3,children:J.map(((e,t)=>{let{doc:n,include:i,status:o,hasDraft:r}=e;const a=I.get(n._type);return u(p.Fragment,{children:[u(S,{align:"center",children:[s(U,{checked:i,onChange:()=>function(e){const t=J.map((t=>(t.doc._id===e&&(t.include=!t.include),t)));K(t)}(n._id)}),s(_,{flex:1,paddingX:3,children:a?s(d,{value:n,schemaType:a}):s(T,{tone:"caution",children:"Invalid schema type"})}),u(S,{align:"center",gap:2,children:[r?s(te,{status:"UNPUBLISHED",isAsset:!1}):null,s(te,{status:o,isAsset:q(n._id)})]})]}),"svg"===(null==n?void 0:n.extension)&&t===he&&s(T,{padding:3,radius:2,shadow:1,tone:"caution",children:u(C,{size:1,children:["Due to how SVGs are sanitized after first uploaded, duplicated SVG assets may have new ",s("code",{children:"_id"}),"'s at the destination. The newly generated ",s("code",{children:"_id"})," will be the same in each duplication, but it will never be the same ",s("code",{children:"_id"})," as the first time this Asset was uploaded. References to the asset will be updated to use the new"," ",s("code",{children:"_id"}),"."]})})]},n._id)}))}),u(k,{space:2,padding:4,paddingTop:0,children:[Z&&s(A,{fontSize:2,padding:4,tone:"positive",mode:"ghost",icon:v,onClick:async function(){le(!0);const e=c.map((e=>e._id)),t=await Q({fetchIds:e,client:E,pluginConfig:b}),n=await Q({fetchIds:e.map((e=>"drafts.".concat(e))),client:E,projection:"{_id}",pluginConfig:b}),i=new Set(n.map((e=>{let{_id:t}=e;return t}))),o=t.map((e=>({doc:e,include:!0,status:void 0,hasDraft:i.has("drafts.".concat(e._id))})));K(o),se(o),le(!1)},text:"Gather References",disabled:oe||!ge||ae}),s(A,{fontSize:2,padding:4,tone:"positive",icon:w,onClick:async function(){if(!B)return;re(!0);const e=J.filter((e=>{let{doc:t,include:n}=e;return n&&q(t._id)})).length;let t=0;ce([t,e]),Y({text:"Duplicating...",tone:"default"});const n=E.withConfig(i(i({},ie),{},{dataset:B.dataset,projectId:B.projectId})),o=[],r=[];async function a(i){if(q(i._id)){const a=H(i),l=a?i.url:"".concat(i.url,"?dlRaw=true"),d=a?{}:{headers:{Authorization:"Bearer ".concat(m)}};return await fetch(l,d).then((async e=>{const t=await e.blob(),l={filename:i.originalFilename},d=await n.assets.upload(a?"file":"image",t,l);"svg"===(null==i?void 0:i.extension)&&r.push({old:i._id,new:d._id}),o.push(d)})),t+=1,Y({text:"Duplicating ".concat(t,"/").concat(e," ").concat("Assets"),tone:"default"}),ce([t,e])}return o.push(i)}const l=new Promise(((e,t)=>{const n=J.filter((e=>e.include)).map((e=>e.doc));L(n,3,X(a),(n=>{n&&(re(!1),Y({tone:"critical",text:"Duplication Failed"}),console.error(n),t(new Error("Duplication Failed"))),e()}))}));await l;const d=o.map((e=>{const t=V(".._ref",e);return t.length?(t.forEach((t=>{var n;const i=null==(n=r.find((e=>e.old===t.value)))?void 0:n.new;if(i){const n=t.path.join(".");W(e,n,i)}})),e):e})),c=n.transaction();d.forEach((e=>{c.createOrReplace(e)})),await c.commit().then((e=>{Y({tone:"positive",text:"Duplication complete!"}),se()})).catch((e=>{Y({tone:"critical",text:e.details.description})})),re(!1),ce([0,0])},text:ve,disabled:oe||!ge||ae})]})]})})})})}function re(e){var t,n;const{token:i,pluginConfig:o}=e,l=r(ie),d=a().getTypeNames(),[c,h]=f(""),[p,m]=f({docs:[]});function b(e){e&&e.preventDefault(),l.fetch(c).then((e=>{const t=e.length?e.filter((e=>d.includes(e._type))).filter((e=>!e._id.startsWith("drafts."))):[];m({docs:t})})).catch((e=>console.error(e)))}return g((()=>{var e;!(null==(e=p.docs)?void 0:e.length)&&c&&b()}),[]),s(j,{width:[1,1,1,3],padding:[0,0,0,5],children:u(z,{columns:[1,1,1,2],gap:[1,1,1,4],children:[s(_,{padding:[2,2,2,0],children:s(T,{padding:4,radius:3,border:!0,children:u(k,{space:4,children:[s(_,{children:s(P,{children:"Initial Documents Query"})}),s(_,{children:s(C,{children:"Start with a valid GROQ query to load initial documents. The query will need to return an Array of Objects. Drafts will be removed from the results."})}),s("form",{onSubmit:b,children:u(S,{children:[s(_,{flex:1,paddingRight:2,children:s(B,{style:{fontFamily:"monospace"},fontSize:2,onChange:e=>h(e.currentTarget.value),padding:4,placeholder:'*[_type == "article"]',value:null!=c?c:""})}),s(A,{padding:2,paddingX:4,tone:"primary",onClick:b,text:"Query",disabled:!c})]})})]})})}),!(null==(t=p.docs)?void 0:t.length)||p.docs.length<1&&s(j,{width:1,children:s(T,{padding:5,children:c?"No Documents registered to the Schema match this query":"Start with a valid GROQ query"})}),(null==(n=p.docs)?void 0:n.length)>0&&s(oe,{docs:p.docs,token:i,pluginConfig:o})]})})}function ae(e){const{docs:t,token:n,pluginConfig:i}=e,[o,a]=f([]),{follow:l=[]}=i,[d,c]=f(1===l.length?l[0]:"outbound"),h=r();return g((()=>{(async()=>{if(l.includes("inbound")){const n=await h.fetch("*[references($id)]",{id:t[0]._id});a([...e.docs,...n])}})()}),[]),u(j,{children:[l.length>1&&(l.includes("inbound")||l.includes("outbound"))?s(T,{paddingX:4,paddingBottom:4,marginBottom:4,borderBottom:!0,children:u(z,{columns:2,gap:4,children:[l.includes("outbound")?s(A,{mode:"ghost",tone:"primary",selected:"outbound"===d,onClick:()=>c("outbound"),text:"Outbound"}):null,l.includes("inbound")?s(A,{mode:"ghost",tone:"primary",selected:"inbound"===d,onClick:()=>c("inbound"),disabled:0===o.length,text:o.length>0?"Inbound (".concat(o.length,")"):"No inbound references"}):null]})}):null,s(oe,{docs:"outbound"===d?t:o,token:n,pluginConfig:i})]})}const le="CrossDatasetDuplicator",de={tool:!0,types:[],filter:"",follow:["outbound"]};function ce(){const e=r(ie),t=p.useCallback((()=>{e.delete({query:'*[_id == "secrets.'.concat(le,'"]')})}),[e]);return s(S,{align:"center",justify:"flex-end",paddingX:[2,2,2,5],paddingY:5,children:s(A,{text:"Reset Secret",onClick:t,mode:"ghost",tone:"critical",fontSize:1,padding:2})})}const se=m(de);function ue(){return b(se)}const he=[{key:"bearerToken",title:"An API token with Viewer permissions is required to duplicate the original files of assets, and will be used for all Duplications. Create one at sanity.io/manage",description:""}];function pe(e){const{mode:t="tool",docs:n=[]}=null!=e?e:{},i=ue(),{loading:o,secrets:r}=D(le),[a,l]=f(!1);return g((()=>{r&&l(!(null==r?void 0:r.bearerToken))}),[r]),o?s(S,{justify:"center",align:"center",children:s(_,{padding:5,children:s(N,{})})}):!o&&a||!(null==r?void 0:r.bearerToken)?s(E,{title:"Token Required",namespace:le,keys:he,onClose:()=>l(!1)}):"tool"===t&&i?u(h,{children:[s(re,{token:null==r?void 0:r.bearerToken,pluginConfig:i}),s(ce,{})]}):(null==n?void 0:n.length)?i?s(ae,{docs:n,token:null==r?void 0:r.bearerToken,pluginConfig:i}):s(ne,{children:"No plugin config"}):s(ne,{children:"No docs passed into Duplicator Tool"})}function fe(e){const{docs:t=[]}=e;return s(pe,{mode:"action",docs:t})}const ge=e=>{const{draft:t,published:n,onComplete:i}=e,[o,r]=f(!1);return{disabled:t,title:t?"Document must be Published to begin":null,label:"Duplicate to...",dialog:o&&n&&{type:"modal",title:"Cross Dataset Duplicator",content:s(fe,{docs:[n]}),onClose:()=>{i(),r(!1)}},onHandle:()=>r(!0),icon:w}};function me(e){var t;const{docs:n=[]}=null!=(t=e.tool.options)?t:{};return s(pe,{mode:"tool",docs:n})}ge.action="duplicateTo";const be=c((function(){let n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const o=i(i({},de),n),{types:r}=o;return{name:"@sanity/cross-dataset-duplicator",tools:e=>o.tool?[...e,{title:"Duplicator",name:"duplicator",icon:w,component:me,options:{docs:[]}}]:e,studio:{components:{layout:n=>function(n){const{pluginConfig:i}=n,o=t(n,e);return s(se.Provider,{value:i,children:n.renderDefault(o)})}(i(i({},n),{},{pluginConfig:o}))}},document:{actions:(e,t)=>{let{schemaType:n}=t;return r&&r.includes(n)?[...e,ge]:e}}}}));export{fe as CrossDatasetDuplicatorAction,ge as DuplicateToAction,be as crossDatasetDuplicator,ue as useCrossDatasetDuplicatorConfig};//# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/lib/index.js CHANGED
@@ -1,21 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- Object.defineProperty(exports, "CrossDatasetDuplicator", {
7
- enumerable: true,
8
- get: function get() {
9
- return _CrossDatasetDuplicator.default;
10
- }
11
- });
12
- Object.defineProperty(exports, "DuplicateToAction", {
13
- enumerable: true,
14
- get: function get() {
15
- return _DuplicateToAction.default;
16
- }
17
- });
18
- var _DuplicateToAction = _interopRequireDefault(require("./actions/DuplicateToAction"));
19
- var _CrossDatasetDuplicator = _interopRequireDefault(require("./components/CrossDatasetDuplicator"));
20
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
21
- //# sourceMappingURL=index.js.map
1
+ "use strict";const e=["pluginConfig"];function t(e,t){if(null==e)return{};var n,i,o=function(e,t){if(null==e)return{};var n,i,o={},s=Object.keys(e);for(i=0;i<s.length;i++)n=s[i],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(i=0;i<s.length;i++)n=s[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function n(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function i(e){for(var t=1;t<arguments.length;t++){var i=null!=arguments[t]?arguments[t]:{};t%2?n(Object(i),!0).forEach((function(t){o(e,t,i[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(i)):n(Object(i)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(i,t))}))}return e}function o(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var i=n.call(e,t||"default");if("object"!=typeof i)return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}Object.defineProperty(exports,"__esModule",{value:!0});var s=require("sanity"),a=require("react/jsx-runtime"),r=require("react"),d=require("@sanity/icons"),l=require("@sanity/studio-secrets"),c=require("@sanity/ui"),u=require("async/mapLimit"),h=require("async/asyncify"),p=require("@sanity/mutator"),f=require("dset"),g=require("@sanity/asset-utils");function x(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var m=x(r),j=x(u),b=x(h);function y(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return[1===e?"This Document contains":"These ".concat(e," Documents contain"),1===t?"1 Reference.":"".concat(t," References."),1===t?"That Document":"Those Documents","may have References too. If referenced Documents do not exist at the target Destination, this transaction will fail."].join(" ")}const v=function(){return{position:"sticky",top:0,zIndex:100,backgroundColor:!(arguments.length>0&&void 0!==arguments[0])||arguments[0]?"rgba(10,10,10,0.95)":"rgba(255,255,255,0.95)"}};async function S(e){const{fetchIds:t,client:n,pluginConfig:i,currentIds:o,projection:s}=e,a=[],r=["_id in $fetchIds",i.filter].filter(Boolean).join(" && "),d="*[".concat(r,"]").concat(null!=s?s:""),l=await n.fetch(d,{fetchIds:null!=t?t:[]});if(!(null==l?void 0:l.length))return[];const c=null!=o?o:new Set,u=new Set(l.map((e=>e._id)).filter((e=>(null==o?void 0:o.size)?!c.has(e):Boolean(e))));u.size&&(a.push(...l),c.add(...u),await Promise.all(l.map((async e=>{const t=p.extractWithPath(".._ref",e).map((e=>e.value));if(t.length){const e=new Set(t.filter((e=>!c.has(e))));if(e.size){const t=await S({fetchIds:Array.from(e),currentIds:c,client:n,pluginConfig:i});(null==t?void 0:t.length)&&a.push(...t)}}}))));return a.filter(Boolean).reduce(((e,t)=>e.some((e=>e._id===t._id))?e:[...e,t]),[])}const w=["All","None",null,"New","Existing","Older",null,"Documents","Assets"];function D(e){const{payload:t,setPayload:n}=e,[i,o]=r.useState([]);return r.useEffect((()=>{!(null==i?void 0:i.length)&&t.every((e=>e.include))&&o(["ALL"])}),[null==i?void 0:i.length,t]),a.jsx(c.Card,{padding:1,radius:3,shadow:1,children:a.jsx(c.Flex,{gap:2,children:w.map(((e,s)=>e?a.jsx(c.Button,{fontSize:1,mode:"bleed",padding:2,text:e,disabled:i.includes(e.toUpperCase()),onClick:()=>function(e){if(!e||!t.length)return;const i=[...t];switch(e){case"ALL":i.map((e=>e.include=!0));break;case"NONE":i.map((e=>e.include=!1));break;case"NEW":i.map((e=>e.include=Boolean("CREATE"===e.status)));break;case"EXISTING":i.map((e=>e.include=Boolean("EXISTS"===e.status)));break;case"OLDER":i.map((e=>e.include=Boolean("OVERWRITE"===e.status)));break;case"ASSETS":i.map((e=>e.include=g.isAssetId(e.doc._id)));break;case"DOCUMENTS":i.map((e=>e.include=!g.isAssetId(e.doc._id)))}o([e]),n(i)}(e.toUpperCase())},e):a.jsx(c.Card,{borderLeft:!0},"divider-".concat(s))))})})}const C={EXISTS:"primary",OVERWRITE:"critical",UPDATE:"caution",CREATE:"positive",UNPUBLISHED:"caution"},E={EXISTS:"critical",OVERWRITE:"critical",UPDATE:"critical",CREATE:"positive",UNPUBLISHED:"default"},T={EXISTS:"This document already exists at the Destination with the same ID with the same Updated time.",OVERWRITE:"A newer version of this document exists at the Destination, and it will be overwritten with this version.",UPDATE:"An older version of this document exists at the Destination, and it will be overwritten with this version.",CREATE:"This document will be created at the destination.",UNPUBLISHED:"A Draft version of this Document exists in this Dataset, but only the Published version will be duplicated to the destination."},I={EXISTS:"This Asset already exists at the Destination",OVERWRITE:"This Asset already exists at the Destination",UPDATE:"This Asset already exists at the Destination",CREATE:"This Asset does not yet exist at the Destination",UNPUBLISHED:""},A={EXISTS:"RE-UPLOAD",OVERWRITE:"RE-UPLOAD",UPDATE:"RE-UPLOAD",CREATE:"UPLOAD",UNPUBLISHED:""};function O(e){const{status:t,isAsset:n}=e;if(!t)return null;const i=n?E[t]:C[t];if(!i)return a.jsx(c.Badge,{muted:!0,padding:2,fontSize:1,mode:"outline",children:"Checking..."});const o=n?I[t]:T[t],s=n?A[t]:t;return a.jsx(c.Tooltip,{content:a.jsx(c.Box,{padding:3,style:{maxWidth:200},children:a.jsx(c.Text,{size:1,children:o})}),fallbackPlacements:["right","left"],placement:"top",portal:!0,children:a.jsx(c.Badge,{muted:!0,padding:2,fontSize:1,tone:i,mode:"outline",children:s})})}function _(e){const{children:t,tone:n="caution"}=e;return a.jsx(c.Card,{padding:3,radius:2,shadow:1,tone:n,children:a.jsx(c.Text,{size:1,children:t})})}const k={apiVersion:"2021-05-19"};function P(e){var t,n,o;const{docs:l,token:u,pluginConfig:h}=e,x=c.useTheme().sanity.color.dark,w=s.useClient(k),C=s.useSchema(),E=s.useWorkspaces(),T=E.map((e=>i(i({},e),{},{disabled:e.dataset===w.config().dataset}))),[I,A]=r.useState(E.length&&null!=(t=T.find((e=>!e.disabled)))?t:null),[P,R]=r.useState(null),[B,U]=r.useState([]),[L,z]=r.useState(!1),[N,q]=r.useState(!1),[F,W]=r.useState(!1),[X,V]=r.useState([0,0]);async function G(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];const t=e.length?e:B;if(!t.length||!(null==I?void 0:I.name))return;const n=t.map((e=>{let{doc:t}=e;return t._id})),o=w.withConfig(i(i({},k),{},{dataset:I.dataset,projectId:I.projectId})),s=await o.fetch("*[_id in $payloadIds]{ _id, _updatedAt }",{payloadIds:n}),a=t.map((e=>{var t;const n=s.find((t=>t._id===e.doc._id));return(null==n?void 0:n._updatedAt)&&(null==(t=null==e?void 0:e.doc)?void 0:t._updatedAt)?n._updatedAt===e.doc._updatedAt?e.status="EXISTS":n._updatedAt&&e.doc._updatedAt&&(e.status=new Date(n._updatedAt)>new Date(e.doc._updatedAt)?"OVERWRITE":"UPDATE"):e.status="CREATE",e}));U(a)}r.useEffect((()=>{const e=[],t=[];l.forEach((n=>{const i=p.extractWithPath(".._ref",n).map((e=>e.value));e.push(...i),t.push({include:!0,doc:n})})),U(t);const n=l.length,i=e.length;e.length&&(z(!0),R({tone:"caution",text:y(n,i)}))}),[l]),r.useEffect((()=>{G()}),[I]);const H=B.length,Q=B.findIndex((e=>{let{doc:t}=e;return"svg"===t.extension})),M=B.filter((e=>e.include&&!g.isAssetId(e.doc._id))).length,$=B.filter((e=>e.include&&g.isAssetId(e.doc._id))).length,Y=M+$,J=null!=(n=null==I?void 0:I.title)?n:null==I?void 0:I.name,K=new Set(T.map((e=>null==e?void 0:e.projectId)).filter(Boolean)).size>1,Z=[Y,"/",H,"Documents and Assets selected"].join(" "),ee=m.default.useMemo((()=>{const e=["Duplicate"];return M>1&&e.push(String(M),1===M?"Document":"Documents"),$>1&&e.push("and",String($),1===$?"Asset":"Assets"),w.config().projectId!==(null==I?void 0:I.projectId)&&e.push("between Projects"),e.push("to",String(J)),e.join(" ")}),[M,$,w,null==I?void 0:I.projectId,J]);return T.length<2?a.jsxs(_,{tone:"critical",children:[a.jsx("code",{children:"sanity.config.ts"})," must contain at least two Workspaces to use this plugin."]}):a.jsx(c.Container,{width:1,children:a.jsx(c.Card,{border:!0,children:a.jsx(c.Stack,{children:a.jsxs(a.Fragment,{children:[a.jsx(c.Card,{borderBottom:!0,padding:4,style:v(x),children:a.jsxs(c.Stack,{space:4,children:[a.jsxs(c.Flex,{gap:3,children:[a.jsxs(c.Stack,{style:{flex:1},space:3,children:[a.jsx(c.Label,{children:"Duplicate from"}),a.jsx(c.Select,{readOnly:!0,value:null==(o=T.find((e=>e.disabled)))?void 0:o.name,children:T.filter((e=>e.disabled)).map((e=>{var t;return a.jsxs("option",{value:e.name,disabled:e.disabled,children:[null!=(t=e.title)?t:e.name,K?" (".concat(e.projectId,")"):""]},e.name)}))})]}),a.jsx(c.Box,{padding:4,paddingTop:5,paddingBottom:0,children:a.jsx(c.Text,{size:3,children:a.jsx(d.ArrowRightIcon,{})})}),a.jsxs(c.Stack,{style:{flex:1},space:3,children:[a.jsx(c.Label,{children:"To Destination"}),a.jsx(c.Select,{onChange:function(e){if(!T.length)return;const t=T.find((t=>t.name===e.currentTarget.value));t&&A(t)},children:T.map((e=>{var t;return a.jsxs("option",{value:e.name,disabled:e.disabled,children:[null!=(t=e.title)?t:e.name,K?" (".concat(e.projectId,")"):"",e.disabled?" (Current)":""]},e.name)}))})]})]}),N&&a.jsx(c.Card,{border:!0,radius:2,children:a.jsx(c.Card,{style:{width:"100%",transform:"scaleX(".concat(X[0]/X[1],")"),transformOrigin:"left",transition:"transform .2s ease",boxSizing:"border-box"},padding:1,tone:"positive"})}),B.length>0&&a.jsxs(a.Fragment,{children:[a.jsx(c.Label,{children:Z}),a.jsx(D,{payload:B,setPayload:U})]})]})}),P&&a.jsx(c.Box,{paddingX:4,paddingTop:4,children:a.jsx(c.Card,{padding:3,radius:2,shadow:1,tone:P.tone,children:a.jsx(c.Text,{size:1,children:P.text})})}),B.length>0&&a.jsx(c.Stack,{padding:4,space:3,children:B.map(((e,t)=>{let{doc:n,include:i,status:o,hasDraft:r}=e;const d=C.get(n._type);return a.jsxs(m.default.Fragment,{children:[a.jsxs(c.Flex,{align:"center",children:[a.jsx(c.Checkbox,{checked:i,onChange:()=>function(e){const t=B.map((t=>(t.doc._id===e&&(t.include=!t.include),t)));U(t)}(n._id)}),a.jsx(c.Box,{flex:1,paddingX:3,children:d?a.jsx(s.Preview,{value:n,schemaType:d}):a.jsx(c.Card,{tone:"caution",children:"Invalid schema type"})}),a.jsxs(c.Flex,{align:"center",gap:2,children:[r?a.jsx(O,{status:"UNPUBLISHED",isAsset:!1}):null,a.jsx(O,{status:o,isAsset:g.isAssetId(n._id)})]})]}),"svg"===(null==n?void 0:n.extension)&&t===Q&&a.jsx(c.Card,{padding:3,radius:2,shadow:1,tone:"caution",children:a.jsxs(c.Text,{size:1,children:["Due to how SVGs are sanitized after first uploaded, duplicated SVG assets may have new ",a.jsx("code",{children:"_id"}),"'s at the destination. The newly generated ",a.jsx("code",{children:"_id"})," will be the same in each duplication, but it will never be the same ",a.jsx("code",{children:"_id"})," as the first time this Asset was uploaded. References to the asset will be updated to use the new"," ",a.jsx("code",{children:"_id"}),"."]})})]},n._id)}))}),a.jsxs(c.Stack,{space:2,padding:4,paddingTop:0,children:[L&&a.jsx(c.Button,{fontSize:2,padding:4,tone:"positive",mode:"ghost",icon:d.SearchIcon,onClick:async function(){W(!0);const e=l.map((e=>e._id)),t=await S({fetchIds:e,client:w,pluginConfig:h}),n=await S({fetchIds:e.map((e=>"drafts.".concat(e))),client:w,projection:"{_id}",pluginConfig:h}),i=new Set(n.map((e=>{let{_id:t}=e;return t}))),o=t.map((e=>({doc:e,include:!0,status:void 0,hasDraft:i.has("drafts.".concat(e._id))})));U(o),G(o),W(!1)},text:"Gather References",disabled:N||!Y||F}),a.jsx(c.Button,{fontSize:2,padding:4,tone:"positive",icon:d.LaunchIcon,onClick:async function(){if(!I)return;q(!0);const e=B.filter((e=>{let{doc:t,include:n}=e;return n&&g.isAssetId(t._id)})).length;let t=0;V([t,e]),R({text:"Duplicating...",tone:"default"});const n=w.withConfig(i(i({},k),{},{dataset:I.dataset,projectId:I.projectId})),o=[],s=[];async function a(i){if(g.isAssetId(i._id)){const a=g.isSanityFileAsset(i),r=a?i.url:"".concat(i.url,"?dlRaw=true"),d=a?{}:{headers:{Authorization:"Bearer ".concat(u)}};return await fetch(r,d).then((async e=>{const t=await e.blob(),r={filename:i.originalFilename},d=await n.assets.upload(a?"file":"image",t,r);"svg"===(null==i?void 0:i.extension)&&s.push({old:i._id,new:d._id}),o.push(d)})),t+=1,R({text:"Duplicating ".concat(t,"/").concat(e," ").concat("Assets"),tone:"default"}),V([t,e])}return o.push(i)}const r=new Promise(((e,t)=>{const n=B.filter((e=>e.include)).map((e=>e.doc));j.default(n,3,b.default(a),(n=>{n&&(q(!1),R({tone:"critical",text:"Duplication Failed"}),console.error(n),t(new Error("Duplication Failed"))),e()}))}));await r;const d=o.map((e=>{const t=p.extractWithPath(".._ref",e);return t.length?(t.forEach((t=>{var n;const i=null==(n=s.find((e=>e.old===t.value)))?void 0:n.new;if(i){const n=t.path.join(".");f.dset(e,n,i)}})),e):e})),l=n.transaction();d.forEach((e=>{l.createOrReplace(e)})),await l.commit().then((e=>{R({tone:"positive",text:"Duplication complete!"}),G()})).catch((e=>{R({tone:"critical",text:e.details.description})})),q(!1),V([0,0])},text:ee,disabled:N||!Y||F})]})]})})})})}function R(e){var t,n;const{token:i,pluginConfig:o}=e,d=s.useClient(k),l=s.useSchema().getTypeNames(),[u,h]=r.useState(""),[p,f]=r.useState({docs:[]});function g(e){e&&e.preventDefault(),d.fetch(u).then((e=>{const t=e.length?e.filter((e=>l.includes(e._type))).filter((e=>!e._id.startsWith("drafts."))):[];f({docs:t})})).catch((e=>console.error(e)))}return r.useEffect((()=>{var e;!(null==(e=p.docs)?void 0:e.length)&&u&&g()}),[]),a.jsx(c.Container,{width:[1,1,1,3],padding:[0,0,0,5],children:a.jsxs(c.Grid,{columns:[1,1,1,2],gap:[1,1,1,4],children:[a.jsx(c.Box,{padding:[2,2,2,0],children:a.jsx(c.Card,{padding:4,radius:3,border:!0,children:a.jsxs(c.Stack,{space:4,children:[a.jsx(c.Box,{children:a.jsx(c.Label,{children:"Initial Documents Query"})}),a.jsx(c.Box,{children:a.jsx(c.Text,{children:"Start with a valid GROQ query to load initial documents. The query will need to return an Array of Objects. Drafts will be removed from the results."})}),a.jsx("form",{onSubmit:g,children:a.jsxs(c.Flex,{children:[a.jsx(c.Box,{flex:1,paddingRight:2,children:a.jsx(c.TextInput,{style:{fontFamily:"monospace"},fontSize:2,onChange:e=>h(e.currentTarget.value),padding:4,placeholder:'*[_type == "article"]',value:null!=u?u:""})}),a.jsx(c.Button,{padding:2,paddingX:4,tone:"primary",onClick:g,text:"Query",disabled:!u})]})})]})})}),!(null==(t=p.docs)?void 0:t.length)||p.docs.length<1&&a.jsx(c.Container,{width:1,children:a.jsx(c.Card,{padding:5,children:u?"No Documents registered to the Schema match this query":"Start with a valid GROQ query"})}),(null==(n=p.docs)?void 0:n.length)>0&&a.jsx(P,{docs:p.docs,token:i,pluginConfig:o})]})})}function B(e){const{docs:t,token:n,pluginConfig:i}=e,[o,d]=r.useState([]),{follow:l=[]}=i,[u,h]=r.useState(1===l.length?l[0]:"outbound"),p=s.useClient();return r.useEffect((()=>{(async()=>{if(l.includes("inbound")){const n=await p.fetch("*[references($id)]",{id:t[0]._id});d([...e.docs,...n])}})()}),[]),a.jsxs(c.Container,{children:[l.length>1&&(l.includes("inbound")||l.includes("outbound"))?a.jsx(c.Card,{paddingX:4,paddingBottom:4,marginBottom:4,borderBottom:!0,children:a.jsxs(c.Grid,{columns:2,gap:4,children:[l.includes("outbound")?a.jsx(c.Button,{mode:"ghost",tone:"primary",selected:"outbound"===u,onClick:()=>h("outbound"),text:"Outbound"}):null,l.includes("inbound")?a.jsx(c.Button,{mode:"ghost",tone:"primary",selected:"inbound"===u,onClick:()=>h("inbound"),disabled:0===o.length,text:o.length>0?"Inbound (".concat(o.length,")"):"No inbound references"}):null]})}):null,a.jsx(P,{docs:"outbound"===u?t:o,token:n,pluginConfig:i})]})}const U="CrossDatasetDuplicator",L={tool:!0,types:[],filter:"",follow:["outbound"]};function z(){const e=s.useClient(k),t=m.default.useCallback((()=>{e.delete({query:'*[_id == "secrets.'.concat(U,'"]')})}),[e]);return a.jsx(c.Flex,{align:"center",justify:"flex-end",paddingX:[2,2,2,5],paddingY:5,children:a.jsx(c.Button,{text:"Reset Secret",onClick:t,mode:"ghost",tone:"critical",fontSize:1,padding:2})})}const N=r.createContext(L);function q(){return r.useContext(N)}const F=[{key:"bearerToken",title:"An API token with Viewer permissions is required to duplicate the original files of assets, and will be used for all Duplications. Create one at sanity.io/manage",description:""}];function W(e){const{mode:t="tool",docs:n=[]}=null!=e?e:{},i=q(),{loading:o,secrets:s}=l.useSecrets(U),[d,u]=r.useState(!1);return r.useEffect((()=>{s&&u(!(null==s?void 0:s.bearerToken))}),[s]),o?a.jsx(c.Flex,{justify:"center",align:"center",children:a.jsx(c.Box,{padding:5,children:a.jsx(c.Spinner,{})})}):!o&&d||!(null==s?void 0:s.bearerToken)?a.jsx(l.SettingsView,{title:"Token Required",namespace:U,keys:F,onClose:()=>u(!1)}):"tool"===t&&i?a.jsxs(a.Fragment,{children:[a.jsx(R,{token:null==s?void 0:s.bearerToken,pluginConfig:i}),a.jsx(z,{})]}):(null==n?void 0:n.length)?i?a.jsx(B,{docs:n,token:null==s?void 0:s.bearerToken,pluginConfig:i}):a.jsx(_,{children:"No plugin config"}):a.jsx(_,{children:"No docs passed into Duplicator Tool"})}function X(e){const{docs:t=[]}=e;return a.jsx(W,{mode:"action",docs:t})}const V=e=>{const{draft:t,published:n,onComplete:i}=e,[o,s]=r.useState(!1);return{disabled:t,title:t?"Document must be Published to begin":null,label:"Duplicate to...",dialog:o&&n&&{type:"modal",title:"Cross Dataset Duplicator",content:a.jsx(X,{docs:[n]}),onClose:()=>{i(),s(!1)}},onHandle:()=>s(!0),icon:d.LaunchIcon}};function G(e){var t;const{docs:n=[]}=null!=(t=e.tool.options)?t:{};return a.jsx(W,{mode:"tool",docs:n})}V.action="duplicateTo";const H=s.definePlugin((function(){let n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const o=i(i({},L),n),{types:s}=o;return{name:"@sanity/cross-dataset-duplicator",tools:e=>o.tool?[...e,{title:"Duplicator",name:"duplicator",icon:d.LaunchIcon,component:G,options:{docs:[]}}]:e,studio:{components:{layout:n=>function(n){const{pluginConfig:i}=n,o=t(n,e);return a.jsx(N.Provider,{value:i,children:n.renderDefault(o)})}(i(i({},n),{},{pluginConfig:o}))}},document:{actions:(e,t)=>{let{schemaType:n}=t;return s&&s.includes(n)?[...e,V]:e}}}}));exports.CrossDatasetDuplicatorAction=X,exports.DuplicateToAction=V,exports.crossDatasetDuplicator=H,exports.useCrossDatasetDuplicatorConfig=q;//# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.js"],"sourcesContent":["import DuplicateToAction from \"./actions/DuplicateToAction\";\nimport CrossDatasetDuplicator from \"./components/CrossDatasetDuplicator\";\n\nexport {\n DuplicateToAction,\n CrossDatasetDuplicator\n}"],"mappings":";;;;;;;;;;;;;;;;;AAAA;AACA;AAAyE"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}