@sanity/sdk 2.4.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +346 -110
- package/dist/index.js +428 -136
- package/dist/index.js.map +1 -1
- package/package.json +10 -9
- package/src/_exports/index.ts +15 -3
- package/src/auth/authStore.test.ts +13 -13
- package/src/auth/refreshStampedToken.test.ts +16 -16
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +6 -6
- package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +4 -4
- package/src/client/clientStore.test.ts +45 -43
- package/src/client/clientStore.ts +23 -9
- package/src/comlink/controller/actions/destroyController.test.ts +2 -2
- package/src/comlink/controller/actions/getOrCreateChannel.test.ts +6 -6
- package/src/comlink/controller/actions/getOrCreateController.test.ts +5 -5
- package/src/comlink/controller/actions/getOrCreateController.ts +1 -1
- package/src/comlink/controller/actions/releaseChannel.test.ts +3 -2
- package/src/comlink/controller/comlinkControllerStore.test.ts +4 -4
- package/src/comlink/node/actions/getOrCreateNode.test.ts +7 -7
- package/src/comlink/node/actions/releaseNode.test.ts +2 -2
- package/src/comlink/node/comlinkNodeStore.test.ts +4 -3
- package/src/config/loggingConfig.ts +149 -0
- package/src/config/sanityConfig.ts +47 -23
- package/src/document/actions.ts +11 -7
- package/src/document/applyDocumentActions.test.ts +9 -6
- package/src/document/applyDocumentActions.ts +9 -49
- package/src/document/documentStore.test.ts +128 -115
- package/src/document/documentStore.ts +40 -10
- package/src/document/permissions.test.ts +9 -9
- package/src/document/permissions.ts +17 -7
- package/src/document/processActions.test.ts +248 -0
- package/src/document/processActions.ts +173 -0
- package/src/document/reducers.ts +13 -6
- package/src/presence/presenceStore.ts +13 -7
- package/src/preview/previewStore.test.ts +10 -2
- package/src/preview/previewStore.ts +2 -1
- package/src/preview/subscribeToStateAndFetchBatches.test.ts +8 -5
- package/src/preview/subscribeToStateAndFetchBatches.ts +9 -3
- package/src/projection/projectionStore.test.ts +18 -2
- package/src/projection/projectionStore.ts +2 -1
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +6 -5
- package/src/projection/subscribeToStateAndFetchBatches.ts +9 -3
- package/src/query/queryStore.ts +3 -1
- package/src/releases/getPerspectiveState.ts +2 -2
- package/src/releases/releasesStore.ts +10 -4
- package/src/store/createActionBinder.test.ts +8 -6
- package/src/store/createActionBinder.ts +54 -28
- package/src/store/createSanityInstance.test.ts +85 -1
- package/src/store/createSanityInstance.ts +53 -4
- package/src/store/createStateSourceAction.test.ts +12 -11
- package/src/store/createStateSourceAction.ts +6 -6
- package/src/store/createStoreInstance.test.ts +29 -16
- package/src/store/createStoreInstance.ts +6 -5
- package/src/store/defineStore.test.ts +1 -1
- package/src/store/defineStore.ts +12 -7
- package/src/utils/logger-usage-example.md +141 -0
- package/src/utils/logger.test.ts +757 -0
- package/src/utils/logger.ts +537 -0
|
@@ -49,7 +49,7 @@ describe('getOrCreateChannel', () => {
|
|
|
49
49
|
it('should create a new channel using the controller', () => {
|
|
50
50
|
const createChannelSpy = vi.spyOn(mockController, 'createChannel')
|
|
51
51
|
|
|
52
|
-
const channel = getOrCreateChannel({state, instance}, channelConfig)
|
|
52
|
+
const channel = getOrCreateChannel({state, instance, key: null}, channelConfig)
|
|
53
53
|
|
|
54
54
|
expect(createChannelSpy).toHaveBeenCalledWith(channelConfig)
|
|
55
55
|
expect(channel.on).toBeDefined()
|
|
@@ -70,17 +70,17 @@ describe('getOrCreateChannel', () => {
|
|
|
70
70
|
channels: new Map(),
|
|
71
71
|
})
|
|
72
72
|
|
|
73
|
-
expect(() => getOrCreateChannel({state, instance}, channelConfig)).toThrow(
|
|
73
|
+
expect(() => getOrCreateChannel({state, instance, key: null}, channelConfig)).toThrow(
|
|
74
74
|
'Controller must be initialized before using or creating channels',
|
|
75
75
|
)
|
|
76
76
|
})
|
|
77
77
|
|
|
78
78
|
it('should retrieve channel directly from store once created', () => {
|
|
79
|
-
const createdChannel = getOrCreateChannel({state, instance}, channelConfig)
|
|
79
|
+
const createdChannel = getOrCreateChannel({state, instance, key: null}, channelConfig)
|
|
80
80
|
vi.clearAllMocks() // Clear call counts
|
|
81
81
|
|
|
82
82
|
// Retrieve channel again
|
|
83
|
-
const retrievedChannel = getOrCreateChannel({state, instance}, channelConfig)
|
|
83
|
+
const retrievedChannel = getOrCreateChannel({state, instance, key: null}, channelConfig)
|
|
84
84
|
expect(retrievedChannel).toBeDefined()
|
|
85
85
|
expect(retrievedChannel).toBe(createdChannel)
|
|
86
86
|
|
|
@@ -95,10 +95,10 @@ describe('getOrCreateChannel', () => {
|
|
|
95
95
|
})
|
|
96
96
|
|
|
97
97
|
it('should throw error when trying to create channel with different options', () => {
|
|
98
|
-
getOrCreateChannel({state, instance}, channelConfig)
|
|
98
|
+
getOrCreateChannel({state, instance, key: null}, channelConfig)
|
|
99
99
|
|
|
100
100
|
expect(() =>
|
|
101
|
-
getOrCreateChannel({state, instance}, {...channelConfig, connectTo: 'window'}),
|
|
101
|
+
getOrCreateChannel({state, instance, key: null}, {...channelConfig, connectTo: 'window'}),
|
|
102
102
|
).toThrow('Channel "test" already exists with different options')
|
|
103
103
|
})
|
|
104
104
|
})
|
|
@@ -43,7 +43,7 @@ describe('getOrCreateController', () => {
|
|
|
43
43
|
const controllerSpy = vi.spyOn(comlink, 'createController')
|
|
44
44
|
const targetOrigin = 'https://test.sanity.dev'
|
|
45
45
|
|
|
46
|
-
const controller = getOrCreateController({state, instance}, targetOrigin)
|
|
46
|
+
const controller = getOrCreateController({state, instance, key: null}, targetOrigin)
|
|
47
47
|
|
|
48
48
|
expect(controllerSpy).toHaveBeenCalledWith({targetOrigin})
|
|
49
49
|
expect(controller).toBeDefined()
|
|
@@ -56,8 +56,8 @@ describe('getOrCreateController', () => {
|
|
|
56
56
|
const controllerSpy = vi.spyOn(comlink, 'createController')
|
|
57
57
|
const targetOrigin = 'https://test.sanity.dev'
|
|
58
58
|
|
|
59
|
-
const firstController = getOrCreateController({state, instance}, targetOrigin)
|
|
60
|
-
const secondController = getOrCreateController({state, instance}, targetOrigin)
|
|
59
|
+
const firstController = getOrCreateController({state, instance, key: null}, targetOrigin)
|
|
60
|
+
const secondController = getOrCreateController({state, instance, key: null}, targetOrigin)
|
|
61
61
|
|
|
62
62
|
expect(controllerSpy).toHaveBeenCalledTimes(1)
|
|
63
63
|
expect(firstController).toBe(secondController)
|
|
@@ -68,9 +68,9 @@ describe('getOrCreateController', () => {
|
|
|
68
68
|
const targetOrigin = 'https://test.sanity.dev'
|
|
69
69
|
const targetOrigin2 = 'https://test2.sanity.dev'
|
|
70
70
|
|
|
71
|
-
const firstController = getOrCreateController({state, instance}, targetOrigin)
|
|
71
|
+
const firstController = getOrCreateController({state, instance, key: null}, targetOrigin)
|
|
72
72
|
const destroySpy = vi.spyOn(firstController, 'destroy')
|
|
73
|
-
const secondController = getOrCreateController({state, instance}, targetOrigin2)
|
|
73
|
+
const secondController = getOrCreateController({state, instance, key: null}, targetOrigin2)
|
|
74
74
|
|
|
75
75
|
expect(controllerSpy).toHaveBeenCalledTimes(2)
|
|
76
76
|
expect(destroySpy).toHaveBeenCalled()
|
|
@@ -21,7 +21,7 @@ export const getOrCreateController = (
|
|
|
21
21
|
// if the target origin has changed, we'll create a new controller,
|
|
22
22
|
// but need to clean up first
|
|
23
23
|
if (controller) {
|
|
24
|
-
destroyController({state, instance})
|
|
24
|
+
destroyController({state, instance, key: undefined})
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const newController = createController({targetOrigin})
|
|
@@ -19,6 +19,7 @@ const channelConfig = {
|
|
|
19
19
|
describe('releaseChannel', () => {
|
|
20
20
|
let instance: SanityInstance
|
|
21
21
|
let store: StoreInstance<ComlinkControllerState>
|
|
22
|
+
const key = {name: 'global', projectId: 'test-project-id', dataset: 'test-dataset'}
|
|
22
23
|
|
|
23
24
|
let getOrCreateChannel: (
|
|
24
25
|
inst: SanityInstance,
|
|
@@ -29,14 +30,14 @@ describe('releaseChannel', () => {
|
|
|
29
30
|
|
|
30
31
|
beforeEach(() => {
|
|
31
32
|
instance = createSanityInstance({projectId: 'test-project-id', dataset: 'test-dataset'})
|
|
32
|
-
store = createStoreInstance(instance, comlinkControllerStore)
|
|
33
|
+
store = createStoreInstance(instance, key, comlinkControllerStore)
|
|
33
34
|
|
|
34
35
|
const bind =
|
|
35
36
|
<TParams extends unknown[], TReturn>(
|
|
36
37
|
action: StoreAction<ComlinkControllerState, TParams, TReturn>,
|
|
37
38
|
) =>
|
|
38
39
|
(inst: SanityInstance, ...params: TParams) =>
|
|
39
|
-
action({instance: inst, state: store.state}, ...params)
|
|
40
|
+
action({instance: inst, state: store.state, key}, ...params)
|
|
40
41
|
|
|
41
42
|
getOrCreateChannel = bind(unboundGetOrCreateChannel)
|
|
42
43
|
getOrCreateController = bind(unboundGetOrCreateController)
|
|
@@ -31,7 +31,7 @@ describe('comlinkControllerStore', () => {
|
|
|
31
31
|
|
|
32
32
|
// Create store state directly
|
|
33
33
|
const state = createStoreState<ComlinkControllerState>(
|
|
34
|
-
comlinkControllerStore.getInitialState(instance),
|
|
34
|
+
comlinkControllerStore.getInitialState(instance, null),
|
|
35
35
|
)
|
|
36
36
|
|
|
37
37
|
const initialState = state.get()
|
|
@@ -56,13 +56,13 @@ describe('comlinkControllerStore', () => {
|
|
|
56
56
|
vi.mocked(bindActionGlobally).mockImplementation(
|
|
57
57
|
(_storeDef, action) =>
|
|
58
58
|
(inst: SanityInstance, ...params: unknown[]) =>
|
|
59
|
-
action({instance: inst, state}, ...params),
|
|
59
|
+
action({instance: inst, state, key: {name: 'global'}}, ...params),
|
|
60
60
|
)
|
|
61
61
|
|
|
62
62
|
const {comlinkControllerStore} = await import('./comlinkControllerStore')
|
|
63
63
|
|
|
64
64
|
// Get the cleanup function from the store
|
|
65
|
-
const dispose = comlinkControllerStore.initialize?.({state, instance})
|
|
65
|
+
const dispose = comlinkControllerStore.initialize?.({state, instance, key: null})
|
|
66
66
|
|
|
67
67
|
// Run cleanup
|
|
68
68
|
dispose?.()
|
|
@@ -82,7 +82,7 @@ describe('comlinkControllerStore', () => {
|
|
|
82
82
|
})
|
|
83
83
|
|
|
84
84
|
// Get the cleanup function
|
|
85
|
-
const cleanup = comlinkControllerStore.initialize?.({state, instance})
|
|
85
|
+
const cleanup = comlinkControllerStore.initialize?.({state, instance, key: null})
|
|
86
86
|
|
|
87
87
|
// Should not throw when no controller exists
|
|
88
88
|
expect(() => cleanup?.()).not.toThrow()
|
|
@@ -40,24 +40,24 @@ describe('getOrCreateNode', () => {
|
|
|
40
40
|
})
|
|
41
41
|
|
|
42
42
|
it('should create and start a node', () => {
|
|
43
|
-
const node = getOrCreateNode({state, instance}, nodeConfig)
|
|
43
|
+
const node = getOrCreateNode({state, instance, key: null}, nodeConfig)
|
|
44
44
|
|
|
45
45
|
expect(comlink.createNode).toHaveBeenCalledWith(nodeConfig)
|
|
46
46
|
expect(node.start).toHaveBeenCalled()
|
|
47
47
|
})
|
|
48
48
|
|
|
49
49
|
it('should store the node in nodeStore', () => {
|
|
50
|
-
const node = getOrCreateNode({state, instance}, nodeConfig)
|
|
50
|
+
const node = getOrCreateNode({state, instance, key: null}, nodeConfig)
|
|
51
51
|
|
|
52
|
-
expect(getOrCreateNode({state, instance}, nodeConfig)).toBe(node)
|
|
52
|
+
expect(getOrCreateNode({state, instance, key: null}, nodeConfig)).toBe(node)
|
|
53
53
|
})
|
|
54
54
|
|
|
55
55
|
it('should throw error when trying to create node with different options', () => {
|
|
56
|
-
getOrCreateNode({state, instance}, nodeConfig)
|
|
56
|
+
getOrCreateNode({state, instance, key: null}, nodeConfig)
|
|
57
57
|
|
|
58
58
|
expect(() =>
|
|
59
59
|
getOrCreateNode(
|
|
60
|
-
{state, instance},
|
|
60
|
+
{state, instance, key: null},
|
|
61
61
|
{
|
|
62
62
|
...nodeConfig,
|
|
63
63
|
connectTo: 'window',
|
|
@@ -74,7 +74,7 @@ describe('getOrCreateNode', () => {
|
|
|
74
74
|
return statusUnsubMock
|
|
75
75
|
})
|
|
76
76
|
|
|
77
|
-
getOrCreateNode({state, instance}, nodeConfig)
|
|
77
|
+
getOrCreateNode({state, instance, key: null}, nodeConfig)
|
|
78
78
|
|
|
79
79
|
expect(mockNode.onStatus).toHaveBeenCalled()
|
|
80
80
|
expect(state.get().nodes.get(nodeConfig.name)?.statusUnsub).toBe(statusUnsubMock)
|
|
@@ -92,7 +92,7 @@ describe('getOrCreateNode', () => {
|
|
|
92
92
|
return statusUnsubMock
|
|
93
93
|
})
|
|
94
94
|
|
|
95
|
-
getOrCreateNode({state, instance}, nodeConfig)
|
|
95
|
+
getOrCreateNode({state, instance, key: null}, nodeConfig)
|
|
96
96
|
|
|
97
97
|
// Remove the node entry before triggering the status callback
|
|
98
98
|
state.get().nodes.delete(nodeConfig.name)
|
|
@@ -47,7 +47,7 @@ describe('releaseNode', () => {
|
|
|
47
47
|
expect(state.get().nodes.has('test-node')).toBe(true)
|
|
48
48
|
|
|
49
49
|
// Release the node
|
|
50
|
-
releaseNode({state, instance}, 'test-node')
|
|
50
|
+
releaseNode({state, instance, key: null}, 'test-node')
|
|
51
51
|
|
|
52
52
|
// Check node is removed
|
|
53
53
|
expect(mockNode.stop).toHaveBeenCalled()
|
|
@@ -64,7 +64,7 @@ describe('releaseNode', () => {
|
|
|
64
64
|
})
|
|
65
65
|
state.set('setup', {nodes})
|
|
66
66
|
|
|
67
|
-
releaseNode({state, instance}, 'test-node')
|
|
67
|
+
releaseNode({state, instance, key: null}, 'test-node')
|
|
68
68
|
|
|
69
69
|
expect(statusUnsub).toHaveBeenCalled()
|
|
70
70
|
})
|
|
@@ -14,7 +14,7 @@ describe('nodeStore', () => {
|
|
|
14
14
|
})
|
|
15
15
|
|
|
16
16
|
it('should have correct initial state', () => {
|
|
17
|
-
const initialState = comlinkNodeStore.getInitialState(instance)
|
|
17
|
+
const initialState = comlinkNodeStore.getInitialState(instance, null)
|
|
18
18
|
|
|
19
19
|
expect(initialState.nodes).toBeInstanceOf(Map)
|
|
20
20
|
expect(initialState.nodes.size).toBe(0)
|
|
@@ -25,16 +25,17 @@ describe('nodeStore', () => {
|
|
|
25
25
|
stop: vi.fn(),
|
|
26
26
|
} as unknown as Node<WindowMessage, FrameMessage>
|
|
27
27
|
|
|
28
|
-
const initialState = comlinkNodeStore.getInitialState(instance)
|
|
28
|
+
const initialState = comlinkNodeStore.getInitialState(instance, null)
|
|
29
29
|
initialState.nodes.set('test-node', {
|
|
30
30
|
options: {name: 'test-node', connectTo: 'parent'},
|
|
31
31
|
node: mockNode,
|
|
32
|
-
|
|
32
|
+
status: 'idle',
|
|
33
33
|
})
|
|
34
34
|
|
|
35
35
|
const cleanup = comlinkNodeStore.initialize?.({
|
|
36
36
|
instance,
|
|
37
37
|
state: createStoreState(initialState),
|
|
38
|
+
key: null,
|
|
38
39
|
})
|
|
39
40
|
|
|
40
41
|
cleanup?.()
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API for configuring SDK logging
|
|
3
|
+
*
|
|
4
|
+
* @module loggingConfig
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {configureLogging as _configureLogging, type LoggerConfig} from '../utils/logger'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Configure logging for the Sanity SDK
|
|
11
|
+
*
|
|
12
|
+
* This function allows you to control what logs are output by the SDK,
|
|
13
|
+
* making it easier to debug issues in development or production.
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* **Zero-Config via Environment Variable (Recommended):**
|
|
17
|
+
*
|
|
18
|
+
* The SDK automatically reads the `DEBUG` environment variable, making it
|
|
19
|
+
* easy to enable logging without code changes:
|
|
20
|
+
*
|
|
21
|
+
* ```bash
|
|
22
|
+
* # Enable all SDK logging at debug level
|
|
23
|
+
* DEBUG=sanity:* npm start
|
|
24
|
+
*
|
|
25
|
+
* # Enable specific namespaces
|
|
26
|
+
* DEBUG=sanity:auth,sanity:document npm start
|
|
27
|
+
*
|
|
28
|
+
* # Enable trace level for all namespaces
|
|
29
|
+
* DEBUG=sanity:trace:* npm start
|
|
30
|
+
*
|
|
31
|
+
* # Enable internal/maintainer logs
|
|
32
|
+
* DEBUG=sanity:*:internal npm start
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* This matches the pattern used by Sanity CLI and Studio, making it familiar
|
|
36
|
+
* and easy for support teams to help troubleshoot issues.
|
|
37
|
+
*
|
|
38
|
+
* **Programmatic Configuration (Advanced):**
|
|
39
|
+
*
|
|
40
|
+
* For more control (custom handlers, dynamic configuration), call this function
|
|
41
|
+
* explicitly. Programmatic configuration overrides environment variables.
|
|
42
|
+
*
|
|
43
|
+
* **For Application Developers:**
|
|
44
|
+
* Use `info`, `warn`, or `error` levels to see high-level SDK activity
|
|
45
|
+
* without being overwhelmed by internal details.
|
|
46
|
+
*
|
|
47
|
+
* **For SDK Maintainers:**
|
|
48
|
+
* Use `debug` or `trace` levels with `internal: true` to see detailed
|
|
49
|
+
* information about store operations, RxJS streams, and state transitions.
|
|
50
|
+
*
|
|
51
|
+
* **Instance Context:**
|
|
52
|
+
* Logs automatically include instance information (projectId, dataset, instanceId)
|
|
53
|
+
* when available, making it easier to debug multi-instance scenarios:
|
|
54
|
+
* ```
|
|
55
|
+
* [INFO] [auth] [project:abc] [dataset:production] User logged in
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* **Available Namespaces:**
|
|
59
|
+
* - `sdk` - SDK initialization, configuration, and lifecycle
|
|
60
|
+
* - `auth` - Authentication and authorization (when instrumented in the future)
|
|
61
|
+
* - And more as logging is added to modules
|
|
62
|
+
*
|
|
63
|
+
* @example Zero-config via environment variable (recommended for debugging)
|
|
64
|
+
* ```bash
|
|
65
|
+
* # Just set DEBUG and run your app - no code changes needed!
|
|
66
|
+
* DEBUG=sanity:* npm start
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @example Programmatic configuration (application developer)
|
|
70
|
+
* ```ts
|
|
71
|
+
* import {configureLogging} from '@sanity/sdk'
|
|
72
|
+
*
|
|
73
|
+
* // Log warnings and errors for auth and document operations
|
|
74
|
+
* configureLogging({
|
|
75
|
+
* level: 'warn',
|
|
76
|
+
* namespaces: ['auth', 'document']
|
|
77
|
+
* })
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @example Programmatic configuration (SDK maintainer)
|
|
81
|
+
* ```ts
|
|
82
|
+
* import {configureLogging} from '@sanity/sdk'
|
|
83
|
+
*
|
|
84
|
+
* // Enable all logs including internal traces
|
|
85
|
+
* configureLogging({
|
|
86
|
+
* level: 'trace',
|
|
87
|
+
* namespaces: ['*'],
|
|
88
|
+
* internal: true
|
|
89
|
+
* })
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @example Custom handler (for testing)
|
|
93
|
+
* ```ts
|
|
94
|
+
* import {configureLogging} from '@sanity/sdk'
|
|
95
|
+
*
|
|
96
|
+
* const logs: string[] = []
|
|
97
|
+
* configureLogging({
|
|
98
|
+
* level: 'info',
|
|
99
|
+
* namespaces: ['*'],
|
|
100
|
+
* handler: {
|
|
101
|
+
* error: (msg) => logs.push(msg),
|
|
102
|
+
* warn: (msg) => logs.push(msg),
|
|
103
|
+
* info: (msg) => logs.push(msg),
|
|
104
|
+
* debug: (msg) => logs.push(msg),
|
|
105
|
+
* trace: (msg) => logs.push(msg),
|
|
106
|
+
* }
|
|
107
|
+
* })
|
|
108
|
+
* ```
|
|
109
|
+
*
|
|
110
|
+
* @public
|
|
111
|
+
*/
|
|
112
|
+
export function configureLogging(config: LoggerConfig): void {
|
|
113
|
+
_configureLogging(config)
|
|
114
|
+
|
|
115
|
+
// Always log configuration (bypasses namespace filtering)
|
|
116
|
+
// This ensures users see the message regardless of which namespaces they enable
|
|
117
|
+
const configLevel = config.level || 'warn'
|
|
118
|
+
const shouldLog = ['info', 'debug', 'trace'].includes(configLevel) || configLevel === 'warn'
|
|
119
|
+
|
|
120
|
+
if (shouldLog && config.handler?.info) {
|
|
121
|
+
config.handler.info(`[${new Date().toISOString()}] [INFO] [sdk] Logging configured`, {
|
|
122
|
+
level: configLevel,
|
|
123
|
+
namespaces: config.namespaces || [],
|
|
124
|
+
internal: config.internal || false,
|
|
125
|
+
source: 'programmatic',
|
|
126
|
+
})
|
|
127
|
+
} else if (shouldLog) {
|
|
128
|
+
// eslint-disable-next-line no-console
|
|
129
|
+
console.info(`[${new Date().toISOString()}] [INFO] [sdk] Logging configured`, {
|
|
130
|
+
level: configLevel,
|
|
131
|
+
namespaces: config.namespaces || [],
|
|
132
|
+
internal: config.internal || false,
|
|
133
|
+
source: 'programmatic',
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Re-export types for public API
|
|
140
|
+
* @public
|
|
141
|
+
*/
|
|
142
|
+
export type {
|
|
143
|
+
InstanceContext,
|
|
144
|
+
LogContext,
|
|
145
|
+
Logger,
|
|
146
|
+
LoggerConfig,
|
|
147
|
+
LogLevel,
|
|
148
|
+
LogNamespace,
|
|
149
|
+
} from '../utils/logger'
|
|
@@ -29,15 +29,24 @@ export interface PerspectiveHandle {
|
|
|
29
29
|
* @public
|
|
30
30
|
*/
|
|
31
31
|
export interface DatasetHandle<TDataset extends string = string, TProjectId extends string = string>
|
|
32
|
-
extends ProjectHandle<TProjectId>,
|
|
33
|
-
PerspectiveHandle {
|
|
32
|
+
extends ProjectHandle<TProjectId>, PerspectiveHandle {
|
|
34
33
|
dataset?: TDataset
|
|
34
|
+
/**
|
|
35
|
+
* @beta
|
|
36
|
+
* The name of the source to use for this operation.
|
|
37
|
+
*/
|
|
38
|
+
sourceName?: string
|
|
39
|
+
/**
|
|
40
|
+
* @beta
|
|
41
|
+
* Explicit source object to use for this operation.
|
|
42
|
+
*/
|
|
43
|
+
source?: DocumentSource
|
|
35
44
|
}
|
|
36
45
|
|
|
37
46
|
/**
|
|
38
47
|
* Identifies a specific document type within a Sanity dataset and project.
|
|
39
48
|
* Includes `projectId`, `dataset`, and `documentType`.
|
|
40
|
-
* Optionally includes a `documentId
|
|
49
|
+
* Optionally includes a `documentId` and `liveEdit` flag.
|
|
41
50
|
* @public
|
|
42
51
|
*/
|
|
43
52
|
export interface DocumentTypeHandle<
|
|
@@ -47,6 +56,12 @@ export interface DocumentTypeHandle<
|
|
|
47
56
|
> extends DatasetHandle<TDataset, TProjectId> {
|
|
48
57
|
documentId?: string
|
|
49
58
|
documentType: TDocumentType
|
|
59
|
+
/**
|
|
60
|
+
* Indicates whether this document uses liveEdit mode.
|
|
61
|
+
* When `true`, the document does not use the draft/published model and edits are applied directly to the document.
|
|
62
|
+
* @see https://www.sanity.io/docs/content-lake/drafts#ca0663a8f002
|
|
63
|
+
*/
|
|
64
|
+
liveEdit?: boolean
|
|
50
65
|
}
|
|
51
66
|
|
|
52
67
|
/**
|
|
@@ -80,45 +95,54 @@ export interface SanityConfig extends DatasetHandle, PerspectiveHandle {
|
|
|
80
95
|
studioMode?: {
|
|
81
96
|
enabled: boolean
|
|
82
97
|
}
|
|
83
|
-
}
|
|
84
98
|
|
|
85
|
-
|
|
99
|
+
/**
|
|
100
|
+
* @beta
|
|
101
|
+
* A list of named sources to use for this instance.
|
|
102
|
+
*/
|
|
103
|
+
sources?: Record<string, DocumentSource>
|
|
104
|
+
}
|
|
86
105
|
|
|
87
106
|
/**
|
|
88
107
|
* A document source can be used for querying.
|
|
108
|
+
* This will soon be the default way to identify where you are querying from.
|
|
89
109
|
*
|
|
90
110
|
* @beta
|
|
91
|
-
* @see datasetSource Construct a document source for a given projectId and dataset.
|
|
92
|
-
* @see mediaLibrarySource Construct a document source for a mediaLibraryId.
|
|
93
|
-
* @see canvasSource Construct a document source for a canvasId.
|
|
94
111
|
*/
|
|
95
|
-
export type DocumentSource =
|
|
96
|
-
|
|
97
|
-
|
|
112
|
+
export type DocumentSource = DatasetSource | MediaLibrarySource | CanvasSource
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @beta
|
|
116
|
+
*/
|
|
117
|
+
export type DatasetSource = {projectId: string; dataset: string}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @beta
|
|
121
|
+
*/
|
|
122
|
+
export type MediaLibrarySource = {mediaLibraryId: string}
|
|
98
123
|
|
|
99
124
|
/**
|
|
100
|
-
* Returns a document source for a projectId and dataset.
|
|
101
|
-
*
|
|
102
125
|
* @beta
|
|
103
126
|
*/
|
|
104
|
-
export
|
|
105
|
-
|
|
127
|
+
export type CanvasSource = {canvasId: string}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @beta
|
|
131
|
+
*/
|
|
132
|
+
export function isDatasetSource(source: DocumentSource): source is DatasetSource {
|
|
133
|
+
return 'projectId' in source && 'dataset' in source
|
|
106
134
|
}
|
|
107
135
|
|
|
108
136
|
/**
|
|
109
|
-
* Returns a document source for a Media Library.
|
|
110
|
-
*
|
|
111
137
|
* @beta
|
|
112
138
|
*/
|
|
113
|
-
export function
|
|
114
|
-
return
|
|
139
|
+
export function isMediaLibrarySource(source: DocumentSource): source is MediaLibrarySource {
|
|
140
|
+
return 'mediaLibraryId' in source
|
|
115
141
|
}
|
|
116
142
|
|
|
117
143
|
/**
|
|
118
|
-
* Returns a document source for a Canvas.
|
|
119
|
-
*
|
|
120
144
|
* @beta
|
|
121
145
|
*/
|
|
122
|
-
export function
|
|
123
|
-
return
|
|
146
|
+
export function isCanvasSource(source: DocumentSource): source is CanvasSource {
|
|
147
|
+
return 'canvasId' in source
|
|
124
148
|
}
|
package/src/document/actions.ts
CHANGED
|
@@ -143,7 +143,9 @@ export function createDocument<
|
|
|
143
143
|
return {
|
|
144
144
|
type: 'document.create',
|
|
145
145
|
...doc,
|
|
146
|
-
...(doc.documentId && {
|
|
146
|
+
...(doc.documentId && {
|
|
147
|
+
documentId: doc.liveEdit ? doc.documentId : getPublishedId(doc.documentId),
|
|
148
|
+
}),
|
|
147
149
|
...(initialValue && {initialValue}),
|
|
148
150
|
}
|
|
149
151
|
}
|
|
@@ -164,7 +166,7 @@ export function deleteDocument<
|
|
|
164
166
|
return {
|
|
165
167
|
type: 'document.delete',
|
|
166
168
|
...doc,
|
|
167
|
-
documentId: getPublishedId(doc.documentId),
|
|
169
|
+
documentId: doc.liveEdit ? doc.documentId : getPublishedId(doc.documentId),
|
|
168
170
|
}
|
|
169
171
|
}
|
|
170
172
|
|
|
@@ -228,12 +230,14 @@ export function editDocument<
|
|
|
228
230
|
doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,
|
|
229
231
|
patches?: PatchOperations | PatchOperations[] | SanityMutatePatchMutation,
|
|
230
232
|
): EditDocumentAction<TDocumentType, TDataset, TProjectId> {
|
|
233
|
+
const documentId = doc.liveEdit ? doc.documentId : getPublishedId(doc.documentId)
|
|
234
|
+
|
|
231
235
|
if (isSanityMutatePatch(patches)) {
|
|
232
236
|
const converted = convertSanityMutatePatch(patches) ?? []
|
|
233
237
|
return {
|
|
234
238
|
...doc,
|
|
235
239
|
type: 'document.edit',
|
|
236
|
-
documentId
|
|
240
|
+
documentId,
|
|
237
241
|
patches: converted,
|
|
238
242
|
}
|
|
239
243
|
}
|
|
@@ -241,7 +245,7 @@ export function editDocument<
|
|
|
241
245
|
return {
|
|
242
246
|
...doc,
|
|
243
247
|
type: 'document.edit',
|
|
244
|
-
documentId
|
|
248
|
+
documentId,
|
|
245
249
|
...(patches && {patches: Array.isArray(patches) ? patches : [patches]}),
|
|
246
250
|
}
|
|
247
251
|
}
|
|
@@ -262,7 +266,7 @@ export function publishDocument<
|
|
|
262
266
|
return {
|
|
263
267
|
type: 'document.publish',
|
|
264
268
|
...doc,
|
|
265
|
-
documentId: getPublishedId(doc.documentId),
|
|
269
|
+
documentId: doc.liveEdit ? doc.documentId : getPublishedId(doc.documentId),
|
|
266
270
|
}
|
|
267
271
|
}
|
|
268
272
|
|
|
@@ -282,7 +286,7 @@ export function unpublishDocument<
|
|
|
282
286
|
return {
|
|
283
287
|
type: 'document.unpublish',
|
|
284
288
|
...doc,
|
|
285
|
-
documentId: getPublishedId(doc.documentId),
|
|
289
|
+
documentId: doc.liveEdit ? doc.documentId : getPublishedId(doc.documentId),
|
|
286
290
|
}
|
|
287
291
|
}
|
|
288
292
|
|
|
@@ -302,6 +306,6 @@ export function discardDocument<
|
|
|
302
306
|
return {
|
|
303
307
|
type: 'document.discard',
|
|
304
308
|
...doc,
|
|
305
|
-
documentId: getPublishedId(doc.documentId),
|
|
309
|
+
documentId: doc.liveEdit ? doc.documentId : getPublishedId(doc.documentId),
|
|
306
310
|
}
|
|
307
311
|
}
|
|
@@ -48,11 +48,11 @@ describe('applyDocumentActions', () => {
|
|
|
48
48
|
}
|
|
49
49
|
state = createStoreState(initialState)
|
|
50
50
|
instance = createSanityInstance({projectId: 'p', dataset: 'd'})
|
|
51
|
+
const key = {name: 'p.d', projectId: 'p', dataset: 'd'}
|
|
51
52
|
|
|
52
53
|
vi.mocked(bindActionByDataset).mockImplementation(
|
|
53
|
-
(_storeDef, action) =>
|
|
54
|
-
(
|
|
55
|
-
action({instance: instanceParam, state}, ...params),
|
|
54
|
+
(_storeDef, action) => (instanceParam: SanityInstance, options) =>
|
|
55
|
+
action({instance: instanceParam, state, key}, options),
|
|
56
56
|
)
|
|
57
57
|
// Import dynamically to ensure mocks are set up before the module under test is loaded
|
|
58
58
|
const module = await import('./applyDocumentActions')
|
|
@@ -72,7 +72,8 @@ describe('applyDocumentActions', () => {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// Call applyDocumentActions with a fixed transactionId for reproducibility.
|
|
75
|
-
const applyPromise = applyDocumentActions(instance,
|
|
75
|
+
const applyPromise = applyDocumentActions(instance, {
|
|
76
|
+
actions: [action],
|
|
76
77
|
transactionId: 'txn-success',
|
|
77
78
|
})
|
|
78
79
|
|
|
@@ -128,7 +129,8 @@ describe('applyDocumentActions', () => {
|
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
// Call applyDocumentActions with a fixed transactionId.
|
|
131
|
-
const applyPromise = applyDocumentActions(instance,
|
|
132
|
+
const applyPromise = applyDocumentActions(instance, {
|
|
133
|
+
actions: [action],
|
|
132
134
|
transactionId: 'txn-error',
|
|
133
135
|
})
|
|
134
136
|
|
|
@@ -160,7 +162,8 @@ describe('applyDocumentActions', () => {
|
|
|
160
162
|
dataset: 'd',
|
|
161
163
|
}
|
|
162
164
|
// Call applyDocumentActions with the context using childInstance, but with action requiring parent's config
|
|
163
|
-
const applyPromise = applyDocumentActions(childInstance,
|
|
165
|
+
const applyPromise = applyDocumentActions(childInstance, {
|
|
166
|
+
actions: [action],
|
|
164
167
|
transactionId: 'txn-child-match',
|
|
165
168
|
})
|
|
166
169
|
|