@sanity/sdk-react 3.0.0-rc.0 → 3.0.0-rc.2

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 (168) hide show
  1. package/README.md +103 -155
  2. package/dist/index.d.ts +2488 -2019
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +4435 -1529
  5. package/dist/index.js.map +1 -1
  6. package/package.json +55 -59
  7. package/src/_exports/index.ts +2 -4
  8. package/src/_exports/sdk-react.ts +30 -18
  9. package/src/components/SDKProvider.test.tsx +173 -32
  10. package/src/components/SDKProvider.tsx +87 -28
  11. package/src/components/SanityApp.test.tsx +68 -12
  12. package/src/components/SanityApp.tsx +93 -87
  13. package/src/components/auth/AuthBoundary.recovery.test.tsx +86 -0
  14. package/src/components/auth/AuthBoundary.test.tsx +7 -7
  15. package/src/components/auth/AuthBoundary.tsx +29 -6
  16. package/src/components/auth/DashboardAccessRequest.tsx +37 -0
  17. package/src/components/auth/LoginError.test.tsx +191 -5
  18. package/src/components/auth/LoginError.tsx +104 -61
  19. package/src/components/errors/ChunkLoadError.test.tsx +59 -0
  20. package/src/components/errors/ChunkLoadError.tsx +56 -0
  21. package/src/components/errors/CorsErrorComponent.tsx +2 -2
  22. package/src/components/errors/chunkReloadStorage.ts +57 -0
  23. package/src/config/handles.ts +29 -27
  24. package/src/constants.ts +5 -0
  25. package/src/context/ComlinkTokenRefresh.test.tsx +10 -27
  26. package/src/context/ComlinkTokenRefresh.tsx +3 -5
  27. package/src/context/DefaultResourceContext.ts +3 -3
  28. package/src/context/OrganizationResourcesProvider.test.tsx +189 -0
  29. package/src/context/OrganizationResourcesProvider.tsx +111 -0
  30. package/src/context/PerspectiveContext.ts +3 -3
  31. package/src/context/ProjectContext.ts +15 -0
  32. package/src/context/ResourceProvider.test.tsx +93 -90
  33. package/src/context/ResourceProvider.tsx +62 -93
  34. package/src/context/SDKStudioContext.test.tsx +28 -33
  35. package/src/context/SanityInstanceProvider.test.tsx +100 -0
  36. package/src/context/SanityInstanceProvider.tsx +71 -0
  37. package/src/context/WorkbenchTokenRefresh.test.tsx +106 -0
  38. package/src/context/WorkbenchTokenRefresh.tsx +61 -0
  39. package/src/context/renderSanityApp.test.tsx +151 -49
  40. package/src/context/renderSanityApp.tsx +12 -8
  41. package/src/context/workbenchToken.ts +63 -0
  42. package/src/hooks/access/useCheckPermissions.test-d.ts +15 -0
  43. package/src/hooks/access/useCheckPermissions.test.tsx +53 -0
  44. package/src/hooks/access/useCheckPermissions.ts +24 -0
  45. package/src/hooks/agent/agentActions.test.tsx +2 -2
  46. package/src/hooks/agent/agentActions.ts +52 -72
  47. package/src/hooks/agent/useAgentResourceContext.ts +1 -1
  48. package/src/hooks/applications/useApplication.test-d.ts +30 -0
  49. package/src/hooks/applications/useApplication.ts +22 -0
  50. package/src/hooks/applications/useApplications.test-d.ts +31 -0
  51. package/src/hooks/applications/useApplications.ts +25 -0
  52. package/src/hooks/applications/useDeleteApplication.test-d.ts +12 -0
  53. package/src/hooks/applications/useDeleteApplication.ts +11 -0
  54. package/src/hooks/applications/useUpdateApplication.test-d.ts +12 -0
  55. package/src/hooks/applications/useUpdateApplication.ts +11 -0
  56. package/src/hooks/auth/useDashboardOrganizationId.test.tsx +2 -8
  57. package/src/hooks/auth/useVerifyOrgProjects.test.tsx +8 -32
  58. package/src/hooks/auth/useVerifyOrgProjects.tsx +13 -6
  59. package/src/hooks/client/useClient.test.tsx +1 -4
  60. package/src/hooks/client/useClient.ts +1 -0
  61. package/src/hooks/comlink/useFrameConnection.test.tsx +3 -2
  62. package/src/hooks/comlink/useFrameConnection.ts +1 -1
  63. package/src/hooks/comlink/useWindowConnection.test.tsx +4 -3
  64. package/src/hooks/comlink/useWindowConnection.ts +2 -3
  65. package/src/hooks/comments/useCommentActions.test.tsx +150 -0
  66. package/src/hooks/comments/useCommentActions.ts +109 -0
  67. package/src/hooks/comments/useCommentList.ts +79 -0
  68. package/src/hooks/comments/useCommentThreads.test.tsx +107 -0
  69. package/src/hooks/comments/useCommentThreads.ts +73 -0
  70. package/src/hooks/comments/useComments.test.tsx +242 -0
  71. package/src/hooks/comments/useComments.ts +59 -0
  72. package/src/hooks/context/useResource.test.tsx +32 -0
  73. package/src/hooks/context/useResource.ts +24 -0
  74. package/src/hooks/context/useSanityInstance.test.tsx +18 -1
  75. package/src/hooks/context/useSanityInstance.ts +6 -6
  76. package/src/hooks/dashboard/useManageFavorite.test.tsx +110 -46
  77. package/src/hooks/dashboard/useManageFavorite.ts +11 -17
  78. package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +5 -7
  79. package/src/hooks/dashboard/useNavigateToStudioDocument.ts +2 -6
  80. package/src/hooks/dashboard/useRecordDocumentHistoryEvent.test.ts +0 -2
  81. package/src/hooks/dashboard/useRecordDocumentHistoryEvent.ts +2 -2
  82. package/src/hooks/dashboard/useWindowTitle.test.ts +244 -0
  83. package/src/hooks/dashboard/useWindowTitle.ts +114 -0
  84. package/src/hooks/datasets/useDatasets.test.tsx +116 -0
  85. package/src/hooks/datasets/useDatasets.ts +33 -13
  86. package/src/hooks/document/useApplyDocumentActions.test.tsx +48 -0
  87. package/src/hooks/document/useApplyDocumentActions.ts +66 -48
  88. package/src/hooks/document/useCreateDocument.test.tsx +83 -0
  89. package/src/hooks/document/useCreateDocument.ts +117 -0
  90. package/src/hooks/document/useDocument.test.tsx +37 -8
  91. package/src/hooks/document/useDocument.ts +131 -66
  92. package/src/hooks/document/useDocumentEvent.test.tsx +16 -4
  93. package/src/hooks/document/useDocumentEvent.ts +16 -10
  94. package/src/hooks/document/useDocumentPermissions.test.tsx +8 -7
  95. package/src/hooks/document/useDocumentPermissions.ts +71 -39
  96. package/src/hooks/document/useDocumentSyncStatus.test.ts +1 -0
  97. package/src/hooks/document/useDocumentSyncStatus.ts +13 -9
  98. package/src/hooks/document/useEditDocument.test.tsx +21 -14
  99. package/src/hooks/document/useEditDocument.ts +163 -17
  100. package/src/hooks/documents/useDocuments.test.tsx +64 -44
  101. package/src/hooks/documents/useDocuments.ts +31 -20
  102. package/src/hooks/helpers/createCallbackHook.test.tsx +13 -19
  103. package/src/hooks/helpers/createFetcherHook.test.tsx +180 -0
  104. package/src/hooks/helpers/createFetcherHook.ts +69 -0
  105. package/src/hooks/helpers/createMutationHook.test.tsx +125 -0
  106. package/src/hooks/helpers/createMutationHook.tsx +93 -0
  107. package/src/hooks/helpers/createStateSourceHook.test.tsx +10 -10
  108. package/src/hooks/helpers/createStateSourceHook.tsx +2 -1
  109. package/src/hooks/{document/useApplyDocumentActions.test.ts → helpers/useApplyActions.test.tsx} +92 -53
  110. package/src/hooks/helpers/useApplyActions.ts +68 -0
  111. package/src/hooks/helpers/useNormalizedResourceOptions.test.tsx +226 -0
  112. package/src/hooks/helpers/useNormalizedResourceOptions.ts +79 -48
  113. package/src/hooks/helpers/useResolvedProjectId.test.tsx +59 -0
  114. package/src/hooks/helpers/useResolvedProjectId.ts +35 -0
  115. package/src/hooks/helpers/useTrackHookUsage.ts +37 -0
  116. package/src/hooks/installations/useInstallation.test-d.ts +19 -0
  117. package/src/hooks/installations/useInstallation.ts +22 -0
  118. package/src/hooks/installations/useInstallations.test-d.ts +26 -0
  119. package/src/hooks/installations/useInstallations.ts +25 -0
  120. package/src/hooks/organizations/useOrganization.test-d.ts +60 -0
  121. package/src/hooks/organizations/useOrganization.test.ts +63 -0
  122. package/src/hooks/organizations/useOrganization.ts +34 -0
  123. package/src/hooks/organizations/useOrganizations.test-d.ts +68 -0
  124. package/src/hooks/organizations/useOrganizations.test.ts +63 -0
  125. package/src/hooks/organizations/useOrganizations.ts +39 -0
  126. package/src/hooks/paginatedDocuments/usePaginatedDocuments.test.tsx +34 -27
  127. package/src/hooks/paginatedDocuments/usePaginatedDocuments.ts +35 -22
  128. package/src/hooks/presence/usePresence.test.tsx +0 -15
  129. package/src/hooks/presence/usePresence.ts +22 -20
  130. package/src/hooks/presence/usePresenceForDocument.test.tsx +141 -0
  131. package/src/hooks/presence/usePresenceForDocument.ts +104 -0
  132. package/src/hooks/presence/useReportPresence.test.tsx +202 -0
  133. package/src/hooks/presence/useReportPresence.ts +170 -0
  134. package/src/hooks/preview/useDocumentPreview.test.tsx +0 -1
  135. package/src/hooks/preview/useDocumentPreview.tsx +12 -14
  136. package/src/hooks/projection/useDocumentProjection.test.tsx +78 -9
  137. package/src/hooks/projection/useDocumentProjection.ts +81 -2
  138. package/src/hooks/projects/useProject.test-d.ts +56 -0
  139. package/src/hooks/projects/useProject.test.tsx +120 -0
  140. package/src/hooks/projects/useProject.ts +42 -43
  141. package/src/hooks/projects/useProjects.test-d.ts +60 -0
  142. package/src/hooks/projects/useProjects.test.ts +45 -98
  143. package/src/hooks/projects/useProjects.ts +17 -32
  144. package/src/hooks/query/useQuery.test.tsx +6 -5
  145. package/src/hooks/query/useQuery.ts +94 -17
  146. package/src/hooks/releases/useActiveReleases.test.tsx +13 -7
  147. package/src/hooks/releases/useActiveReleases.ts +28 -24
  148. package/src/hooks/releases/useAllReleases.test.tsx +93 -0
  149. package/src/hooks/releases/useAllReleases.ts +62 -0
  150. package/src/hooks/releases/useApplyReleaseActions.test.tsx +66 -0
  151. package/src/hooks/releases/useApplyReleaseActions.ts +82 -0
  152. package/src/hooks/releases/usePerspective.test.tsx +18 -10
  153. package/src/hooks/releases/usePerspective.ts +13 -17
  154. package/src/hooks/users/useUser.test.tsx +3 -9
  155. package/src/hooks/users/useUser.ts +4 -8
  156. package/src/hooks/users/useUsers.test.tsx +103 -7
  157. package/src/hooks/users/useUsers.ts +38 -5
  158. package/src/utils/resolveOrgResources.test.ts +111 -0
  159. package/src/utils/resolveOrgResources.ts +69 -0
  160. package/src/hooks/context/useDefaultResource.test.tsx +0 -25
  161. package/src/hooks/context/useDefaultResource.ts +0 -30
  162. package/src/hooks/dashboard/useDispatchIntent.test.ts +0 -254
  163. package/src/hooks/dashboard/useDispatchIntent.ts +0 -157
  164. package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.test.ts +0 -107
  165. package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.ts +0 -39
  166. package/src/hooks/datasets/useDatasets.test.ts +0 -66
  167. package/src/hooks/helpers/useNormalizedResourceOptions.test.ts +0 -65
  168. package/src/hooks/projects/useProject.test.ts +0 -79
package/package.json CHANGED
@@ -1,95 +1,91 @@
1
1
  {
2
2
  "name": "@sanity/sdk-react",
3
- "version": "3.0.0-rc.0",
3
+ "version": "3.0.0-rc.2",
4
4
  "private": false,
5
5
  "description": "Sanity SDK React toolkit for Content OS",
6
6
  "keywords": [
7
- "sanity",
8
- "sdk",
9
- "content operating system",
10
7
  "cms",
8
+ "content",
9
+ "content operating system",
11
10
  "headless",
12
11
  "realtime",
13
- "content"
12
+ "sanity",
13
+ "sdk"
14
14
  ],
15
15
  "homepage": "https://github.com/sanity-io/sdk/tree/main/packages/react/README.md",
16
16
  "bugs": {
17
17
  "url": "https://github.com/sanity-io/sdk/issues"
18
18
  },
19
+ "license": "MIT",
20
+ "author": "Sanity <developers@sanity.io>",
19
21
  "repository": {
20
22
  "type": "git",
21
23
  "url": "git+https://github.com/sanity-io/sdk.git",
22
24
  "directory": "packages/react"
23
25
  },
24
- "license": "MIT",
25
- "author": "Sanity <developers@sanity.io>",
26
- "sideEffects": false,
27
- "type": "module",
28
- "exports": {
29
- ".": {
30
- "source": "./src/_exports/index.ts",
31
- "import": "./dist/index.js",
32
- "default": "./dist/index.js"
33
- },
34
- "./package.json": "./package.json"
35
- },
36
- "main": "./dist/index.js",
37
- "module": "./dist/index.js",
38
- "types": "./dist/index.d.ts",
39
26
  "files": [
40
27
  "dist",
41
28
  "src"
42
29
  ],
43
- "browserslist": "extends @sanity/browserslist-config",
44
- "prettier": "@sanity/prettier-config",
30
+ "type": "module",
31
+ "sideEffects": false,
32
+ "main": "./dist/index.js",
33
+ "module": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "exports": {
36
+ ".": "./dist/index.js",
37
+ "./package.json": "./package.json"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
45
42
  "dependencies": {
46
- "@sanity/client": "^7.14.1",
47
- "@sanity/message-protocol": "^0.18.0",
48
- "@sanity/types": "^5.2.0",
49
- "@types/lodash-es": "^4.17.12",
50
- "lodash-es": "^4.17.21",
51
- "react-error-boundary": "^5.0.0",
43
+ "@sanity/client": "^7.26.2",
44
+ "@sanity/message-protocol": "^0.24.0",
45
+ "@sanity/types": "^6.9.1",
46
+ "@sanity/workbench": "0.1.0-alpha.24",
47
+ "groq": "3.88.1-typegen-experimental.0",
48
+ "react-compiler-runtime": "^1.0.0",
49
+ "react-error-boundary": "^6.1.2",
52
50
  "rxjs": "^7.8.2",
53
- "@sanity/sdk": "3.0.0-rc.0"
51
+ "xstate": "^5.31.0",
52
+ "@sanity/sdk": "3.0.0-rc.2"
54
53
  },
55
54
  "devDependencies": {
56
55
  "@sanity/browserslist-config": "^1.0.5",
57
- "@sanity/comlink": "^3.1.1",
58
- "@sanity/pkg-utils": "^8.1.29",
59
- "@sanity/prettier-config": "^1.0.6",
56
+ "@sanity/comlink": "^4.0.3",
57
+ "@sanity/pkg-utils": "^12.1.0",
60
58
  "@testing-library/jest-dom": "^6.9.1",
61
- "@testing-library/react": "^16.3.0",
62
- "@types/node": "^22.19.1",
63
- "@types/react": "^19.2.7",
59
+ "@testing-library/react": "^16.3.2",
60
+ "@types/node": "^24.13.3",
61
+ "@types/react": "^19.2.17",
64
62
  "@types/react-dom": "^19.2.3",
65
- "@vitejs/plugin-react": "^4.7.0",
66
- "@vitest/coverage-v8": "3.2.4",
67
- "babel-plugin-react-compiler": "19.1.0-rc.1",
68
- "eslint": "^9.22.0",
69
- "groq-js": "^1.22.0",
70
- "jsdom": "^25.0.1",
71
- "prettier": "^3.7.3",
72
- "react": "^19.2.1",
73
- "react-dom": "^19.2.1",
74
- "rollup-plugin-visualizer": "^5.14.0",
75
- "typescript": "^5.8.3",
76
- "vite": "^6.3.4",
77
- "vitest": "^3.2.4",
63
+ "@vitejs/plugin-react": "^6.0.3",
64
+ "@vitest/coverage-v8": "^4.1.10",
65
+ "babel-plugin-react-compiler": "^1.0.0",
66
+ "eslint": "^10.6.0",
67
+ "groq-js": "^2.0.0",
68
+ "jsdom": "^29.1.1",
69
+ "oxfmt": "^0.58.0",
70
+ "react": "^19.2.7",
71
+ "react-dom": "^19.2.7",
72
+ "rolldown": "^1.2.3",
73
+ "typescript": "^6.0.3",
74
+ "vite": "^8.1.5",
75
+ "vitest": "^4.1.10",
76
+ "@repo/package.bundle": "3.82.0",
78
77
  "@repo/config-eslint": "0.0.0",
79
78
  "@repo/config-test": "0.0.1",
80
- "@repo/package.bundle": "3.82.0",
81
- "@repo/tsconfig": "0.0.1",
82
- "@repo/package.config": "0.0.1"
79
+ "@repo/package.config": "0.0.1",
80
+ "@repo/tsconfig": "0.0.1"
83
81
  },
84
82
  "peerDependencies": {
85
- "react": "^19.2.0",
86
- "react-dom": "^19.2.0"
83
+ "react": "^18.0.0 || ^19.0.0",
84
+ "react-dom": "^18.0.0 || ^19.0.0"
87
85
  },
88
- "engines": {
89
- "node": ">=20.19"
90
- },
91
- "publishConfig": {
92
- "access": "public"
86
+ "browserslist": "extends @sanity/browserslist-config",
87
+ "inlinedDependencies": {
88
+ "@sanity/comlink": "4.0.3"
93
89
  },
94
90
  "scripts": {
95
91
  "build": "pkg build --strict --clean --check",
@@ -97,7 +93,7 @@
97
93
  "clean": "rimraf dist",
98
94
  "dev": "pkg watch",
99
95
  "docs": "typedoc --json docs/typedoc.json --tsconfig ./tsconfig.dist.json",
100
- "format": "prettier --write --cache --ignore-unknown .",
96
+ "format": "oxfmt",
101
97
  "lint": "eslint .",
102
98
  "test": "vitest run",
103
99
  "test:coverage": "vitest run --coverage",
@@ -1,6 +1,4 @@
1
1
  export * from './sdk-react.ts'
2
2
  export * from '@sanity/sdk'
3
-
4
- // Explicitly re-export React-layer handle types to resolve ambiguity with @sanity/sdk versions.
5
- // These shadow the core (strict) versions for @sanity/sdk-react consumers.
6
- export {type DocumentHandle, type DocumentTypeHandle, type ResourceHandle} from './sdk-react.ts'
3
+ // React-layer handles shadow core equivalents when imported from @sanity/sdk-react
4
+ export type {DocumentHandle, DocumentTypeHandle, ResourceHandle} from './sdk-react.ts'
@@ -1,21 +1,19 @@
1
1
  /**
2
2
  * @module exports
3
3
  */
4
-
5
- // React-layer handle types (relaxed) — shadow core versions for @sanity/sdk-react consumers.
6
- // resource is optional and resolved from context when not provided.
7
4
  export {AuthBoundary, type AuthBoundaryProps} from '../components/auth/AuthBoundary'
8
5
  export {SanityApp, type SanityAppProps} from '../components/SanityApp'
9
6
  export {SDKProvider, type SDKProviderProps} from '../components/SDKProvider'
10
- export {
11
- type DocumentHandle as DocumentHandle,
12
- type DocumentTypeHandle as DocumentTypeHandle,
13
- type ResourceHandle as ResourceHandle,
14
- } from '../config/handles'
7
+ export {type DocumentHandle, type DocumentTypeHandle, type ResourceHandle} from '../config/handles'
15
8
  export {ComlinkTokenRefreshProvider} from '../context/ComlinkTokenRefresh'
16
9
  export {renderSanityApp} from '../context/renderSanityApp'
17
10
  export {ResourceProvider, type ResourceProviderProps} from '../context/ResourceProvider'
11
+ export {
12
+ SanityInstanceProvider,
13
+ type SanityInstanceProviderProps,
14
+ } from '../context/SanityInstanceProvider'
18
15
  export {SDKStudioContext, type StudioWorkspaceHandle} from '../context/SDKStudioContext'
16
+ export {useCheckPermissions} from '../hooks/access/useCheckPermissions'
19
17
  export {
20
18
  useAgentGenerate,
21
19
  useAgentPatch,
@@ -27,6 +25,10 @@ export {
27
25
  type AgentResourceContextOptions,
28
26
  useAgentResourceContext,
29
27
  } from '../hooks/agent/useAgentResourceContext'
28
+ export {useApplication} from '../hooks/applications/useApplication'
29
+ export {useApplications} from '../hooks/applications/useApplications'
30
+ export {useDeleteApplication} from '../hooks/applications/useDeleteApplication'
31
+ export {useUpdateApplication} from '../hooks/applications/useUpdateApplication'
30
32
  export {useAuthState} from '../hooks/auth/useAuthState'
31
33
  export {useAuthToken} from '../hooks/auth/useAuthToken'
32
34
  export {useCurrentUser} from '../hooks/auth/useCurrentUser'
@@ -48,10 +50,12 @@ export {
48
50
  type WindowConnection,
49
51
  type WindowMessageHandler,
50
52
  } from '../hooks/comlink/useWindowConnection'
51
- export {useResource} from '../hooks/context/useDefaultResource'
53
+ export {type CommentActions, useCommentActions} from '../hooks/comments/useCommentActions'
54
+ export {useComments, type UseCommentsResult} from '../hooks/comments/useComments'
55
+ export {useCommentThreads, type UseCommentThreadsResult} from '../hooks/comments/useCommentThreads'
56
+ export {useResource} from '../hooks/context/useResource'
52
57
  export {useSanityInstance} from '../hooks/context/useSanityInstance'
53
58
  export {useDashboardNavigate} from '../hooks/dashboard/useDashboardNavigate'
54
- export {useDispatchIntent} from '../hooks/dashboard/useDispatchIntent'
55
59
  export {useManageFavorite} from '../hooks/dashboard/useManageFavorite'
56
60
  export {
57
61
  type NavigateToStudioResult,
@@ -59,8 +63,10 @@ export {
59
63
  } from '../hooks/dashboard/useNavigateToStudioDocument'
60
64
  export {useRecordDocumentHistoryEvent} from '../hooks/dashboard/useRecordDocumentHistoryEvent'
61
65
  export {useStudioWorkspacesByProjectIdDataset} from '../hooks/dashboard/useStudioWorkspacesByProjectIdDataset'
66
+ export {useWindowTitle} from '../hooks/dashboard/useWindowTitle'
62
67
  export {useDatasets} from '../hooks/datasets/useDatasets'
63
68
  export {useApplyDocumentActions} from '../hooks/document/useApplyDocumentActions'
69
+ export {type CreateDocumentOverrides, useCreateDocument} from '../hooks/document/useCreateDocument'
64
70
  export {useDocument} from '../hooks/document/useDocument'
65
71
  export {useDocumentEvent} from '../hooks/document/useDocumentEvent'
66
72
  export {useDocumentPermissions} from '../hooks/document/useDocumentPermissions'
@@ -71,12 +77,23 @@ export {
71
77
  type DocumentsResponse,
72
78
  useDocuments,
73
79
  } from '../hooks/documents/useDocuments'
80
+ export {type FetcherHookResult} from '../hooks/helpers/createFetcherHook'
81
+ export {type MutationHookResult} from '../hooks/helpers/createMutationHook'
82
+ export {useInstallation} from '../hooks/installations/useInstallation'
83
+ export {useInstallations} from '../hooks/installations/useInstallations'
84
+ export {useOrganization} from '../hooks/organizations/useOrganization'
85
+ export {useOrganizations} from '../hooks/organizations/useOrganizations'
74
86
  export {
75
87
  type PaginatedDocumentsOptions,
76
88
  type PaginatedDocumentsResponse,
77
89
  usePaginatedDocuments,
78
90
  } from '../hooks/paginatedDocuments/usePaginatedDocuments'
79
91
  export {usePresence} from '../hooks/presence/usePresence'
92
+ export {
93
+ usePresenceForDocument,
94
+ type UsePresenceForDocumentOptions,
95
+ } from '../hooks/presence/usePresenceForDocument'
96
+ export {useReportPresence, type UseReportPresenceOptions} from '../hooks/presence/useReportPresence'
80
97
  export {
81
98
  useDocumentPreview,
82
99
  type useDocumentPreviewOptions,
@@ -88,20 +105,15 @@ export {
88
105
  type useDocumentProjectionResults,
89
106
  } from '../hooks/projection/useDocumentProjection'
90
107
  export {useProject} from '../hooks/projects/useProject'
91
- export {type ProjectWithoutMembers, useProjects} from '../hooks/projects/useProjects'
108
+ export {useProjects} from '../hooks/projects/useProjects'
92
109
  export {useQuery} from '../hooks/query/useQuery'
93
110
  export {useActiveReleases} from '../hooks/releases/useActiveReleases'
111
+ export {useAllReleases} from '../hooks/releases/useAllReleases'
112
+ export {useApplyReleaseActions} from '../hooks/releases/useApplyReleaseActions'
94
113
  export {usePerspective} from '../hooks/releases/usePerspective'
95
114
  export {type UserResult, useUser} from '../hooks/users/useUser'
96
115
  export {type UsersResult, useUsers} from '../hooks/users/useUsers'
97
116
  export {REACT_SDK_VERSION} from '../version'
98
117
  export {type DatasetsResponse, type SanityProjectMember} from '@sanity/client'
99
118
  export type {Status as ComlinkStatus} from '@sanity/comlink'
100
-
101
- // Strict core versions — available for users who need explicit resource
102
- export {
103
- type DocumentHandle as StrictDocumentHandle,
104
- type DocumentTypeHandle as StrictDocumentTypeHandle,
105
- type ResourceHandle as StrictResourceHandle,
106
- } from '@sanity/sdk'
107
119
  export {type SanityDocument, type SortOrderingItem} from '@sanity/types'
@@ -1,68 +1,209 @@
1
+ import {createSanityInstance, type SanityConfig} from '@sanity/sdk'
1
2
  import {render} from '@testing-library/react'
2
3
  import React from 'react'
3
- import {describe, expect, it, vi} from 'vitest'
4
+ import {beforeEach, describe, expect, it, vi} from 'vitest'
4
5
 
6
+ import {useDashboardOrganizationId} from '../hooks/auth/useDashboardOrganizationId'
7
+ import {resolveOrgResources} from '../utils/resolveOrgResources'
5
8
  import {SDKProvider} from './SDKProvider'
6
9
 
10
+ const instance = createSanityInstance()
11
+
12
+ // Returns the same object on every call so the module-level WeakMap cache works across renders.
13
+ vi.mock('../hooks/context/useSanityInstance', () => {
14
+ return {useSanityInstance: () => instance}
15
+ })
16
+
17
+ vi.mock('../hooks/auth/useDashboardOrganizationId', () => ({
18
+ useDashboardOrganizationId: vi.fn(),
19
+ }))
20
+
21
+ vi.mock('../utils/resolveOrgResources', () => ({
22
+ resolveOrgResources: vi.fn(),
23
+ }))
24
+
7
25
  vi.mock('../context/ResourceProvider', () => ({
8
- ResourceProvider: ({children, resource}: {children: React.ReactNode; resource?: unknown}) => {
9
- return (
10
- <div data-testid="resource-provider" data-resource={JSON.stringify(resource ?? null)}>
11
- {children}
12
- </div>
13
- )
14
- },
26
+ ResourceProvider: ({
27
+ children,
28
+ // exclude fallback: it's a real ResourceProvider prop but not part of config
29
+ fallback: _fallback,
30
+ resource,
31
+ ...config
32
+ }: {
33
+ children: React.ReactNode
34
+ fallback?: React.ReactNode
35
+ resource?: {projectId?: string; dataset?: string}
36
+ }) => (
37
+ <div
38
+ data-testid="resource-provider"
39
+ data-config={JSON.stringify(config)}
40
+ data-resource={JSON.stringify(resource ?? null)}
41
+ >
42
+ {children}
43
+ </div>
44
+ ),
15
45
  }))
16
46
 
17
47
  vi.mock('./auth/AuthBoundary', () => ({
18
- AuthBoundary: ({children}: {children: React.ReactNode}) => {
19
- return <div data-testid="auth-boundary">{children}</div>
20
- },
48
+ AuthBoundary: ({children, projectIds}: {children: React.ReactNode; projectIds?: string[]}) => (
49
+ <div data-testid="auth-boundary" data-project-ids={JSON.stringify(projectIds ?? [])}>
50
+ {children}
51
+ </div>
52
+ ),
21
53
  }))
22
54
 
55
+ const mockResolveOrgResources = vi.mocked(resolveOrgResources)
56
+ const mockUseDashboardOrganizationId = vi.mocked(useDashboardOrganizationId)
57
+
23
58
  describe('SDKProvider', () => {
24
- it('renders a single ResourceProvider with AuthBoundary', () => {
25
- const config = {}
26
- const resources = {
27
- default: {projectId: 'test-project', dataset: 'production'},
59
+ beforeEach(() => {
60
+ vi.clearAllMocks()
61
+ mockResolveOrgResources.mockResolvedValue({})
62
+ mockUseDashboardOrganizationId.mockReturnValue(undefined)
63
+ })
64
+
65
+ it('renders single ResourceProvider with AuthBoundary for a single config', () => {
66
+ const config = {
67
+ projectId: 'test-project',
68
+ dataset: 'production',
28
69
  }
29
70
 
30
- const {getByTestId} = render(
31
- <SDKProvider config={config} resources={resources} fallback={<div>Loading...</div>}>
71
+ const {getAllByTestId, getByTestId} = render(
72
+ <SDKProvider config={[config]} fallback={<div>Loading...</div>}>
32
73
  <div>Child Content</div>
33
74
  </SDKProvider>,
34
75
  )
35
76
 
36
- const provider = getByTestId('resource-provider')
37
- expect(provider).toBeInTheDocument()
77
+ // Should create a single ResourceProvider
78
+ const providers = getAllByTestId('resource-provider')
79
+ expect(providers.length).toBe(1)
80
+
81
+ // Should create an AuthBoundary inside
38
82
  expect(getByTestId('auth-boundary')).toBeInTheDocument()
39
83
 
40
- // resource prop should be the default resource
41
- expect(JSON.parse(provider.getAttribute('data-resource') || 'null')).toEqual({
84
+ // Verify provider has the correct config
85
+ expect(JSON.parse(providers[0].getAttribute('data-config') || '{}')).toEqual({
42
86
  projectId: 'test-project',
43
87
  dataset: 'production',
44
88
  })
45
89
  })
46
90
 
47
- it('renders with multiple named resources', () => {
48
- const config = {}
49
- const resources = {
50
- default: {projectId: 'project-1', dataset: 'production'},
51
- secondary: {projectId: 'project-2', dataset: 'staging'},
52
- }
91
+ it('renders a single ResourceProvider using the first config when multiple configs are provided', () => {
92
+ const configs = [
93
+ {
94
+ projectId: 'project-1',
95
+ dataset: 'production',
96
+ },
97
+ {
98
+ projectId: 'project-2',
99
+ dataset: 'staging',
100
+ },
101
+ ]
53
102
 
54
- const {getByTestId} = render(
55
- <SDKProvider config={config} resources={resources} fallback={<div>Loading...</div>}>
103
+ const {getAllByTestId, getByTestId} = render(
104
+ <SDKProvider config={configs} fallback={<div>Loading...</div>}>
56
105
  <div>Child Content</div>
57
106
  </SDKProvider>,
58
107
  )
59
108
 
60
- const provider = getByTestId('resource-provider')
61
- expect(provider).toBeInTheDocument()
109
+ // Should create a single ResourceProvider using the first config
110
+ const providers = getAllByTestId('resource-provider')
111
+ expect(providers.length).toBe(1)
62
112
 
63
- expect(JSON.parse(provider.getAttribute('data-resource') || 'null')).toEqual({
113
+ // Should create an AuthBoundary inside
114
+ expect(getByTestId('auth-boundary')).toBeInTheDocument()
115
+
116
+ // Verify the provider uses the first config
117
+ expect(JSON.parse(providers[0].getAttribute('data-config') || '{}')).toEqual({
64
118
  projectId: 'project-1',
65
119
  dataset: 'production',
66
120
  })
67
121
  })
122
+
123
+ it('preserves non-resource config fields (e.g. studio auth, perspective) on the root provider', () => {
124
+ const tokenSource = {subscribe: () => ({unsubscribe: () => {}})}
125
+ const config: SanityConfig = {
126
+ projectId: 'test-project',
127
+ dataset: 'production',
128
+ perspective: 'published',
129
+ studio: {authenticated: true, auth: {token: tokenSource}},
130
+ }
131
+
132
+ const {getAllByTestId} = render(
133
+ <SDKProvider config={config} fallback={<div>Loading...</div>}>
134
+ <div>Child Content</div>
135
+ </SDKProvider>,
136
+ )
137
+
138
+ const providers = getAllByTestId('resource-provider')
139
+ // The full first config is spread onto the root ResourceProvider so the
140
+ // root SanityInstance retains auth/perspective/etc. (The token source is a
141
+ // function and doesn't survive JSON serialization, so assert on the
142
+ // serializable fields and that the studio config object is carried through.)
143
+ const spreadConfig = JSON.parse(providers[0].getAttribute('data-config') || '{}')
144
+ expect(spreadConfig).toMatchObject({
145
+ projectId: 'test-project',
146
+ dataset: 'production',
147
+ perspective: 'published',
148
+ studio: {authenticated: true},
149
+ })
150
+ })
151
+
152
+ it('synthesizes the default resource from the first config when none is explicit', () => {
153
+ const config = {projectId: 'test-project', dataset: 'production'}
154
+
155
+ const {getAllByTestId} = render(
156
+ <SDKProvider config={config} fallback={<div>Loading...</div>}>
157
+ <div>Child Content</div>
158
+ </SDKProvider>,
159
+ )
160
+
161
+ const providers = getAllByTestId('resource-provider')
162
+ expect(JSON.parse(providers[0].getAttribute('data-resource') || 'null')).toEqual({
163
+ projectId: 'test-project',
164
+ dataset: 'production',
165
+ })
166
+ })
167
+
168
+ it('uses an explicitly provided "default" resource over the config-derived one', () => {
169
+ const config = {projectId: 'config-project', dataset: 'production'}
170
+ const resources = {default: {projectId: 'explicit-project', dataset: 'staging'}}
171
+
172
+ const {getAllByTestId} = render(
173
+ <SDKProvider config={config} resources={resources} fallback={<div>Loading...</div>}>
174
+ <div>Child Content</div>
175
+ </SDKProvider>,
176
+ )
177
+
178
+ const providers = getAllByTestId('resource-provider')
179
+ // Config fields still flow through for instance creation…
180
+ expect(JSON.parse(providers[0].getAttribute('data-config') || '{}')).toEqual(config)
181
+ // …but the explicit default resource wins as the resource context value.
182
+ expect(JSON.parse(providers[0].getAttribute('data-resource') || 'null')).toEqual(
183
+ resources.default,
184
+ )
185
+ })
186
+
187
+ it('collects deduped projectIds from both config and explicit resources', () => {
188
+ const configs = [
189
+ {projectId: 'project-1', dataset: 'production'},
190
+ {projectId: 'project-2', dataset: 'production'},
191
+ ]
192
+ const resources = {
193
+ // duplicates project-1 (should be deduped)
194
+ default: {projectId: 'project-1', dataset: 'production'},
195
+ other: {projectId: 'project-3', dataset: 'production'},
196
+ }
197
+
198
+ const {getByTestId} = render(
199
+ <SDKProvider config={configs} resources={resources} fallback={<div>Loading...</div>}>
200
+ <div>Child Content</div>
201
+ </SDKProvider>,
202
+ )
203
+
204
+ const projectIds = JSON.parse(
205
+ getByTestId('auth-boundary').getAttribute('data-project-ids') || '[]',
206
+ )
207
+ expect([...projectIds].sort()).toEqual(['project-1', 'project-2', 'project-3'])
208
+ })
68
209
  })