sanity-plugin-media 4.3.1 → 4.3.2

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 (145) hide show
  1. package/LICENSE +4 -4
  2. package/README.md +12 -12
  3. package/dist/index.d.mts +263 -195
  4. package/dist/index.d.ts +263 -195
  5. package/dist/index.js +83 -203
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +84 -203
  8. package/dist/index.mjs.map +1 -1
  9. package/package.json +41 -63
  10. package/src/__tests__/fixtures/createEpicTestStore.ts +5 -4
  11. package/src/__tests__/fixtures/mockSanityClient.ts +8 -8
  12. package/src/__tests__/fixtures/renderWithProviders.tsx +8 -7
  13. package/src/__tests__/fixtures/rootState.ts +4 -4
  14. package/src/components/AssetGridVirtualized/index.tsx +8 -7
  15. package/src/components/AssetMetadata/index.tsx +6 -5
  16. package/src/components/AssetTableVirtualized/index.tsx +7 -6
  17. package/src/components/AutoTagInputWrapper/index.tsx +9 -4
  18. package/src/components/Browser/Browser.test.tsx +9 -8
  19. package/src/components/Browser/index.tsx +2 -1
  20. package/src/components/Browser/useBrowserInit.ts +9 -9
  21. package/src/components/ButtonAssetCopy/index.tsx +1 -0
  22. package/src/components/ButtonViewGroup/index.tsx +4 -3
  23. package/src/components/CardAsset/CardAsset.test.tsx +53 -52
  24. package/src/components/CardAsset/index.tsx +52 -49
  25. package/src/components/CardUpload/index.tsx +7 -6
  26. package/src/components/Controls/index.tsx +7 -6
  27. package/src/components/DebugControls/index.tsx +5 -4
  28. package/src/components/DialogAssetEdit/Details.tsx +3 -2
  29. package/src/components/DialogAssetEdit/DialogAssetEdit.test.tsx +28 -27
  30. package/src/components/DialogAssetEdit/index.tsx +37 -37
  31. package/src/components/DialogConfirm/index.tsx +2 -1
  32. package/src/components/DialogSearchFacets/index.tsx +3 -2
  33. package/src/components/DialogTagCreate/DialogTagCreate.test.tsx +16 -15
  34. package/src/components/DialogTagCreate/index.tsx +11 -10
  35. package/src/components/DialogTagEdit/DialogTagEdit.test.tsx +28 -27
  36. package/src/components/DialogTagEdit/index.tsx +17 -16
  37. package/src/components/DialogTags/index.tsx +4 -3
  38. package/src/components/Dialogs/index.tsx +2 -3
  39. package/src/components/DocumentList/index.tsx +2 -3
  40. package/src/components/FileAssetPreview/index.tsx +2 -2
  41. package/src/components/FormBuilderTool/FormBuilderTool.test.tsx +12 -11
  42. package/src/components/FormBuilderTool/index.tsx +2 -1
  43. package/src/components/FormFieldInputLabel/index.tsx +1 -2
  44. package/src/components/FormFieldInputTags/index.tsx +4 -3
  45. package/src/components/FormSubmitButton/index.tsx +1 -1
  46. package/src/components/Header/index.tsx +3 -3
  47. package/src/components/Image/index.tsx +10 -4
  48. package/src/components/Items/index.tsx +5 -4
  49. package/src/components/Notifications/index.tsx +3 -2
  50. package/src/components/OrderSelect/index.tsx +4 -3
  51. package/src/components/PickedBar/index.tsx +2 -1
  52. package/src/components/Progress/index.tsx +3 -3
  53. package/src/components/ReduxProvider/index.tsx +15 -12
  54. package/src/components/SearchFacet/index.tsx +3 -2
  55. package/src/components/SearchFacetNumber/index.tsx +8 -8
  56. package/src/components/SearchFacetSelect/index.tsx +7 -8
  57. package/src/components/SearchFacetString/index.tsx +1 -1
  58. package/src/components/SearchFacetTags/index.tsx +13 -12
  59. package/src/components/SearchFacets/index.tsx +2 -3
  60. package/src/components/SearchFacetsControl/index.tsx +13 -12
  61. package/src/components/TableHeader/index.tsx +18 -17
  62. package/src/components/TableHeaderItem/index.tsx +4 -4
  63. package/src/components/TableRowAsset/index.tsx +37 -36
  64. package/src/components/TableRowUpload/index.tsx +7 -6
  65. package/src/components/Tag/index.tsx +8 -7
  66. package/src/components/TagView/index.tsx +2 -2
  67. package/src/components/TagViewHeader/index.tsx +5 -4
  68. package/src/components/TagsPanel/index.tsx +3 -3
  69. package/src/components/TagsVirtualized/index.tsx +25 -24
  70. package/src/components/TextInputSearch/index.tsx +3 -2
  71. package/src/components/UploadDropzone/UploadDropzone.test.tsx +8 -7
  72. package/src/components/UploadDropzone/index.tsx +14 -13
  73. package/src/config/orders.ts +6 -6
  74. package/src/config/searchFacets.ts +56 -55
  75. package/src/constants.ts +15 -14
  76. package/src/contexts/AssetSourceDispatchContext.tsx +1 -1
  77. package/src/contexts/ToolOptionsContext.tsx +6 -5
  78. package/src/formSchema/index.test.ts +6 -5
  79. package/src/formSchema/index.ts +5 -5
  80. package/src/hooks/useBreakpointIndex.ts +6 -6
  81. package/src/hooks/useKeyPress.ts +2 -2
  82. package/src/hooks/usePortalPopoverProps.ts +1 -1
  83. package/src/modules/assets/actions.ts +8 -7
  84. package/src/modules/assets/deleteAndUpdateEpics.test.ts +18 -17
  85. package/src/modules/assets/fetchEpic.test.ts +12 -11
  86. package/src/modules/assets/index.ts +134 -133
  87. package/src/modules/assets/reducer.test.ts +9 -8
  88. package/src/modules/assets/tagsAndListenerEpics.test.ts +36 -35
  89. package/src/modules/debug/index.ts +3 -3
  90. package/src/modules/dialog/actions.ts +2 -2
  91. package/src/modules/dialog/epics.test.ts +29 -28
  92. package/src/modules/dialog/index.ts +36 -35
  93. package/src/modules/dialog/reducer.test.ts +31 -30
  94. package/src/modules/index.ts +9 -9
  95. package/src/modules/notifications/epics.test.ts +71 -70
  96. package/src/modules/notifications/index.ts +50 -49
  97. package/src/modules/notifications/reducer.test.ts +8 -7
  98. package/src/modules/search/index.test.ts +2 -1
  99. package/src/modules/search/index.ts +22 -22
  100. package/src/modules/selected/index.ts +2 -2
  101. package/src/modules/selectors.test.ts +4 -3
  102. package/src/modules/selectors.ts +5 -5
  103. package/src/modules/tags/epics.test.ts +16 -15
  104. package/src/modules/tags/index.test.ts +2 -1
  105. package/src/modules/tags/index.ts +82 -81
  106. package/src/modules/uploads/actions.ts +3 -3
  107. package/src/modules/uploads/epics.test.ts +13 -12
  108. package/src/modules/uploads/index.test.ts +8 -7
  109. package/src/modules/uploads/index.ts +48 -47
  110. package/src/operators/checkTagName.test.ts +7 -6
  111. package/src/operators/checkTagName.ts +6 -5
  112. package/src/operators/debugThrottle.ts +4 -4
  113. package/src/plugin.tsx +18 -18
  114. package/src/schemas/tag.ts +7 -7
  115. package/src/styled/react-select/creatable.tsx +40 -39
  116. package/src/styled/react-select/single.tsx +39 -38
  117. package/src/types/index.ts +4 -3
  118. package/src/utils/applyMediaTags.ts +11 -10
  119. package/src/utils/blocksToText.test.ts +5 -4
  120. package/src/utils/blocksToText.ts +2 -2
  121. package/src/utils/constructFilter.test.ts +15 -14
  122. package/src/utils/constructFilter.ts +7 -7
  123. package/src/utils/generatePreviewBlobUrl.test.ts +6 -5
  124. package/src/utils/generatePreviewBlobUrl.ts +2 -2
  125. package/src/utils/getAssetResolution.test.ts +3 -2
  126. package/src/utils/getDocumentAssetIds.test.ts +7 -6
  127. package/src/utils/getDocumentAssetIds.ts +2 -2
  128. package/src/utils/getSchemeColor.test.ts +1 -0
  129. package/src/utils/getSchemeColor.ts +9 -9
  130. package/src/utils/getTagSelectOptions.test.ts +6 -5
  131. package/src/utils/getTagSelectOptions.ts +1 -1
  132. package/src/utils/getUniqueDocuments.test.ts +4 -3
  133. package/src/utils/getUniqueDocuments.ts +2 -2
  134. package/src/utils/imageDprUrl.test.ts +4 -3
  135. package/src/utils/imageDprUrl.ts +1 -1
  136. package/src/utils/isSupportedAssetType.test.ts +1 -0
  137. package/src/utils/mediaField.ts +4 -3
  138. package/src/utils/sanitizeFormData.test.ts +14 -13
  139. package/src/utils/typeGuards.test.ts +2 -1
  140. package/src/utils/uploadSanityAsset.test.ts +5 -4
  141. package/src/utils/uploadSanityAsset.ts +17 -16
  142. package/src/utils/withMaxConcurrency.test.ts +5 -4
  143. package/src/utils/withMaxConcurrency.ts +4 -4
  144. package/src/utils/zodFormResolver.ts +17 -0
  145. package/v2-incompatible.js +2 -2
@@ -1,13 +1,14 @@
1
1
  import {describe, expect, it} from 'vitest'
2
- import getTagSelectOptions from './getTagSelectOptions'
2
+
3
3
  import type {Tag, TagItem} from '../types'
4
+ import getTagSelectOptions from './getTagSelectOptions'
4
5
 
5
6
  function tagItem(partial: Partial<TagItem> & Pick<TagItem, 'tag'>): TagItem {
6
7
  return {
7
8
  _type: 'tag',
8
9
  picked: false,
9
10
  updating: false,
10
- ...partial
11
+ ...partial,
11
12
  }
12
13
  }
13
14
 
@@ -17,7 +18,7 @@ const makeTag = (id: string, name: string): Tag => ({
17
18
  _createdAt: '',
18
19
  _updatedAt: '',
19
20
  _rev: 'r1',
20
- name: {_type: 'slug', current: name}
21
+ name: {_type: 'slug', current: name},
21
22
  })
22
23
 
23
24
  describe('getTagSelectOptions', () => {
@@ -25,7 +26,7 @@ describe('getTagSelectOptions', () => {
25
26
  const tags = [tagItem({tag: makeTag('t1', 'alpha')}), tagItem({tag: makeTag('t2', 'beta')})]
26
27
  expect(getTagSelectOptions(tags)).toEqual([
27
28
  {label: 'alpha', value: 't1'},
28
- {label: 'beta', value: 't2'}
29
+ {label: 'beta', value: 't2'},
29
30
  ])
30
31
  })
31
32
 
@@ -36,7 +37,7 @@ describe('getTagSelectOptions', () => {
36
37
  it('skips items without a tag', () => {
37
38
  const tags = [
38
39
  tagItem({tag: makeTag('t1', 'ok')}),
39
- {_type: 'tag', tag: undefined, picked: false, updating: false} as unknown as TagItem
40
+ {_type: 'tag', tag: undefined, picked: false, updating: false} as unknown as TagItem,
40
41
  ]
41
42
  expect(getTagSelectOptions(tags)).toEqual([{label: 'ok', value: 't1'}])
42
43
  })
@@ -6,7 +6,7 @@ const getTagSelectOptions = (tags: TagItem[]): TagSelectOption[] => {
6
6
  if (tag) {
7
7
  acc.push({
8
8
  label: tag?.name?.current,
9
- value: tag?._id
9
+ value: tag?._id,
10
10
  })
11
11
  }
12
12
  return acc
@@ -1,12 +1,13 @@
1
- import {describe, expect, it} from 'vitest'
2
1
  import type {SanityDocument} from '@sanity/client'
2
+ import {describe, expect, it} from 'vitest'
3
+
3
4
  import {getUniqueDocuments} from './getUniqueDocuments'
4
5
 
5
6
  describe('getUniqueDocuments', () => {
6
7
  it('drops published documents when a drafts.* sibling exists', () => {
7
8
  const docs: SanityDocument[] = [
8
9
  {_id: 'drafts.post1', _type: 'post'} as SanityDocument,
9
- {_id: 'post1', _type: 'post'} as SanityDocument
10
+ {_id: 'post1', _type: 'post'} as SanityDocument,
10
11
  ]
11
12
  expect(getUniqueDocuments(docs)).toEqual([{_id: 'drafts.post1', _type: 'post'}])
12
13
  })
@@ -14,7 +15,7 @@ describe('getUniqueDocuments', () => {
14
15
  it('keeps published-only and draft-only ids', () => {
15
16
  const docs: SanityDocument[] = [
16
17
  {_id: 'onlyPub', _type: 'x'} as SanityDocument,
17
- {_id: 'drafts.onlyDraft', _type: 'x'} as SanityDocument
18
+ {_id: 'drafts.onlyDraft', _type: 'x'} as SanityDocument,
18
19
  ]
19
20
  expect(getUniqueDocuments(docs)).toEqual(docs)
20
21
  })
@@ -4,11 +4,11 @@ export function getUniqueDocuments(documents: SanityDocument[]): SanityDocument[
4
4
  const draftIds = documents.reduce(
5
5
  (acc: string[], doc: SanityDocument) =>
6
6
  doc._id.startsWith('drafts.') ? acc.concat(doc._id.slice(7)) : acc,
7
- []
7
+ [],
8
8
  )
9
9
 
10
10
  const filteredDocuments: SanityDocument[] = documents.filter(
11
- (doc: SanityDocument) => !draftIds.includes(doc._id)
11
+ (doc: SanityDocument) => !draftIds.includes(doc._id),
12
12
  )
13
13
 
14
14
  return filteredDocuments
@@ -1,6 +1,7 @@
1
1
  import {afterEach, describe, expect, it} from 'vitest'
2
- import imageDprUrl from './imageDprUrl'
2
+
3
3
  import type {ImageAsset} from '../types'
4
+ import imageDprUrl from './imageDprUrl'
4
5
 
5
6
  const asset = {
6
7
  _id: 'a1',
@@ -12,7 +13,7 @@ const asset = {
12
13
  size: 1,
13
14
  mimeType: 'image/png',
14
15
  url: 'https://cdn.test/image.png',
15
- metadata: {dimensions: {width: 100, height: 100}, isOpaque: true}
16
+ metadata: {dimensions: {width: 100, height: 100}, isOpaque: true},
16
17
  } as ImageAsset
17
18
 
18
19
  describe('imageDprUrl', () => {
@@ -37,7 +38,7 @@ describe('imageDprUrl', () => {
37
38
  it('uses multiplier 1 when devicePixelRatio is missing', () => {
38
39
  Object.defineProperty(window, 'devicePixelRatio', {
39
40
  value: undefined as unknown as number,
40
- configurable: true
41
+ configurable: true,
41
42
  })
42
43
  const url = imageDprUrl(asset, {width: 100})
43
44
  expect(url).toBe('https://cdn.test/image.png?fit=max&w=100')
@@ -5,7 +5,7 @@ const imageDprUrl = (
5
5
  options: {
6
6
  width: number
7
7
  height?: number
8
- }
8
+ },
9
9
  ): string => {
10
10
  const dpi =
11
11
  typeof window === 'undefined' || !window.devicePixelRatio
@@ -1,4 +1,5 @@
1
1
  import {describe, expect, it} from 'vitest'
2
+
2
3
  import {isSupportedAssetType} from './isSupportedAssetType'
3
4
 
4
5
  describe('isSupportedAssetType', () => {
@@ -3,8 +3,9 @@ import type {
3
3
  FileDefinition,
4
4
  ImageDefinition,
5
5
  WidenInitialValue,
6
- WidenValidation
6
+ WidenValidation,
7
7
  } from 'sanity'
8
+
8
9
  import {AutoTagInput} from '../components/AutoTagInputWrapper'
9
10
 
10
11
  type ImageMediaFieldConfig = Omit<ImageDefinition, 'options'> &
@@ -59,7 +60,7 @@ type FileMediaFieldResult = Omit<FileDefinition, 'options'> &
59
60
  export function mediaField(config: ImageMediaFieldConfig): ImageMediaFieldResult
60
61
  export function mediaField(config: FileMediaFieldConfig): FileMediaFieldResult
61
62
  export function mediaField(
62
- config: ImageMediaFieldConfig | FileMediaFieldConfig
63
+ config: ImageMediaFieldConfig | FileMediaFieldConfig,
63
64
  ): ImageMediaFieldResult | FileMediaFieldResult {
64
65
  const {mediaTags, options, components, ...rest} = config as ImageMediaFieldConfig & {
65
66
  components?: Record<string, unknown>
@@ -67,6 +68,6 @@ export function mediaField(
67
68
  return {
68
69
  ...rest,
69
70
  options: {...options, mediaTags},
70
- components: {...components, input: AutoTagInput}
71
+ components: {...components, input: AutoTagInput},
71
72
  } as unknown as ImageMediaFieldResult
72
73
  }
@@ -1,6 +1,7 @@
1
1
  // @vitest-environment node
2
2
 
3
3
  import {describe, expect, it} from 'vitest'
4
+
4
5
  import sanitizeFormData from './sanitizeFormData'
5
6
 
6
7
  describe('sanitizeFormData', () => {
@@ -9,12 +10,12 @@ describe('sanitizeFormData', () => {
9
10
  sanitizeFormData({
10
11
  a: '',
11
12
  b: undefined,
12
- c: []
13
- })
13
+ c: [],
14
+ }),
14
15
  ).toEqual({
15
16
  a: null,
16
17
  b: null,
17
- c: null
18
+ c: null,
18
19
  })
19
20
  })
20
21
 
@@ -27,16 +28,16 @@ describe('sanitizeFormData', () => {
27
28
  sanitizeFormData({
28
29
  opt: {
29
30
  media: {
30
- tags: []
31
- }
32
- }
33
- })
31
+ tags: [],
32
+ },
33
+ },
34
+ }),
34
35
  ).toEqual({
35
36
  opt: {
36
37
  media: {
37
- tags: null
38
- }
39
- }
38
+ tags: null,
39
+ },
40
+ },
40
41
  })
41
42
  })
42
43
 
@@ -44,11 +45,11 @@ describe('sanitizeFormData', () => {
44
45
  expect(
45
46
  sanitizeFormData({
46
47
  kept: null,
47
- tags: [{_ref: 't1'}]
48
- })
48
+ tags: [{_ref: 't1'}],
49
+ }),
49
50
  ).toEqual({
50
51
  kept: null,
51
- tags: [{_ref: 't1'}]
52
+ tags: [{_ref: 't1'}],
52
53
  })
53
54
  })
54
55
 
@@ -1,6 +1,7 @@
1
1
  import {describe, expect, it} from 'vitest'
2
- import {isFileAsset, isImageAsset} from './typeGuards'
2
+
3
3
  import type {Asset} from '../types'
4
+ import {isFileAsset, isImageAsset} from './typeGuards'
4
5
 
5
6
  describe('typeGuards', () => {
6
7
  it('isFileAsset narrows sanity.fileAsset', () => {
@@ -1,5 +1,6 @@
1
- import {afterEach, describe, expect, it} from 'vitest'
2
1
  import {firstValueFrom} from 'rxjs'
2
+ import {afterEach, describe, expect, it} from 'vitest'
3
+
3
4
  import {hashFile$} from './uploadSanityAsset'
4
5
 
5
6
  describe('hashFile$', () => {
@@ -9,7 +10,7 @@ describe('hashFile$', () => {
9
10
  Object.defineProperty(globalThis, 'crypto', {
10
11
  value: cryptoRef,
11
12
  configurable: true,
12
- writable: true
13
+ writable: true,
13
14
  })
14
15
  })
15
16
 
@@ -17,12 +18,12 @@ describe('hashFile$', () => {
17
18
  Object.defineProperty(globalThis, 'crypto', {
18
19
  value: undefined,
19
20
  configurable: true,
20
- writable: true
21
+ writable: true,
21
22
  })
22
23
 
23
24
  await expect(firstValueFrom(hashFile$(new File(['x'], 'blob.bin')))).rejects.toMatchObject({
24
25
  message: expect.stringMatching(/secure contexts/i),
25
- statusCode: 500
26
+ statusCode: 500,
26
27
  })
27
28
  })
28
29
  })
@@ -2,26 +2,27 @@
2
2
  // https://github.com/sanity-io/sanity/blob/ccb777e115a8cdf20d81a9a2bc9d8c228568faff/packages/%40sanity/form-builder/src/sanity/inputs/client-adapters/assets.ts
3
3
 
4
4
  import type {SanityAssetDocument, SanityClient, SanityImageAssetDocument} from '@sanity/client'
5
- import type {HttpError} from '../types'
6
5
  import {Observable, of, throwError} from 'rxjs'
7
6
  import {map, mergeMap} from 'rxjs/operators'
7
+
8
+ import type {HttpError} from '../types'
8
9
  import {withMaxConcurrency} from './withMaxConcurrency'
9
10
 
10
11
  const fetchExisting$ = (client: SanityClient, type: string, hash: string) => {
11
12
  return client.observable.fetch('*[_type == $documentType && sha1hash == $hash][0]', {
12
13
  documentType: type,
13
- hash
14
+ hash,
14
15
  })
15
16
  }
16
17
 
17
18
  const readFile$ = (file: File): Observable<ArrayBuffer> => {
18
- return new Observable(subscriber => {
19
+ return new Observable((subscriber) => {
19
20
  const reader = new FileReader()
20
21
  reader.onload = () => {
21
22
  subscriber.next(reader.result as ArrayBuffer)
22
23
  subscriber.complete()
23
24
  }
24
- reader.onerror = err => {
25
+ reader.onerror = (err) => {
25
26
  subscriber.error(err)
26
27
  }
27
28
  reader.readAsArrayBuffer(file)
@@ -33,7 +34,7 @@ const readFile$ = (file: File): Observable<ArrayBuffer> => {
33
34
 
34
35
  const hexFromBuffer = (buffer: ArrayBuffer): string => {
35
36
  return Array.prototype.map
36
- .call(new Uint8Array(buffer), x => `00${x.toString(16)}`.slice(-2))
37
+ .call(new Uint8Array(buffer), (x) => `00${x.toString(16)}`.slice(-2))
37
38
  .join('')
38
39
  }
39
40
 
@@ -41,12 +42,12 @@ export const hashFile$ = (file: File): Observable<string> => {
41
42
  if (!window.crypto || !window.crypto.subtle || !window.FileReader) {
42
43
  return throwError({
43
44
  message: 'Unable to generate hash: uploads are only allowed in secure contexts',
44
- statusCode: 500
45
+ statusCode: 500,
45
46
  })
46
47
  }
47
48
  return readFile$(file).pipe(
48
- mergeMap(arrayBuffer => window.crypto.subtle.digest('SHA-1', arrayBuffer)),
49
- map(hexFromBuffer)
49
+ mergeMap((arrayBuffer) => window.crypto.subtle.digest('SHA-1', arrayBuffer)),
50
+ map(hexFromBuffer),
50
51
  )
51
52
  }
52
53
 
@@ -54,7 +55,7 @@ const uploadSanityAsset$ = (
54
55
  client: SanityClient,
55
56
  assetType: 'file' | 'image',
56
57
  file: File,
57
- hash: string
58
+ hash: string,
58
59
  ) => {
59
60
  return of(null).pipe(
60
61
  // NOTE: the sanity api will still dedupe unique files, but this saves us from uploading the asset file entirely
@@ -64,7 +65,7 @@ const uploadSanityAsset$ = (
64
65
  if (existingAsset) {
65
66
  return throwError({
66
67
  message: 'Asset already exists',
67
- statusCode: 409
68
+ statusCode: 409,
68
69
  } as HttpError)
69
70
  }
70
71
 
@@ -75,21 +76,21 @@ const uploadSanityAsset$ = (
75
76
  return client.observable.assets
76
77
  .upload(assetType, file, {
77
78
  extract: ['blurhash', 'exif', 'location', 'lqip', 'palette'],
78
- preserveFilename: true
79
+ preserveFilename: true,
79
80
  })
80
81
  .pipe(
81
- map(event =>
82
+ map((event) =>
82
83
  event.type === 'response'
83
84
  ? {
84
85
  // rewrite to a 'complete' event
85
86
  asset: event.body.document,
86
87
  id: event.body.document._id,
87
- type: 'complete'
88
+ type: 'complete',
88
89
  }
89
- : event
90
- )
90
+ : event,
91
+ ),
91
92
  )
92
- })
93
+ }),
93
94
  )
94
95
  }
95
96
 
@@ -1,5 +1,6 @@
1
- import {describe, expect, it} from 'vitest'
2
1
  import {Observable, firstValueFrom} from 'rxjs'
2
+ import {describe, expect, it} from 'vitest'
3
+
3
4
  import {createThrottler, withMaxConcurrency} from './withMaxConcurrency'
4
5
 
5
6
  describe('createThrottler', () => {
@@ -9,7 +10,7 @@ describe('createThrottler', () => {
9
10
  const request = createThrottler(2)
10
11
 
11
12
  const mk = () =>
12
- new Observable<number>(sub => {
13
+ new Observable<number>((sub) => {
13
14
  active++
14
15
  maxActive = Math.max(maxActive, active)
15
16
  queueMicrotask(() => {
@@ -22,7 +23,7 @@ describe('createThrottler', () => {
22
23
  await Promise.all([
23
24
  firstValueFrom(request(mk())),
24
25
  firstValueFrom(request(mk())),
25
- firstValueFrom(request(mk()))
26
+ firstValueFrom(request(mk())),
26
27
  ])
27
28
 
28
29
  expect(maxActive).toBe(2)
@@ -32,7 +33,7 @@ describe('createThrottler', () => {
32
33
  describe('withMaxConcurrency', () => {
33
34
  it('wraps a function so each call returns a single-value observable', async () => {
34
35
  const fn = (n: number) =>
35
- new Observable<number>(sub => {
36
+ new Observable<number>((sub) => {
36
37
  sub.next(n)
37
38
  sub.complete()
38
39
  })
@@ -1,7 +1,7 @@
1
+ import {Subject, Subscription, Observable, from} from 'rxjs'
1
2
  // Takes a observable-returning function and returns a new function that limits on the number of
2
3
  // concurrent observables.
3
4
  import {first, mergeMap} from 'rxjs/operators'
4
- import {Subject, Subscription, Observable, from} from 'rxjs'
5
5
 
6
6
  const DEFAULT_CONCURRENCY = 4
7
7
 
@@ -19,7 +19,7 @@ export const createThrottler = (concurrency: number = DEFAULT_CONCURRENCY) => {
19
19
  const ready$ = new Subject()
20
20
 
21
21
  function request(observable: Observable<any>): Observable<any> {
22
- return new Observable(observer => {
22
+ return new Observable((observer) => {
23
23
  if (currentSubscriptions.length >= concurrency) {
24
24
  return scheduleAndWait$(observable)
25
25
  .pipe(mergeMap(request)) //
@@ -40,7 +40,7 @@ export const createThrottler = (concurrency: number = DEFAULT_CONCURRENCY) => {
40
40
 
41
41
  function scheduleAndWait$(observable: Observable<any>): Observable<any> {
42
42
  pendingObservables.push(observable)
43
- return ready$.asObservable().pipe(first(obs => obs === observable))
43
+ return ready$.asObservable().pipe(first((obs) => obs === observable))
44
44
  }
45
45
 
46
46
  return request
@@ -48,7 +48,7 @@ export const createThrottler = (concurrency: number = DEFAULT_CONCURRENCY) => {
48
48
 
49
49
  export const withMaxConcurrency = (
50
50
  func: (...args: any[]) => Observable<any>,
51
- concurrency: number = DEFAULT_CONCURRENCY
51
+ concurrency: number = DEFAULT_CONCURRENCY,
52
52
  ) => {
53
53
  const throttler = createThrottler(concurrency)
54
54
  return (...args: Array<any>) => from(throttler(func(...args)))
@@ -0,0 +1,17 @@
1
+ import {zodResolver} from '@hookform/resolvers/zod'
2
+ import type {FieldValues, Resolver} from 'react-hook-form'
3
+
4
+ /**
5
+ * `@hookform/resolvers/zod` resolves its own `zod` typings through dependency
6
+ * hoisting, which in this monorepo lands on zod v4, while this plugin authors its
7
+ * form schemas with zod v3. The resolver accepts v3 schema instances correctly at
8
+ * runtime, so this thin wrapper only bridges the type-only v3/v4 mismatch. The
9
+ * react-hook-form field types stay fully checked at the call sites.
10
+ */
11
+ export default function zodFormResolver<TFieldValues extends FieldValues>(
12
+ schema: unknown,
13
+ ): Resolver<TFieldValues> {
14
+ return zodResolver(
15
+ schema as Parameters<typeof zodResolver>[0],
16
+ ) as unknown as Resolver<TFieldValues>
17
+ }
@@ -5,7 +5,7 @@ export default showIncompatiblePluginDialog({
5
5
  name: name,
6
6
  versions: {
7
7
  v3: version,
8
- v2: '^1.4.13'
8
+ v2: '^1.4.13',
9
9
  },
10
- sanityExchangeUrl: 'https://www.sanity.io/plugins/sanity-plugin-media'
10
+ sanityExchangeUrl: 'https://www.sanity.io/plugins/sanity-plugin-media',
11
11
  })