@sanity/sdk-react 2.5.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +164 -19
  2. package/dist/index.d.ts +571 -26
  3. package/dist/index.js +149 -78
  4. package/dist/index.js.map +1 -1
  5. package/package.json +7 -7
  6. package/src/_exports/sdk-react.ts +2 -0
  7. package/src/components/SDKProvider.tsx +8 -3
  8. package/src/components/SanityApp.test.tsx +72 -2
  9. package/src/components/SanityApp.tsx +53 -10
  10. package/src/components/auth/AuthBoundary.tsx +5 -5
  11. package/src/context/ComlinkTokenRefresh.test.tsx +2 -2
  12. package/src/context/ComlinkTokenRefresh.tsx +3 -2
  13. package/src/context/SDKStudioContext.test.tsx +126 -0
  14. package/src/context/SDKStudioContext.ts +65 -0
  15. package/src/context/SourcesContext.tsx +7 -0
  16. package/src/context/renderSanityApp.test.tsx +355 -0
  17. package/src/context/renderSanityApp.tsx +48 -0
  18. package/src/hooks/agent/agentActions.ts +436 -21
  19. package/src/hooks/dashboard/useDispatchIntent.test.ts +26 -20
  20. package/src/hooks/dashboard/useDispatchIntent.ts +10 -11
  21. package/src/hooks/dashboard/utils/{getResourceIdFromDocumentHandle.test.ts → useResourceIdFromDocumentHandle.test.ts} +33 -60
  22. package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.ts +46 -0
  23. package/src/hooks/document/useEditDocument.ts +3 -0
  24. package/src/hooks/documents/useDocuments.ts +3 -2
  25. package/src/hooks/helpers/useNormalizedSourceOptions.ts +85 -0
  26. package/src/hooks/paginatedDocuments/usePaginatedDocuments.ts +1 -0
  27. package/src/hooks/projection/useDocumentProjection.ts +15 -4
  28. package/src/hooks/query/useQuery.ts +30 -11
  29. package/src/hooks/dashboard/types.ts +0 -12
  30. package/src/hooks/dashboard/utils/getResourceIdFromDocumentHandle.ts +0 -53
package/README.md CHANGED
@@ -322,6 +322,29 @@ The SDK handles updating the document state automatically:
322
322
  - `publishDocument()` copies draft → published, deletes draft
323
323
  - `discardDraft()` deletes draft, reverts to published
324
324
 
325
+ #### LiveEdit Documents
326
+
327
+ For documents that don't need the draft/published workflow (such as settings, configuration, or real-time collaborative documents), you can use **liveEdit mode** by setting `liveEdit: true` in the document handle:
328
+
329
+ ```tsx
330
+ const settingsHandle: DocumentHandle = {
331
+ documentId: 'site-settings',
332
+ documentType: 'settings',
333
+ liveEdit: true, // Edits apply directly without creating a draft
334
+ }
335
+
336
+ // Edits are applied immediately to the published document
337
+ const editSettings = useEditDocument(settingsHandle)
338
+ ```
339
+
340
+ **When using liveEdit documents:**
341
+
342
+ - Drafts will not be created when the document is edited
343
+ - Edits will be applied directly to the published document
344
+ - `publishDocument()`, `unpublishDocument()`, and `discardDraft()` actions cannot be used (since liveEdit documents are always published and do not have drafts)
345
+
346
+ For more details, see the [Sanity documentation on liveEdit documents](https://www.sanity.io/docs/content-lake/drafts).
347
+
325
348
  ---
326
349
 
327
350
  ### Real-Time Behavior
@@ -340,43 +363,165 @@ Any mutation to a subscribed document (even fields you don't display) will trigg
340
363
 
341
364
  ### Multi-Project Access
342
365
 
343
- #### Specify Source in Handle
366
+ The SDK supports accessing documents from multiple projects and datasets simultaneously. There are two main approaches:
367
+
368
+ #### Approach 1: Specify Project/Dataset Directly in the Handle
369
+
370
+ Pass `projectId` and `dataset` directly in document handles to fetch data from specific projects (note that any `projectId` and `dataset` pair you pass must be defined in your application’s array of [SanityConfig objects](https://www.sanity.io/docs/app-sdk/sdk-configuration#d95b8773097c)):
344
371
 
345
372
  ```tsx
346
- const handle: DocumentHandle = {
347
- documentId: 'xyz',
348
- documentType: 'product',
349
- projectId: 'project-a',
350
- dataset: 'production',
373
+ import {useDocument} from '@sanity/sdk-react'
374
+
375
+ function MultiProjectComponent() {
376
+ // Fetch from Project A
377
+ const {data: productA} = useDocument({
378
+ documentId: 'product-123',
379
+ documentType: 'product',
380
+ projectId: 'project-a',
381
+ dataset: 'production',
382
+ })
383
+
384
+ // Fetch from Project B
385
+ const {data: productB} = useDocument({
386
+ documentId: 'product-456',
387
+ documentType: 'product',
388
+ projectId: 'project-b',
389
+ dataset: 'staging',
390
+ })
391
+
392
+ return (
393
+ <div>
394
+ <h2>{productA?.title} (Project A)</h2>
395
+ <h2>{productB?.title} (Project B)</h2>
396
+ </div>
397
+ )
351
398
  }
352
399
  ```
353
400
 
354
- #### Use Resource Provider Context
401
+ #### Approach 2: Use ResourceProvider to Set Context
402
+
403
+ Wrap components in `ResourceProvider` to set default project/dataset values for all child components:
355
404
 
356
405
  ```tsx
357
406
  // App.tsx
358
- import {ResourceProvider} from '@sanity/sdk-react'
407
+ import {ResourceProvider, useDocument, useSanityInstance} from '@sanity/sdk-react'
359
408
 
360
- import {ProductCard} from './ProductCard'
409
+ function ProductCard({productId}: {productId: string}) {
410
+ // Get the current project/dataset from context
411
+ const {config} = useSanityInstance()
412
+
413
+ // No need to specify projectId/dataset - inherited from ResourceProvider
414
+ const {data: product} = useDocument({
415
+ documentId: productId,
416
+ documentType: 'product',
417
+ })
361
418
 
362
- export function WrappedProductCard() {
363
419
  return (
364
- <ResourceProvider projectId="project-a" dataset="production">
365
- <ProductCard productId="xyz" />
366
- </ResourceProvider>
420
+ <div>
421
+ <h3>{product?.title}</h3>
422
+ <p>
423
+ From: {config.projectId}.{config.dataset}
424
+ </p>
425
+ </div>
367
426
  )
368
427
  }
369
428
 
370
- // ProductCard.tsx
371
- import {useProjectId, useDataset} from '@sanity/sdk-react'
429
+ export function MultiProjectApp() {
430
+ return (
431
+ <div>
432
+ {/* Products from Project A */}
433
+ <ResourceProvider projectId="project-a" dataset="production" fallback={<div>Loading...</div>}>
434
+ <h2>Project A Products</h2>
435
+ <ProductCard productId="product-123" />
436
+ <ProductCard productId="product-456" />
437
+ </ResourceProvider>
438
+
439
+ {/* Products from Project B */}
440
+ <ResourceProvider projectId="project-b" dataset="staging" fallback={<div>Loading...</div>}>
441
+ <h2>Project B Products</h2>
442
+ <ProductCard productId="product-789" />
443
+ </ResourceProvider>
444
+ </div>
445
+ )
446
+ }
447
+ ```
372
448
 
373
- function ProductCard({productId}: {productId: string}) {
374
- const projectId = useProjectId() // "project-a" from nearest configured ResourceProvider
375
- const dataset = useDataset() // "production" from nearest configured ResourceProvider
376
- // ...
449
+ **Key Points:**
450
+
451
+ - When using hooks that take document handles as arguments (such useDocument, useEditDocument, useQuery, etc.), the document handles’ `projectId` and `dataset` values can be explicitly set to fetch documents from arbitrary projects and datasets
452
+ - The ResourceProvider component is used to create a project ID and dataset context that child components will inherit from; this can negate the need to specify the project ID and dataset values for document handles in hooks called by child components
453
+ - Use `useSanityInstance()` to access the context configuration for the current component: `const {config} = useSanityInstance()`
454
+ - You can nest ResourceProvider components to create component trees with different project/dataset configurations — but be aware that, when the project ID and dataset values for document handles are _not_ specified, the project ID and dataset from the closest ResourceProvider context will be used
455
+ - Regardless of the approach you use, the project IDs and dataset names you reference (whether in document handles or ResourceProviders) must be enumerated in your application’s [SanityConfig objects](https://www.sanity.io/docs/app-sdk/sdk-configuration#d95b8773097c)
456
+
457
+ ---
458
+
459
+ ### Using the SDK inside Sanity Studio
460
+
461
+ The SDK can be embedded directly inside a Sanity Studio with zero manual configuration. Sanity Studio provides `SDKStudioContext` automatically, so `SanityApp` derives `projectId`, `dataset`, and auth from the Studio's workspace without any setup.
462
+
463
+ #### Zero-config setup (recommended)
464
+
465
+ Sanity Studio automatically provides `SDKStudioContext` to SDK components, so your SDK component needs no `config` prop at all:
466
+
467
+ ```tsx
468
+ import {SanityApp} from '@sanity/sdk-react'
469
+
470
+ // Inside a Sanity Studio — no config needed:
471
+ function MyStudioTool() {
472
+ return (
473
+ <SanityApp fallback={<div>Loading...</div>}>
474
+ <MyComponent />
475
+ </SanityApp>
476
+ )
377
477
  }
378
478
  ```
379
479
 
480
+ Under the hood, the Studio wraps its component tree with `SDKStudioContext.Provider`, passing its workspace to the SDK:
481
+
482
+ ```tsx
483
+ import {SDKStudioContext} from '@sanity/sdk-react'
484
+ import {useWorkspace} from 'sanity'
485
+
486
+ // This is done automatically by Sanity Studio — shown here for reference only
487
+ function StudioSDKWrapper({children}) {
488
+ const workspace = useWorkspace()
489
+ return <SDKStudioContext.Provider value={workspace}>{children}</SDKStudioContext.Provider>
490
+ }
491
+ ```
492
+
493
+ #### Explicit config takes precedence
494
+
495
+ If you pass a `config` prop to `SanityApp`, this config will take precedence over any workspace config picked up by `SDKStudioContext`:
496
+
497
+ ```tsx
498
+ // This uses the explicit config, not the Studio workspace
499
+ <SanityApp config={{projectId: 'other-project', dataset: 'staging'}} fallback={<Loading />}>
500
+ <MyComponent />
501
+ </SanityApp>
502
+ ```
503
+
504
+ #### Reactive auth token sync
505
+
506
+ If the Studio provides a reactive token source via `workspace.auth.token`, the SDK subscribes to it and stays in sync automatically. The Studio remains the single authority for auth — the SDK does not perform its own token refresh.
507
+
508
+ For older Studios that don't expose a token source, the SDK falls back to discovering the auth token from `localStorage` or cookie auth.
509
+
510
+ #### Migrating from `studioMode`
511
+
512
+ The `studioMode` config field is deprecated. If you are currently using it, the recommended replacement is to use the zero-config `SDKStudioContext` approach described above — which requires no `config` prop at all.
513
+
514
+ If you need to pass an explicit config, replace `studioMode` with `studio`:
515
+
516
+ ```diff
517
+ const config: SanityConfig = {
518
+ projectId: 'my-project',
519
+ dataset: 'production',
520
+ - studioMode: { enabled: true },
521
+ + studio: {},
522
+ }
523
+ ```
524
+
380
525
  ---
381
526
 
382
527
  ### TypeScript & TypeGen