@sanity/sdk 2.1.0 → 2.1.1
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 +1705 -2163
- package/dist/index.js +497 -187
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/_exports/index.ts +22 -1
- package/src/auth/authStore.ts +1 -0
- package/src/auth/refreshStampedToken.test.ts +225 -6
- package/src/auth/refreshStampedToken.ts +95 -30
- package/src/presence/bifurTransport.test.ts +257 -0
- package/src/presence/bifurTransport.ts +108 -0
- package/src/presence/presenceStore.test.ts +247 -0
- package/src/presence/presenceStore.ts +163 -0
- package/src/presence/types.ts +70 -0
- package/src/query/queryStore.test.ts +4 -1
- package/src/releases/releasesStore.test.ts +5 -2
- package/src/users/reducers.ts +9 -3
- package/src/users/types.ts +17 -0
- package/src/users/usersConstants.ts +1 -0
- package/src/users/usersStore.test.ts +129 -9
- package/src/users/usersStore.ts +132 -4
- package/src/utils/defineIntent.test.ts +477 -0
- package/src/utils/defineIntent.ts +244 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filter criteria for intent matching. Can be combined to create more specific intents.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* // matches only geopoints in the travel-project project, production dataset
|
|
7
|
+
* const filter: IntentFilter = {
|
|
8
|
+
* projectId: 'travel-project',
|
|
9
|
+
* dataset: 'production',
|
|
10
|
+
* types: ['geopoint']
|
|
11
|
+
* }
|
|
12
|
+
*
|
|
13
|
+
* // matches all documents in the travel-project project
|
|
14
|
+
* const filter: IntentFilter = {
|
|
15
|
+
* projectId: 'travel-project',
|
|
16
|
+
* types: ['*']
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* // matches geopoints in the travel-project production dataset and map pins in all projects in the org
|
|
20
|
+
* const filters: IntentFilter[] = [
|
|
21
|
+
* {
|
|
22
|
+
* projectId: 'travel-project',
|
|
23
|
+
* dataset: 'production',
|
|
24
|
+
* types: ['geopoint']
|
|
25
|
+
* },
|
|
26
|
+
* {
|
|
27
|
+
* types: ['map-pin']
|
|
28
|
+
* }
|
|
29
|
+
* ]
|
|
30
|
+
* ```
|
|
31
|
+
* @public
|
|
32
|
+
*/
|
|
33
|
+
export interface IntentFilter {
|
|
34
|
+
/**
|
|
35
|
+
* Project ID to match against
|
|
36
|
+
* @remarks When specified, the intent will only match for the specified project.
|
|
37
|
+
*/
|
|
38
|
+
projectId?: string
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Dataset to match against
|
|
42
|
+
* @remarks When specified, the intent will only match for the specified dataset. Requires projectId to be specified.
|
|
43
|
+
*/
|
|
44
|
+
dataset?: string
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Document types that this intent can handle
|
|
48
|
+
* @remarks This is required for all filters. Use ['*'] to match all document types.
|
|
49
|
+
*/
|
|
50
|
+
types: string[]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Intent definition structure for registering user intents
|
|
55
|
+
* @public
|
|
56
|
+
*/
|
|
57
|
+
export interface Intent {
|
|
58
|
+
/**
|
|
59
|
+
* Unique identifier for this intent
|
|
60
|
+
* @remarks Should be unique across all registered intents in an org for proper matching
|
|
61
|
+
*/
|
|
62
|
+
id: string
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The action that this intent performs
|
|
66
|
+
* @remarks Examples: "view", "edit", "create", "delete"
|
|
67
|
+
*/
|
|
68
|
+
action: 'view' | 'edit' | 'create' | 'delete'
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Human-readable title for this intent
|
|
72
|
+
* @remarks Used for display purposes in UI or logs
|
|
73
|
+
*/
|
|
74
|
+
title: string
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Detailed description of what this intent does
|
|
78
|
+
* @remarks Helps users understand the purpose and behavior of the intent
|
|
79
|
+
*/
|
|
80
|
+
description?: string
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Array of filter criteria for intent matching
|
|
84
|
+
* @remarks At least one filter is required. Use `{types: ['*']}` to match everything
|
|
85
|
+
*/
|
|
86
|
+
filters: IntentFilter[]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Creates a properly typed intent definition for registration with the backend.
|
|
91
|
+
*
|
|
92
|
+
* This utility function provides TypeScript support and validation for intent declarations.
|
|
93
|
+
* It is also used in the CLI if intents are declared as bare objects in an intents file.
|
|
94
|
+
*
|
|
95
|
+
* @param intent - The intent definition object
|
|
96
|
+
* @returns The same intent object with proper typing
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* // Specific filter for a document type
|
|
101
|
+
* const viewGeopointInMapApp = defineIntent({
|
|
102
|
+
* id: 'viewGeopointInMapApp',
|
|
103
|
+
* action: 'view',
|
|
104
|
+
* title: 'View a geopoint in the map app',
|
|
105
|
+
* description: 'This lets you view a geopoint in the map app',
|
|
106
|
+
* filters: [
|
|
107
|
+
* {
|
|
108
|
+
* projectId: 'travel-project',
|
|
109
|
+
* dataset: 'production',
|
|
110
|
+
* types: ['geopoint']
|
|
111
|
+
* }
|
|
112
|
+
* ]
|
|
113
|
+
* })
|
|
114
|
+
*
|
|
115
|
+
* export default viewGeopointInMapApp
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* If your intent is asynchronous, resolve the promise before defining / returning the intent
|
|
119
|
+
* ```typescript
|
|
120
|
+
* async function createAsyncIntent() {
|
|
121
|
+
* const currentProject = await asyncProjectFunction()
|
|
122
|
+
* const currentDataset = await asyncDatasetFunction()
|
|
123
|
+
*
|
|
124
|
+
* return defineIntent({
|
|
125
|
+
* id: 'dynamicIntent',
|
|
126
|
+
* action: 'view',
|
|
127
|
+
* title: 'Dynamic Intent',
|
|
128
|
+
* description: 'Intent with dynamically resolved values',
|
|
129
|
+
* filters: [
|
|
130
|
+
* {
|
|
131
|
+
* projectId: currentProject, // Resolved value
|
|
132
|
+
* dataset: currentDataset, // Resolved value
|
|
133
|
+
* types: ['document']
|
|
134
|
+
* }
|
|
135
|
+
* ]
|
|
136
|
+
* })
|
|
137
|
+
* }
|
|
138
|
+
*
|
|
139
|
+
* const intent = await createAsyncIntent()
|
|
140
|
+
* export default intent
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* @public
|
|
144
|
+
*/
|
|
145
|
+
export function defineIntent(intent: Intent): Intent {
|
|
146
|
+
// Validate required fields
|
|
147
|
+
if (!intent.id) {
|
|
148
|
+
throw new Error('Intent must have an id')
|
|
149
|
+
}
|
|
150
|
+
if (!intent.action) {
|
|
151
|
+
throw new Error('Intent must have an action')
|
|
152
|
+
}
|
|
153
|
+
if (!intent.title) {
|
|
154
|
+
throw new Error('Intent must have a title')
|
|
155
|
+
}
|
|
156
|
+
if (!Array.isArray(intent.filters)) {
|
|
157
|
+
throw new Error('Intent must have a filters array')
|
|
158
|
+
}
|
|
159
|
+
if (intent.filters.length === 0) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
"Intent must have at least one filter. If you want to match everything, use {types: ['*']}",
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Validate each filter
|
|
166
|
+
intent.filters.forEach((filter, index) => {
|
|
167
|
+
validateFilter(filter, index)
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
// Return the intent as-is, providing type safety and runtime validation
|
|
171
|
+
return intent
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Validates an individual filter object
|
|
176
|
+
* @param filter - The filter to validate
|
|
177
|
+
* @param index - The filter's index in the array (for error messages)
|
|
178
|
+
* @internal
|
|
179
|
+
*/
|
|
180
|
+
function validateFilter(filter: IntentFilter, index: number): void {
|
|
181
|
+
const filterContext = `Filter at index ${index}`
|
|
182
|
+
|
|
183
|
+
// Check that filter is an object
|
|
184
|
+
if (!filter || typeof filter !== 'object') {
|
|
185
|
+
throw new Error(`${filterContext} must be an object`)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check that types is required
|
|
189
|
+
if (filter.types === undefined) {
|
|
190
|
+
throw new Error(
|
|
191
|
+
`${filterContext} must have a types property. Use ['*'] to match all document types.`,
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Validate projectId
|
|
196
|
+
if (filter.projectId !== undefined) {
|
|
197
|
+
if (typeof filter.projectId !== 'string') {
|
|
198
|
+
throw new Error(`${filterContext}: projectId must be a string`)
|
|
199
|
+
}
|
|
200
|
+
if (filter.projectId.trim() === '') {
|
|
201
|
+
throw new Error(`${filterContext}: projectId cannot be empty`)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Validate dataset
|
|
206
|
+
if (filter.dataset !== undefined) {
|
|
207
|
+
if (typeof filter.dataset !== 'string') {
|
|
208
|
+
throw new Error(`${filterContext}: dataset must be a string`)
|
|
209
|
+
}
|
|
210
|
+
if (filter.dataset.trim() === '') {
|
|
211
|
+
throw new Error(`${filterContext}: dataset cannot be empty`)
|
|
212
|
+
}
|
|
213
|
+
// Dataset requires projectId to be specified
|
|
214
|
+
if (filter.projectId === undefined) {
|
|
215
|
+
throw new Error(`${filterContext}: dataset cannot be specified without projectId`)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Validate types (now required)
|
|
220
|
+
if (!Array.isArray(filter.types)) {
|
|
221
|
+
throw new Error(`${filterContext}: types must be an array`)
|
|
222
|
+
}
|
|
223
|
+
if (filter.types.length === 0) {
|
|
224
|
+
throw new Error(`${filterContext}: types array cannot be empty`)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Validate each type
|
|
228
|
+
filter.types.forEach((type, typeIndex) => {
|
|
229
|
+
if (typeof type !== 'string') {
|
|
230
|
+
throw new Error(`${filterContext}: types[${typeIndex}] must be a string`)
|
|
231
|
+
}
|
|
232
|
+
if (type.trim() === '') {
|
|
233
|
+
throw new Error(`${filterContext}: types[${typeIndex}] cannot be empty`)
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
// Check for wildcard exclusivity
|
|
238
|
+
const hasWildcard = filter.types.includes('*')
|
|
239
|
+
if (hasWildcard && filter.types.length > 1) {
|
|
240
|
+
throw new Error(
|
|
241
|
+
`${filterContext}: when using wildcard '*', it must be the only type in the array`,
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
}
|