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.
- package/LICENSE +4 -4
- package/README.md +12 -12
- package/dist/index.d.mts +263 -195
- package/dist/index.d.ts +263 -195
- package/dist/index.js +83 -203
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +84 -203
- package/dist/index.mjs.map +1 -1
- package/package.json +41 -63
- package/src/__tests__/fixtures/createEpicTestStore.ts +5 -4
- package/src/__tests__/fixtures/mockSanityClient.ts +8 -8
- package/src/__tests__/fixtures/renderWithProviders.tsx +8 -7
- package/src/__tests__/fixtures/rootState.ts +4 -4
- package/src/components/AssetGridVirtualized/index.tsx +8 -7
- package/src/components/AssetMetadata/index.tsx +6 -5
- package/src/components/AssetTableVirtualized/index.tsx +7 -6
- package/src/components/AutoTagInputWrapper/index.tsx +9 -4
- package/src/components/Browser/Browser.test.tsx +9 -8
- package/src/components/Browser/index.tsx +2 -1
- package/src/components/Browser/useBrowserInit.ts +9 -9
- package/src/components/ButtonAssetCopy/index.tsx +1 -0
- package/src/components/ButtonViewGroup/index.tsx +4 -3
- package/src/components/CardAsset/CardAsset.test.tsx +53 -52
- package/src/components/CardAsset/index.tsx +52 -49
- package/src/components/CardUpload/index.tsx +7 -6
- package/src/components/Controls/index.tsx +7 -6
- package/src/components/DebugControls/index.tsx +5 -4
- package/src/components/DialogAssetEdit/Details.tsx +3 -2
- package/src/components/DialogAssetEdit/DialogAssetEdit.test.tsx +28 -27
- package/src/components/DialogAssetEdit/index.tsx +37 -37
- package/src/components/DialogConfirm/index.tsx +2 -1
- package/src/components/DialogSearchFacets/index.tsx +3 -2
- package/src/components/DialogTagCreate/DialogTagCreate.test.tsx +16 -15
- package/src/components/DialogTagCreate/index.tsx +11 -10
- package/src/components/DialogTagEdit/DialogTagEdit.test.tsx +28 -27
- package/src/components/DialogTagEdit/index.tsx +17 -16
- package/src/components/DialogTags/index.tsx +4 -3
- package/src/components/Dialogs/index.tsx +2 -3
- package/src/components/DocumentList/index.tsx +2 -3
- package/src/components/FileAssetPreview/index.tsx +2 -2
- package/src/components/FormBuilderTool/FormBuilderTool.test.tsx +12 -11
- package/src/components/FormBuilderTool/index.tsx +2 -1
- package/src/components/FormFieldInputLabel/index.tsx +1 -2
- package/src/components/FormFieldInputTags/index.tsx +4 -3
- package/src/components/FormSubmitButton/index.tsx +1 -1
- package/src/components/Header/index.tsx +3 -3
- package/src/components/Image/index.tsx +10 -4
- package/src/components/Items/index.tsx +5 -4
- package/src/components/Notifications/index.tsx +3 -2
- package/src/components/OrderSelect/index.tsx +4 -3
- package/src/components/PickedBar/index.tsx +2 -1
- package/src/components/Progress/index.tsx +3 -3
- package/src/components/ReduxProvider/index.tsx +15 -12
- package/src/components/SearchFacet/index.tsx +3 -2
- package/src/components/SearchFacetNumber/index.tsx +8 -8
- package/src/components/SearchFacetSelect/index.tsx +7 -8
- package/src/components/SearchFacetString/index.tsx +1 -1
- package/src/components/SearchFacetTags/index.tsx +13 -12
- package/src/components/SearchFacets/index.tsx +2 -3
- package/src/components/SearchFacetsControl/index.tsx +13 -12
- package/src/components/TableHeader/index.tsx +18 -17
- package/src/components/TableHeaderItem/index.tsx +4 -4
- package/src/components/TableRowAsset/index.tsx +37 -36
- package/src/components/TableRowUpload/index.tsx +7 -6
- package/src/components/Tag/index.tsx +8 -7
- package/src/components/TagView/index.tsx +2 -2
- package/src/components/TagViewHeader/index.tsx +5 -4
- package/src/components/TagsPanel/index.tsx +3 -3
- package/src/components/TagsVirtualized/index.tsx +25 -24
- package/src/components/TextInputSearch/index.tsx +3 -2
- package/src/components/UploadDropzone/UploadDropzone.test.tsx +8 -7
- package/src/components/UploadDropzone/index.tsx +14 -13
- package/src/config/orders.ts +6 -6
- package/src/config/searchFacets.ts +56 -55
- package/src/constants.ts +15 -14
- package/src/contexts/AssetSourceDispatchContext.tsx +1 -1
- package/src/contexts/ToolOptionsContext.tsx +6 -5
- package/src/formSchema/index.test.ts +6 -5
- package/src/formSchema/index.ts +5 -5
- package/src/hooks/useBreakpointIndex.ts +6 -6
- package/src/hooks/useKeyPress.ts +2 -2
- package/src/hooks/usePortalPopoverProps.ts +1 -1
- package/src/modules/assets/actions.ts +8 -7
- package/src/modules/assets/deleteAndUpdateEpics.test.ts +18 -17
- package/src/modules/assets/fetchEpic.test.ts +12 -11
- package/src/modules/assets/index.ts +134 -133
- package/src/modules/assets/reducer.test.ts +9 -8
- package/src/modules/assets/tagsAndListenerEpics.test.ts +36 -35
- package/src/modules/debug/index.ts +3 -3
- package/src/modules/dialog/actions.ts +2 -2
- package/src/modules/dialog/epics.test.ts +29 -28
- package/src/modules/dialog/index.ts +36 -35
- package/src/modules/dialog/reducer.test.ts +31 -30
- package/src/modules/index.ts +9 -9
- package/src/modules/notifications/epics.test.ts +71 -70
- package/src/modules/notifications/index.ts +50 -49
- package/src/modules/notifications/reducer.test.ts +8 -7
- package/src/modules/search/index.test.ts +2 -1
- package/src/modules/search/index.ts +22 -22
- package/src/modules/selected/index.ts +2 -2
- package/src/modules/selectors.test.ts +4 -3
- package/src/modules/selectors.ts +5 -5
- package/src/modules/tags/epics.test.ts +16 -15
- package/src/modules/tags/index.test.ts +2 -1
- package/src/modules/tags/index.ts +82 -81
- package/src/modules/uploads/actions.ts +3 -3
- package/src/modules/uploads/epics.test.ts +13 -12
- package/src/modules/uploads/index.test.ts +8 -7
- package/src/modules/uploads/index.ts +48 -47
- package/src/operators/checkTagName.test.ts +7 -6
- package/src/operators/checkTagName.ts +6 -5
- package/src/operators/debugThrottle.ts +4 -4
- package/src/plugin.tsx +18 -18
- package/src/schemas/tag.ts +7 -7
- package/src/styled/react-select/creatable.tsx +40 -39
- package/src/styled/react-select/single.tsx +39 -38
- package/src/types/index.ts +4 -3
- package/src/utils/applyMediaTags.ts +11 -10
- package/src/utils/blocksToText.test.ts +5 -4
- package/src/utils/blocksToText.ts +2 -2
- package/src/utils/constructFilter.test.ts +15 -14
- package/src/utils/constructFilter.ts +7 -7
- package/src/utils/generatePreviewBlobUrl.test.ts +6 -5
- package/src/utils/generatePreviewBlobUrl.ts +2 -2
- package/src/utils/getAssetResolution.test.ts +3 -2
- package/src/utils/getDocumentAssetIds.test.ts +7 -6
- package/src/utils/getDocumentAssetIds.ts +2 -2
- package/src/utils/getSchemeColor.test.ts +1 -0
- package/src/utils/getSchemeColor.ts +9 -9
- package/src/utils/getTagSelectOptions.test.ts +6 -5
- package/src/utils/getTagSelectOptions.ts +1 -1
- package/src/utils/getUniqueDocuments.test.ts +4 -3
- package/src/utils/getUniqueDocuments.ts +2 -2
- package/src/utils/imageDprUrl.test.ts +4 -3
- package/src/utils/imageDprUrl.ts +1 -1
- package/src/utils/isSupportedAssetType.test.ts +1 -0
- package/src/utils/mediaField.ts +4 -3
- package/src/utils/sanitizeFormData.test.ts +14 -13
- package/src/utils/typeGuards.test.ts +2 -1
- package/src/utils/uploadSanityAsset.test.ts +5 -4
- package/src/utils/uploadSanityAsset.ts +17 -16
- package/src/utils/withMaxConcurrency.test.ts +5 -4
- package/src/utils/withMaxConcurrency.ts +4 -4
- package/src/utils/zodFormResolver.ts +17 -0
- package/v2-incompatible.js +2 -2
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import {describe, expect, it} from 'vitest'
|
|
2
|
-
|
|
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
|
})
|
|
@@ -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
|
-
|
|
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')
|
package/src/utils/imageDprUrl.ts
CHANGED
package/src/utils/mediaField.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
+
}
|
package/v2-incompatible.js
CHANGED
|
@@ -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
|
})
|