@sanity/sdk 3.1.0 → 3.3.0-rc.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.
Files changed (86) hide show
  1. package/dist/_exports/_internal.d.ts +52 -21
  2. package/dist/_exports/_internal.d.ts.map +1 -1
  3. package/dist/_exports/_internal.js +10 -9
  4. package/dist/_exports/_internal.js.map +1 -1
  5. package/dist/_exports/agent.d.ts +7 -7
  6. package/dist/_exports/agent.d.ts.map +1 -1
  7. package/dist/_exports/agent.js +1 -1
  8. package/dist/_exports/comlink.d.ts +9 -9
  9. package/dist/_exports/comlink.d.ts.map +1 -1
  10. package/dist/_exports/comlink.js +3 -2
  11. package/dist/_exports/comlink.js.map +1 -1
  12. package/dist/_exports/dashboard.d.ts +2 -0
  13. package/dist/_exports/dashboard.js +2 -0
  14. package/dist/{authStore-C-OEkGaM.d.ts → authStore-BGoEGxOq.d.ts} +531 -63
  15. package/dist/authStore-BGoEGxOq.d.ts.map +1 -0
  16. package/dist/{clientStore-gUALQ0qW.js → clientStore-QDrJNTM4.js} +128 -7
  17. package/dist/clientStore-QDrJNTM4.js.map +1 -0
  18. package/dist/{createSanityInstance-DCePpBYW.d.ts → createSanityInstance-BQIqyGLX.d.ts} +16 -1
  19. package/dist/createSanityInstance-BQIqyGLX.d.ts.map +1 -0
  20. package/dist/{object-BwynGPRZ.js → createStateSourceAction-DdAhEfnZ.js} +2 -82
  21. package/dist/createStateSourceAction-DdAhEfnZ.js.map +1 -0
  22. package/dist/defineProperty-Bho9Ngsi.js +31 -0
  23. package/dist/{getNodeState-E0hjqFOo.js → getNodeState-B4nG0sjm.js} +3 -2
  24. package/dist/{getNodeState-E0hjqFOo.js.map → getNodeState-B4nG0sjm.js.map} +1 -1
  25. package/dist/index.d.ts +3 -3
  26. package/dist/index.js +371 -54
  27. package/dist/index.js.map +1 -1
  28. package/dist/object-CxXlSVg7.js +83 -0
  29. package/dist/object-CxXlSVg7.js.map +1 -0
  30. package/dist/topicStore-Ct7IFNtY.js +565 -0
  31. package/dist/topicStore-Ct7IFNtY.js.map +1 -0
  32. package/dist/topicStore-DXvuH9h4.d.ts +435 -0
  33. package/dist/topicStore-DXvuH9h4.d.ts.map +1 -0
  34. package/dist/{version-C90rNWaX.js → version-CIhX5ppJ.js} +6 -6
  35. package/dist/{version-C90rNWaX.js.map → version-CIhX5ppJ.js.map} +1 -1
  36. package/package.json +20 -19
  37. package/src/_exports/_internal.ts +12 -0
  38. package/src/_exports/dashboard.ts +36 -0
  39. package/src/_exports/index.ts +17 -1
  40. package/src/applications/applications.test-d.ts +28 -4
  41. package/src/applications/applications.ts +39 -14
  42. package/src/auth/authMode.test.ts +24 -0
  43. package/src/auth/authMode.ts +8 -3
  44. package/src/auth/authStore.test.ts +18 -0
  45. package/src/auth/authStore.ts +15 -0
  46. package/src/auth/authStrategy.ts +2 -0
  47. package/src/auth/handleAuthCallback.test.ts +33 -0
  48. package/src/auth/handleAuthCallback.ts +11 -1
  49. package/src/auth/logout.test.ts +53 -0
  50. package/src/auth/logout.ts +19 -1
  51. package/src/auth/oauth/oauthActions.test.ts +593 -0
  52. package/src/auth/oauth/oauthActions.ts +438 -0
  53. package/src/auth/oauth/oauthAuth.test.ts +229 -0
  54. package/src/auth/oauth/oauthAuth.ts +178 -0
  55. package/src/auth/oauth/pkce.test.ts +44 -0
  56. package/src/auth/oauth/pkce.ts +60 -0
  57. package/src/auth/oauth/types.ts +12 -0
  58. package/src/config/authConfig.ts +18 -0
  59. package/src/dashboard/messageBus/__fixtures__/test-topics.ts +18 -0
  60. package/src/dashboard/messageBus/bus.test.ts +1033 -0
  61. package/src/dashboard/messageBus/bus.ts +1246 -0
  62. package/src/dashboard/messageBus/store.test.ts +142 -0
  63. package/src/dashboard/messageBus/store.ts +74 -0
  64. package/src/dashboard/messageBus/topicStore.test.ts +172 -0
  65. package/src/dashboard/messageBus/topicStore.ts +184 -0
  66. package/src/dashboard/messageBus/topics.test.ts +71 -0
  67. package/src/dashboard/messageBus/topics.ts +260 -0
  68. package/src/document/actions.ts +3 -3
  69. package/src/document/applyDocumentActions.ts +3 -3
  70. package/src/document/documentStore.ts +12 -12
  71. package/src/document/processMutations.ts +58 -22
  72. package/src/installations/installations.test-d.ts +3 -3
  73. package/src/installations/installations.ts +2 -19
  74. package/src/projection/getProjectionState.ts +2 -2
  75. package/src/projection/resolveProjection.ts +2 -2
  76. package/src/query/queryStore.ts +3 -3
  77. package/src/typegen/defineProjection.test.ts +17 -0
  78. package/src/typegen/defineProjection.ts +19 -0
  79. package/src/typegen/groqCompat.ts +122 -0
  80. package/src/typegen/resolve.ts +37 -0
  81. package/src/utils/createGroqSearchFilter.test.ts +8 -0
  82. package/src/utils/createGroqSearchFilter.ts +6 -3
  83. package/dist/authStore-C-OEkGaM.d.ts.map +0 -1
  84. package/dist/clientStore-gUALQ0qW.js.map +0 -1
  85. package/dist/createSanityInstance-DCePpBYW.d.ts.map +0 -1
  86. package/dist/object-BwynGPRZ.js.map +0 -1
@@ -0,0 +1,260 @@
1
+ import {type CurrentUser} from '@sanity/types'
2
+
3
+ import {type Application, type ApplicationInclude} from '../../applications/applications'
4
+ import {type OrganizationBase} from '../../organization/organization'
5
+
6
+ /**
7
+ * Identifies a module federation expose and the manifest that serves it.
8
+ * @public
9
+ */
10
+ export interface RemoteModuleRef {
11
+ readonly entry: string
12
+ readonly moduleId: string
13
+ readonly version: string
14
+ }
15
+
16
+ /**
17
+ * Application types that can receive configuration modules.
18
+ *
19
+ * The dashboard supplies this value over the message bus, so any string is accepted; known
20
+ * types are listed for autocomplete. `& {}` keeps the literal from collapsing into `string`.
21
+ * @public
22
+ */
23
+ export type ApplicationConfigAppType = 'media-library' | (string & {})
24
+
25
+ /**
26
+ * Identifies a configuration module for an application or application type.
27
+ * @public
28
+ */
29
+ export interface ApplicationConfig extends RemoteModuleRef {
30
+ readonly appId?: Application['id']
31
+ readonly appType: ApplicationConfigAppType
32
+ }
33
+
34
+ /**
35
+ * Declares a topic that stores and replays its current value.
36
+ * @public
37
+ */
38
+ export type StateTopicDef<T> = {kind: 'state'; value: T}
39
+
40
+ /**
41
+ * Declares a topic that delivers events and an optional reply.
42
+ * @public
43
+ */
44
+ export type EventTopicDef<P, R = never> = {
45
+ kind: 'event'
46
+ payload: P
47
+ reply?: R
48
+ }
49
+
50
+ /**
51
+ * Represents a successful topic value or a failed topic operation.
52
+ * @public
53
+ */
54
+ export type TopicResult<T> = {ok: true; value: T} | {ok: false}
55
+
56
+ /**
57
+ * Identifies a dashboard application and a route within it.
58
+ * @public
59
+ */
60
+ export type NavigationTarget = {
61
+ /** The application ID, or `null` for dashboard-level routes. */
62
+ appId: Application['id'] | null
63
+ /** The route path, including its query string and fragment. */
64
+ path: string
65
+ }
66
+
67
+ /**
68
+ * Describes the current dashboard location and an active navigation.
69
+ * @public
70
+ */
71
+ export type NavigationLocation = NavigationTarget & {
72
+ /** The active navigation, or `null` when navigation is idle. */
73
+ transition: {
74
+ /** Whether the navigation pushes or replaces browser history. */
75
+ navigationType: 'push' | 'replace'
76
+ /** The requested destination. */
77
+ to: NavigationTarget
78
+ } | null
79
+ }
80
+
81
+ /**
82
+ * Declares the topics provided by the dashboard.
83
+ * @public
84
+ */
85
+ export interface DashboardTopics {
86
+ /** The available application configuration modules. */
87
+ 'applications.config': StateTopicDef<ApplicationConfig[] | null>
88
+ /** The foreground application ID, or `null` on dashboard-level routes. */
89
+ 'applications.foreground': StateTopicDef<Application['id'] | null>
90
+ /** The dashboard applications available to the current user. */
91
+ 'applications.list': StateTopicDef<TopicResult<Application<ApplicationInclude>[]> | null>
92
+ /**
93
+ * The session token for the reading connection, or `null` while signed out. The host
94
+ * writes it to each connection separately, so one application never sees another's token.
95
+ */
96
+ 'auth.token': StateTopicDef<string | null>
97
+ /** Requests a dashboard session token. */
98
+ 'auth.token.refresh': EventTopicDef<void, string>
99
+ /** The current dashboard location and active navigation. */
100
+ 'navigation.location': StateTopicDef<NavigationLocation | null>
101
+ /**
102
+ * Requests navigation and replies when the location commits.
103
+ *
104
+ * `ok: false` reasons:
105
+ * - `not-navigable`: the URL cannot be handled by a dashboard application
106
+ * - `interrupted`: another navigation superseded the request
107
+ * - `failed`: the router rejected or did not commit the request
108
+ */
109
+ 'navigation.location.update': EventTopicDef<
110
+ {
111
+ url: string
112
+ history?: 'push' | 'replace'
113
+ },
114
+ {ok: true} | {ok: false; reason: 'not-navigable' | 'interrupted' | 'failed'}
115
+ >
116
+ /** The current organization, or `null` without an active organization. */
117
+ 'organizations.current': StateTopicDef<Pick<OrganizationBase, 'id' | 'name' | 'slug'> | null>
118
+ /** The open panel, its application, display mode, and optional width in pixels. */
119
+ 'panels.mode': StateTopicDef<TopicResult<
120
+ | {appId: string; name: string; mode: 'aside'; size?: number}
121
+ | {appId: string; name: string; mode: 'full'}
122
+ | null
123
+ > | null>
124
+ /** Opens, updates, resizes, or closes an application's panel. */
125
+ 'panels.mode.set': EventTopicDef<
126
+ {name: string; mode: 'aside' | 'full'} | {name: string; size: number} | null
127
+ >
128
+ /** The resolved dashboard color scheme. */
129
+ 'preferences.color-scheme': StateTopicDef<'light' | 'dark'>
130
+ /** Whether the dashboard dock is pinned open. */
131
+ 'preferences.dock-locked': StateTopicDef<boolean>
132
+ /** The signed-in user, or `null` while signed out. */
133
+ 'users.current': StateTopicDef<Pick<CurrentUser, 'id' | 'name' | 'email' | 'profileImage'> | null>
134
+ }
135
+
136
+ /**
137
+ * Declares every topic available through the message bus.
138
+ * @public
139
+ */
140
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- Declaration merging extends the SDK manifest.
141
+ export interface Topics extends DashboardTopics {}
142
+
143
+ /**
144
+ * Every declared topic name.
145
+ * @public
146
+ */
147
+ export type TopicName = keyof Topics
148
+
149
+ type StateTopicsOf<T> = {
150
+ [K in keyof T]: T[K] extends {kind: 'state'} ? K : never
151
+ }[keyof T]
152
+
153
+ /**
154
+ * Names of all declared state topics.
155
+ * @public
156
+ */
157
+ export type StateTopic = StateTopicsOf<Topics>
158
+
159
+ type TopicOwnership = {readonly type: 'same_app'} | {readonly type: 'any_app'}
160
+
161
+ // State is only ever written by the host, one connection at a time, so it carries no ownership.
162
+ type TopicManifestEntry<T> = T extends {kind: 'state'; value: infer V}
163
+ ? {readonly kind: 'state'; readonly seed: V | undefined}
164
+ : {readonly kind: 'event'; readonly ownership: TopicOwnership}
165
+
166
+ // The value a connection's copy of the topic starts with; `undefined` means unpublished.
167
+ const stateTopic = <const V>(seed: V) => ({kind: 'state', seed}) as const
168
+
169
+ // `same_app` restricts responding to the application that installed the bus.
170
+ const dashboardEvent = {kind: 'event', ownership: {type: 'same_app'}} as const
171
+
172
+ /**
173
+ * Defines the runtime kind, ownership, and initial value of dashboard topics.
174
+ * @internal
175
+ */
176
+ export const DASHBOARD_TOPIC_MANIFEST: {
177
+ readonly [K in keyof DashboardTopics]: TopicManifestEntry<DashboardTopics[K]>
178
+ } = {
179
+ 'applications.config': stateTopic(undefined),
180
+ 'applications.foreground': stateTopic(undefined),
181
+ 'applications.list': stateTopic(undefined),
182
+ 'auth.token': stateTopic(undefined),
183
+ 'auth.token.refresh': dashboardEvent,
184
+ 'navigation.location': stateTopic(undefined),
185
+ 'navigation.location.update': dashboardEvent,
186
+ 'organizations.current': stateTopic(undefined),
187
+ 'panels.mode': stateTopic({ok: true, value: null}),
188
+ 'panels.mode.set': dashboardEvent,
189
+ 'preferences.color-scheme': stateTopic(undefined),
190
+ 'preferences.dock-locked': stateTopic(undefined),
191
+ 'users.current': stateTopic(undefined),
192
+ }
193
+
194
+ /**
195
+ * Defines the runtime manifest accepted from any message bus version.
196
+ * @internal
197
+ */
198
+ export type TopicManifest = Readonly<
199
+ Record<
200
+ string,
201
+ | {readonly kind: 'state'; readonly seed: unknown}
202
+ | {readonly kind: 'event'; readonly ownership: TopicOwnership}
203
+ >
204
+ >
205
+
206
+ /**
207
+ * Names of all declared event topics.
208
+ * @public
209
+ */
210
+ export type EventTopic = {
211
+ [K in TopicName]: Topics[K] extends {kind: 'event'} ? K : never
212
+ }[TopicName]
213
+
214
+ /**
215
+ * The value type of a state topic.
216
+ * @public
217
+ */
218
+ export type ValueOf<K extends StateTopic> = Topics[K] extends StateTopicDef<infer T> ? T : never
219
+
220
+ /**
221
+ * The payload type of an event topic.
222
+ * @public
223
+ */
224
+ export type PayloadOf<K extends EventTopic> =
225
+ Topics[K] extends EventTopicDef<infer P, infer _R> ? P : never
226
+
227
+ /**
228
+ * The reply type of an event topic (`never` if it declares none).
229
+ * @public
230
+ */
231
+ export type ReplyOf<K extends EventTopic> =
232
+ Topics[K] extends EventTopicDef<infer _P, infer R> ? R : never
233
+
234
+ /**
235
+ * Converts a topic value between 2 adjacent versions.
236
+ * @internal
237
+ */
238
+ export interface TopicMigration {
239
+ /** The older version. */
240
+ readonly from: number
241
+ /** The newer version. */
242
+ readonly to: number
243
+ /** Converts an older state value or event payload to the newer version. */
244
+ up(older: unknown): unknown
245
+ /** Converts a newer state value or event payload to the older version. */
246
+ down(newer: unknown): unknown
247
+ /** Converts event replies between these versions. */
248
+ readonly reply?: {
249
+ /** Converts an older reply to the newer version. */
250
+ up(older: unknown): unknown
251
+ /** Converts a newer reply to the older version. */
252
+ down(newer: unknown): unknown
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Defines the bundled migration chain for each topic.
258
+ * @internal
259
+ */
260
+ export const topicMigrations: Partial<Record<TopicName, readonly TopicMigration[]>> = {}
@@ -2,13 +2,13 @@ import {type ReleaseDocument} from '@sanity/client'
2
2
  import {SanityEncoder} from '@sanity/mutate'
3
3
  import {type PatchMutation as SanityMutatePatchMutation} from '@sanity/mutate/_unstable_store'
4
4
  import {type PatchMutation, type PatchOperations} from '@sanity/types'
5
- import {type SanityDocument} from 'groq'
6
5
 
7
6
  import {
8
7
  type DocumentHandle,
9
8
  type DocumentTypeHandle,
10
9
  type ReleaseHandle,
11
10
  } from '../config/sanityConfig'
11
+ import {type ResolveDocument} from '../typegen/resolve'
12
12
  import {getEffectiveDocumentId} from './util'
13
13
 
14
14
  const isSanityMutatePatch = (value: unknown): value is SanityMutatePatchMutation => {
@@ -37,7 +37,7 @@ export interface CreateDocumentAction<
37
37
  */
38
38
  initialValue?: Partial<
39
39
  Omit<
40
- SanityDocument<TDocumentType, `${TProjectId}.${TDataset}`>,
40
+ ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`>,
41
41
  '_id' | '_type' | '_rev' | '_createdAt' | '_updatedAt'
42
42
  >
43
43
  >
@@ -166,7 +166,7 @@ export function createDocument<
166
166
  doc: DocumentTypeHandle<TDocumentType, TDataset, TProjectId>,
167
167
  initialValue?: Partial<
168
168
  Omit<
169
- SanityDocument<TDocumentType, `${TProjectId}.${TDataset}`>,
169
+ ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`>,
170
170
  '_id' | '_type' | '_rev' | '_createdAt' | '_updatedAt'
171
171
  >
172
172
  >,
@@ -1,10 +1,10 @@
1
- import {type SanityDocument} from 'groq'
2
1
  import {distinctUntilChanged, filter, first, firstValueFrom, map, race} from 'rxjs'
3
2
 
4
3
  import {type DocumentResource} from '../config/sanityConfig'
5
4
  import {bindActionByResource} from '../store/createActionBinder'
6
5
  import {type SanityInstance} from '../store/createSanityInstance'
7
6
  import {type StoreContext} from '../store/defineStore'
7
+ import {type ResolveDocument} from '../typegen/resolve'
8
8
  import {randomUuid} from '../utils/ids'
9
9
  import {type Action} from './actions'
10
10
  import {documentStore, type DocumentStoreState} from './documentStore'
@@ -14,7 +14,7 @@ import {type AppliedTransaction, type QueuedTransaction, queueTransaction} from
14
14
  import {normalizeActionsForResource} from './resourceRules'
15
15
 
16
16
  /** @beta */
17
- export interface ActionsResult<TDocument extends SanityDocument = SanityDocument> {
17
+ export interface ActionsResult<TDocument extends ResolveDocument = ResolveDocument> {
18
18
  transactionId: string
19
19
  documents: DocumentSet<TDocument>
20
20
  previous: DocumentSet<TDocument>
@@ -56,7 +56,7 @@ export function applyDocumentActions<
56
56
  >(
57
57
  instance: SanityInstance,
58
58
  options: ApplyDocumentActionsOptions,
59
- ): Promise<ActionsResult<SanityDocument<TDocumentType, `${TProjectId}.${TDataset}`>>>
59
+ ): Promise<ActionsResult<ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`>>>
60
60
  /** @beta */
61
61
  export function applyDocumentActions(
62
62
  instance: SanityInstance,
@@ -1,7 +1,6 @@
1
1
  import {type Action, ClientError, CorsOriginError, type Mutation} from '@sanity/client'
2
2
  import {DocumentId, getDraftId, getPublishedId, getVersionId} from '@sanity/id-utils'
3
3
  import {jsonMatch} from '@sanity/json-match'
4
- import {type SanityDocument} from 'groq'
5
4
  import {type ExprNode} from 'groq-js'
6
5
  import {
7
6
  catchError,
@@ -46,6 +45,7 @@ import {
46
45
  import {type SanityInstance} from '../store/createSanityInstance'
47
46
  import {createStateSourceAction, type StateSource} from '../store/createStateSourceAction'
48
47
  import {defineStore, type StoreContext} from '../store/defineStore'
48
+ import {type ResolveDocument} from '../typegen/resolve'
49
49
  import {type DocumentAction} from './actions'
50
50
  import {
51
51
  ACL_RETRY_BASE_DELAY,
@@ -98,7 +98,7 @@ export interface DocumentStoreState {
98
98
  identity?: string
99
99
  error?: unknown
100
100
  sharedListener: SharedListener
101
- fetchDocument: (documentId: string) => Observable<SanityDocument | null>
101
+ fetchDocument: (documentId: string) => Observable<ResolveDocument | null>
102
102
  events: Subject<DocumentEvent>
103
103
  }
104
104
 
@@ -108,12 +108,12 @@ export interface DocumentState {
108
108
  * the "remote" local copy that matches the server. represents the last known
109
109
  * server state. this gets updated every time we confirm remote patches
110
110
  */
111
- remote?: SanityDocument | null
111
+ remote?: ResolveDocument | null
112
112
  /**
113
113
  * the current ephemeral working copy that includes local optimistic changes
114
114
  * that have not yet been confirmed by the server
115
115
  */
116
- local?: SanityDocument | null
116
+ local?: ResolveDocument | null
117
117
  /**
118
118
  * the revision that our remote document is at
119
119
  */
@@ -194,7 +194,7 @@ export function getDocumentState<
194
194
  >(
195
195
  instance: SanityInstance,
196
196
  options: DocumentOptions<undefined, TDocumentType, TDataset, TProjectId>,
197
- ): StateSource<SanityDocument<TDocumentType, `${TProjectId}.${TDataset}`> | undefined | null>
197
+ ): StateSource<ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`> | undefined | null>
198
198
 
199
199
  /** @beta */
200
200
  export function getDocumentState<
@@ -206,7 +206,7 @@ export function getDocumentState<
206
206
  instance: SanityInstance,
207
207
  options: DocumentOptions<TPath, TDocumentType, TDataset, TProjectId>,
208
208
  ): StateSource<
209
- JsonMatch<SanityDocument<TDocumentType, `${TProjectId}.${TDataset}`>, TPath> | undefined
209
+ JsonMatch<ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`>, TPath> | undefined
210
210
  >
211
211
 
212
212
  /** @beta */
@@ -229,12 +229,12 @@ const _getDocumentState = bindActionByResource(
229
229
  const {documentId: docId, path, liveEdit, perspective} = options
230
230
  const documentId = DocumentId(docId)
231
231
  if (error) throw error
232
- let document: SanityDocument | null | undefined
232
+ let document: ResolveDocument | null | undefined
233
233
 
234
234
  if (liveEdit) {
235
235
  document = documentStates[documentId]?.local
236
236
  } else {
237
- let version: SanityDocument | null | undefined
237
+ let version: ResolveDocument | null | undefined
238
238
  if (isReleasePerspective(perspective)) {
239
239
  const versionId = getVersionId(documentId, perspective.releaseName)
240
240
  version = documentStates[versionId]?.local
@@ -267,16 +267,16 @@ export function resolveDocument<
267
267
  >(
268
268
  instance: SanityInstance,
269
269
  docHandle: DocumentHandle<TDocumentType, TDataset, TProjectId>,
270
- ): Promise<SanityDocument<TDocumentType, `${TProjectId}.${TDataset}`> | null>
270
+ ): Promise<ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`> | null>
271
271
  /** @beta */
272
- export function resolveDocument<TData extends SanityDocument>(
272
+ export function resolveDocument<TData extends ResolveDocument>(
273
273
  instance: SanityInstance,
274
274
  docHandle: DocumentHandle<string, string, string>,
275
275
  ): Promise<TData | null>
276
276
  /** @beta */
277
277
  export function resolveDocument(
278
278
  ...args: Parameters<typeof _resolveDocument>
279
- ): Promise<SanityDocument | null> {
279
+ ): Promise<ResolveDocument | null> {
280
280
  return _resolveDocument(...args)
281
281
  }
282
282
  const _resolveDocument = bindActionByResource(
@@ -287,7 +287,7 @@ const _resolveDocument = bindActionByResource(
287
287
  ...docHandle,
288
288
  path: undefined,
289
289
  }).observable.pipe(filter((i) => i !== undefined)),
290
- ) as Promise<SanityDocument | null>
290
+ ) as Promise<ResolveDocument | null>
291
291
  },
292
292
  )
293
293
 
@@ -18,10 +18,9 @@ import {
18
18
  } from './patchOperations'
19
19
 
20
20
  /**
21
- * Represents a set of document that will go into `applyMutations`. Before
22
- * applying a mutation, it's expected that all relevant documents that the
23
- * mutations affect are included, including those that do not exist yet.
24
- * Documents that don't exist have a `null` value.
21
+ * Maps document IDs to documents, using `null` for documents that do not exist.
22
+ *
23
+ * @beta
25
24
  */
26
25
  export type DocumentSet<TDocument extends SanityDocument = SanityDocument> = {
27
26
  [TDocumentId in string]?: TDocument | null
@@ -29,10 +28,29 @@ export type DocumentSet<TDocument extends SanityDocument = SanityDocument> = {
29
28
 
30
29
  type SupportedPatchOperation = Exclude<keyof PatchOperations, 'merge'>
31
30
 
32
- // > If multiple patches are included, then the order of execution is as follows:
33
- // > - set, setIfMissing, unset, inc, dec, insert.
34
- // > https://www.sanity.io/docs/http-mutations#5b4db1396e56
35
- const patchOperations = {
31
+ /**
32
+ * Exposes the patch operations used by {@link processMutations}.
33
+ *
34
+ * @remarks
35
+ * Property order determines execution order in {@link processMutations}:
36
+ * `ifRevisionID`, `set`, `setIfMissing`, `unset`, `inc`, `dec`, `insert`,
37
+ * then `diffMatchPatch`. Reordering the properties changes mutation results.
38
+ * Freezes the collection to prevent callers from replacing the functions
39
+ * used by the SDK's mutation evaluator.
40
+ *
41
+ * @see https://www.sanity.io/docs/http-mutations#5b4db1396e56
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const document = {items: [{_key: 'a', title: 'Before'}]}
46
+ * const updated = patchOperations.set<typeof document>(document, {
47
+ * 'items[_key=="a"].title': 'After',
48
+ * })
49
+ * ```
50
+ *
51
+ * @internal
52
+ */
53
+ export const patchOperations = Object.freeze({
36
54
  ifRevisionID,
37
55
  set,
38
56
  setIfMissing,
@@ -46,7 +64,7 @@ const patchOperations = {
46
64
  input: unknown,
47
65
  pathExpressions: NonNullable<PatchOperations[K]>,
48
66
  ) => unknown
49
- }
67
+ })
50
68
 
51
69
  /**
52
70
  * Implements ID generation:
@@ -69,7 +87,12 @@ export function getId(id?: string): string {
69
87
  return id
70
88
  }
71
89
 
72
- interface ProcessMutationsOptions {
90
+ /**
91
+ * Configures {@link processMutations}.
92
+ *
93
+ * @internal
94
+ */
95
+ export interface ProcessMutationsOptions {
73
96
  /**
74
97
  * The transaction ID that will become the next `_rev` for documents mutated
75
98
  * by the given mutations.
@@ -84,8 +107,8 @@ interface ProcessMutationsOptions {
84
107
  */
85
108
  mutations: Mutation[]
86
109
  /**
87
- * An optional timestamp that will be used for `_createdAt` and `_updatedAt`
88
- * timestamp when applicable.
110
+ * Supplies the default `_createdAt` and `_updatedAt` timestamps.
111
+ * Uses the current time when omitted or empty.
89
112
  */
90
113
  timestamp?: string
91
114
  }
@@ -108,18 +131,31 @@ export function getDocumentIds(selection: MutationSelection): string[] {
108
131
  }
109
132
 
110
133
  /**
111
- * Applies the given mutation to the given document set. Note, it is expected
112
- * that all relevant documents that the mutations affect should be within the
113
- * given `document` set. If a document does not exist, it should have the value
114
- * `null`. If a document is deleted as a result of the mutations, it will still
115
- * have its document ID present in the returns documents, but it will have a
116
- * value of `null`.
134
+ * Applies mutations to an in-memory set of Sanity documents.
135
+ * Deleted documents remain in the returned set with a value of `null`.
136
+ *
137
+ * Include every document affected by `mutations` in `documents`, using `null`
138
+ * for documents that do not exist.
139
+ *
140
+ * Returns `documents` itself when `mutations` is empty. Otherwise, returns a
141
+ * shallow copy of that object. The result can reuse document objects and
142
+ * nested objects from `documents`. Mutating a reused object through the
143
+ * result also changes the input.
144
+ *
145
+ * Sets `_rev` to `transactionId` on created, replaced, and patched documents.
146
+ * Uses `timestamp` for timestamps unless the mutation preserves them.
147
+ * Ignores `merge` patches; `inc` and `dec` skip values that are not numbers.
148
+ * Generates document IDs when omitted or ending in `.`.
149
+ * Patch operations can generate missing array keys.
150
+ *
151
+ * Does not persist documents or run Content Lake's server validation.
117
152
  *
118
- * The given `transactionId` will be used as the resulting `_rev` for documents
119
- * affected by the given set of mutations.
153
+ * @throws If a selection uses a query, a `create` targets an existing document,
154
+ * or a `patch` targets a document that does not exist.
155
+ * @throws If a patch operation fails, including a revision check failure or
156
+ * `diffMatchPatch` applied to a value other than a string or `undefined`.
120
157
  *
121
- * If a `timestamp` is given, that will be used as for the relevant `_updatedAt`
122
- * and `_createdAt` timestamps.
158
+ * @internal
123
159
  */
124
160
  export function processMutations({
125
161
  documents,
@@ -1,5 +1,6 @@
1
1
  import {expectTypeOf, test} from 'vitest'
2
2
 
3
+ import {type ApplicationInterface} from '../applications/applications'
3
4
  import {type SanityInstance} from '../store/createSanityInstance'
4
5
  import {type StateSource} from '../store/createStateSourceAction'
5
6
  import {type FetcherSnapshot} from '../store/fetcherStore'
@@ -10,7 +11,6 @@ import {
10
11
  type InstallationActiveConfig,
11
12
  type InstallationBase,
12
13
  type InstallationInclude,
13
- type InstallationInterface,
14
14
  installations,
15
15
  type InstallationsResponse,
16
16
  } from './installations'
@@ -40,7 +40,7 @@ test('Installation — each token adds its top-level field, required, others abs
40
40
  Extract<keyof Installation<'access'>, 'activeConfig' | 'interfaces'>
41
41
  >().toEqualTypeOf<never>()
42
42
 
43
- expectTypeOf<Installation<'interfaces'>['interfaces']>().toEqualTypeOf<InstallationInterface[]>()
43
+ expectTypeOf<Installation<'interfaces'>['interfaces']>().toEqualTypeOf<ApplicationInterface[]>()
44
44
  expectTypeOf<
45
45
  Installation<'activeConfig'>['activeConfig']
46
46
  >().toEqualTypeOf<InstallationActiveConfig | null>()
@@ -49,7 +49,7 @@ test('Installation — each token adds its top-level field, required, others abs
49
49
  test('Installation — multiple tokens add each field', () => {
50
50
  type Both = Installation<'access' | 'interfaces'>
51
51
  expectTypeOf<Both['access']>().toEqualTypeOf<InstallationAccess[]>()
52
- expectTypeOf<Both['interfaces']>().toEqualTypeOf<InstallationInterface[]>()
52
+ expectTypeOf<Both['interfaces']>().toEqualTypeOf<ApplicationInterface[]>()
53
53
  expectTypeOf<Extract<keyof Both, 'activeConfig'>>().toEqualTypeOf<never>()
54
54
  })
55
55
 
@@ -1,5 +1,6 @@
1
1
  import {switchMap} from 'rxjs'
2
2
 
3
+ import {type ApplicationInterface} from '../applications/applications'
3
4
  import {getClientState} from '../client/clientStore'
4
5
  import {defineFetcher} from '../store/fetcherStore'
5
6
  import {buildQuery} from '../utils/buildQuery'
@@ -29,24 +30,6 @@ export interface InstallationAccess {
29
30
  resourceId: string
30
31
  }
31
32
 
32
- /**
33
- * An interface exposed by an installation, embedded when `interfaces` is
34
- * included.
35
- *
36
- * @see https://www.sanity.io/docs/http-reference/applications-api
37
- * @public
38
- */
39
- export interface InstallationInterface {
40
- id: string
41
- type: 'app' | 'worker' | 'asset_source' | 'panel'
42
- name: string
43
- title: string
44
- version: string
45
- /** Module federation module ID; resolved from the host mf-manifest at runtime */
46
- moduleId: string
47
- metadata: {group?: string; priority?: number} | null
48
- }
49
-
50
33
  /**
51
34
  * The active config of an installation, embedded when `activeConfig` is
52
35
  * included. `null` when nothing is deployed.
@@ -104,7 +87,7 @@ export type Installation<Include extends InstallationInclude = never> = Installa
104
87
  {activeConfig: InstallationActiveConfig | null}
105
88
  > &
106
89
  Included<InstallationInclude, 'access', Include, {access: InstallationAccess[]}> &
107
- Included<InstallationInclude, 'interfaces', Include, {interfaces: InstallationInterface[]}>
90
+ Included<InstallationInclude, 'interfaces', Include, {interfaces: ApplicationInterface[]}>
108
91
 
109
92
  /**
110
93
  * Options for listing an organization's installations.
@@ -1,5 +1,4 @@
1
1
  import {DocumentId, getPublishedId} from '@sanity/id-utils'
2
- import {type SanityProjectionResult} from 'groq'
3
2
 
4
3
  import {type DocumentHandle} from '../config/sanityConfig'
5
4
  import {bindActionByResourceAndPerspective} from '../store/createActionBinder'
@@ -9,6 +8,7 @@ import {
9
8
  type SelectorContext,
10
9
  type StateSource,
11
10
  } from '../store/createStateSourceAction'
11
+ import {type ResolveProjectionResult} from '../typegen/resolve'
12
12
  import {hashString} from '../utils/hashString'
13
13
  import {randomId} from '../utils/ids'
14
14
  import {omitProperty} from '../utils/object'
@@ -39,7 +39,7 @@ export function getProjectionState<
39
39
  options: ProjectionOptions<TProjection, TDocumentType, TDataset, TProjectId>,
40
40
  ): StateSource<
41
41
  | ProjectionValuePending<
42
- SanityProjectionResult<TProjection, TDocumentType, `${TProjectId}.${TDataset}`>
42
+ ResolveProjectionResult<TProjection, TDocumentType, `${TProjectId}.${TDataset}`>
43
43
  >
44
44
  | undefined
45
45
  >
@@ -1,8 +1,8 @@
1
- import {type SanityProjectionResult} from 'groq'
2
1
  import {filter, firstValueFrom} from 'rxjs'
3
2
 
4
3
  import {bindActionByResourceAndPerspective} from '../store/createActionBinder'
5
4
  import {type SanityInstance} from '../store/createSanityInstance'
5
+ import {type ResolveProjectionResult} from '../typegen/resolve'
6
6
  import {getProjectionState, type ProjectionOptions} from './getProjectionState'
7
7
  import {projectionStore} from './projectionStore'
8
8
  import {type ProjectionValuePending} from './types'
@@ -18,7 +18,7 @@ export function resolveProjection<
18
18
  options: ProjectionOptions<TProjection, TDocumentType, TDataset, TProjectId>,
19
19
  ): Promise<
20
20
  ProjectionValuePending<
21
- SanityProjectionResult<TProjection, TDocumentType, `${TProjectId}.${TDataset}`>
21
+ ResolveProjectionResult<TProjection, TDocumentType, `${TProjectId}.${TDataset}`>
22
22
  >
23
23
  >
24
24