@sanity/sdk 3.0.0 → 3.2.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 (132) hide show
  1. package/dist/_exports/_internal.d.ts +72 -19
  2. package/dist/_exports/_internal.d.ts.map +1 -1
  3. package/dist/_exports/_internal.js +130 -11
  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 +2 -2
  11. package/dist/_exports/dashboard.d.ts +2 -0
  12. package/dist/_exports/dashboard.js +2 -0
  13. package/dist/{authStore-C0o1NOxW.d.ts → authStore-BO2vch3I.d.ts} +516 -63
  14. package/dist/authStore-BO2vch3I.d.ts.map +1 -0
  15. package/dist/{authStore-CtE9bLHW.js → clientStore-BI4D8HhJ.js} +390 -15
  16. package/dist/clientStore-BI4D8HhJ.js.map +1 -0
  17. package/dist/{createSanityInstance-DUI1bGIg.d.ts → createSanityInstance-BQIqyGLX.d.ts} +17 -2
  18. package/dist/createSanityInstance-BQIqyGLX.d.ts.map +1 -0
  19. package/dist/{createStateSourceAction-BOKtqFNm.js → createStateSourceAction-DdAhEfnZ.js} +7 -3
  20. package/dist/createStateSourceAction-DdAhEfnZ.js.map +1 -0
  21. package/dist/defineProperty-Bho9Ngsi.js +31 -0
  22. package/dist/{getNodeState-CR3__B2D.js → getNodeState-B4nG0sjm.js} +2 -2
  23. package/dist/{getNodeState-CR3__B2D.js.map → getNodeState-B4nG0sjm.js.map} +1 -1
  24. package/dist/index.d.ts +3 -3
  25. package/dist/index.js +370 -90
  26. package/dist/index.js.map +1 -1
  27. package/dist/topicStore-Ct7IFNtY.js +565 -0
  28. package/dist/topicStore-Ct7IFNtY.js.map +1 -0
  29. package/dist/topicStore-D0WqhQGF.d.ts +435 -0
  30. package/dist/topicStore-D0WqhQGF.d.ts.map +1 -0
  31. package/dist/{queryStore-C7yMHPUI.js → version-BcRmCiYi.js} +48 -16
  32. package/dist/version-BcRmCiYi.js.map +1 -0
  33. package/package.json +19 -18
  34. package/src/_exports/_internal.ts +13 -0
  35. package/src/_exports/dashboard.ts +36 -0
  36. package/src/_exports/index.ts +17 -1
  37. package/src/access/checkPermissions.test.ts +1 -1
  38. package/src/access/checkPermissions.ts +1 -1
  39. package/src/applications/applications.test-d.ts +28 -4
  40. package/src/applications/applications.test.ts +4 -4
  41. package/src/applications/applications.ts +43 -18
  42. package/src/auth/authMode.test.ts +24 -0
  43. package/src/auth/authMode.ts +8 -3
  44. package/src/auth/authStore.ts +12 -0
  45. package/src/auth/authStrategy.ts +2 -0
  46. package/src/auth/handleAuthCallback.test.ts +2 -2
  47. package/src/auth/handleAuthCallback.ts +1 -1
  48. package/src/auth/logout.test.ts +46 -1
  49. package/src/auth/logout.ts +2 -1
  50. package/src/auth/oauth/oauthActions.test.ts +457 -0
  51. package/src/auth/oauth/oauthActions.ts +389 -0
  52. package/src/auth/oauth/oauthAuth.test.ts +206 -0
  53. package/src/auth/oauth/oauthAuth.ts +168 -0
  54. package/src/auth/oauth/pkce.test.ts +44 -0
  55. package/src/auth/oauth/pkce.ts +60 -0
  56. package/src/auth/oauth/types.ts +12 -0
  57. package/src/auth/refreshStampedToken.ts +1 -1
  58. package/src/auth/studioModeAuth.test.ts +1 -1
  59. package/src/auth/studioModeAuth.ts +1 -1
  60. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +2 -2
  61. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +1 -1
  62. package/src/comments/addonDatasetStore.test.ts +2 -2
  63. package/src/comments/addonDatasetStore.ts +2 -2
  64. package/src/comments/commentActions.ts +6 -5
  65. package/src/comments/commentsStore.ts +4 -4
  66. package/src/config/authConfig.ts +18 -0
  67. package/src/dashboard/messageBus/__fixtures__/test-topics.ts +18 -0
  68. package/src/dashboard/messageBus/bus.test.ts +1033 -0
  69. package/src/dashboard/messageBus/bus.ts +1246 -0
  70. package/src/dashboard/messageBus/store.test.ts +142 -0
  71. package/src/dashboard/messageBus/store.ts +74 -0
  72. package/src/dashboard/messageBus/topicStore.test.ts +172 -0
  73. package/src/dashboard/messageBus/topicStore.ts +184 -0
  74. package/src/dashboard/messageBus/topics.test.ts +71 -0
  75. package/src/dashboard/messageBus/topics.ts +260 -0
  76. package/src/document/actions.ts +3 -3
  77. package/src/document/applyDocumentActions.ts +5 -9
  78. package/src/document/documentStore.concurrency.test.ts +2 -1
  79. package/src/document/documentStore.test.ts +19 -18
  80. package/src/document/documentStore.ts +17 -17
  81. package/src/document/permissions.ts +2 -1
  82. package/src/document/processActions.test.ts +2 -1
  83. package/src/document/processMutations.ts +61 -24
  84. package/src/document/reducers.ts +2 -2
  85. package/src/installations/installations.test-d.ts +3 -3
  86. package/src/installations/installations.test.ts +2 -2
  87. package/src/installations/installations.ts +4 -21
  88. package/src/organization/organization.test.ts +2 -2
  89. package/src/organization/organization.ts +1 -1
  90. package/src/organizations/organizations.test.ts +2 -2
  91. package/src/organizations/organizations.ts +1 -1
  92. package/src/presence/presenceStore.test.ts +15 -22
  93. package/src/presence/presenceStore.ts +3 -2
  94. package/src/project/project.test.ts +3 -3
  95. package/src/project/project.ts +1 -1
  96. package/src/projection/getProjectionState.test.ts +6 -4
  97. package/src/projection/getProjectionState.ts +4 -4
  98. package/src/projection/resolveProjection.ts +2 -2
  99. package/src/projects/projects.test.ts +2 -2
  100. package/src/projects/projects.ts +1 -1
  101. package/src/query/queryStore.ts +6 -6
  102. package/src/store/createSanityInstance.test.ts +1 -1
  103. package/src/store/createSanityInstance.ts +4 -4
  104. package/src/store/createStoreInstance.test.ts +0 -5
  105. package/src/telemetry/telemetryManager.test.ts +1 -1
  106. package/src/telemetry/telemetryManager.ts +2 -2
  107. package/src/typegen/defineProjection.test.ts +17 -0
  108. package/src/typegen/defineProjection.ts +19 -0
  109. package/src/typegen/groqCompat.ts +122 -0
  110. package/src/typegen/resolve.ts +37 -0
  111. package/src/users/usersStore.test.ts +1 -1
  112. package/src/users/usersStore.ts +5 -5
  113. package/src/utils/createGroqSearchFilter.test.ts +8 -0
  114. package/src/utils/createGroqSearchFilter.ts +6 -3
  115. package/src/utils/ids.test.ts +40 -15
  116. package/src/utils/ids.ts +47 -2
  117. package/dist/authStore-C0o1NOxW.d.ts.map +0 -1
  118. package/dist/authStore-CtE9bLHW.js.map +0 -1
  119. package/dist/clientStore-BtE2-q2i.js +0 -90
  120. package/dist/clientStore-BtE2-q2i.js.map +0 -1
  121. package/dist/createSanityInstance-DUI1bGIg.d.ts.map +0 -1
  122. package/dist/createStateSourceAction-BOKtqFNm.js.map +0 -1
  123. package/dist/getEnv-D5RGkQLu.js +0 -8
  124. package/dist/getEnv-D5RGkQLu.js.map +0 -1
  125. package/dist/logger-DAtJb5RQ.js +0 -173
  126. package/dist/logger-DAtJb5RQ.js.map +0 -1
  127. package/dist/queryStore-C7yMHPUI.js.map +0 -1
  128. package/dist/rolldown-runtime-vyAXikos.js +0 -9
  129. package/dist/telemetryManager-UIEiMtC0.js +0 -127
  130. package/dist/telemetryManager-UIEiMtC0.js.map +0 -1
  131. package/dist/version-D8RP_Q59.js +0 -9
  132. package/dist/version-D8RP_Q59.js.map +0 -1
@@ -0,0 +1,168 @@
1
+ import {defer, distinctUntilChanged, filter, map, type Subscription} from 'rxjs'
2
+
3
+ import {type StoreContext} from '../../store/defineStore'
4
+ import {DEFAULT_BASE} from '../authConstants'
5
+ import {AuthStateType} from '../authStateType'
6
+ import {type AuthStoreState} from '../authStore'
7
+ import {type AuthStrategyOptions, type AuthStrategyResult} from '../authStrategy'
8
+ import {subscribeToStateAndFetchCurrentUser} from '../subscribeToStateAndFetchCurrentUser'
9
+ import {createLoggedInAuthState, getDefaultStorage, getStorageEvents} from '../utils'
10
+ import {type OAuthTokens} from './types'
11
+
12
+ /** localStorage (or configured `storageArea`) key for persisted OAuth tokens. */
13
+ export const OAUTH_TOKENS_KEY = '__sanity_oauth_tokens'
14
+
15
+ /** The persisted JSON shape of {@link OAuthTokens}, `expiresAt` as an ISO string. */
16
+ interface SerializedOAuthTokens extends Omit<OAuthTokens, 'expiresAt'> {
17
+ expiresAt: string
18
+ }
19
+
20
+ /**
21
+ * Parses persisted token JSON back into {@link OAuthTokens}. Returns `null`
22
+ * when the value is missing or malformed.
23
+ *
24
+ * @internal
25
+ */
26
+ export function deserializeTokens(raw: string | null): OAuthTokens | null {
27
+ if (!raw) return null
28
+ try {
29
+ const parsed: unknown = JSON.parse(raw)
30
+ if (
31
+ typeof parsed !== 'object' ||
32
+ parsed === null ||
33
+ !('accessToken' in parsed) ||
34
+ typeof (parsed as SerializedOAuthTokens).accessToken !== 'string' ||
35
+ !('expiresAt' in parsed) ||
36
+ typeof (parsed as SerializedOAuthTokens).expiresAt !== 'string'
37
+ ) {
38
+ return null
39
+ }
40
+ const value = parsed as SerializedOAuthTokens
41
+ return {
42
+ accessToken: value.accessToken,
43
+ tokenType: 'bearer',
44
+ expiresIn: value.expiresIn,
45
+ expiresAt: new Date(value.expiresAt),
46
+ ...(value.refreshToken !== undefined && {refreshToken: value.refreshToken}),
47
+ }
48
+ } catch {
49
+ return null
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Resolves the initial auth state for OAuth mode.
55
+ *
56
+ * State discovery order:
57
+ * 1. Persisted tokens in `__sanity_oauth_tokens` → `LOGGED_IN`
58
+ * 2. Callback URL (contains `code`/`state`/`error` and matches `redirectUri`)
59
+ * → `LOGGING_IN`
60
+ * 3. Otherwise → `LOGGED_OUT`
61
+ *
62
+ * @internal
63
+ */
64
+ export function getOauthInitialState(options: AuthStrategyOptions): AuthStrategyResult {
65
+ const {authConfig, initialLocationHref} = options
66
+ const storageKey = OAUTH_TOKENS_KEY
67
+ const storageArea = authConfig.storageArea ?? getDefaultStorage()
68
+ const redirectUri = authConfig.oauth?.redirectUri
69
+
70
+ // Persisted tokens win
71
+ const tokens = deserializeTokens(storageArea?.getItem(OAUTH_TOKENS_KEY) ?? null)
72
+ if (tokens) {
73
+ return {
74
+ authState: createLoggedInAuthState(tokens.accessToken, null),
75
+ storageKey,
76
+ storageArea,
77
+ authMethod: 'localstorage',
78
+ dashboardContext: {},
79
+ oauthTokens: tokens,
80
+ }
81
+ }
82
+
83
+ // Callback URL with code/state/error whose origin + pathname match our
84
+ // redirect URI (query and hash are ignored).
85
+ const {searchParams} = new URL(initialLocationHref, DEFAULT_BASE)
86
+ const isCallback =
87
+ searchParams.has('code') || searchParams.has('state') || searchParams.has('error')
88
+ if (redirectUri && isCallback) {
89
+ const loc = new URL(initialLocationHref, DEFAULT_BASE)
90
+ const redirect = new URL(redirectUri, DEFAULT_BASE)
91
+ if (loc.origin === redirect.origin && loc.pathname === redirect.pathname) {
92
+ return {
93
+ authState: {type: AuthStateType.LOGGING_IN, isExchangingToken: false},
94
+ storageKey,
95
+ storageArea,
96
+ authMethod: undefined,
97
+ dashboardContext: {},
98
+ }
99
+ }
100
+ }
101
+
102
+ // No tokens, not a callback
103
+ return {
104
+ authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},
105
+ storageKey,
106
+ storageArea,
107
+ authMethod: undefined,
108
+ dashboardContext: {},
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Subscribes to cross-tab `storage` events for the OAuth tokens key, syncing
114
+ * this tab's auth state when tokens change in another tab.
115
+ *
116
+ * @internal
117
+ */
118
+ export function subscribeToOAuthStorageEvents({state}: StoreContext<AuthStoreState>): Subscription {
119
+ const {storageArea} = state.get().options
120
+
121
+ const tokens$ = defer(getStorageEvents).pipe(
122
+ filter((e) => e.storageArea === storageArea && e.key === OAUTH_TOKENS_KEY),
123
+ map(() => deserializeTokens(storageArea?.getItem(OAUTH_TOKENS_KEY) ?? null)),
124
+ distinctUntilChanged((a, b) => a?.accessToken === b?.accessToken),
125
+ )
126
+
127
+ return tokens$.subscribe((tokens) => {
128
+ state.set('updateOAuthTokensFromStorageEvent', {
129
+ authState: tokens
130
+ ? createLoggedInAuthState(tokens.accessToken, null)
131
+ : {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},
132
+ oauthTokens: tokens ?? undefined,
133
+ })
134
+ })
135
+ }
136
+
137
+ /**
138
+ * Initialize OAuth auth subscriptions:
139
+ * - Subscribe to state changes and fetch current user
140
+ * - Subscribe to cross-tab storage events for the OAuth tokens key
141
+ *
142
+ * OAuth tokens are refreshed via the `refresh_token` grant (see
143
+ * `refreshOAuthTokens`), so the stamped-token refresher is not started here.
144
+ *
145
+ * @internal
146
+ */
147
+ export function initializeOauthAuth(context: StoreContext<AuthStoreState>): {
148
+ dispose: () => void
149
+ tokenRefresherStarted: boolean
150
+ } {
151
+ const subscriptions: Subscription[] = []
152
+
153
+ subscriptions.push(subscribeToStateAndFetchCurrentUser(context, {useProjectHostname: false}))
154
+
155
+ const storageArea = context.state.get().options?.storageArea
156
+ if (storageArea) {
157
+ subscriptions.push(subscribeToOAuthStorageEvents(context))
158
+ }
159
+
160
+ return {
161
+ dispose: () => {
162
+ for (const subscription of subscriptions) {
163
+ subscription.unsubscribe()
164
+ }
165
+ },
166
+ tokenRefresherStarted: false,
167
+ }
168
+ }
@@ -0,0 +1,44 @@
1
+ import {createHash} from 'node:crypto'
2
+
3
+ import {describe, expect, it} from 'vitest'
4
+
5
+ import {base64UrlEncode, generateCodeChallenge, generateCodeVerifier, generateState} from './pkce'
6
+
7
+ describe('base64UrlEncode', () => {
8
+ it('encodes bytes as base64url without padding', () => {
9
+ // 0xff 0xef 0xbf → "/++/" in base64, "_--_" in base64url (minus padding)
10
+ const encoded = base64UrlEncode(new Uint8Array([0xff, 0xef, 0xbf]))
11
+ expect(encoded).not.toContain('+')
12
+ expect(encoded).not.toContain('/')
13
+ expect(encoded).not.toContain('=')
14
+ expect(encoded).toBe('_--_')
15
+ })
16
+ })
17
+
18
+ describe('generateCodeVerifier / generateState', () => {
19
+ it('generates unique, URL-safe strings', () => {
20
+ const a = generateCodeVerifier()
21
+ const b = generateCodeVerifier()
22
+ const s = generateState()
23
+ expect(a).not.toBe(b)
24
+ expect(a).toMatch(/^[A-Za-z0-9_-]+$/)
25
+ expect(s).toMatch(/^[A-Za-z0-9_-]+$/)
26
+ expect(a.length).toBeGreaterThanOrEqual(43)
27
+ })
28
+ })
29
+
30
+ describe('generateCodeChallenge', () => {
31
+ it('produces base64url(sha256(verifier))', async () => {
32
+ const verifier = 'test-verifier'
33
+ const challenge = await generateCodeChallenge(verifier)
34
+
35
+ const expected = createHash('sha256')
36
+ .update(verifier)
37
+ .digest('base64')
38
+ .replace(/\+/g, '-')
39
+ .replace(/\//g, '_')
40
+ .replace(/=+$/, '')
41
+
42
+ expect(challenge).toBe(expected)
43
+ })
44
+ })
@@ -0,0 +1,60 @@
1
+ /**
2
+ * PKCE (Proof Key for Code Exchange, RFC 7636) helpers for the OAuth
3
+ * authorization-code flow.
4
+ *
5
+ * @internal
6
+ */
7
+
8
+ /** Number of random bytes used for the `code_verifier` and `state`. */
9
+ const RANDOM_BYTE_LENGTH = 32
10
+
11
+ /**
12
+ * Encodes a byte array as a base64url string (RFC 4648 §5) with no padding,
13
+ * as required for PKCE `code_verifier`/`code_challenge` values.
14
+ *
15
+ * @internal
16
+ */
17
+ export function base64UrlEncode(bytes: Uint8Array): string {
18
+ let binary = ''
19
+ for (const byte of bytes) {
20
+ binary += String.fromCharCode(byte)
21
+ }
22
+ return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
23
+ }
24
+
25
+ function randomString(): string {
26
+ const bytes = new Uint8Array(RANDOM_BYTE_LENGTH)
27
+ crypto.getRandomValues(bytes)
28
+ return base64UrlEncode(bytes)
29
+ }
30
+
31
+ /**
32
+ * Generates a PKCE `code_verifier`
33
+ *
34
+ * @internal
35
+ */
36
+ export function generateCodeVerifier(): string {
37
+ return randomString()
38
+ }
39
+
40
+ /**
41
+ * Generates the `state` parameter used to defend against CSRF on the OAuth
42
+ * callback.
43
+ *
44
+ * @internal
45
+ */
46
+ export function generateState(): string {
47
+ return randomString()
48
+ }
49
+
50
+ /**
51
+ * Derives the PKCE `code_challenge` from a `code_verifier` using
52
+ * `base64url(sha256(verifier))` (the `S256` method).
53
+ *
54
+ * @internal
55
+ */
56
+ export async function generateCodeChallenge(codeVerifier: string): Promise<string> {
57
+ const data = new TextEncoder().encode(codeVerifier)
58
+ const digest = await crypto.subtle.digest('SHA-256', data)
59
+ return base64UrlEncode(new Uint8Array(digest))
60
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * OAuth tokens issued by Sanity's OAuth token endpoint.
3
+ *
4
+ * @public
5
+ */
6
+ export interface OAuthTokens {
7
+ accessToken: string
8
+ tokenType: 'bearer'
9
+ expiresIn: number
10
+ expiresAt: Date
11
+ refreshToken?: string
12
+ }
@@ -36,7 +36,7 @@ function createTokenRefreshStream(
36
36
 
37
37
  const subscription = client.observable
38
38
  .request<{token: string}>({
39
- uri: 'auth/refresh-token',
39
+ url: 'auth/refresh-token',
40
40
  method: 'POST',
41
41
  tag: 'refresh-token',
42
42
  body: {
@@ -35,7 +35,7 @@ describe('checkForCookieAuth', () => {
35
35
 
36
36
  expect(result).toBe(true)
37
37
  expect(mockClient.request).toHaveBeenCalledWith({
38
- uri: '/users/me',
38
+ url: '/users/me',
39
39
  withCredentials: true,
40
40
  tag: 'users.get-current',
41
41
  })
@@ -30,7 +30,7 @@ export async function checkForCookieAuth(
30
30
  timeout: COOKIE_AUTH_TIMEOUT_MS,
31
31
  })
32
32
  const user = await client.request({
33
- uri: '/users/me',
33
+ url: '/users/me',
34
34
  withCredentials: true,
35
35
  tag: 'users.get-current',
36
36
  })
@@ -57,7 +57,7 @@ describe('subscribeToStateAndFetchCurrentUser', () => {
57
57
  })
58
58
  expect(mockRequest).toHaveBeenCalledWith({
59
59
  method: 'GET',
60
- uri: '/users/me',
60
+ url: '/users/me',
61
61
  tag: 'users.get-current',
62
62
  })
63
63
 
@@ -97,7 +97,7 @@ describe('subscribeToStateAndFetchCurrentUser', () => {
97
97
  })
98
98
  expect(mockRequest).toHaveBeenCalledWith({
99
99
  method: 'GET',
100
- uri: '/users/me',
100
+ url: '/users/me',
101
101
  tag: 'users.get-current',
102
102
  })
103
103
 
@@ -73,7 +73,7 @@ export const subscribeToStateAndFetchCurrentUser = (
73
73
  switchMap((client) =>
74
74
  client.observable
75
75
  .request<CurrentUser>({
76
- uri: '/users/me',
76
+ url: '/users/me',
77
77
  method: 'GET',
78
78
  tag: 'users.get-current',
79
79
  })
@@ -59,7 +59,7 @@ describe('getAddonDatasetState', () => {
59
59
  )
60
60
 
61
61
  expect(observableRequest).toHaveBeenCalledWith({
62
- uri: '/projects/p/datasets?datasetProfile=comments&addonFor=d',
62
+ url: '/projects/p/datasets?datasetProfile=comments&addonFor=d',
63
63
  tag: 'comments.addon-dataset.list',
64
64
  })
65
65
  })
@@ -139,7 +139,7 @@ describe('provisionAddonDataset', () => {
139
139
 
140
140
  await expect(provisionAddonDataset(instance, {})).resolves.toBe('d-comments')
141
141
  expect(request).toHaveBeenCalledWith({
142
- uri: '/comments/d/setup',
142
+ url: '/comments/d/setup',
143
143
  method: 'POST',
144
144
  tag: 'comments.addon-dataset.setup',
145
145
  })
@@ -67,7 +67,7 @@ function requestAddonDataset(
67
67
  ): Observable<string | undefined> {
68
68
  return client.observable
69
69
  .request<{name: string}[] | undefined>({
70
- uri: `/projects/${projectId}/datasets?datasetProfile=${ADDON_DATASET_PROFILE}&addonFor=${dataset}`,
70
+ url: `/projects/${projectId}/datasets?datasetProfile=${ADDON_DATASET_PROFILE}&addonFor=${dataset}`,
71
71
  tag: 'comments.addon-dataset.list',
72
72
  })
73
73
  .pipe(map((datasets) => datasets?.[0]?.name))
@@ -182,7 +182,7 @@ export const provisionAddonDataset: (
182
182
  })
183
183
 
184
184
  const response = await client.request<{datasetName?: string} | undefined>({
185
- uri: `/comments/${resource.dataset}/setup`,
185
+ url: `/comments/${resource.dataset}/setup`,
186
186
  method: 'POST',
187
187
  tag: 'comments.addon-dataset.setup',
188
188
  })
@@ -16,6 +16,7 @@ import {bindActionByResource, type BoundResourceKey} from '../store/createAction
16
16
  import {type SanityInstance} from '../store/createSanityInstance'
17
17
  import {type StoreState} from '../store/createStoreState'
18
18
  import {type StoreContext} from '../store/defineStore'
19
+ import {randomUuid} from '../utils/ids'
19
20
  import {
20
21
  assertDatasetResource,
21
22
  getAddonDatasetState,
@@ -326,7 +327,7 @@ export const createComment: (
326
327
  options,
327
328
  buildCommentPayload({
328
329
  authorId: requireCurrentUserId(instance),
329
- commentId: options.commentId ?? crypto.randomUUID(),
330
+ commentId: options.commentId ?? randomUuid(),
330
331
  contentSnapshot: options.contentSnapshot,
331
332
  context: options.context,
332
333
  documentRevisionId: options.documentRevisionId,
@@ -336,7 +337,7 @@ export const createComment: (
336
337
  fieldPath,
337
338
  selection: options.selection,
338
339
  status: options.status ?? 'open',
339
- threadId: options.threadId ?? crypto.randomUUID(),
340
+ threadId: options.threadId ?? randomUuid(),
340
341
  ...(isReleasePerspective(perspective) ? {documentVersionId: perspective.releaseName} : {}),
341
342
  }),
342
343
  )
@@ -386,7 +387,7 @@ export const replyToComment: (
386
387
  options,
387
388
  buildCommentPayload({
388
389
  authorId: requireCurrentUserId(instance),
389
- commentId: options.commentId ?? crypto.randomUUID(),
390
+ commentId: options.commentId ?? randomUuid(),
390
391
  handle: options,
391
392
  message: options.message,
392
393
  // Replies to a reply belong to the thread's first comment, matching how
@@ -420,7 +421,7 @@ export const updateComment: (
420
421
  {commentId, message}: UpdateCommentOptions,
421
422
  ) => {
422
423
  const lastEditedAt = new Date().toISOString()
423
- const transactionId = crypto.randomUUID()
424
+ const transactionId = randomUuid()
424
425
  const previous = findCommentById(state, commentId)
425
426
 
426
427
  if (previous) {
@@ -468,7 +469,7 @@ export const setCommentStatus: (
468
469
  {state, instance, key}: StoreContext<CommentsStoreState, BoundResourceKey>,
469
470
  {commentId, status}: SetCommentStatusOptions,
470
471
  ) => {
471
- const transactionId = crypto.randomUUID()
472
+ const transactionId = randomUuid()
472
473
  const previousComments: StoredComment[] = []
473
474
 
474
475
  for (const entry of Object.values(state.get().entries)) {
@@ -29,7 +29,7 @@ import {
29
29
  } from '../store/createStateSourceAction'
30
30
  import {type StoreState} from '../store/createStoreState'
31
31
  import {defineStore, type StoreContext} from '../store/defineStore'
32
- import {insecureRandomId} from '../utils/ids'
32
+ import {randomId} from '../utils/ids'
33
33
  import {setCleanupTimeout} from '../utils/setCleanupTimeout'
34
34
  import {observeAddonDatasetClient} from './addonDatasetStore'
35
35
  import {buildCommentThreads} from './buildCommentThreads'
@@ -360,7 +360,7 @@ export const getCommentsState: (
360
360
  selector: selectComments,
361
361
  onSubscribe: ({state, instance}, options: CommentsOptions) => {
362
362
  const key = getCommentsKey(toCommentsKeyParts(instance, options))
363
- const subscriptionId = insecureRandomId()
363
+ const subscriptionId = randomId(16)
364
364
  state.set('addSubscriber', addSubscriber(key, subscriptionId))
365
365
 
366
366
  return () => {
@@ -390,7 +390,7 @@ export const getCommentThreadsState: (
390
390
  selector: selectCommentThreads,
391
391
  onSubscribe: ({state, instance}, options: CommentsOptions) => {
392
392
  const key = getCommentsKey(toCommentsKeyParts(instance, options))
393
- const subscriptionId = insecureRandomId()
393
+ const subscriptionId = randomId(16)
394
394
  state.set('addSubscriber', addSubscriber(key, subscriptionId))
395
395
 
396
396
  return () => {
@@ -452,7 +452,7 @@ function resolveList<T>(
452
452
  // fetch and this promise would never settle. Holding it only for the duration
453
453
  // of the resolve also means a component that suspends and then errors before
454
454
  // mounting does not leave a subscriber-less entry behind holding its error.
455
- const subscriptionId = insecureRandomId()
455
+ const subscriptionId = randomId(16)
456
456
  state.set('addSubscriber', addSubscriber(key, subscriptionId))
457
457
 
458
458
  const release = () => state.set('removeSubscriber', removeSubscriber(key, subscriptionId))
@@ -76,4 +76,22 @@ export interface AuthConfig {
76
76
  * ignoring any storage or callback handling.
77
77
  */
78
78
  token?: string
79
+
80
+ oauth?: {
81
+ /**
82
+ * Opaque OAuth `client_id`.
83
+ */
84
+ clientId: string
85
+
86
+ /**
87
+ * The redirect URI registered for this client. The OAuth provider
88
+ * redirects back here with the authorization `code` and `state`.
89
+ */
90
+ redirectUri: string
91
+
92
+ /**
93
+ * Organisation id used for RFC 8707 resource scoping.
94
+ */
95
+ organizationId: string
96
+ }
79
97
  }
@@ -0,0 +1,18 @@
1
+ import {type EventTopicDef, type StateTopicDef} from '../topics'
2
+
3
+ declare module '../topics' {
4
+ interface Topics {
5
+ 'test.count': StateTopicDef<number>
6
+ 'test.token': StateTopicDef<string | null>
7
+ 'test.scheme': StateTopicDef<'light' | 'dark'>
8
+ 'test.suspending': StateTopicDef<string>
9
+ 'test.ping': EventTopicDef<{n: number}>
10
+ 'test.echo': EventTopicDef<{n: number}, {n: number}>
11
+ 'test.mint': EventTopicDef<void, string>
12
+ 'test.profile': StateTopicDef<{
13
+ fullName: string
14
+ tags: readonly string[]
15
+ }>
16
+ 'test.greet': EventTopicDef<{fullName: string}, {salutation: string; language: string}>
17
+ }
18
+ }