@sanity/sdk 0.0.0-alpha.21 → 0.0.0-alpha.23
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/dist/index.d.ts +428 -325
- package/dist/index.js +1618 -1553
- package/dist/index.js.map +1 -1
- package/package.json +6 -7
- package/src/_exports/index.ts +31 -30
- package/src/auth/authStore.test.ts +149 -104
- package/src/auth/authStore.ts +51 -100
- package/src/auth/handleAuthCallback.test.ts +67 -34
- package/src/auth/handleAuthCallback.ts +8 -7
- package/src/auth/logout.test.ts +61 -29
- package/src/auth/logout.ts +26 -28
- package/src/auth/refreshStampedToken.test.ts +9 -9
- package/src/auth/refreshStampedToken.ts +62 -56
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +5 -5
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +45 -47
- package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +4 -5
- package/src/auth/subscribeToStorageEventsAndSetToken.ts +22 -24
- package/src/client/clientStore.test.ts +131 -67
- package/src/client/clientStore.ts +117 -116
- package/src/comlink/controller/actions/destroyController.test.ts +38 -13
- package/src/comlink/controller/actions/destroyController.ts +11 -15
- package/src/comlink/controller/actions/getOrCreateChannel.test.ts +56 -27
- package/src/comlink/controller/actions/getOrCreateChannel.ts +37 -35
- package/src/comlink/controller/actions/getOrCreateController.test.ts +27 -16
- package/src/comlink/controller/actions/getOrCreateController.ts +23 -22
- package/src/comlink/controller/actions/releaseChannel.test.ts +37 -13
- package/src/comlink/controller/actions/releaseChannel.ts +22 -21
- package/src/comlink/controller/comlinkControllerStore.test.ts +65 -36
- package/src/comlink/controller/comlinkControllerStore.ts +44 -5
- package/src/comlink/node/actions/getOrCreateNode.test.ts +31 -15
- package/src/comlink/node/actions/getOrCreateNode.ts +30 -29
- package/src/comlink/node/actions/releaseNode.test.ts +75 -55
- package/src/comlink/node/actions/releaseNode.ts +19 -21
- package/src/comlink/node/comlinkNodeStore.test.ts +6 -11
- package/src/comlink/node/comlinkNodeStore.ts +22 -5
- package/src/config/authConfig.ts +79 -0
- package/src/config/sanityConfig.ts +48 -0
- package/src/datasets/datasets.test.ts +2 -2
- package/src/datasets/datasets.ts +18 -5
- package/src/document/actions.test.ts +22 -10
- package/src/document/actions.ts +44 -56
- package/src/document/applyDocumentActions.test.ts +96 -36
- package/src/document/applyDocumentActions.ts +140 -99
- package/src/document/documentStore.test.ts +103 -155
- package/src/document/documentStore.ts +247 -237
- package/src/document/listen.ts +56 -55
- package/src/document/patchOperations.ts +0 -43
- package/src/document/permissions.test.ts +25 -12
- package/src/document/permissions.ts +11 -4
- package/src/document/processActions.test.ts +41 -8
- package/src/document/reducers.test.ts +87 -16
- package/src/document/reducers.ts +2 -2
- package/src/document/sharedListener.test.ts +34 -16
- package/src/document/sharedListener.ts +33 -11
- package/src/preview/getPreviewState.test.ts +40 -39
- package/src/preview/getPreviewState.ts +68 -56
- package/src/preview/previewConstants.ts +43 -0
- package/src/preview/previewQuery.test.ts +1 -1
- package/src/preview/previewQuery.ts +4 -5
- package/src/preview/previewStore.test.ts +13 -58
- package/src/preview/previewStore.ts +7 -21
- package/src/preview/resolvePreview.test.ts +33 -104
- package/src/preview/resolvePreview.ts +11 -21
- package/src/preview/subscribeToStateAndFetchBatches.test.ts +96 -97
- package/src/preview/subscribeToStateAndFetchBatches.ts +85 -81
- package/src/preview/util.ts +1 -0
- package/src/project/project.test.ts +3 -3
- package/src/project/project.ts +28 -5
- package/src/projection/getProjectionState.test.ts +69 -49
- package/src/projection/getProjectionState.ts +42 -50
- package/src/projection/projectionQuery.ts +1 -1
- package/src/projection/projectionStore.test.ts +13 -51
- package/src/projection/projectionStore.ts +6 -18
- package/src/projection/resolveProjection.test.ts +32 -127
- package/src/projection/resolveProjection.ts +15 -28
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +105 -90
- package/src/projection/subscribeToStateAndFetchBatches.ts +94 -81
- package/src/projection/util.ts +2 -0
- package/src/projects/projects.test.ts +13 -4
- package/src/projects/projects.ts +6 -1
- package/src/query/queryStore.test.ts +10 -47
- package/src/query/queryStore.ts +151 -133
- package/src/query/queryStoreConstants.ts +2 -0
- package/src/store/createActionBinder.test.ts +153 -0
- package/src/store/createActionBinder.ts +176 -0
- package/src/store/createSanityInstance.test.ts +84 -0
- package/src/store/createSanityInstance.ts +124 -0
- package/src/store/createStateSourceAction.test.ts +196 -0
- package/src/store/createStateSourceAction.ts +260 -0
- package/src/store/createStoreInstance.test.ts +81 -0
- package/src/store/createStoreInstance.ts +80 -0
- package/src/store/createStoreState.test.ts +85 -0
- package/src/store/createStoreState.ts +92 -0
- package/src/store/defineStore.test.ts +18 -0
- package/src/store/defineStore.ts +81 -0
- package/src/users/reducers.test.ts +318 -0
- package/src/users/reducers.ts +88 -0
- package/src/users/types.ts +46 -4
- package/src/users/usersConstants.ts +4 -0
- package/src/users/usersStore.test.ts +350 -223
- package/src/users/usersStore.ts +285 -149
- package/src/utils/createFetcherStore.test.ts +6 -7
- package/src/utils/createFetcherStore.ts +150 -153
- package/src/{common/util.test.ts → utils/hashString.test.ts} +1 -1
- package/src/auth/fetchLoginUrls.test.ts +0 -163
- package/src/auth/fetchLoginUrls.ts +0 -74
- package/src/common/createLiveEventSubscriber.test.ts +0 -121
- package/src/common/createLiveEventSubscriber.ts +0 -55
- package/src/common/types.ts +0 -4
- package/src/instance/identity.test.ts +0 -46
- package/src/instance/identity.ts +0 -29
- package/src/instance/sanityInstance.test.ts +0 -77
- package/src/instance/sanityInstance.ts +0 -57
- package/src/instance/types.ts +0 -37
- package/src/preview/getPreviewProjection.ts +0 -45
- package/src/resources/README.md +0 -370
- package/src/resources/createAction.test.ts +0 -101
- package/src/resources/createAction.ts +0 -44
- package/src/resources/createResource.test.ts +0 -112
- package/src/resources/createResource.ts +0 -102
- package/src/resources/createStateSourceAction.test.ts +0 -114
- package/src/resources/createStateSourceAction.ts +0 -83
- package/src/resources/createStore.test.ts +0 -67
- package/src/resources/createStore.ts +0 -46
- package/src/store/createStore.test.ts +0 -108
- package/src/store/createStore.ts +0 -106
- /package/src/{common/util.ts → utils/hashString.ts} +0 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import {type ChannelInput, type ChannelInstance, type Controller} from '@sanity/comlink'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {bindActionGlobally} from '../../store/createActionBinder'
|
|
4
|
+
import {defineStore} from '../../store/defineStore'
|
|
4
5
|
import {type FrameMessage, type WindowMessage} from '../types'
|
|
5
|
-
import {destroyController} from './actions/destroyController'
|
|
6
|
+
import {destroyController as unboundDestroyController} from './actions/destroyController'
|
|
7
|
+
import {getOrCreateChannel as unboundGetOrCreateChannel} from './actions/getOrCreateChannel'
|
|
8
|
+
import {getOrCreateController as unboundGetOrCreateController} from './actions/getOrCreateController'
|
|
9
|
+
import {releaseChannel as unboundReleaseChannel} from './actions/releaseChannel'
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* Individual channel with its relevant options
|
|
@@ -26,7 +30,7 @@ export interface ComlinkControllerState {
|
|
|
26
30
|
channels: Map<string, ChannelEntry>
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
export const comlinkControllerStore =
|
|
33
|
+
export const comlinkControllerStore = defineStore<ComlinkControllerState>({
|
|
30
34
|
name: 'connectionStore',
|
|
31
35
|
getInitialState: () => {
|
|
32
36
|
const initialState = {
|
|
@@ -36,10 +40,45 @@ export const comlinkControllerStore = createResource<ComlinkControllerState>({
|
|
|
36
40
|
}
|
|
37
41
|
return initialState
|
|
38
42
|
},
|
|
39
|
-
initialize() {
|
|
43
|
+
initialize({instance}) {
|
|
40
44
|
return () => {
|
|
41
45
|
// destroying controller also destroys channels
|
|
42
|
-
destroyController(
|
|
46
|
+
destroyController(instance)
|
|
43
47
|
}
|
|
44
48
|
},
|
|
45
49
|
})
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Calls the destroy method on the controller and resets the controller state.
|
|
53
|
+
* @public
|
|
54
|
+
*/
|
|
55
|
+
export const destroyController = bindActionGlobally(
|
|
56
|
+
comlinkControllerStore,
|
|
57
|
+
unboundDestroyController,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Retrieve or create a channel to be used for communication between
|
|
62
|
+
* an application and the controller.
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
65
|
+
export const getOrCreateChannel = bindActionGlobally(
|
|
66
|
+
comlinkControllerStore,
|
|
67
|
+
unboundGetOrCreateChannel,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Initializes or fetches a controller to handle communication
|
|
72
|
+
* between an application and iframes.
|
|
73
|
+
* @public
|
|
74
|
+
*/
|
|
75
|
+
export const getOrCreateController = bindActionGlobally(
|
|
76
|
+
comlinkControllerStore,
|
|
77
|
+
unboundGetOrCreateController,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Signals to the store that the consumer has stopped using the channel
|
|
82
|
+
* @public
|
|
83
|
+
*/
|
|
84
|
+
export const releaseChannel = bindActionGlobally(comlinkControllerStore, unboundReleaseChannel)
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import * as comlink from '@sanity/comlink'
|
|
2
|
+
import {type Node} from '@sanity/comlink'
|
|
2
3
|
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
|
3
4
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {type
|
|
5
|
+
import {createSanityInstance} from '../../../store/createSanityInstance'
|
|
6
|
+
import {createStoreState} from '../../../store/createStoreState'
|
|
7
|
+
import {type FrameMessage, type WindowMessage} from '../../types'
|
|
8
|
+
import {type ComlinkNodeState} from '../comlinkNodeStore'
|
|
7
9
|
import {getOrCreateNode} from './getOrCreateNode'
|
|
8
10
|
|
|
9
11
|
vi.mock('@sanity/comlink', () => ({
|
|
@@ -17,35 +19,49 @@ const nodeConfig = {
|
|
|
17
19
|
name: 'test-node',
|
|
18
20
|
connectTo: 'parent',
|
|
19
21
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
|
|
23
|
+
describe('getOrCreateNode', () => {
|
|
24
|
+
const instance = createSanityInstance({
|
|
25
|
+
projectId: 'test-project-id',
|
|
26
|
+
dataset: 'test-dataset',
|
|
27
|
+
})
|
|
28
|
+
let state: ReturnType<typeof createStoreState<ComlinkNodeState>>
|
|
29
|
+
let mockNode: Partial<Node<WindowMessage, FrameMessage>> & {
|
|
30
|
+
start: ReturnType<typeof vi.fn>
|
|
31
|
+
stop: ReturnType<typeof vi.fn>
|
|
32
|
+
}
|
|
22
33
|
|
|
23
34
|
beforeEach(() => {
|
|
24
|
-
|
|
35
|
+
mockNode = {start: vi.fn(), stop: vi.fn()}
|
|
36
|
+
vi.mocked(comlink.createNode).mockReturnValue(mockNode as Node<WindowMessage, FrameMessage>)
|
|
37
|
+
state = createStoreState<ComlinkNodeState>({nodes: new Map()})
|
|
25
38
|
vi.clearAllMocks()
|
|
26
39
|
})
|
|
27
40
|
|
|
28
41
|
it('should create and start a node', () => {
|
|
29
|
-
const node = getOrCreateNode(instance, nodeConfig)
|
|
42
|
+
const node = getOrCreateNode({state, instance}, nodeConfig)
|
|
30
43
|
|
|
31
44
|
expect(comlink.createNode).toHaveBeenCalledWith(nodeConfig)
|
|
32
45
|
expect(node.start).toHaveBeenCalled()
|
|
33
46
|
})
|
|
34
47
|
|
|
35
|
-
it('
|
|
36
|
-
const node = getOrCreateNode(instance, nodeConfig)
|
|
48
|
+
it('sshould store the node in nodeStore', () => {
|
|
49
|
+
const node = getOrCreateNode({state, instance}, nodeConfig)
|
|
37
50
|
|
|
38
|
-
expect(getOrCreateNode(instance, nodeConfig)).toBe(node)
|
|
51
|
+
expect(getOrCreateNode({state, instance}, nodeConfig)).toBe(node)
|
|
39
52
|
})
|
|
40
53
|
|
|
41
54
|
it('should throw error when trying to create node with different options', () => {
|
|
42
|
-
getOrCreateNode(instance, nodeConfig)
|
|
55
|
+
getOrCreateNode({state, instance}, nodeConfig)
|
|
43
56
|
|
|
44
57
|
expect(() =>
|
|
45
|
-
getOrCreateNode(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
getOrCreateNode(
|
|
59
|
+
{state, instance},
|
|
60
|
+
{
|
|
61
|
+
...nodeConfig,
|
|
62
|
+
connectTo: 'window',
|
|
63
|
+
},
|
|
64
|
+
),
|
|
49
65
|
).toThrow('Node "test-node" already exists with different options')
|
|
50
66
|
})
|
|
51
67
|
})
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {createNode, type Node, type NodeInput} from '@sanity/comlink'
|
|
2
2
|
import {isEqual} from 'lodash-es'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {type StoreContext} from '../../../store/defineStore'
|
|
5
5
|
import {type FrameMessage, type WindowMessage} from '../../types'
|
|
6
|
-
import {
|
|
6
|
+
import {type ComlinkNodeState} from '../comlinkNodeStore'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Retrieve or create a node to be used for communication between
|
|
@@ -11,35 +11,36 @@ import {comlinkNodeStore} from '../comlinkNodeStore'
|
|
|
11
11
|
* be created within a frame / window to communicate with the controller.
|
|
12
12
|
* @public
|
|
13
13
|
*/
|
|
14
|
-
export const getOrCreateNode =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
state.set('incrementNodeRefCount', {
|
|
26
|
-
nodes: new Map(nodes).set(options.name, {
|
|
27
|
-
...existing,
|
|
28
|
-
refCount: existing.refCount + 1,
|
|
29
|
-
}),
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
existing.node.start()
|
|
33
|
-
return existing.node
|
|
14
|
+
export const getOrCreateNode = (
|
|
15
|
+
{state}: StoreContext<ComlinkNodeState>,
|
|
16
|
+
options: NodeInput,
|
|
17
|
+
): Node<WindowMessage, FrameMessage> => {
|
|
18
|
+
const nodes = state.get().nodes
|
|
19
|
+
const existing = nodes.get(options.name)
|
|
20
|
+
|
|
21
|
+
// limit nodes to one per name
|
|
22
|
+
if (existing) {
|
|
23
|
+
if (!isEqual(existing.options, options)) {
|
|
24
|
+
throw new Error(`Node "${options.name}" already exists with different options`)
|
|
34
25
|
}
|
|
35
26
|
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
state.set('incrementNodeRefCount', {
|
|
28
|
+
nodes: new Map(nodes).set(options.name, {
|
|
29
|
+
...existing,
|
|
30
|
+
refCount: existing.refCount + 1,
|
|
31
|
+
}),
|
|
32
|
+
})
|
|
38
33
|
|
|
39
|
-
|
|
34
|
+
existing.node.start()
|
|
35
|
+
return existing.node
|
|
36
|
+
}
|
|
40
37
|
|
|
41
|
-
|
|
38
|
+
const node: Node<WindowMessage, FrameMessage> = createNode(options)
|
|
39
|
+
node.start()
|
|
42
40
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
})
|
|
41
|
+
nodes.set(options.name, {node, options, refCount: 1})
|
|
42
|
+
|
|
43
|
+
state.set('createNode', {nodes})
|
|
44
|
+
|
|
45
|
+
return node
|
|
46
|
+
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
+
import {type Node} from '@sanity/comlink'
|
|
1
2
|
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {type
|
|
6
|
-
import {
|
|
7
|
-
import {comlinkNodeStore} from '../comlinkNodeStore'
|
|
8
|
-
import {getOrCreateNode} from './getOrCreateNode'
|
|
4
|
+
import {createSanityInstance, type SanityInstance} from '../../../store/createSanityInstance'
|
|
5
|
+
import {createStoreState} from '../../../store/createStoreState'
|
|
6
|
+
import {type FrameMessage, type WindowMessage} from '../../types'
|
|
7
|
+
import {type ComlinkNodeState, type NodeEntry} from '../comlinkNodeStore'
|
|
9
8
|
import {releaseNode} from './releaseNode'
|
|
10
9
|
|
|
11
10
|
const nodeConfig = {
|
|
@@ -13,104 +12,125 @@ const nodeConfig = {
|
|
|
13
12
|
connectTo: 'parent',
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
vi.mock('@sanity/comlink', () => ({
|
|
17
|
-
createNode: vi.fn(() => ({
|
|
18
|
-
start: vi.fn(),
|
|
19
|
-
stop: vi.fn(),
|
|
20
|
-
})),
|
|
21
|
-
}))
|
|
22
|
-
|
|
23
15
|
describe('releaseNode', () => {
|
|
24
16
|
let instance: SanityInstance
|
|
17
|
+
let state: ReturnType<typeof createStoreState<ComlinkNodeState>>
|
|
18
|
+
let mockNode: Partial<Node<WindowMessage, FrameMessage>> & {
|
|
19
|
+
start: ReturnType<typeof vi.fn>
|
|
20
|
+
stop: ReturnType<typeof vi.fn>
|
|
21
|
+
}
|
|
25
22
|
|
|
26
23
|
beforeEach(() => {
|
|
27
|
-
instance = createSanityInstance(
|
|
24
|
+
instance = createSanityInstance({
|
|
25
|
+
projectId: 'test-project-id',
|
|
26
|
+
dataset: 'test-dataset',
|
|
27
|
+
})
|
|
28
|
+
mockNode = {start: vi.fn(), stop: vi.fn()}
|
|
29
|
+
state = createStoreState<ComlinkNodeState>({nodes: new Map()})
|
|
28
30
|
vi.clearAllMocks()
|
|
29
31
|
})
|
|
30
32
|
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
instance.dispose()
|
|
35
|
+
})
|
|
36
|
+
|
|
31
37
|
it('should stop and remove node when released', () => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
// Set up a node in the state
|
|
39
|
+
const nodes = new Map()
|
|
40
|
+
nodes.set('test-node', {
|
|
41
|
+
node: mockNode as Node<WindowMessage, FrameMessage>,
|
|
42
|
+
options: nodeConfig,
|
|
43
|
+
refCount: 1,
|
|
44
|
+
})
|
|
45
|
+
state.set('setup', {nodes})
|
|
36
46
|
|
|
37
|
-
expect(
|
|
47
|
+
expect(state.get().nodes.has('test-node')).toBe(true)
|
|
38
48
|
|
|
39
49
|
// Release the node
|
|
40
|
-
releaseNode(instance, 'test-node')
|
|
50
|
+
releaseNode({state, instance}, 'test-node')
|
|
41
51
|
|
|
42
52
|
// Check node is removed
|
|
43
|
-
expect(
|
|
44
|
-
expect(
|
|
53
|
+
expect(mockNode.stop).toHaveBeenCalled()
|
|
54
|
+
expect(state.get().nodes.has('test-node')).toBe(false)
|
|
45
55
|
})
|
|
46
56
|
|
|
47
57
|
it('should not stop the node if refCount is still above 0', () => {
|
|
48
58
|
// Create a node twice to increment refCount
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
const nodes = new Map()
|
|
60
|
+
nodes.set('test-node', {
|
|
61
|
+
node: mockNode as Node<WindowMessage, FrameMessage>,
|
|
62
|
+
options: nodeConfig,
|
|
63
|
+
refCount: 2,
|
|
64
|
+
})
|
|
65
|
+
state.set('setup', {nodes})
|
|
52
66
|
|
|
53
67
|
// Release once
|
|
54
|
-
releaseNode(instance, 'test-node')
|
|
68
|
+
releaseNode({state, instance}, 'test-node')
|
|
55
69
|
|
|
56
70
|
// Node should not be stopped
|
|
57
|
-
expect(
|
|
71
|
+
expect(mockNode.stop).not.toHaveBeenCalled()
|
|
58
72
|
|
|
59
73
|
// Verify refCount is 1
|
|
60
|
-
const
|
|
61
|
-
const nodeEntry = store.state.get().nodes.get('test-node')
|
|
74
|
+
const nodeEntry = state.get().nodes.get('test-node') as NodeEntry
|
|
62
75
|
expect(nodeEntry?.refCount).toBe(1)
|
|
63
76
|
})
|
|
64
77
|
|
|
65
78
|
it('should handle multiple releases gracefully', () => {
|
|
66
|
-
//
|
|
67
|
-
|
|
79
|
+
// Set up a node in the state
|
|
80
|
+
const nodes = new Map()
|
|
81
|
+
nodes.set('test-node', {
|
|
82
|
+
node: mockNode as Node<WindowMessage, FrameMessage>,
|
|
83
|
+
options: nodeConfig,
|
|
84
|
+
refCount: 1,
|
|
85
|
+
})
|
|
86
|
+
state.set('setup', {nodes})
|
|
68
87
|
|
|
69
88
|
// Release multiple times
|
|
70
|
-
releaseNode(instance, 'test-node')
|
|
71
|
-
releaseNode(instance, 'test-node')
|
|
72
|
-
releaseNode(instance, 'test-node')
|
|
73
|
-
|
|
74
|
-
// Verify
|
|
75
|
-
|
|
76
|
-
|
|
89
|
+
releaseNode({state, instance}, 'test-node')
|
|
90
|
+
releaseNode({state, instance}, 'test-node')
|
|
91
|
+
releaseNode({state, instance}, 'test-node')
|
|
92
|
+
|
|
93
|
+
// Verify node is removed after first release
|
|
94
|
+
expect(state.get().nodes.has('test-node')).toBe(false)
|
|
95
|
+
// Stop should be called exactly once
|
|
96
|
+
expect(mockNode.stop).toHaveBeenCalledTimes(1)
|
|
77
97
|
})
|
|
78
98
|
|
|
79
99
|
it('should handle releasing non-existent nodes', () => {
|
|
80
100
|
// Should not throw when releasing non-existent node
|
|
81
|
-
expect(() => releaseNode(instance, 'non-existent')).not.toThrow()
|
|
101
|
+
expect(() => releaseNode({state, instance}, 'non-existent')).not.toThrow()
|
|
82
102
|
})
|
|
83
103
|
|
|
84
104
|
it('should maintain correct state after complex operations', () => {
|
|
85
|
-
//
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
105
|
+
// Set up a node with refCount = 3
|
|
106
|
+
const nodes = new Map()
|
|
107
|
+
nodes.set('test-node', {
|
|
108
|
+
node: mockNode as Node<WindowMessage, FrameMessage>,
|
|
109
|
+
options: nodeConfig,
|
|
110
|
+
refCount: 3,
|
|
111
|
+
})
|
|
112
|
+
state.set('setup', {nodes})
|
|
92
113
|
|
|
93
114
|
// Initial refCount should be 3
|
|
115
|
+
let nodeEntry = state.get().nodes.get('test-node') as NodeEntry
|
|
94
116
|
expect(nodeEntry?.refCount).toBe(3)
|
|
95
117
|
|
|
96
118
|
// Release twice
|
|
97
|
-
releaseNode(instance, 'test-node')
|
|
98
|
-
releaseNode(instance, 'test-node')
|
|
119
|
+
releaseNode({state, instance}, 'test-node')
|
|
120
|
+
releaseNode({state, instance}, 'test-node')
|
|
99
121
|
|
|
100
|
-
nodeEntry =
|
|
122
|
+
nodeEntry = state.get().nodes.get('test-node') as NodeEntry
|
|
101
123
|
expect(nodeEntry?.refCount).toBe(1)
|
|
102
124
|
|
|
103
125
|
// Verify node hasn't been stopped yet
|
|
104
|
-
|
|
105
|
-
expect(stopSpy).not.toHaveBeenCalled()
|
|
126
|
+
expect(mockNode.stop).not.toHaveBeenCalled()
|
|
106
127
|
|
|
107
128
|
// Release final reference
|
|
108
|
-
releaseNode(instance, 'test-node')
|
|
129
|
+
releaseNode({state, instance}, 'test-node')
|
|
109
130
|
|
|
110
131
|
// Verify node was stopped
|
|
111
|
-
expect(
|
|
132
|
+
expect(mockNode.stop).toHaveBeenCalled()
|
|
112
133
|
|
|
113
|
-
|
|
114
|
-
expect(store.state.get().nodes.has('test-node')).toBe(false)
|
|
134
|
+
expect(state.get().nodes.has('test-node')).toBe(false)
|
|
115
135
|
})
|
|
116
136
|
})
|
|
@@ -1,30 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import {type StoreContext} from '../../../store/defineStore'
|
|
2
|
+
import {type ComlinkNodeState} from '../comlinkNodeStore'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Signals to the store that the consumer has stopped using the node
|
|
6
6
|
* @public
|
|
7
7
|
*/
|
|
8
|
-
export const releaseNode =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const nodeEntry = nodes.get(name)
|
|
8
|
+
export const releaseNode = ({state}: StoreContext<ComlinkNodeState>, name: string): void => {
|
|
9
|
+
const nodes = state.get().nodes
|
|
10
|
+
const nodeEntry = nodes.get(name)
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
if (nodeEntry) {
|
|
13
|
+
const newRefCount = nodeEntry.refCount === 0 ? 0 : nodeEntry.refCount - 1
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
15
|
+
if (newRefCount === 0) {
|
|
16
|
+
nodeEntry.node.stop()
|
|
17
|
+
nodes.delete(name)
|
|
18
|
+
state.set('releaseNode', {nodes: new Map(nodes)})
|
|
19
|
+
} else {
|
|
20
|
+
state.set('releaseNode', {
|
|
21
|
+
nodes: new Map(nodes).set(name, {
|
|
22
|
+
...nodeEntry,
|
|
23
|
+
refCount: newRefCount,
|
|
24
|
+
}),
|
|
25
|
+
})
|
|
28
26
|
}
|
|
29
27
|
}
|
|
30
|
-
}
|
|
28
|
+
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import {type Node} from '@sanity/comlink'
|
|
2
2
|
import {beforeEach, describe, expect, it} from 'vitest'
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {type SanityInstance} from '../../instance/types'
|
|
7
|
-
import {createResourceState} from '../../resources/createResource'
|
|
4
|
+
import {createSanityInstance, type SanityInstance} from '../../store/createSanityInstance'
|
|
5
|
+
import {createStoreState} from '../../store/createStoreState'
|
|
8
6
|
import {type FrameMessage, type WindowMessage} from '../types'
|
|
9
7
|
import {comlinkNodeStore} from './comlinkNodeStore'
|
|
10
8
|
|
|
@@ -12,7 +10,7 @@ describe('nodeStore', () => {
|
|
|
12
10
|
let instance: SanityInstance
|
|
13
11
|
|
|
14
12
|
beforeEach(() => {
|
|
15
|
-
instance = createSanityInstance(
|
|
13
|
+
instance = createSanityInstance({projectId: 'test-project-id', dataset: 'test-dataset'})
|
|
16
14
|
})
|
|
17
15
|
|
|
18
16
|
it('should have correct initial state', () => {
|
|
@@ -34,13 +32,10 @@ describe('nodeStore', () => {
|
|
|
34
32
|
refCount: 1,
|
|
35
33
|
})
|
|
36
34
|
|
|
37
|
-
const cleanup = comlinkNodeStore.initialize
|
|
38
|
-
{
|
|
39
|
-
instance,
|
|
40
|
-
state: createResourceState(initialState),
|
|
41
|
-
},
|
|
35
|
+
const cleanup = comlinkNodeStore.initialize?.({
|
|
42
36
|
instance,
|
|
43
|
-
|
|
37
|
+
state: createStoreState(initialState),
|
|
38
|
+
})
|
|
44
39
|
|
|
45
40
|
cleanup?.()
|
|
46
41
|
expect(mockNode.stop).toHaveBeenCalled()
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import {type Node, type NodeInput} from '@sanity/comlink'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {bindActionGlobally} from '../../store/createActionBinder'
|
|
4
|
+
import {defineStore} from '../../store/defineStore'
|
|
4
5
|
import {type FrameMessage, type WindowMessage} from '../types'
|
|
6
|
+
import {getOrCreateNode as unboundGetOrCreateNode} from './actions/getOrCreateNode'
|
|
7
|
+
import {releaseNode as unboundReleaseNode} from './actions/releaseNode'
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* Individual node with its relevant options
|
|
@@ -23,17 +26,31 @@ export interface ComlinkNodeState {
|
|
|
23
26
|
nodes: Map<string, NodeEntry>
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
export const comlinkNodeStore =
|
|
29
|
+
export const comlinkNodeStore = defineStore<ComlinkNodeState>({
|
|
27
30
|
name: 'nodeStore',
|
|
28
31
|
getInitialState: () => ({
|
|
29
32
|
nodes: new Map(),
|
|
30
33
|
}),
|
|
31
|
-
|
|
34
|
+
|
|
35
|
+
initialize({state}) {
|
|
32
36
|
return () => {
|
|
33
|
-
|
|
34
|
-
state.nodes.forEach(({node}) => {
|
|
37
|
+
state.get().nodes.forEach(({node}) => {
|
|
35
38
|
node.stop()
|
|
36
39
|
})
|
|
37
40
|
}
|
|
38
41
|
},
|
|
39
42
|
})
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Signals to the store that the consumer has stopped using the node
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
export const releaseNode = bindActionGlobally(comlinkNodeStore, unboundReleaseNode)
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Retrieve or create a node to be used for communication between
|
|
52
|
+
* an application and the controller -- specifically, a node should
|
|
53
|
+
* be created within a frame / window to communicate with the controller.
|
|
54
|
+
* @public
|
|
55
|
+
*/
|
|
56
|
+
export const getOrCreateNode = bindActionGlobally(comlinkNodeStore, unboundGetOrCreateNode)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {type ClientConfig, type SanityClient} from '@sanity/client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for an authentication provider
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export interface AuthProvider {
|
|
8
|
+
/**
|
|
9
|
+
* Unique identifier for the auth provider (e.g., 'google', 'github')
|
|
10
|
+
*/
|
|
11
|
+
name: string
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Display name for the auth provider in the UI
|
|
15
|
+
*/
|
|
16
|
+
title: string
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Complete authentication URL including callback and token parameters
|
|
20
|
+
*/
|
|
21
|
+
url: string
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Optional URL for direct sign-up flow
|
|
25
|
+
*/
|
|
26
|
+
signUpUrl?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Configuration options for creating an auth store.
|
|
31
|
+
*
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
34
|
+
export interface AuthConfig {
|
|
35
|
+
/**
|
|
36
|
+
* The initial location href to use when handling auth callbacks.
|
|
37
|
+
* Defaults to the current window location if available.
|
|
38
|
+
*/
|
|
39
|
+
initialLocationHref?: string
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Factory function to create a SanityClient instance.
|
|
43
|
+
* Defaults to the standard Sanity client factory if not provided.
|
|
44
|
+
*/
|
|
45
|
+
clientFactory?: (config: ClientConfig) => SanityClient
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Custom authentication providers to use instead of or in addition to the default ones.
|
|
49
|
+
* Can be an array of providers or a function that takes the default providers and returns
|
|
50
|
+
* a modified array or a Promise resolving to one.
|
|
51
|
+
*/
|
|
52
|
+
providers?: AuthProvider[] | ((prev: AuthProvider[]) => AuthProvider[] | Promise<AuthProvider[]>)
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The API hostname for requests. Usually leave this undefined, but it can be set
|
|
56
|
+
* if using a custom domain or CNAME for the API endpoint.
|
|
57
|
+
*/
|
|
58
|
+
apiHost?: string
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Storage implementation to persist authentication state.
|
|
62
|
+
* Defaults to `localStorage` if available.
|
|
63
|
+
*/
|
|
64
|
+
storageArea?: Storage
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* A callback URL for your application.
|
|
68
|
+
* If none is provided, the auth API will redirect back to the current location (`location.href`).
|
|
69
|
+
* When handling callbacks, this URL's pathname is checked to ensure it matches the callback.
|
|
70
|
+
*/
|
|
71
|
+
callbackUrl?: string
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* A static authentication token to use instead of handling the OAuth flow.
|
|
75
|
+
* When provided, the auth store will remain in a logged-in state with this token,
|
|
76
|
+
* ignoring any storage or callback handling.
|
|
77
|
+
*/
|
|
78
|
+
token?: string
|
|
79
|
+
}
|