@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.
Files changed (127) hide show
  1. package/dist/index.d.ts +428 -325
  2. package/dist/index.js +1618 -1553
  3. package/dist/index.js.map +1 -1
  4. package/package.json +6 -7
  5. package/src/_exports/index.ts +31 -30
  6. package/src/auth/authStore.test.ts +149 -104
  7. package/src/auth/authStore.ts +51 -100
  8. package/src/auth/handleAuthCallback.test.ts +67 -34
  9. package/src/auth/handleAuthCallback.ts +8 -7
  10. package/src/auth/logout.test.ts +61 -29
  11. package/src/auth/logout.ts +26 -28
  12. package/src/auth/refreshStampedToken.test.ts +9 -9
  13. package/src/auth/refreshStampedToken.ts +62 -56
  14. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +5 -5
  15. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +45 -47
  16. package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +4 -5
  17. package/src/auth/subscribeToStorageEventsAndSetToken.ts +22 -24
  18. package/src/client/clientStore.test.ts +131 -67
  19. package/src/client/clientStore.ts +117 -116
  20. package/src/comlink/controller/actions/destroyController.test.ts +38 -13
  21. package/src/comlink/controller/actions/destroyController.ts +11 -15
  22. package/src/comlink/controller/actions/getOrCreateChannel.test.ts +56 -27
  23. package/src/comlink/controller/actions/getOrCreateChannel.ts +37 -35
  24. package/src/comlink/controller/actions/getOrCreateController.test.ts +27 -16
  25. package/src/comlink/controller/actions/getOrCreateController.ts +23 -22
  26. package/src/comlink/controller/actions/releaseChannel.test.ts +37 -13
  27. package/src/comlink/controller/actions/releaseChannel.ts +22 -21
  28. package/src/comlink/controller/comlinkControllerStore.test.ts +65 -36
  29. package/src/comlink/controller/comlinkControllerStore.ts +44 -5
  30. package/src/comlink/node/actions/getOrCreateNode.test.ts +31 -15
  31. package/src/comlink/node/actions/getOrCreateNode.ts +30 -29
  32. package/src/comlink/node/actions/releaseNode.test.ts +75 -55
  33. package/src/comlink/node/actions/releaseNode.ts +19 -21
  34. package/src/comlink/node/comlinkNodeStore.test.ts +6 -11
  35. package/src/comlink/node/comlinkNodeStore.ts +22 -5
  36. package/src/config/authConfig.ts +79 -0
  37. package/src/config/sanityConfig.ts +48 -0
  38. package/src/datasets/datasets.test.ts +2 -2
  39. package/src/datasets/datasets.ts +18 -5
  40. package/src/document/actions.test.ts +22 -10
  41. package/src/document/actions.ts +44 -56
  42. package/src/document/applyDocumentActions.test.ts +96 -36
  43. package/src/document/applyDocumentActions.ts +140 -99
  44. package/src/document/documentStore.test.ts +103 -155
  45. package/src/document/documentStore.ts +247 -237
  46. package/src/document/listen.ts +56 -55
  47. package/src/document/patchOperations.ts +0 -43
  48. package/src/document/permissions.test.ts +25 -12
  49. package/src/document/permissions.ts +11 -4
  50. package/src/document/processActions.test.ts +41 -8
  51. package/src/document/reducers.test.ts +87 -16
  52. package/src/document/reducers.ts +2 -2
  53. package/src/document/sharedListener.test.ts +34 -16
  54. package/src/document/sharedListener.ts +33 -11
  55. package/src/preview/getPreviewState.test.ts +40 -39
  56. package/src/preview/getPreviewState.ts +68 -56
  57. package/src/preview/previewConstants.ts +43 -0
  58. package/src/preview/previewQuery.test.ts +1 -1
  59. package/src/preview/previewQuery.ts +4 -5
  60. package/src/preview/previewStore.test.ts +13 -58
  61. package/src/preview/previewStore.ts +7 -21
  62. package/src/preview/resolvePreview.test.ts +33 -104
  63. package/src/preview/resolvePreview.ts +11 -21
  64. package/src/preview/subscribeToStateAndFetchBatches.test.ts +96 -97
  65. package/src/preview/subscribeToStateAndFetchBatches.ts +85 -81
  66. package/src/preview/util.ts +1 -0
  67. package/src/project/project.test.ts +3 -3
  68. package/src/project/project.ts +28 -5
  69. package/src/projection/getProjectionState.test.ts +69 -49
  70. package/src/projection/getProjectionState.ts +42 -50
  71. package/src/projection/projectionQuery.ts +1 -1
  72. package/src/projection/projectionStore.test.ts +13 -51
  73. package/src/projection/projectionStore.ts +6 -18
  74. package/src/projection/resolveProjection.test.ts +32 -127
  75. package/src/projection/resolveProjection.ts +15 -28
  76. package/src/projection/subscribeToStateAndFetchBatches.test.ts +105 -90
  77. package/src/projection/subscribeToStateAndFetchBatches.ts +94 -81
  78. package/src/projection/util.ts +2 -0
  79. package/src/projects/projects.test.ts +13 -4
  80. package/src/projects/projects.ts +6 -1
  81. package/src/query/queryStore.test.ts +10 -47
  82. package/src/query/queryStore.ts +151 -133
  83. package/src/query/queryStoreConstants.ts +2 -0
  84. package/src/store/createActionBinder.test.ts +153 -0
  85. package/src/store/createActionBinder.ts +176 -0
  86. package/src/store/createSanityInstance.test.ts +84 -0
  87. package/src/store/createSanityInstance.ts +124 -0
  88. package/src/store/createStateSourceAction.test.ts +196 -0
  89. package/src/store/createStateSourceAction.ts +260 -0
  90. package/src/store/createStoreInstance.test.ts +81 -0
  91. package/src/store/createStoreInstance.ts +80 -0
  92. package/src/store/createStoreState.test.ts +85 -0
  93. package/src/store/createStoreState.ts +92 -0
  94. package/src/store/defineStore.test.ts +18 -0
  95. package/src/store/defineStore.ts +81 -0
  96. package/src/users/reducers.test.ts +318 -0
  97. package/src/users/reducers.ts +88 -0
  98. package/src/users/types.ts +46 -4
  99. package/src/users/usersConstants.ts +4 -0
  100. package/src/users/usersStore.test.ts +350 -223
  101. package/src/users/usersStore.ts +285 -149
  102. package/src/utils/createFetcherStore.test.ts +6 -7
  103. package/src/utils/createFetcherStore.ts +150 -153
  104. package/src/{common/util.test.ts → utils/hashString.test.ts} +1 -1
  105. package/src/auth/fetchLoginUrls.test.ts +0 -163
  106. package/src/auth/fetchLoginUrls.ts +0 -74
  107. package/src/common/createLiveEventSubscriber.test.ts +0 -121
  108. package/src/common/createLiveEventSubscriber.ts +0 -55
  109. package/src/common/types.ts +0 -4
  110. package/src/instance/identity.test.ts +0 -46
  111. package/src/instance/identity.ts +0 -29
  112. package/src/instance/sanityInstance.test.ts +0 -77
  113. package/src/instance/sanityInstance.ts +0 -57
  114. package/src/instance/types.ts +0 -37
  115. package/src/preview/getPreviewProjection.ts +0 -45
  116. package/src/resources/README.md +0 -370
  117. package/src/resources/createAction.test.ts +0 -101
  118. package/src/resources/createAction.ts +0 -44
  119. package/src/resources/createResource.test.ts +0 -112
  120. package/src/resources/createResource.ts +0 -102
  121. package/src/resources/createStateSourceAction.test.ts +0 -114
  122. package/src/resources/createStateSourceAction.ts +0 -83
  123. package/src/resources/createStore.test.ts +0 -67
  124. package/src/resources/createStore.ts +0 -46
  125. package/src/store/createStore.test.ts +0 -108
  126. package/src/store/createStore.ts +0 -106
  127. /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 {createResource} from '../../resources/createResource'
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 = createResource<ComlinkControllerState>({
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(this)
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 {config} from '../../../../test/fixtures'
5
- import {createSanityInstance} from '../../../instance/sanityInstance'
6
- import {type SanityInstance} from '../../../instance/types'
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
- describe('createNode', () => {
21
- let instance: SanityInstance
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
- instance = createSanityInstance(config)
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('should store the node in nodeStore', () => {
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(instance, {
46
- ...nodeConfig,
47
- connectTo: 'window',
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 {createAction} from '../../../resources/createAction'
4
+ import {type StoreContext} from '../../../store/defineStore'
5
5
  import {type FrameMessage, type WindowMessage} from '../../types'
6
- import {comlinkNodeStore} from '../comlinkNodeStore'
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 = createAction(comlinkNodeStore, ({state}) => {
15
- return (options: NodeInput) => {
16
- const nodes = state.get().nodes
17
- const existing = nodes.get(options.name)
18
-
19
- // limit nodes to one per name
20
- if (existing) {
21
- if (!isEqual(existing.options, options)) {
22
- throw new Error(`Node "${options.name}" already exists with different options`)
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
- const node: Node<WindowMessage, FrameMessage> = createNode(options)
37
- node.start()
27
+ state.set('incrementNodeRefCount', {
28
+ nodes: new Map(nodes).set(options.name, {
29
+ ...existing,
30
+ refCount: existing.refCount + 1,
31
+ }),
32
+ })
38
33
 
39
- nodes.set(options.name, {node, options, refCount: 1})
34
+ existing.node.start()
35
+ return existing.node
36
+ }
40
37
 
41
- state.set('createNode', {nodes})
38
+ const node: Node<WindowMessage, FrameMessage> = createNode(options)
39
+ node.start()
42
40
 
43
- return node
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 {config} from '../../../../test/fixtures'
4
- import {createSanityInstance} from '../../../instance/sanityInstance'
5
- import {type SanityInstance} from '../../../instance/types'
6
- import {getOrCreateResource} from '../../../resources/createResource'
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(config)
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
- const store = getOrCreateResource(instance, comlinkNodeStore)
33
- // Create a node
34
- const node = getOrCreateNode(instance, nodeConfig)
35
- const stopSpy = vi.spyOn(node, 'stop')
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(store.state.get().nodes.has('test-node')).toBe(true)
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(stopSpy).toHaveBeenCalled()
44
- expect(store.state.get().nodes.has('test-node')).toBe(false)
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 node = getOrCreateNode(instance, nodeConfig)
50
- getOrCreateNode(instance, nodeConfig)
51
- const stopSpy = vi.spyOn(node, 'stop')
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(stopSpy).not.toHaveBeenCalled()
71
+ expect(mockNode.stop).not.toHaveBeenCalled()
58
72
 
59
73
  // Verify refCount is 1
60
- const store = getOrCreateResource(instance, comlinkNodeStore)
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
- // Create a node
67
- getOrCreateNode(instance, nodeConfig)
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 refCount doesn't go below 0
75
- const store = getOrCreateResource(instance, comlinkNodeStore)
76
- expect(store.state.get().nodes.has('test-node')).toBe(false)
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
- // Create node multiple times
86
- const node = getOrCreateNode(instance, nodeConfig)
87
- getOrCreateNode(instance, nodeConfig)
88
- getOrCreateNode(instance, nodeConfig)
89
-
90
- const store = getOrCreateResource(instance, comlinkNodeStore)
91
- let nodeEntry = store.state.get().nodes.get('test-node')
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 = store.state.get().nodes.get('test-node')
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
- const stopSpy = vi.spyOn(node, 'stop')
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(stopSpy).toHaveBeenCalled()
132
+ expect(mockNode.stop).toHaveBeenCalled()
112
133
 
113
- nodeEntry = store.state.get().nodes.get('test-node')
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 {createAction} from '../../../resources/createAction'
2
- import {comlinkNodeStore} from '../comlinkNodeStore'
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 = createAction(comlinkNodeStore, ({state}) => {
9
- return (name: string) => {
10
- const nodes = state.get().nodes
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
- if (nodeEntry) {
14
- const newRefCount = nodeEntry.refCount === 0 ? 0 : nodeEntry.refCount - 1
12
+ if (nodeEntry) {
13
+ const newRefCount = nodeEntry.refCount === 0 ? 0 : nodeEntry.refCount - 1
15
14
 
16
- if (newRefCount === 0) {
17
- nodeEntry.node.stop()
18
- nodes.delete(name)
19
- state.set('releaseNode', {nodes: new Map(nodes)})
20
- } else {
21
- state.set('releaseNode', {
22
- nodes: new Map(nodes).set(name, {
23
- ...nodeEntry,
24
- refCount: newRefCount,
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 {config} from '../../../test/fixtures'
5
- import {createSanityInstance} from '../../instance/sanityInstance'
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(config)
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!.call(
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 {createResource} from '../../resources/createResource'
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 = createResource<ComlinkNodeState>({
29
+ export const comlinkNodeStore = defineStore<ComlinkNodeState>({
27
30
  name: 'nodeStore',
28
31
  getInitialState: () => ({
29
32
  nodes: new Map(),
30
33
  }),
31
- initialize() {
34
+
35
+ initialize({state}) {
32
36
  return () => {
33
- const state = this.state.get()
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
+ }