@sanity/cross-dataset-duplicator 1.0.0 → 1.2.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 Sanity.io
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
@@ -19,9 +19,9 @@ This plugin is designed as a convenience for Authors to make small, infrequent c
19
19
 
20
20
  - This plugin should be used in conjunction with a reliable backup strategy.
21
21
  - Proceed with caution as this plugin can instantly write changes to Datasets.
22
- - Larger migrations may take more time, especially with Assets. The plugin tries to mitigate this by 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.
23
23
  - If an Asset is already present at the destination, there's no need to duplicate it again.
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 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.
25
25
 
26
26
  ## Tool
27
27
 
@@ -35,17 +35,15 @@ The **Duplicate to...** Document Action allows you to migrate an individual Docu
35
35
 
36
36
  ![Cross Dataset Duplicator Action in Sanity Studio v3](./img/cdd-action.png)
37
37
 
38
- **Note:** If your Studio registered its own Document Actions, the plugin config will be overruled. See "Importing the Document Action" below.
39
-
40
38
  ## Required Setup
41
39
 
42
40
  ### 1. Workspaces
43
41
 
44
42
  You must have more than one [Workspace configured](https://www.sanity.io/docs/config-api-reference#37c85e3072b2) to use this plugin.
45
43
 
46
- All Datasets and Project ID's setup as Workspaces will become selectable "destinations" for Migrations.
44
+ All Datasets and Project IDs set up as Workspaces will become selectable "destinations" for Migrations.
47
45
 
48
- Once setup, you will see a dropdown menu next to the Search bar in the Studio with the Datasets you have configured.
46
+ Once set up, you will see a dropdown menu next to the Search bar in the Studio with the Datasets you have configured.
49
47
 
50
48
  ### 2. Configuration
51
49
 
@@ -73,13 +71,63 @@ The plugin has some configuration options. These can be set by adding a config f
73
71
  })
74
72
  ```
75
73
 
76
- Options:
74
+ #### Options:
77
75
 
78
76
  - `tool` (boolean, default: true) – Set whether the Migration **Tool** is enabled.
79
77
  - `types` (Array[String], default: []) – Set which Schema Types the Migration Action should be enabled in.
80
78
  - `filter` (String, default: undefined) - Set a predicate for documents when gathering dependencies.
81
79
  - `follow` (("inbound" | "outbound")[], default: []) – Add buttons to allow the user to begin with just the existing document or first fetch all inbound references.
82
80
 
81
+ #### Action Options
82
+
83
+ The Document Action has additional config options:
84
+
85
+ - `onDuplicated` (`() => Promise<void>`, default: undefined) - fire a callback after documents have been duplicated.
86
+
87
+ The `onDuplicated` callback could be used to update update metadata after documents have been synced, or to perform arbitrary cleanup tasks like closing the dialog:
88
+
89
+ ```tsx
90
+ const DuplicatorAction = ({ published, onComplete }: DocumentActionProps) => {
91
+ const [dialogOpen, setDialogOpen] = useState(false)
92
+ const [submitting, setSubmitting] = useState(false)
93
+ const [duplicated, setDuplicated] = useState(false)
94
+
95
+ return {
96
+ label: 'Duplicate',
97
+ title: 'Duplicate',
98
+ tone: 'positive',
99
+ disabled: submitting || duplicated,
100
+ loading: submitting,
101
+ icon: PublishIcon,
102
+ dialog: dialogOpen && published && {
103
+ type: 'popover',
104
+ title: 'Cross Dataset Duplicator',
105
+ content: (
106
+ <CrossDatasetDuplicatorAction
107
+ docs={[published]}
108
+ onDuplicated={async () => {
109
+ alert('data migrated')
110
+ await new Promise((resolve) => {
111
+ setTimeout(() => {
112
+ setDialogOpen(false);
113
+ setDuplicated(true)
114
+ resolve()
115
+ }, 1000)
116
+ })
117
+ }}
118
+ />
119
+ ),
120
+ onHandle: () => setDialogOpen(true),
121
+ onClose: () => {
122
+ onComplete()
123
+ setDialogOpen(false)
124
+ setSubmitting(false)
125
+ }
126
+ },
127
+ }
128
+ }
129
+ ```
130
+
83
131
  ### 3. Authentication Key
84
132
 
85
133
  To Duplicate the original files of Assets, an API Token with Viewer permissions is required. You will be prompted for this the first time you attempt to use either the Tool or Document Action on any Dataset.
@@ -115,4 +163,8 @@ on how to run this plugin with hotreload in the studio.
115
163
  Run ["CI & Release" workflow](https://github.com/sanity-io/cross-dataset-duplicator/actions/workflows/main.yml).
116
164
  Make sure to select the main branch and check "Release new version".
117
165
 
118
- Semantic release will only release on configured branches, so it is safe to run release on any branch.
166
+ Semantic release will only release on configured branches, so it is safe to run release on any branch.
167
+
168
+ ## License
169
+
170
+ [MIT](LICENSE) © Sanity.io
@@ -0,0 +1,551 @@
1
+ /// <reference types="react" />
2
+
3
+ import {DocumentActionProps} from 'sanity'
4
+ import {ForwardRefExoticComponent} from 'react'
5
+ import {Plugin as Plugin_2} from 'sanity'
6
+ import {RefAttributes} from 'react'
7
+ import {SanityDocument} from 'sanity'
8
+ import {SVGProps} from 'react'
9
+
10
+ /**
11
+ * Plugin: Cross Dataset Duplicator
12
+ * @public
13
+ */
14
+ export declare const crossDatasetDuplicator: Plugin_2<void | PluginConfig>
15
+
16
+ /**
17
+ * Component to perform a migration from the Cross Dataset Duplicator plugin
18
+ * @public
19
+ */
20
+ export declare function CrossDatasetDuplicatorAction(
21
+ props: CrossDatasetDuplicatorActionProps
22
+ ): JSX.Element
23
+
24
+ /**
25
+ * Cross Dataset Duplicator document action props
26
+ * @public
27
+ */
28
+ export declare type CrossDatasetDuplicatorActionProps = {
29
+ docs: SanityDocument[]
30
+ onDuplicated?: () => Promise<void>
31
+ }
32
+
33
+ /**
34
+ * Document action from the Cross Dataset Duplicator plugin
35
+ * @public
36
+ */
37
+ export declare const DuplicateToAction: {
38
+ (props: DocumentActionProps): {
39
+ disabled: SanityDocument | null
40
+ title: string | null
41
+ label: string
42
+ dialog:
43
+ | false
44
+ | {
45
+ type: string
46
+ title: string
47
+ content: JSX.Element
48
+ onClose: () => void
49
+ }
50
+ | null
51
+ onHandle: () => void
52
+ icon: ForwardRefExoticComponent<
53
+ Pick<
54
+ SVGProps<SVGSVGElement>,
55
+ | 'string'
56
+ | 'filter'
57
+ | 'fill'
58
+ | 'values'
59
+ | 'height'
60
+ | 'crossOrigin'
61
+ | 'href'
62
+ | 'max'
63
+ | 'media'
64
+ | 'method'
65
+ | 'min'
66
+ | 'name'
67
+ | 'target'
68
+ | 'type'
69
+ | 'width'
70
+ | 'className'
71
+ | 'id'
72
+ | 'lang'
73
+ | 'style'
74
+ | 'tabIndex'
75
+ | 'role'
76
+ | 'color'
77
+ | 'aria-activedescendant'
78
+ | 'aria-atomic'
79
+ | 'aria-autocomplete'
80
+ | 'aria-busy'
81
+ | 'aria-checked'
82
+ | 'aria-colcount'
83
+ | 'aria-colindex'
84
+ | 'aria-colspan'
85
+ | 'aria-controls'
86
+ | 'aria-current'
87
+ | 'aria-describedby'
88
+ | 'aria-details'
89
+ | 'aria-disabled'
90
+ | 'aria-dropeffect'
91
+ | 'aria-errormessage'
92
+ | 'aria-expanded'
93
+ | 'aria-flowto'
94
+ | 'aria-grabbed'
95
+ | 'aria-haspopup'
96
+ | 'aria-hidden'
97
+ | 'aria-invalid'
98
+ | 'aria-keyshortcuts'
99
+ | 'aria-label'
100
+ | 'aria-labelledby'
101
+ | 'aria-level'
102
+ | 'aria-live'
103
+ | 'aria-modal'
104
+ | 'aria-multiline'
105
+ | 'aria-multiselectable'
106
+ | 'aria-orientation'
107
+ | 'aria-owns'
108
+ | 'aria-placeholder'
109
+ | 'aria-posinset'
110
+ | 'aria-pressed'
111
+ | 'aria-readonly'
112
+ | 'aria-relevant'
113
+ | 'aria-required'
114
+ | 'aria-roledescription'
115
+ | 'aria-rowcount'
116
+ | 'aria-rowindex'
117
+ | 'aria-rowspan'
118
+ | 'aria-selected'
119
+ | 'aria-setsize'
120
+ | 'aria-sort'
121
+ | 'aria-valuemax'
122
+ | 'aria-valuemin'
123
+ | 'aria-valuenow'
124
+ | 'aria-valuetext'
125
+ | 'children'
126
+ | 'dangerouslySetInnerHTML'
127
+ | 'onCopy'
128
+ | 'onCopyCapture'
129
+ | 'onCut'
130
+ | 'onCutCapture'
131
+ | 'onPaste'
132
+ | 'onPasteCapture'
133
+ | 'onCompositionEnd'
134
+ | 'onCompositionEndCapture'
135
+ | 'onCompositionStart'
136
+ | 'onCompositionStartCapture'
137
+ | 'onCompositionUpdate'
138
+ | 'onCompositionUpdateCapture'
139
+ | 'onFocus'
140
+ | 'onFocusCapture'
141
+ | 'onBlur'
142
+ | 'onBlurCapture'
143
+ | 'onChange'
144
+ | 'onChangeCapture'
145
+ | 'onBeforeInput'
146
+ | 'onBeforeInputCapture'
147
+ | 'onInput'
148
+ | 'onInputCapture'
149
+ | 'onReset'
150
+ | 'onResetCapture'
151
+ | 'onSubmit'
152
+ | 'onSubmitCapture'
153
+ | 'onInvalid'
154
+ | 'onInvalidCapture'
155
+ | 'onLoad'
156
+ | 'onLoadCapture'
157
+ | 'onError'
158
+ | 'onErrorCapture'
159
+ | 'onKeyDown'
160
+ | 'onKeyDownCapture'
161
+ | 'onKeyPress'
162
+ | 'onKeyPressCapture'
163
+ | 'onKeyUp'
164
+ | 'onKeyUpCapture'
165
+ | 'onAbort'
166
+ | 'onAbortCapture'
167
+ | 'onCanPlay'
168
+ | 'onCanPlayCapture'
169
+ | 'onCanPlayThrough'
170
+ | 'onCanPlayThroughCapture'
171
+ | 'onDurationChange'
172
+ | 'onDurationChangeCapture'
173
+ | 'onEmptied'
174
+ | 'onEmptiedCapture'
175
+ | 'onEncrypted'
176
+ | 'onEncryptedCapture'
177
+ | 'onEnded'
178
+ | 'onEndedCapture'
179
+ | 'onLoadedData'
180
+ | 'onLoadedDataCapture'
181
+ | 'onLoadedMetadata'
182
+ | 'onLoadedMetadataCapture'
183
+ | 'onLoadStart'
184
+ | 'onLoadStartCapture'
185
+ | 'onPause'
186
+ | 'onPauseCapture'
187
+ | 'onPlay'
188
+ | 'onPlayCapture'
189
+ | 'onPlaying'
190
+ | 'onPlayingCapture'
191
+ | 'onProgress'
192
+ | 'onProgressCapture'
193
+ | 'onRateChange'
194
+ | 'onRateChangeCapture'
195
+ | 'onResize'
196
+ | 'onResizeCapture'
197
+ | 'onSeeked'
198
+ | 'onSeekedCapture'
199
+ | 'onSeeking'
200
+ | 'onSeekingCapture'
201
+ | 'onStalled'
202
+ | 'onStalledCapture'
203
+ | 'onSuspend'
204
+ | 'onSuspendCapture'
205
+ | 'onTimeUpdate'
206
+ | 'onTimeUpdateCapture'
207
+ | 'onVolumeChange'
208
+ | 'onVolumeChangeCapture'
209
+ | 'onWaiting'
210
+ | 'onWaitingCapture'
211
+ | 'onAuxClick'
212
+ | 'onAuxClickCapture'
213
+ | 'onClick'
214
+ | 'onClickCapture'
215
+ | 'onContextMenu'
216
+ | 'onContextMenuCapture'
217
+ | 'onDoubleClick'
218
+ | 'onDoubleClickCapture'
219
+ | 'onDrag'
220
+ | 'onDragCapture'
221
+ | 'onDragEnd'
222
+ | 'onDragEndCapture'
223
+ | 'onDragEnter'
224
+ | 'onDragEnterCapture'
225
+ | 'onDragExit'
226
+ | 'onDragExitCapture'
227
+ | 'onDragLeave'
228
+ | 'onDragLeaveCapture'
229
+ | 'onDragOver'
230
+ | 'onDragOverCapture'
231
+ | 'onDragStart'
232
+ | 'onDragStartCapture'
233
+ | 'onDrop'
234
+ | 'onDropCapture'
235
+ | 'onMouseDown'
236
+ | 'onMouseDownCapture'
237
+ | 'onMouseEnter'
238
+ | 'onMouseLeave'
239
+ | 'onMouseMove'
240
+ | 'onMouseMoveCapture'
241
+ | 'onMouseOut'
242
+ | 'onMouseOutCapture'
243
+ | 'onMouseOver'
244
+ | 'onMouseOverCapture'
245
+ | 'onMouseUp'
246
+ | 'onMouseUpCapture'
247
+ | 'onSelect'
248
+ | 'onSelectCapture'
249
+ | 'onTouchCancel'
250
+ | 'onTouchCancelCapture'
251
+ | 'onTouchEnd'
252
+ | 'onTouchEndCapture'
253
+ | 'onTouchMove'
254
+ | 'onTouchMoveCapture'
255
+ | 'onTouchStart'
256
+ | 'onTouchStartCapture'
257
+ | 'onPointerDown'
258
+ | 'onPointerDownCapture'
259
+ | 'onPointerMove'
260
+ | 'onPointerMoveCapture'
261
+ | 'onPointerUp'
262
+ | 'onPointerUpCapture'
263
+ | 'onPointerCancel'
264
+ | 'onPointerCancelCapture'
265
+ | 'onPointerEnter'
266
+ | 'onPointerEnterCapture'
267
+ | 'onPointerLeave'
268
+ | 'onPointerLeaveCapture'
269
+ | 'onPointerOver'
270
+ | 'onPointerOverCapture'
271
+ | 'onPointerOut'
272
+ | 'onPointerOutCapture'
273
+ | 'onGotPointerCapture'
274
+ | 'onGotPointerCaptureCapture'
275
+ | 'onLostPointerCapture'
276
+ | 'onLostPointerCaptureCapture'
277
+ | 'onScroll'
278
+ | 'onScrollCapture'
279
+ | 'onWheel'
280
+ | 'onWheelCapture'
281
+ | 'onAnimationStart'
282
+ | 'onAnimationStartCapture'
283
+ | 'onAnimationEnd'
284
+ | 'onAnimationEndCapture'
285
+ | 'onAnimationIteration'
286
+ | 'onAnimationIterationCapture'
287
+ | 'onTransitionEnd'
288
+ | 'onTransitionEndCapture'
289
+ | 'key'
290
+ | 'display'
291
+ | 'overflow'
292
+ | 'radius'
293
+ | 'clipPath'
294
+ | 'mask'
295
+ | 'path'
296
+ | 'direction'
297
+ | 'fontSize'
298
+ | 'mode'
299
+ | 'to'
300
+ | 'accentHeight'
301
+ | 'accumulate'
302
+ | 'additive'
303
+ | 'alignmentBaseline'
304
+ | 'allowReorder'
305
+ | 'alphabetic'
306
+ | 'amplitude'
307
+ | 'arabicForm'
308
+ | 'ascent'
309
+ | 'attributeName'
310
+ | 'attributeType'
311
+ | 'autoReverse'
312
+ | 'azimuth'
313
+ | 'baseFrequency'
314
+ | 'baselineShift'
315
+ | 'baseProfile'
316
+ | 'bbox'
317
+ | 'begin'
318
+ | 'bias'
319
+ | 'by'
320
+ | 'calcMode'
321
+ | 'capHeight'
322
+ | 'clip'
323
+ | 'clipPathUnits'
324
+ | 'clipRule'
325
+ | 'colorInterpolation'
326
+ | 'colorInterpolationFilters'
327
+ | 'colorProfile'
328
+ | 'colorRendering'
329
+ | 'contentScriptType'
330
+ | 'contentStyleType'
331
+ | 'cursor'
332
+ | 'cx'
333
+ | 'cy'
334
+ | 'd'
335
+ | 'decelerate'
336
+ | 'descent'
337
+ | 'diffuseConstant'
338
+ | 'divisor'
339
+ | 'dominantBaseline'
340
+ | 'dur'
341
+ | 'dx'
342
+ | 'dy'
343
+ | 'edgeMode'
344
+ | 'elevation'
345
+ | 'enableBackground'
346
+ | 'end'
347
+ | 'exponent'
348
+ | 'externalResourcesRequired'
349
+ | 'fillOpacity'
350
+ | 'fillRule'
351
+ | 'filterRes'
352
+ | 'filterUnits'
353
+ | 'floodColor'
354
+ | 'floodOpacity'
355
+ | 'focusable'
356
+ | 'fontFamily'
357
+ | 'fontSizeAdjust'
358
+ | 'fontStretch'
359
+ | 'fontStyle'
360
+ | 'fontVariant'
361
+ | 'fontWeight'
362
+ | 'format'
363
+ | 'fr'
364
+ | 'from'
365
+ | 'fx'
366
+ | 'fy'
367
+ | 'g1'
368
+ | 'g2'
369
+ | 'glyphName'
370
+ | 'glyphOrientationHorizontal'
371
+ | 'glyphOrientationVertical'
372
+ | 'glyphRef'
373
+ | 'gradientTransform'
374
+ | 'gradientUnits'
375
+ | 'hanging'
376
+ | 'horizAdvX'
377
+ | 'horizOriginX'
378
+ | 'ideographic'
379
+ | 'imageRendering'
380
+ | 'in2'
381
+ | 'in'
382
+ | 'intercept'
383
+ | 'k1'
384
+ | 'k2'
385
+ | 'k3'
386
+ | 'k4'
387
+ | 'k'
388
+ | 'kernelMatrix'
389
+ | 'kernelUnitLength'
390
+ | 'kerning'
391
+ | 'keyPoints'
392
+ | 'keySplines'
393
+ | 'keyTimes'
394
+ | 'lengthAdjust'
395
+ | 'letterSpacing'
396
+ | 'lightingColor'
397
+ | 'limitingConeAngle'
398
+ | 'local'
399
+ | 'markerEnd'
400
+ | 'markerHeight'
401
+ | 'markerMid'
402
+ | 'markerStart'
403
+ | 'markerUnits'
404
+ | 'markerWidth'
405
+ | 'maskContentUnits'
406
+ | 'maskUnits'
407
+ | 'mathematical'
408
+ | 'numOctaves'
409
+ | 'offset'
410
+ | 'opacity'
411
+ | 'operator'
412
+ | 'order'
413
+ | 'orient'
414
+ | 'orientation'
415
+ | 'origin'
416
+ | 'overlinePosition'
417
+ | 'overlineThickness'
418
+ | 'paintOrder'
419
+ | 'panose1'
420
+ | 'pathLength'
421
+ | 'patternContentUnits'
422
+ | 'patternTransform'
423
+ | 'patternUnits'
424
+ | 'pointerEvents'
425
+ | 'points'
426
+ | 'pointsAtX'
427
+ | 'pointsAtY'
428
+ | 'pointsAtZ'
429
+ | 'preserveAlpha'
430
+ | 'preserveAspectRatio'
431
+ | 'primitiveUnits'
432
+ | 'r'
433
+ | 'refX'
434
+ | 'refY'
435
+ | 'renderingIntent'
436
+ | 'repeatCount'
437
+ | 'repeatDur'
438
+ | 'requiredExtensions'
439
+ | 'requiredFeatures'
440
+ | 'restart'
441
+ | 'result'
442
+ | 'rotate'
443
+ | 'rx'
444
+ | 'ry'
445
+ | 'scale'
446
+ | 'seed'
447
+ | 'shapeRendering'
448
+ | 'slope'
449
+ | 'spacing'
450
+ | 'specularConstant'
451
+ | 'specularExponent'
452
+ | 'speed'
453
+ | 'spreadMethod'
454
+ | 'startOffset'
455
+ | 'stdDeviation'
456
+ | 'stemh'
457
+ | 'stemv'
458
+ | 'stitchTiles'
459
+ | 'stopColor'
460
+ | 'stopOpacity'
461
+ | 'strikethroughPosition'
462
+ | 'strikethroughThickness'
463
+ | 'stroke'
464
+ | 'strokeDasharray'
465
+ | 'strokeDashoffset'
466
+ | 'strokeLinecap'
467
+ | 'strokeLinejoin'
468
+ | 'strokeMiterlimit'
469
+ | 'strokeOpacity'
470
+ | 'strokeWidth'
471
+ | 'surfaceScale'
472
+ | 'systemLanguage'
473
+ | 'tableValues'
474
+ | 'targetX'
475
+ | 'targetY'
476
+ | 'textAnchor'
477
+ | 'textDecoration'
478
+ | 'textLength'
479
+ | 'textRendering'
480
+ | 'transform'
481
+ | 'u1'
482
+ | 'u2'
483
+ | 'underlinePosition'
484
+ | 'underlineThickness'
485
+ | 'unicode'
486
+ | 'unicodeBidi'
487
+ | 'unicodeRange'
488
+ | 'unitsPerEm'
489
+ | 'vAlphabetic'
490
+ | 'vectorEffect'
491
+ | 'version'
492
+ | 'vertAdvY'
493
+ | 'vertOriginX'
494
+ | 'vertOriginY'
495
+ | 'vHanging'
496
+ | 'vIdeographic'
497
+ | 'viewBox'
498
+ | 'viewTarget'
499
+ | 'visibility'
500
+ | 'vMathematical'
501
+ | 'widths'
502
+ | 'wordSpacing'
503
+ | 'writingMode'
504
+ | 'x1'
505
+ | 'x2'
506
+ | 'x'
507
+ | 'xChannelSelector'
508
+ | 'xHeight'
509
+ | 'xlinkActuate'
510
+ | 'xlinkArcrole'
511
+ | 'xlinkHref'
512
+ | 'xlinkRole'
513
+ | 'xlinkShow'
514
+ | 'xlinkTitle'
515
+ | 'xlinkType'
516
+ | 'xmlBase'
517
+ | 'xmlLang'
518
+ | 'xmlns'
519
+ | 'xmlnsXlink'
520
+ | 'xmlSpace'
521
+ | 'y1'
522
+ | 'y2'
523
+ | 'y'
524
+ | 'yChannelSelector'
525
+ | 'z'
526
+ | 'zoomAndPan'
527
+ > &
528
+ RefAttributes<SVGSVGElement>
529
+ >
530
+ }
531
+ action: string
532
+ }
533
+
534
+ /**
535
+ * Plugin configuration
536
+ * @public
537
+ */
538
+ export declare interface PluginConfig {
539
+ tool?: boolean
540
+ types?: string[]
541
+ filter?: string
542
+ follow?: ('inbound' | 'outbound')[]
543
+ }
544
+
545
+ /**
546
+ * Plugin config context hook from the Cross Dataset Duplicator plugin
547
+ * @public
548
+ */
549
+ export declare function useCrossDatasetDuplicatorConfig(): PluginConfig
550
+
551
+ export {}