@sanity/sdk 2.5.0 → 2.7.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 +429 -27
- package/dist/index.js +657 -266
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/src/_exports/index.ts +18 -3
- package/src/auth/authMode.test.ts +56 -0
- package/src/auth/authMode.ts +71 -0
- package/src/auth/authStore.test.ts +85 -4
- package/src/auth/authStore.ts +63 -125
- package/src/auth/authStrategy.ts +39 -0
- package/src/auth/dashboardAuth.ts +132 -0
- package/src/auth/standaloneAuth.ts +109 -0
- package/src/auth/studioAuth.ts +217 -0
- package/src/auth/studioModeAuth.test.ts +43 -1
- package/src/auth/studioModeAuth.ts +10 -1
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +21 -6
- package/src/client/clientStore.test.ts +45 -43
- package/src/client/clientStore.ts +23 -9
- package/src/config/loggingConfig.ts +149 -0
- package/src/config/sanityConfig.ts +82 -22
- package/src/projection/getProjectionState.ts +6 -5
- package/src/projection/projectionQuery.test.ts +38 -55
- package/src/projection/projectionQuery.ts +27 -31
- package/src/projection/projectionStore.test.ts +4 -4
- package/src/projection/projectionStore.ts +3 -2
- package/src/projection/resolveProjection.ts +2 -2
- package/src/projection/statusQuery.test.ts +35 -0
- package/src/projection/statusQuery.ts +71 -0
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +63 -50
- package/src/projection/subscribeToStateAndFetchBatches.ts +106 -27
- package/src/projection/types.ts +12 -0
- package/src/projection/util.ts +0 -1
- package/src/query/queryStore.test.ts +64 -0
- package/src/query/queryStore.ts +33 -11
- package/src/releases/getPerspectiveState.test.ts +17 -14
- package/src/releases/getPerspectiveState.ts +58 -38
- package/src/releases/releasesStore.test.ts +59 -61
- package/src/releases/releasesStore.ts +21 -35
- package/src/releases/utils/isReleasePerspective.ts +7 -0
- package/src/store/createActionBinder.test.ts +211 -1
- package/src/store/createActionBinder.ts +102 -13
- package/src/store/createSanityInstance.test.ts +85 -1
- package/src/store/createSanityInstance.ts +55 -4
- package/src/utils/logger-usage-example.md +141 -0
- package/src/utils/logger.test.ts +757 -0
- package/src/utils/logger.ts +537 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {describe, expect, it, vi} from 'vitest'
|
|
1
|
+
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
|
|
2
2
|
|
|
3
|
+
import {configureLogging, type LogHandler, resetLogging} from '../utils/logger'
|
|
3
4
|
import {createSanityInstance} from './createSanityInstance'
|
|
4
5
|
|
|
5
6
|
describe('createSanityInstance', () => {
|
|
@@ -81,4 +82,87 @@ describe('createSanityInstance', () => {
|
|
|
81
82
|
const child = parent.createChild({auth: {token: 'my-token'}})
|
|
82
83
|
expect(child.config.auth).toEqual({apiHost: 'api.sanity.work', token: 'my-token'})
|
|
83
84
|
})
|
|
85
|
+
|
|
86
|
+
describe('logging', () => {
|
|
87
|
+
const mockHandler: LogHandler = {
|
|
88
|
+
error: vi.fn(),
|
|
89
|
+
warn: vi.fn(),
|
|
90
|
+
info: vi.fn(),
|
|
91
|
+
debug: vi.fn(),
|
|
92
|
+
trace: vi.fn(),
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
beforeEach(() => {
|
|
96
|
+
vi.clearAllMocks()
|
|
97
|
+
configureLogging({
|
|
98
|
+
level: 'debug',
|
|
99
|
+
namespaces: ['sdk'],
|
|
100
|
+
handler: mockHandler,
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
afterEach(() => {
|
|
105
|
+
resetLogging()
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('should log instance creation at info level', () => {
|
|
109
|
+
createSanityInstance({projectId: 'test-proj', dataset: 'test-ds'})
|
|
110
|
+
|
|
111
|
+
expect(mockHandler.info).toHaveBeenCalledWith(
|
|
112
|
+
expect.stringContaining('[INFO] [sdk]'),
|
|
113
|
+
expect.objectContaining({
|
|
114
|
+
hasProjectId: true,
|
|
115
|
+
hasDataset: true,
|
|
116
|
+
}),
|
|
117
|
+
)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('should log configuration details at debug level', () => {
|
|
121
|
+
createSanityInstance({projectId: 'test-proj', dataset: 'test-ds'})
|
|
122
|
+
|
|
123
|
+
expect(mockHandler.debug).toHaveBeenCalledWith(
|
|
124
|
+
expect.stringContaining('[DEBUG] [sdk]'),
|
|
125
|
+
expect.objectContaining({
|
|
126
|
+
projectId: 'test-proj',
|
|
127
|
+
dataset: 'test-ds',
|
|
128
|
+
}),
|
|
129
|
+
)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('should log instance disposal', () => {
|
|
133
|
+
const instance = createSanityInstance({projectId: 'test-proj'})
|
|
134
|
+
vi.clearAllMocks() // Clear creation logs
|
|
135
|
+
|
|
136
|
+
instance.dispose()
|
|
137
|
+
|
|
138
|
+
expect(mockHandler.info).toHaveBeenCalledWith(
|
|
139
|
+
expect.stringContaining('Instance disposed'),
|
|
140
|
+
expect.anything(),
|
|
141
|
+
)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('should log child instance creation at debug level', () => {
|
|
145
|
+
const parent = createSanityInstance({projectId: 'parent-proj'})
|
|
146
|
+
vi.clearAllMocks() // Clear parent creation logs
|
|
147
|
+
|
|
148
|
+
parent.createChild({dataset: 'child-ds'})
|
|
149
|
+
|
|
150
|
+
expect(mockHandler.debug).toHaveBeenCalledWith(
|
|
151
|
+
expect.stringContaining('Creating child instance'),
|
|
152
|
+
expect.objectContaining({
|
|
153
|
+
overridingDataset: true,
|
|
154
|
+
}),
|
|
155
|
+
)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('should include instance context in logs', () => {
|
|
159
|
+
createSanityInstance({projectId: 'my-project', dataset: 'my-dataset'})
|
|
160
|
+
|
|
161
|
+
// Check that logs include the instance context (project and dataset)
|
|
162
|
+
expect(mockHandler.info).toHaveBeenCalledWith(
|
|
163
|
+
expect.stringMatching(/\[project:my-project\].*\[dataset:my-dataset\]/),
|
|
164
|
+
expect.anything(),
|
|
165
|
+
)
|
|
166
|
+
})
|
|
167
|
+
})
|
|
84
168
|
})
|
|
@@ -2,6 +2,7 @@ import {pick} from 'lodash-es'
|
|
|
2
2
|
|
|
3
3
|
import {type SanityConfig} from '../config/sanityConfig'
|
|
4
4
|
import {insecureRandomId} from '../utils/ids'
|
|
5
|
+
import {createLogger, type InstanceContext} from '../utils/logger'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Represents a Sanity.io resource instance with its own configuration and lifecycle
|
|
@@ -76,15 +77,53 @@ export function createSanityInstance(config: SanityConfig = {}): SanityInstance
|
|
|
76
77
|
const disposeListeners = new Map<string, () => void>()
|
|
77
78
|
const disposed = {current: false}
|
|
78
79
|
|
|
80
|
+
// Create instance context for logging
|
|
81
|
+
const instanceContext: InstanceContext = {
|
|
82
|
+
instanceId,
|
|
83
|
+
projectId: config.projectId,
|
|
84
|
+
dataset: config.dataset,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Create logger with instance context
|
|
88
|
+
const logger = createLogger('sdk', {instanceContext})
|
|
89
|
+
|
|
90
|
+
// Log instance creation
|
|
91
|
+
logger.info('Sanity instance created', {
|
|
92
|
+
hasProjectId: !!config.projectId,
|
|
93
|
+
hasDataset: !!config.dataset,
|
|
94
|
+
hasAuth: !!config.auth,
|
|
95
|
+
hasPerspective: !!config.perspective,
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
// Log configuration details at debug level
|
|
99
|
+
logger.debug('Instance configuration', {
|
|
100
|
+
projectId: config.projectId,
|
|
101
|
+
dataset: config.dataset,
|
|
102
|
+
perspective: config.perspective,
|
|
103
|
+
hasStudioConfig: !!config.studio,
|
|
104
|
+
hasStudioTokenSource: !!config.studio?.auth?.token,
|
|
105
|
+
legacyStudioMode: config.studioMode?.enabled,
|
|
106
|
+
hasAuthProviders: !!config.auth?.providers,
|
|
107
|
+
hasAuthToken: !!config.auth?.token,
|
|
108
|
+
})
|
|
109
|
+
|
|
79
110
|
const instance: SanityInstance = {
|
|
80
111
|
instanceId,
|
|
81
112
|
config,
|
|
82
113
|
isDisposed: () => disposed.current,
|
|
83
114
|
dispose: () => {
|
|
84
|
-
if (disposed.current)
|
|
115
|
+
if (disposed.current) {
|
|
116
|
+
logger.trace('Dispose called on already disposed instance', {internal: true})
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
logger.trace('Disposing instance', {
|
|
120
|
+
internal: true,
|
|
121
|
+
listenerCount: disposeListeners.size,
|
|
122
|
+
})
|
|
85
123
|
disposed.current = true
|
|
86
124
|
disposeListeners.forEach((listener) => listener())
|
|
87
125
|
disposeListeners.clear()
|
|
126
|
+
logger.info('Instance disposed')
|
|
88
127
|
},
|
|
89
128
|
onDispose: (cb) => {
|
|
90
129
|
const listenerId = insecureRandomId()
|
|
@@ -94,8 +133,14 @@ export function createSanityInstance(config: SanityConfig = {}): SanityInstance
|
|
|
94
133
|
}
|
|
95
134
|
},
|
|
96
135
|
getParent: () => undefined,
|
|
97
|
-
createChild: (next) =>
|
|
98
|
-
|
|
136
|
+
createChild: (next) => {
|
|
137
|
+
logger.debug('Creating child instance', {
|
|
138
|
+
parentInstanceId: instanceId.slice(0, 8),
|
|
139
|
+
overridingProjectId: !!next.projectId,
|
|
140
|
+
overridingDataset: !!next.dataset,
|
|
141
|
+
overridingAuth: !!next.auth,
|
|
142
|
+
})
|
|
143
|
+
const child = Object.assign(
|
|
99
144
|
createSanityInstance({
|
|
100
145
|
...config,
|
|
101
146
|
...next,
|
|
@@ -104,7 +149,13 @@ export function createSanityInstance(config: SanityConfig = {}): SanityInstance
|
|
|
104
149
|
: config.auth && next.auth && {auth: {...config.auth, ...next.auth}}),
|
|
105
150
|
}),
|
|
106
151
|
{getParent: () => instance},
|
|
107
|
-
)
|
|
152
|
+
)
|
|
153
|
+
logger.trace('Child instance created', {
|
|
154
|
+
internal: true,
|
|
155
|
+
childInstanceId: child.instanceId.slice(0, 8),
|
|
156
|
+
})
|
|
157
|
+
return child
|
|
158
|
+
},
|
|
108
159
|
match: (targetConfig) => {
|
|
109
160
|
if (
|
|
110
161
|
Object.entries(pick(targetConfig, 'auth', 'projectId', 'dataset')).every(
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Logger Usage Examples
|
|
2
|
+
|
|
3
|
+
This document shows how to use the logging infrastructure in different scenarios.
|
|
4
|
+
|
|
5
|
+
## For SDK Users
|
|
6
|
+
|
|
7
|
+
### Basic Setup
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import {configureLogging, createSanityInstance} from '@sanity/sdk'
|
|
11
|
+
|
|
12
|
+
// Enable logging in development
|
|
13
|
+
if (process.env.NODE_ENV === 'development') {
|
|
14
|
+
configureLogging({
|
|
15
|
+
level: 'info',
|
|
16
|
+
namespaces: ['auth', 'document', 'query'],
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const instance = createSanityInstance({
|
|
21
|
+
projectId: 'my-project',
|
|
22
|
+
dataset: 'production',
|
|
23
|
+
})
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Expected Output
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
[2024-12-01T22:15:29.000Z] [INFO] [sdk] Logging configured
|
|
30
|
+
level: "info"
|
|
31
|
+
namespaces: ["auth", "document", "query"]
|
|
32
|
+
[2024-12-01T22:15:30.123Z] [INFO] [auth] Checking URL for auth code
|
|
33
|
+
[2024-12-01T22:15:30.456Z] [INFO] [auth] [project:my-project] [dataset:production] User logged in
|
|
34
|
+
userId: "user-123"
|
|
35
|
+
[2024-12-01T22:15:31.789Z] [INFO] [document] [project:my-project] [dataset:production] Document synced
|
|
36
|
+
documentId: "draft.post-1"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## For SDK Maintainers
|
|
40
|
+
|
|
41
|
+
### Using Logger in Stores
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// In authStore.ts
|
|
45
|
+
import {createLogger, getInstanceContext} from '../utils/logger'
|
|
46
|
+
import {defineStore} from '../store/defineStore'
|
|
47
|
+
|
|
48
|
+
export const authStore = defineStore<AuthStoreState>({
|
|
49
|
+
name: 'Auth',
|
|
50
|
+
|
|
51
|
+
initialize(context) {
|
|
52
|
+
const {instance} = context
|
|
53
|
+
|
|
54
|
+
// Create logger with instance context
|
|
55
|
+
const logger = createLogger('auth', {
|
|
56
|
+
instanceContext: getInstanceContext(instance),
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// Logs will automatically include [project:x] [dataset:y]
|
|
60
|
+
logger.info('Auth store initialized')
|
|
61
|
+
|
|
62
|
+
// With additional context
|
|
63
|
+
logger.debug('Fetching current user', {
|
|
64
|
+
method: 'cookie',
|
|
65
|
+
projectId: instance.config.projectId,
|
|
66
|
+
})
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Using Logger in Static Utils
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// In utils/ids.ts (no instance context)
|
|
75
|
+
import {createLogger} from './logger'
|
|
76
|
+
|
|
77
|
+
const logger = createLogger('sdk')
|
|
78
|
+
|
|
79
|
+
export function getDraftId(id: string): string {
|
|
80
|
+
logger.trace('Converting to draft ID', {inputId: id})
|
|
81
|
+
return `drafts.${id}`
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Performance Timing
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import {createTimer} from '../utils/logger'
|
|
89
|
+
|
|
90
|
+
async function fetchDocument(id: string) {
|
|
91
|
+
const timer = createTimer('document', 'fetchDocument')
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const doc = await client.fetch(query, {id})
|
|
95
|
+
timer.end('Document fetched successfully', {documentId: id})
|
|
96
|
+
return doc
|
|
97
|
+
} catch (error) {
|
|
98
|
+
timer.end('Document fetch failed', {documentId: id, error})
|
|
99
|
+
throw error
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Multi-Instance Debugging
|
|
105
|
+
|
|
106
|
+
When working with multiple instances, logs automatically show which instance they came from:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
const prodInstance = createSanityInstance({
|
|
110
|
+
projectId: 'my-project',
|
|
111
|
+
dataset: 'production',
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
const stagingInstance = createSanityInstance({
|
|
115
|
+
projectId: 'my-project',
|
|
116
|
+
dataset: 'staging',
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// Logs will clearly show which instance:
|
|
120
|
+
// [INFO] [query] [project:my-project] [dataset:production] Query executed
|
|
121
|
+
// [INFO] [query] [project:my-project] [dataset:staging] Query executed
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Environment-Based Configuration
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// Configure based on environment
|
|
128
|
+
const logLevel = process.env.SANITY_LOG_LEVEL || 'warn'
|
|
129
|
+
const logNamespaces = process.env.SANITY_LOG_NAMESPACES?.split(',') || []
|
|
130
|
+
|
|
131
|
+
configureLogging({
|
|
132
|
+
level: logLevel as LogLevel,
|
|
133
|
+
namespaces: logNamespaces,
|
|
134
|
+
})
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Then run your app with:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
SANITY_LOG_LEVEL=debug SANITY_LOG_NAMESPACES=auth,document npm start
|
|
141
|
+
```
|