@sanity/workflow-studio 0.4.2 → 0.6.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.
package/README.md CHANGED
@@ -1,9 +1,26 @@
1
1
  # @sanity/workflow-studio
2
2
 
3
- Reactive workflow adapter for **Sanity Studio**. `useWorkflowInstance` drives
4
- an `@sanity/workflow-engine` session and returns
5
- `{evaluation, ready, guards, tick, fireAction}`. Must render inside Studio
6
- source context (`useDocumentStore` / `useSource`).
3
+ Reactive workflow adapter for **Sanity Studio**. Everything must render inside
4
+ Studio source context (`useDocumentStore` / `useSource` / `useClient`).
5
+
6
+ - `useWorkflowEngine({workflowResource, tag, resourceClients?, apiVersion?})`
7
+ build the Studio-side engine once: the workspace client rebound to the
8
+ engine's state dataset (pinned to the engine's API version unless
9
+ `apiVersion` overrides), with `studioResourceClients` (also exported) as the
10
+ default per-resource routing that reaches any `dataset:` GDR by rebinding
11
+ the same client. A custom `resourceClients` replaces the default — delegate
12
+ to `studioResourceClients` for the dataset arm and handle other schemes
13
+ (media-library, canvas) yourself.
14
+ - `useWorkflowSession` — drives an engine session for one instance and
15
+ returns `{evaluation, ready, guards, tick, fireAction, editField}`.
16
+ - `useWorkflowInstances` / `useDocumentWorkflows` — live instance lists
17
+ (filterable collection / per-document discovery), each returning
18
+ `{instances, loading}`. The list observes `documentStore.listenQuery` when
19
+ the engine dataset is the workspace's own, else the App SDK store — same
20
+ routing (and same `sdk` rules) as the instance doc below. On multi-hook
21
+ surfaces (a board with per-row `useWorkflowSession`), pass one shared
22
+ `sdk` instance to the hooks — each hook otherwise bootstraps its own
23
+ embedded SDK instance when the engine dataset is foreign.
7
24
 
8
25
  ## How observation routes (per watched ref)
9
26
 
@@ -12,10 +29,10 @@ project + dataset and observed by the store that can actually address it:
12
29
 
13
30
  | Ref | Observed via |
14
31
  | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
- | **Mounted workspace dataset** | `documentStore.pair.editState` — Studio's optimistic pair store, **including local uncommitted edits** (the one stream only Studio has). Note: `pair.editState` is `@internal` in Studio |
16
- | **Engine state dataset** (instance doc) | The workspace's `documentStore` when the engine dataset IS the workspace; otherwise the **App SDK store** (`sdkInstanceDocStore` from `@sanity/workflow-sdk`) |
32
+ | **Mounted workspace dataset** | `documentStore.pair.editState` — Studio's optimistic pair store, **including local uncommitted edits** (the one stream only Studio has). Non-public Studio surface see **Private Studio seam** below |
33
+ | **Engine state dataset** (instance doc) | The workspace's `documentStore` when the engine dataset IS the workspace; otherwise the **App SDK store** (`sdkInstanceDocStore` from `@sanity/workflow-sdk/observer`) |
17
34
  | **Guards** (`observeGuards`) | Fanned per-resource across the datasets the instance's GDRs name: mounted-dataset guards via `documentStore.listenQuery`, any foreign dataset (engine's or a subject's) via `sdkGuardStore`, merged by `combineGuardStores` |
18
- | **Any other dataset / project** | The **App SDK document store** (`sdkContentDocStore` from `@sanity/workflow-sdk`) — live, GDR-routed per-resource — through the bootstrapped or supplied SDK instance (see below) |
35
+ | **Any other dataset / project** | The **App SDK document store** (`sdkContentDocStore` from `@sanity/workflow-sdk/observer`) — live, GDR-routed per-resource — through the bootstrapped or supplied SDK instance (see below) |
19
36
  | **Non-dataset schemes** (`canvas:`, `media-library:`) | Routed like foreign resources; the SDK store rejects them loudly (it addresses dataset resources only) |
20
37
 
21
38
  There is **no silent fallback**: a ref the configured stores can't address
@@ -25,12 +42,13 @@ throws, instead of being misread from the workspace dataset under the same
25
42
  - **The engine's `workflowResource` is routinely a dedicated dataset** (e.g.
26
43
  `workflows`), foreign to every content workspace. When it differs from the
27
44
  workspace and no `sdk` is supplied, the hook **bootstraps an App SDK
28
- instance automatically** from the Studio session's token; that instance
29
- also observes foreign content refs. Cookie-authenticated studios have no
30
- readable session token the hook throws an actionable error; supply your
31
- own instance: `useWorkflowInstance(engine, id, {sdk})`.
45
+ instance automatically** from the Studio session's token, read off the
46
+ source client's config (`source.getClient(...).config().token` public
47
+ Studio API); that instance also observes foreign content refs. Under cookie
48
+ auth the client carries no token — the hook throws an actionable error;
49
+ supply your own instance: `useWorkflowSession({engine, instanceId, sdk})`.
32
50
  - When the engine dataset IS the workspace and a watch-set ref points at a
33
- foreign resource, there is no bootstrap signal — pass `{sdk}` or the
51
+ foreign resource, there is no bootstrap signal — pass `sdk` or the
34
52
  observer throws the config-mismatch error for that ref.
35
53
  - Guards (`observeGuards`) are observed wherever they live: a guard
36
54
  co-locates with the subject it locks, so the stream spans the datasets the
@@ -46,7 +64,35 @@ throws, instead of being misread from the workspace dataset under the same
46
64
  Remaining gaps (ancestor-instance guards, full cascade write-set pre-flight,
47
65
  live cross-dataset end-to-end verification) are known follow-up work.
48
66
 
67
+ ## Packaging
68
+
69
+ This adapter is a **superset of `@sanity/workflow-sdk`'s store layer**, not
70
+ its peer. Foreign-resource observation is the routine path (the engine's
71
+ state dataset is usually dedicated), and it routes through the SDK stores —
72
+ so `@sanity/workflow-sdk` is a hard dependency, consumed through its headless
73
+ `./observer` entry. What that means for installs:
74
+
75
+ - **`@sanity/sdk` is required** (peer) — it addresses every resource outside
76
+ the mounted workspace.
77
+ - **`@sanity/sdk-react` is NOT required** — nothing here touches the App
78
+ SDK's React binding.
79
+
80
+ ## Private Studio seam
81
+
82
+ One stream rests on non-public Studio surface; expect Studio to be able to
83
+ move it in a minor, and this package to track it:
84
+
85
+ - **`documentStore.pair.editState`** (reached via `useDocumentStore`; Studio
86
+ marks both `@hidden` / `@beta`) — the only stream of the optimistic pair,
87
+ local uncommitted edits included. No public Studio API exposes it; until
88
+ one does, this dependency is deliberate and documented rather than hidden.
89
+
90
+ (The context hooks themselves — `useDocumentStore`, `useSource` — are
91
+ non-public Studio surface too, the standard plugin-context caveat.)
92
+
49
93
  ## Notes
50
94
 
51
95
  - See `@sanity/workflow-react`'s README for the guard-verdict semantics
52
- (what `mutation-guard-denied` does and doesn't cover).
96
+ (what `mutation-guard-denied` does and doesn't cover) and the `editField`
97
+ commit caveat (discrete commits — never bind to an `onChange` per
98
+ keystroke).
package/dist/index.cjs CHANGED
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: !0 });
3
- var workflowEngine = require("@sanity/workflow-engine"), workflowReact = require("@sanity/workflow-react"), workflowSdk = require("@sanity/workflow-sdk"), sdk = require("@sanity/sdk"), react = require("react"), sanity = require("sanity");
4
- function observableStore(observable, resolve, initial) {
3
+ var workflowEngine = require("@sanity/workflow-engine"), observer = require("@sanity/workflow-react/observer"), observer$1 = require("@sanity/workflow-sdk/observer"), react = require("react"), sanity = require("sanity"), workflowReact = require("@sanity/workflow-react"), sdk = require("@sanity/sdk");
4
+ function observableStore({
5
+ observable,
6
+ resolve,
7
+ initial
8
+ }) {
5
9
  let latest = initial;
6
10
  return {
7
11
  subscribe(onChange) {
@@ -14,74 +18,121 @@ function observableStore(observable, resolve, initial) {
14
18
  };
15
19
  }
16
20
  function makeStudioObserver(documentStore, options) {
17
- const { mounted, engineResource, sdk: sdk2 } = options, engineIsMounted = workflowReact.sameDataset(engineResource, mounted), editStateStore = (id, type, release) => observableStore(
18
- documentStore.pair.editState(id, type, release),
19
- resolveEditState,
20
- void 0
21
- ), requireSdk = (what) => {
21
+ const { mounted, engineResource, sdk: sdk2 } = options, engineIsMounted = observer.sameDataset(engineResource, mounted), editStateStore = ({
22
+ id,
23
+ type,
24
+ release
25
+ }) => observableStore({
26
+ observable: documentStore.pair.editState(id, type, release),
27
+ resolve: resolveEditState,
28
+ initial: void 0
29
+ }), requireSdk = (what) => {
22
30
  if (sdk2 !== void 0) return sdk2;
23
31
  throw new Error(
24
- `@sanity/workflow-studio: ${what} \u2014 Studio's document store is bound to ${mounted.projectId}.${mounted.dataset} and cannot observe it. Pass an App SDK instance (\`useWorkflowInstance(engine, id, {sdk})\`) to observe foreign resources.`
32
+ `@sanity/workflow-studio: ${what} \u2014 Studio's document store is bound to ${mounted.projectId}.${mounted.dataset} and cannot observe it. Pass an App SDK instance via the hook's \`sdk\` option (e.g. \`useWorkflowSession({engine, instanceId, sdk})\`) to observe foreign resources.`
25
33
  );
26
34
  };
27
35
  return {
28
- observeInstance: (instanceId) => engineIsMounted ? editStateStore(instanceId, workflowEngine.WORKFLOW_INSTANCE_TYPE, void 0) : workflowSdk.sdkInstanceDocStore(
29
- requireSdk(
36
+ observeInstance: (instanceId) => engineIsMounted ? editStateStore({ id: instanceId, type: workflowEngine.WORKFLOW_INSTANCE_TYPE, release: void 0 }) : observer$1.sdkInstanceDocStore({
37
+ sdk: requireSdk(
30
38
  `instance "${instanceId}" lives in the engine dataset ${engineResource.projectId}.${engineResource.dataset}`
31
39
  ),
32
40
  instanceId,
33
41
  engineResource
34
- ),
35
- observeDocs: (documents, perspective) => workflowReact.combineDocStores(
36
- documents.map((ref) => workflowReact.classifyRef(ref, mounted) === "mounted-dataset" ? {
42
+ }),
43
+ observeDocs: (documents, perspective) => observer.combineDocStores(
44
+ documents.map((ref) => observer.classifyRef(ref, mounted) === "mounted-dataset" ? {
37
45
  key: ref.globalDocumentId,
38
- store: editStateStore(
39
- ref.documentId,
40
- ref.type,
41
- workflowEngine.contentReleaseName({ ref, perspective })
42
- )
46
+ store: editStateStore({
47
+ id: ref.documentId,
48
+ type: ref.type,
49
+ release: workflowEngine.contentReleaseName({ ref, perspective })
50
+ })
43
51
  } : {
44
52
  key: ref.globalDocumentId,
45
- store: workflowSdk.sdkContentDocStore(
46
- requireSdk(`"${ref.globalDocumentId}" lives outside this workspace`),
53
+ store: observer$1.sdkContentDocStore({
54
+ sdk: requireSdk(`"${ref.globalDocumentId}" lives outside this workspace`),
47
55
  ref,
48
56
  perspective
49
- )
57
+ })
50
58
  })
51
59
  ),
52
- observeGuards: (instanceId, resources) => workflowReact.combineGuardStores(
60
+ observeGuards: (instanceId, resources) => observer.combineGuardStores(
53
61
  resources.map((resource) => {
54
- if (workflowReact.sameDataset(resource, mounted)) {
62
+ if (observer.sameDataset(resource, mounted)) {
55
63
  const { query, params } = workflowEngine.instanceGuardQuery(instanceId);
56
- return observableStore(
57
- documentStore.listenQuery(query, params, {}),
58
- (docs) => docs,
59
- workflowReact.NO_GUARDS
60
- );
64
+ return observableStore({
65
+ observable: documentStore.listenQuery(query, params, {}),
66
+ resolve: (docs) => docs,
67
+ initial: void 0
68
+ });
61
69
  }
62
- return workflowSdk.sdkGuardStore(
63
- requireSdk(
70
+ return observer$1.sdkGuardStore({
71
+ sdk: requireSdk(
64
72
  `guards for "${instanceId}" live in ${resource.projectId}.${resource.dataset}`
65
73
  ),
66
74
  instanceId,
67
75
  resource
68
- );
76
+ });
69
77
  })
70
- )
78
+ ),
79
+ observeInstances: (query) => engineIsMounted ? observableStore({
80
+ observable: documentStore.listenQuery(query.query, query.params, {}),
81
+ resolve: (docs) => docs,
82
+ initial: void 0
83
+ }) : observer$1.sdkInstancesStore({
84
+ sdk: requireSdk(
85
+ `the instance list lives in the engine dataset ${engineResource.projectId}.${engineResource.dataset}`
86
+ ),
87
+ query,
88
+ resource: engineResource
89
+ })
71
90
  };
72
91
  }
73
92
  function resolveEditState(editState) {
74
93
  if (editState.ready)
75
94
  return editState.version ?? editState.draft ?? editState.published;
76
95
  }
77
- function useWorkflowInstance(engine, instanceId, opts) {
78
- const documentStore = sanity.useDocumentStore(), { projectId, dataset } = sanity.useSource(), mounted = react.useMemo(() => ({ projectId, dataset }), [projectId, dataset]), engineResource = react.useMemo(() => workflowReact.datasetResourceId(engine.workflowResource), [engine]), sdk2 = useEmbeddedSdk(
79
- opts?.sdk,
96
+ function useWorkflowEngine({
97
+ workflowResource,
98
+ tag,
99
+ resourceClients,
100
+ apiVersion
101
+ }) {
102
+ const client = sanity.useClient({ apiVersion: apiVersion ?? workflowEngine.ENGINE_API_VERSION }), resource = observer.useKeyed([workflowResource.type, workflowResource.id], () => workflowResource);
103
+ return react.useMemo(() => {
104
+ const engineDataset = observer.datasetResourceId(resource), engineClient = client.withConfig({
105
+ projectId: engineDataset.projectId,
106
+ dataset: engineDataset.dataset,
107
+ useCdn: !1
108
+ });
109
+ return workflowEngine.createEngine({
110
+ client: engineClient,
111
+ workflowResource: resource,
112
+ tag,
113
+ resourceClients: resourceClients ?? studioResourceClients(client)
114
+ });
115
+ }, [client, resource, tag, resourceClients]);
116
+ }
117
+ function studioResourceClients(client) {
118
+ return (parsed) => parsed.scheme === "dataset" && parsed.projectId !== void 0 && parsed.dataset !== void 0 ? client.withConfig({ projectId: parsed.projectId, dataset: parsed.dataset, useCdn: !1 }) : void 0;
119
+ }
120
+ const SESSION_CLIENT_OPTIONS = { apiVersion: workflowEngine.ENGINE_API_VERSION };
121
+ function useStudioObserver({
122
+ engine,
123
+ sdk: suppliedSdk
124
+ }) {
125
+ const documentStore = sanity.useDocumentStore(), source = sanity.useSource(), { projectId, dataset } = source, mounted = react.useMemo(() => ({ projectId, dataset }), [projectId, dataset]), engineResource = react.useMemo(() => observer.datasetResourceId(engine.workflowResource), [engine]), sdk2 = useEmbeddedSdk({
126
+ supplied: suppliedSdk,
80
127
  // The engine dataset being foreign is the signal an SDK instance is
81
128
  // needed; foreign content refs without one still fail loud per ref.
82
- !workflowReact.sameDataset(engineResource, mounted),
83
- mounted
84
- ), observer = react.useMemo(
129
+ needed: !observer.sameDataset(engineResource, mounted),
130
+ mounted,
131
+ // The session's token rides the source client's config (absent under
132
+ // cookie auth) — the public read; Studio's token storage is private.
133
+ sessionToken: source.getClient(SESSION_CLIENT_OPTIONS).config().token
134
+ });
135
+ return react.useMemo(
85
136
  () => makeStudioObserver(documentStore, {
86
137
  mounted,
87
138
  engineResource,
@@ -89,23 +140,27 @@ function useWorkflowInstance(engine, instanceId, opts) {
89
140
  }),
90
141
  [documentStore, mounted, engineResource, sdk2]
91
142
  );
92
- return workflowReact.useWorkflowSession(engine, instanceId, observer, sessionOptions(opts));
93
143
  }
94
- function sessionOptions(opts = {}) {
95
- const { sdk: _, ...session } = opts;
96
- return session;
97
- }
98
- function useEmbeddedSdk(supplied, needed, mounted) {
144
+ function useEmbeddedSdk({
145
+ supplied,
146
+ needed,
147
+ mounted,
148
+ sessionToken
149
+ }) {
99
150
  const wanted = supplied === void 0 && needed, held = react.useRef(void 0), key = `${mounted.projectId}.${mounted.dataset}`;
100
- wanted && (held.current?.key !== key || held.current.instance.isDisposed()) && (held.current = { key, instance: bootstrapInstance(mounted) });
151
+ wanted && (held.current?.key !== key || held.current.instance.isDisposed()) && (held.current = { key, instance: bootstrapInstance(mounted, sessionToken) });
101
152
  const embedded = wanted ? held.current?.instance : void 0;
102
153
  return useDeferredDispose(embedded), supplied ?? embedded;
103
154
  }
104
- function bootstrapInstance(mounted) {
155
+ function bootstrapInstance(mounted, sessionToken) {
156
+ if (sessionToken === void 0)
157
+ throw new Error(
158
+ "@sanity/workflow-studio: the engine's state dataset is not this workspace's dataset, and no Studio session token is readable to bootstrap an App SDK instance (cookie-authenticated studio?). Pass one explicitly via the hook's `sdk` option, e.g. useWorkflowSession({engine, instanceId, sdk})."
159
+ );
105
160
  return sdk.createSanityInstance({
106
161
  projectId: mounted.projectId,
107
162
  dataset: mounted.dataset,
108
- auth: { token: studioSessionToken(mounted.projectId) }
163
+ auth: { token: sessionToken }
109
164
  });
110
165
  }
111
166
  function useDeferredDispose(instance) {
@@ -122,24 +177,36 @@ function useDeferredDispose(instance) {
122
177
  };
123
178
  }, [instance]);
124
179
  }
125
- function studioSessionToken(projectId) {
126
- const token = persistedToken(projectId);
127
- if (typeof token != "string")
128
- throw new Error(
129
- "@sanity/workflow-studio: the engine's state dataset is not this workspace's dataset, and no Studio session token is readable to bootstrap an App SDK instance (cookie-authenticated studio?). Pass one explicitly: useWorkflowInstance(engine, id, {sdk})."
130
- );
131
- return token;
180
+ function useWorkflowSession({
181
+ engine,
182
+ instanceId,
183
+ access,
184
+ grantsFromPath,
185
+ sdk: sdk2
186
+ }) {
187
+ const observer2 = useStudioObserver({ engine, sdk: sdk2 });
188
+ return workflowReact.useWorkflowSession({ engine, instanceId, observer: observer2, opts: { access, grantsFromPath } });
189
+ }
190
+ function useWorkflowInstances({
191
+ engine,
192
+ filter,
193
+ sdk: sdk2
194
+ }) {
195
+ const observer2 = useStudioObserver({ engine, sdk: sdk2 });
196
+ return workflowReact.useWorkflowInstances({ engine, observer: observer2, ...filter !== void 0 ? { filter } : {} });
132
197
  }
133
- function persistedToken(projectId) {
134
- const raw = window.localStorage.getItem(`__studio_auth_token_${projectId}`);
135
- if (raw !== null)
136
- try {
137
- const parsed = JSON.parse(raw);
138
- return typeof parsed != "object" || parsed === null ? void 0 : parsed.token;
139
- } catch {
140
- return;
141
- }
198
+ function useDocumentWorkflows({
199
+ engine,
200
+ document,
201
+ sdk: sdk2
202
+ }) {
203
+ const observer2 = useStudioObserver({ engine, sdk: sdk2 });
204
+ return workflowReact.useDocumentWorkflows({ engine, observer: observer2, document });
142
205
  }
143
206
  exports.makeStudioObserver = makeStudioObserver;
144
- exports.useWorkflowInstance = useWorkflowInstance;
207
+ exports.studioResourceClients = studioResourceClients;
208
+ exports.useDocumentWorkflows = useDocumentWorkflows;
209
+ exports.useWorkflowEngine = useWorkflowEngine;
210
+ exports.useWorkflowInstances = useWorkflowInstances;
211
+ exports.useWorkflowSession = useWorkflowSession;
145
212
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/studio-observer.ts","../src/use-workflow-instance.tsx"],"sourcesContent":["import type {SanityInstance} from '@sanity/sdk'\nimport {\n contentReleaseName,\n instanceGuardQuery,\n WORKFLOW_INSTANCE_TYPE,\n type MutationGuardDoc,\n} from '@sanity/workflow-engine'\nimport {\n classifyRef,\n combineDocStores,\n combineGuardStores,\n NO_GUARDS,\n sameDataset,\n type DatasetResourceId,\n type DocStore,\n type ObservedDoc,\n type WorkflowObserver,\n} from '@sanity/workflow-react'\nimport {sdkContentDocStore, sdkGuardStore, sdkInstanceDocStore} from '@sanity/workflow-sdk'\nimport type {DocumentStore, EditStateFor} from 'sanity'\n\n/** The slice of an RxJS observable this module needs — kept structural so the\n * file doesn't take an `rxjs` import just to name the type. */\ninterface ObservableLike<E> {\n subscribe: (next: (value: E) => void) => {unsubscribe: () => void}\n}\n\n/** Bridge a replayed RxJS observable into a {@link DocStore}: cache the latest\n * resolved value and notify `useSyncExternalStore` on each emission. Both the\n * per-doc `editState` stream and the guard `listenQuery` stream share it. */\nfunction observableStore<E, T>(\n observable: ObservableLike<E>,\n resolve: (value: E) => T,\n initial: T,\n): DocStore<T> {\n let latest = initial\n return {\n subscribe(onChange) {\n const subscription = observable.subscribe((value) => {\n latest = resolve(value)\n onChange()\n })\n return () => subscription.unsubscribe()\n },\n getSnapshot: () => latest,\n }\n}\n\nexport interface StudioObserverOptions {\n /** The workspace's project + dataset — the only resource Studio's\n * `documentStore` can address. */\n mounted: DatasetResourceId\n /** The engine's state dataset (`datasetResourceId(engine.workflowResource)`)\n * — where the instance doc and its verdict-relevant guards live. Routinely\n * a dedicated `workflows` dataset distinct from the workspace. */\n engineResource: DatasetResourceId\n /** Observe resources OUTSIDE the mounted dataset (foreign content refs, and\n * the engine dataset when it differs from the workspace) through the App\n * SDK store. Omit ⇒ any foreign observation throws an actionable\n * config-mismatch error instead of being silently misread. */\n sdk?: SanityInstance\n}\n\n/**\n * A {@link WorkflowObserver} that routes every stream to the store that can\n * actually address it:\n *\n * - Anything in the **mounted workspace dataset** → `documentStore`\n * (`pair.editState` for docs — including Studio's local uncommitted edits,\n * the one stream only Studio has — and `listenQuery` for guards).\n * - Anything else (foreign content refs; the engine's state dataset when it\n * isn't the workspace) → the App SDK store via {@link sdkInstanceDocStore} /\n * {@link sdkContentDocStore} / {@link sdkGuardStore} when\n * {@link StudioObserverOptions.sdk} is supplied; otherwise an actionable\n * error. Never a silent wrong-dataset read.\n */\nexport function makeStudioObserver(\n documentStore: DocumentStore,\n options: StudioObserverOptions,\n): WorkflowObserver {\n const {mounted, engineResource, sdk} = options\n const engineIsMounted = sameDataset(engineResource, mounted)\n\n const editStateStore = (\n id: string,\n type: string,\n release: string | undefined,\n ): DocStore<ObservedDoc> =>\n observableStore<EditStateFor, ObservedDoc>(\n documentStore.pair.editState(id, type, release),\n resolveEditState,\n undefined,\n )\n\n const requireSdk = (what: string): SanityInstance => {\n if (sdk !== undefined) return sdk\n throw new Error(\n `@sanity/workflow-studio: ${what} — Studio's document store is bound to ${mounted.projectId}.${mounted.dataset} and cannot observe it. Pass an App SDK instance (\\`useWorkflowInstance(engine, id, {sdk})\\`) to observe foreign resources.`,\n )\n }\n\n return {\n observeInstance: (instanceId) => {\n if (engineIsMounted) return editStateStore(instanceId, WORKFLOW_INSTANCE_TYPE, undefined)\n return sdkInstanceDocStore(\n requireSdk(\n `instance \"${instanceId}\" lives in the engine dataset ${engineResource.projectId}.${engineResource.dataset}`,\n ),\n instanceId,\n engineResource,\n )\n },\n observeDocs: (documents, perspective) =>\n combineDocStores(\n documents.map((ref) => {\n if (classifyRef(ref, mounted) === 'mounted-dataset') {\n return {\n key: ref.globalDocumentId,\n store: editStateStore(\n ref.documentId,\n ref.type,\n contentReleaseName({ref, perspective}),\n ),\n }\n }\n return {\n key: ref.globalDocumentId,\n store: sdkContentDocStore(\n requireSdk(`\"${ref.globalDocumentId}\" lives outside this workspace`),\n ref,\n perspective,\n ),\n }\n }),\n ),\n observeGuards: (instanceId, resources) =>\n combineGuardStores(\n resources.map((resource) => {\n if (sameDataset(resource, mounted)) {\n const {query, params} = instanceGuardQuery(instanceId)\n return observableStore<MutationGuardDoc[], readonly MutationGuardDoc[]>(\n documentStore.listenQuery(query, params, {}),\n (docs) => docs,\n NO_GUARDS,\n )\n }\n return sdkGuardStore(\n requireSdk(\n `guards for \"${instanceId}\" live in ${resource.projectId}.${resource.dataset}`,\n ),\n instanceId,\n resource,\n )\n }),\n ),\n }\n}\n\n/**\n * Resolve an `editState` emission to a single observed value: `undefined` while\n * not ready (gates the session feed), otherwise the version / draft / published\n * doc in precedence order (`null` when the doc doesn't exist).\n */\nfunction resolveEditState(editState: EditStateFor): ObservedDoc {\n if (!editState.ready) return undefined\n return (editState.version ?? editState.draft ?? editState.published) as ObservedDoc\n}\n","import {createSanityInstance, type SanityInstance} from '@sanity/sdk'\nimport type {Engine, WorkflowAccessOverride} from '@sanity/workflow-engine'\nimport {\n datasetResourceId,\n sameDataset,\n useWorkflowSession,\n type DatasetResourceId,\n type WorkflowInstanceController,\n} from '@sanity/workflow-react'\nimport {useEffect, useMemo, useRef} from 'react'\nimport {useDocumentStore, useSource} from 'sanity'\n\nimport {makeStudioObserver} from './studio-observer.ts'\n\n/**\n * Drive a workflow instance reactively from Sanity Studio. Watched docs in the\n * workspace's own dataset observe through `documentStore.pair.editState`\n * (including local uncommitted edits); everything else — the engine's state\n * dataset (routinely a dedicated `workflows` dataset) and foreign content\n * refs — observes through the App SDK store. When the engine dataset differs\n * from the workspace, an SDK instance is bootstrapped automatically from the\n * Studio session (pass `opts.sdk` to supply your own — required for\n * cookie-authenticated studios, where no session token is readable). Returns\n * `{evaluation, ready, guards, tick, fireAction, editField}`; the consumer decides when\n * to advance. Must render inside Studio source context (so\n * `useDocumentStore`/`useSource` resolve).\n */\nexport function useWorkflowInstance(\n engine: Engine,\n instanceId: string,\n opts?: {access?: WorkflowAccessOverride; grantsFromPath?: string; sdk?: SanityInstance},\n): WorkflowInstanceController {\n const documentStore = useDocumentStore()\n const {projectId, dataset} = useSource()\n const mounted = useMemo(() => ({projectId, dataset}), [projectId, dataset])\n const engineResource = useMemo(() => datasetResourceId(engine.workflowResource), [engine])\n const sdk = useEmbeddedSdk(\n opts?.sdk,\n // The engine dataset being foreign is the signal an SDK instance is\n // needed; foreign content refs without one still fail loud per ref.\n !sameDataset(engineResource, mounted),\n mounted,\n )\n const observer = useMemo(\n () =>\n makeStudioObserver(documentStore, {\n mounted,\n engineResource,\n ...(sdk !== undefined ? {sdk} : {}),\n }),\n [documentStore, mounted, engineResource, sdk],\n )\n return useWorkflowSession(engine, instanceId, observer, sessionOptions(opts))\n}\n\n/** Only the session options cross into the engine — `sdk` is a store handle\n * for the observer, never an `engine.instance(...)` argument. */\nfunction sessionOptions(\n opts: {access?: WorkflowAccessOverride; grantsFromPath?: string; sdk?: SanityInstance} = {},\n): {access?: WorkflowAccessOverride; grantsFromPath?: string} {\n const {sdk: _, ...session} = opts\n return session\n}\n\n/**\n * The supplied SDK instance, or one bootstrapped from the Studio session's\n * token when foreign observation is needed. Lifecycle mirrors the SDK's own\n * `ResourceProvider`: created in render (the observer needs it synchronously),\n * recreated when a previous unmount disposed it, and released via\n * {@link useDeferredDispose} — so StrictMode's unmount/remount probe neither\n * leaks an instance nor hands the remount a dead one. Exported for tests only.\n */\nexport function useEmbeddedSdk(\n supplied: SanityInstance | undefined,\n needed: boolean,\n mounted: DatasetResourceId,\n): SanityInstance | undefined {\n const wanted = supplied === undefined && needed\n const held = useRef<{key: string; instance: SanityInstance} | undefined>(undefined)\n const key = `${mounted.projectId}.${mounted.dataset}`\n if (wanted && (held.current?.key !== key || held.current.instance.isDisposed())) {\n held.current = {key, instance: bootstrapInstance(mounted)}\n }\n const embedded = wanted ? held.current?.instance : undefined\n useDeferredDispose(embedded)\n return supplied ?? embedded\n}\n\n/** An SDK instance over the workspace's own project, authed with the Studio\n * session's token — the engine dataset is reached per-resource from here. */\nfunction bootstrapInstance(mounted: DatasetResourceId): SanityInstance {\n return createSanityInstance({\n projectId: mounted.projectId,\n dataset: mounted.dataset,\n auth: {token: studioSessionToken(mounted.projectId)},\n })\n}\n\n/**\n * Dispose `instance` after unmount via a zero-delay timer that an immediate\n * remount of the same instance cancels — the SDK `ResourceProvider`'s own\n * idiom for surviving StrictMode's unmount/remount probe.\n */\nfunction useDeferredDispose(instance: SanityInstance | undefined): void {\n const disposal = useRef<\n {instance: SanityInstance; timeoutId: ReturnType<typeof setTimeout>} | undefined\n >(undefined)\n useEffect(() => {\n if (instance === undefined) return undefined\n if (disposal.current?.instance === instance) {\n clearTimeout(disposal.current.timeoutId)\n disposal.current = undefined\n }\n return () => {\n disposal.current = {\n instance,\n timeoutId: setTimeout(() => {\n if (!instance.isDisposed()) instance.dispose()\n }, 0),\n }\n }\n }, [instance])\n}\n\n/** The Studio session's token, as persisted by Studio's own auth store.\n * Exported for tests only. */\nexport function studioSessionToken(projectId: string): string {\n const token = persistedToken(projectId)\n if (typeof token !== 'string') {\n throw new Error(\n `@sanity/workflow-studio: the engine's state dataset is not this workspace's dataset, and no Studio session token is readable to bootstrap an App SDK instance (cookie-authenticated studio?). Pass one explicitly: useWorkflowInstance(engine, id, {sdk}).`,\n )\n }\n return token\n}\n\n/** Best-effort read of Studio's persisted `{token}` localStorage entry — any\n * unreadable shape (absent key, malformed JSON, non-object value) resolves to\n * `undefined` so every failure mode funnels into the one actionable error. */\nfunction persistedToken(projectId: string): unknown {\n const raw = window.localStorage.getItem(`__studio_auth_token_${projectId}`)\n if (raw === null) return undefined\n try {\n const parsed: unknown = JSON.parse(raw)\n if (typeof parsed !== 'object' || parsed === null) return undefined\n return (parsed as {token?: unknown}).token\n } catch {\n return undefined\n }\n}\n"],"names":["sdk","sameDataset","WORKFLOW_INSTANCE_TYPE","sdkInstanceDocStore","combineDocStores","classifyRef","contentReleaseName","sdkContentDocStore","combineGuardStores","instanceGuardQuery","NO_GUARDS","sdkGuardStore","useDocumentStore","useSource","useMemo","datasetResourceId","useWorkflowSession","useRef","createSanityInstance","useEffect"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/studio-observer.ts","../src/use-workflow-engine.ts","../src/use-studio-observer.ts","../src/use-workflow-session.tsx","../src/use-workflow-instances.tsx"],"sourcesContent":["import type {SanityInstance} from '@sanity/sdk'\nimport {\n contentReleaseName,\n instanceGuardQuery,\n WORKFLOW_INSTANCE_TYPE,\n type MutationGuardDoc,\n type WorkflowInstance,\n} from '@sanity/workflow-engine'\nimport {\n classifyRef,\n combineDocStores,\n combineGuardStores,\n sameDataset,\n type DatasetResourceId,\n type DocStore,\n type ObservedDoc,\n type WorkflowObserver,\n} from '@sanity/workflow-react/observer'\nimport {\n sdkContentDocStore,\n sdkGuardStore,\n sdkInstanceDocStore,\n sdkInstancesStore,\n} from '@sanity/workflow-sdk/observer'\nimport type {DocumentStore, EditStateFor} from 'sanity'\n\n/** The slice of an RxJS observable this module needs — kept structural so the\n * file doesn't take an `rxjs` import just to name the type. */\ninterface ObservableLike<E> {\n subscribe: (next: (value: E) => void) => {unsubscribe: () => void}\n}\n\n/** Bridge a replayed RxJS observable into a {@link DocStore}: cache the latest\n * resolved value and notify `useSyncExternalStore` on each emission. Both the\n * per-doc `editState` stream and the guard `listenQuery` stream share it. */\nfunction observableStore<E, T>({\n observable,\n resolve,\n initial,\n}: {\n observable: ObservableLike<E>\n resolve: (value: E) => T\n initial: T\n}): DocStore<T> {\n let latest = initial\n return {\n subscribe(onChange) {\n const subscription = observable.subscribe((value) => {\n latest = resolve(value)\n onChange()\n })\n return () => subscription.unsubscribe()\n },\n getSnapshot: () => latest,\n }\n}\n\nexport interface StudioObserverOptions {\n /** The workspace's project + dataset — the only resource Studio's\n * `documentStore` can address. */\n mounted: DatasetResourceId\n /** The engine's state dataset (`datasetResourceId(engine.workflowResource)`)\n * — where the instance doc and its verdict-relevant guards live. Routinely\n * a dedicated `workflows` dataset distinct from the workspace. */\n engineResource: DatasetResourceId\n /** Observe resources OUTSIDE the mounted dataset (foreign content refs, and\n * the engine dataset when it differs from the workspace) through the App\n * SDK store. Omit ⇒ any foreign observation throws an actionable\n * config-mismatch error instead of being silently misread. */\n sdk?: SanityInstance\n}\n\n/**\n * A {@link WorkflowObserver} that routes every stream to the store that can\n * actually address it:\n *\n * - Anything in the **mounted workspace dataset** → `documentStore`\n * (`pair.editState` for docs — including Studio's local uncommitted edits,\n * the one stream only Studio has — and `listenQuery` for guards and\n * instance lists).\n * - Anything else (foreign content refs; the engine's state dataset when it\n * isn't the workspace) → the App SDK store via {@link sdkInstanceDocStore} /\n * {@link sdkContentDocStore} / {@link sdkGuardStore} when\n * {@link StudioObserverOptions.sdk} is supplied; otherwise an actionable\n * error. Never a silent wrong-dataset read.\n */\nexport function makeStudioObserver(\n documentStore: DocumentStore,\n options: StudioObserverOptions,\n): WorkflowObserver {\n const {mounted, engineResource, sdk} = options\n const engineIsMounted = sameDataset(engineResource, mounted)\n\n const editStateStore = ({\n id,\n type,\n release,\n }: {\n id: string\n type: string\n release: string | undefined\n }): DocStore<ObservedDoc> =>\n observableStore<EditStateFor, ObservedDoc>({\n observable: documentStore.pair.editState(id, type, release),\n resolve: resolveEditState,\n initial: undefined,\n })\n\n const requireSdk = (what: string): SanityInstance => {\n if (sdk !== undefined) return sdk\n throw new Error(\n `@sanity/workflow-studio: ${what} — Studio's document store is bound to ${mounted.projectId}.${mounted.dataset} and cannot observe it. Pass an App SDK instance via the hook's \\`sdk\\` option (e.g. \\`useWorkflowSession({engine, instanceId, sdk})\\`) to observe foreign resources.`,\n )\n }\n\n return {\n observeInstance: (instanceId) => {\n if (engineIsMounted)\n return editStateStore({id: instanceId, type: WORKFLOW_INSTANCE_TYPE, release: undefined})\n return sdkInstanceDocStore({\n sdk: requireSdk(\n `instance \"${instanceId}\" lives in the engine dataset ${engineResource.projectId}.${engineResource.dataset}`,\n ),\n instanceId,\n engineResource,\n })\n },\n observeDocs: (documents, perspective) =>\n combineDocStores(\n documents.map((ref) => {\n if (classifyRef(ref, mounted) === 'mounted-dataset') {\n return {\n key: ref.globalDocumentId,\n store: editStateStore({\n id: ref.documentId,\n type: ref.type,\n release: contentReleaseName({ref, perspective}),\n }),\n }\n }\n return {\n key: ref.globalDocumentId,\n store: sdkContentDocStore({\n sdk: requireSdk(`\"${ref.globalDocumentId}\" lives outside this workspace`),\n ref,\n perspective,\n }),\n }\n }),\n ),\n observeGuards: (instanceId, resources) =>\n combineGuardStores(\n resources.map((resource) => {\n if (sameDataset(resource, mounted)) {\n const {query, params} = instanceGuardQuery(instanceId)\n // `undefined` until the first `listenQuery` emission — the loading\n // signal the guard merge and the hook's `ready` gate distinguish\n // from a resolved-empty guard list.\n return observableStore<MutationGuardDoc[], readonly MutationGuardDoc[] | undefined>({\n observable: documentStore.listenQuery(query, params, {}),\n resolve: (docs) => docs,\n initial: undefined,\n })\n }\n return sdkGuardStore({\n sdk: requireSdk(\n `guards for \"${instanceId}\" live in ${resource.projectId}.${resource.dataset}`,\n ),\n instanceId,\n resource,\n })\n }),\n ),\n observeInstances: (query) => {\n if (engineIsMounted) {\n // `initial: undefined` (not a shared empty) — a pending first read and\n // a confirmed empty list must stay distinguishable for `loading`.\n return observableStore<WorkflowInstance[], readonly WorkflowInstance[] | undefined>({\n observable: documentStore.listenQuery(query.query, query.params, {}),\n resolve: (docs) => docs,\n initial: undefined,\n })\n }\n return sdkInstancesStore({\n sdk: requireSdk(\n `the instance list lives in the engine dataset ${engineResource.projectId}.${engineResource.dataset}`,\n ),\n query,\n resource: engineResource,\n })\n },\n }\n}\n\n/**\n * Resolve an `editState` emission to a single observed value: `undefined` while\n * not ready (gates the session feed), otherwise the version / draft / published\n * doc in precedence order (`null` when the doc doesn't exist).\n */\nfunction resolveEditState(editState: EditStateFor): ObservedDoc {\n if (!editState.ready) return undefined\n return (editState.version ?? editState.draft ?? editState.published) as ObservedDoc\n}\n","import {\n createEngine,\n ENGINE_API_VERSION,\n type Engine,\n type ResourceClientResolver,\n type WorkflowClient,\n type WorkflowResource,\n} from '@sanity/workflow-engine'\nimport {datasetResourceId, useKeyed} from '@sanity/workflow-react/observer'\nimport {useMemo} from 'react'\nimport {useClient} from 'sanity'\n\n/**\n * Build the Studio-side engine once: the workspace client rebound to the\n * engine's state dataset, plus {@link studioResourceClients} as the default\n * per-resource routing — so subjects and refs across many content datasets\n * resolve without call sites assembling clients. Pass `resourceClients` to\n * override the routing (compose with {@link studioResourceClients} to keep\n * dataset-GDR routing). Memoized on the config's content, so inline\n * `workflowResource` literals are fine; a custom `resourceClients` function\n * should still be stable. Must render inside Studio source context.\n */\nexport function useWorkflowEngine({\n workflowResource,\n tag,\n resourceClients,\n apiVersion,\n}: {\n workflowResource: WorkflowResource\n tag: string\n resourceClients?: ResourceClientResolver\n apiVersion?: string\n}): Engine {\n const client = useClient({apiVersion: apiVersion ?? ENGINE_API_VERSION})\n const resource = useKeyed([workflowResource.type, workflowResource.id], () => workflowResource)\n return useMemo(() => {\n const engineDataset = datasetResourceId(resource)\n const engineClient = asWorkflowClient(\n client.withConfig({\n projectId: engineDataset.projectId,\n dataset: engineDataset.dataset,\n useCdn: false,\n }),\n )\n return createEngine({\n client: engineClient,\n workflowResource: resource,\n tag,\n resourceClients: resourceClients ?? studioResourceClients(client),\n })\n }, [client, resource, tag, resourceClients])\n}\n\n/**\n * The default Studio {@link ResourceClientResolver}: any fully-addressed\n * `dataset:` GDR resolves to the workspace client rebound to that\n * project/dataset; other schemes (media-library, canvas) return `undefined` so\n * the engine falls back to its own client. A custom resolver can delegate to\n * this for the dataset arm and handle other schemes itself.\n */\nexport function studioResourceClients(client: StudioClient): ResourceClientResolver {\n return (parsed) =>\n parsed.scheme === 'dataset' && parsed.projectId !== undefined && parsed.dataset !== undefined\n ? asWorkflowClient(\n client.withConfig({projectId: parsed.projectId, dataset: parsed.dataset, useCdn: false}),\n )\n : undefined\n}\n\ntype StudioClient = ReturnType<typeof useClient>\n\n/** The Studio client is structurally assignable to the engine's narrower\n * client — a typed identity, so drift breaks loudly at compile time. */\nfunction asWorkflowClient(client: StudioClient): WorkflowClient {\n return client\n}\n","import {createSanityInstance, type SanityInstance} from '@sanity/sdk'\nimport {ENGINE_API_VERSION, type Engine} from '@sanity/workflow-engine'\nimport {\n datasetResourceId,\n sameDataset,\n type DatasetResourceId,\n type WorkflowObserver,\n} from '@sanity/workflow-react/observer'\nimport {useEffect, useMemo, useRef} from 'react'\nimport {useDocumentStore, useSource} from 'sanity'\n\nimport {makeStudioObserver} from './studio-observer.ts'\n\n/** The client is only read for its auth config — no request is ever issued\n * with it — but `getClient` requires an apiVersion to hand one out. */\nconst SESSION_CLIENT_OPTIONS = {apiVersion: ENGINE_API_VERSION}\n\n/**\n * The Studio observer for this engine — the shared wiring under every\n * reactive hook in this package. Workspace-dataset streams ride\n * `documentStore`; anything foreign (routinely the engine's dedicated\n * `workflows` dataset) rides an App SDK instance, bootstrapped from the\n * Studio session when the caller doesn't supply one. Must run inside Studio\n * source context (so `useDocumentStore`/`useSource` resolve).\n */\nexport function useStudioObserver({\n engine,\n sdk: suppliedSdk,\n}: {\n engine: Engine\n sdk?: SanityInstance | undefined\n}): WorkflowObserver {\n const documentStore = useDocumentStore()\n const source = useSource()\n const {projectId, dataset} = source\n const mounted = useMemo(() => ({projectId, dataset}), [projectId, dataset])\n const engineResource = useMemo(() => datasetResourceId(engine.workflowResource), [engine])\n const sdk = useEmbeddedSdk({\n supplied: suppliedSdk,\n // The engine dataset being foreign is the signal an SDK instance is\n // needed; foreign content refs without one still fail loud per ref.\n needed: !sameDataset(engineResource, mounted),\n mounted,\n // The session's token rides the source client's config (absent under\n // cookie auth) — the public read; Studio's token storage is private.\n sessionToken: source.getClient(SESSION_CLIENT_OPTIONS).config().token,\n })\n return useMemo(\n () =>\n makeStudioObserver(documentStore, {\n mounted,\n engineResource,\n ...(sdk !== undefined ? {sdk} : {}),\n }),\n [documentStore, mounted, engineResource, sdk],\n )\n}\n\n/**\n * The supplied SDK instance, or one bootstrapped from the Studio session's\n * token when foreign observation is needed. Lifecycle mirrors the SDK's own\n * `ResourceProvider`: created in render (the observer needs it synchronously),\n * recreated when a previous unmount disposed it, and released via\n * {@link useDeferredDispose} — so StrictMode's unmount/remount probe neither\n * leaks an instance nor hands the remount a dead one. `sessionToken` is\n * captured at bootstrap, deliberately not part of the recreate key: a rotated\n * token surfaces as auth failures until remount, and keying on it would churn\n * the instance on every rotation. Exported for tests only.\n */\nexport function useEmbeddedSdk({\n supplied,\n needed,\n mounted,\n sessionToken,\n}: {\n supplied: SanityInstance | undefined\n needed: boolean\n mounted: DatasetResourceId\n sessionToken: string | undefined\n}): SanityInstance | undefined {\n const wanted = supplied === undefined && needed\n const held = useRef<{key: string; instance: SanityInstance} | undefined>(undefined)\n const key = `${mounted.projectId}.${mounted.dataset}`\n if (wanted && (held.current?.key !== key || held.current.instance.isDisposed())) {\n held.current = {key, instance: bootstrapInstance(mounted, sessionToken)}\n }\n const embedded = wanted ? held.current?.instance : undefined\n useDeferredDispose(embedded)\n return supplied ?? embedded\n}\n\n/** An SDK instance over the workspace's own project, authed with the Studio\n * session's token — the engine dataset is reached per-resource from here. */\nfunction bootstrapInstance(\n mounted: DatasetResourceId,\n sessionToken: string | undefined,\n): SanityInstance {\n if (sessionToken === undefined) {\n throw new Error(\n `@sanity/workflow-studio: the engine's state dataset is not this workspace's dataset, and no Studio session token is readable to bootstrap an App SDK instance (cookie-authenticated studio?). Pass one explicitly via the hook's \\`sdk\\` option, e.g. useWorkflowSession({engine, instanceId, sdk}).`,\n )\n }\n return createSanityInstance({\n projectId: mounted.projectId,\n dataset: mounted.dataset,\n auth: {token: sessionToken},\n })\n}\n\n/**\n * Dispose `instance` after unmount via a zero-delay timer that an immediate\n * remount of the same instance cancels — the SDK `ResourceProvider`'s own\n * idiom for surviving StrictMode's unmount/remount probe.\n */\nfunction useDeferredDispose(instance: SanityInstance | undefined): void {\n const disposal = useRef<\n {instance: SanityInstance; timeoutId: ReturnType<typeof setTimeout>} | undefined\n >(undefined)\n useEffect(() => {\n if (instance === undefined) return undefined\n if (disposal.current?.instance === instance) {\n clearTimeout(disposal.current.timeoutId)\n disposal.current = undefined\n }\n return () => {\n disposal.current = {\n instance,\n timeoutId: setTimeout(() => {\n if (!instance.isDisposed()) instance.dispose()\n }, 0),\n }\n }\n }, [instance])\n}\n","import type {SanityInstance} from '@sanity/sdk'\nimport type {Engine, WorkflowAccessOverride} from '@sanity/workflow-engine'\nimport {\n useWorkflowSession as useWorkflowSessionCore,\n type WorkflowSession,\n} from '@sanity/workflow-react'\n\nimport {useStudioObserver} from './use-studio-observer.ts'\n\n/**\n * Drive a workflow instance reactively from Sanity Studio. Watched docs in the\n * workspace's own dataset observe through `documentStore.pair.editState`\n * (including local uncommitted edits); everything else — the engine's state\n * dataset (routinely a dedicated `workflows` dataset) and foreign content\n * refs — observes through the App SDK store. When the engine dataset differs\n * from the workspace, an SDK instance is bootstrapped automatically from the\n * Studio session (pass `sdk` to supply your own — required for\n * cookie-authenticated studios, where no session token is readable). Returns\n * `{evaluation, ready, guards, tick, fireAction, editField}`; the consumer decides when\n * to advance. Must render inside Studio source context.\n */\nexport function useWorkflowSession({\n engine,\n instanceId,\n access,\n grantsFromPath,\n sdk,\n}: {\n engine: Engine\n instanceId: string\n access?: WorkflowAccessOverride\n grantsFromPath?: string\n sdk?: SanityInstance\n}): WorkflowSession {\n const observer = useStudioObserver({engine, sdk})\n // `sdk` is a store handle for the observer, never an `engine.session(...)`\n // argument — only the session options cross into the engine.\n return useWorkflowSessionCore({engine, instanceId, observer, opts: {access, grantsFromPath}})\n}\n","import type {SanityInstance} from '@sanity/sdk'\nimport type {Engine, GdrUri, InstancesQueryFilter} from '@sanity/workflow-engine'\nimport {\n useDocumentWorkflows as useDocumentWorkflowsCore,\n useWorkflowInstances as useWorkflowInstancesCore,\n type WorkflowInstanceList,\n} from '@sanity/workflow-react'\n\nimport {useStudioObserver} from './use-studio-observer.ts'\n\n/** A live, filterable workflow-instance list observed from Sanity Studio —\n * through `documentStore.listenQuery` when the engine dataset is the\n * workspace's own, else through an App SDK instance (auto-bootstrapped, or\n * supply `sdk`). Must render inside Studio source context. */\nexport function useWorkflowInstances({\n engine,\n filter,\n sdk,\n}: {\n engine: Engine\n filter?: InstancesQueryFilter\n sdk?: SanityInstance\n}): WorkflowInstanceList {\n const observer = useStudioObserver({engine, sdk})\n return useWorkflowInstancesCore({engine, observer, ...(filter !== undefined ? {filter} : {})})\n}\n\n/** Which in-flight instances reference this document (GDR URI) — live,\n * observed from Sanity Studio. Same routing and `sdk` rules as\n * {@link useWorkflowInstances}. */\nexport function useDocumentWorkflows({\n engine,\n document,\n sdk,\n}: {\n engine: Engine\n document: GdrUri\n sdk?: SanityInstance\n}): WorkflowInstanceList {\n const observer = useStudioObserver({engine, sdk})\n return useDocumentWorkflowsCore({engine, observer, document})\n}\n"],"names":["sdk","sameDataset","WORKFLOW_INSTANCE_TYPE","sdkInstanceDocStore","combineDocStores","classifyRef","contentReleaseName","sdkContentDocStore","combineGuardStores","instanceGuardQuery","sdkGuardStore","sdkInstancesStore","useClient","ENGINE_API_VERSION","useKeyed","useMemo","datasetResourceId","createEngine","useDocumentStore","useSource","useRef","createSanityInstance","useEffect","observer","useWorkflowSessionCore","useWorkflowInstancesCore","useDocumentWorkflowsCore"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,11 +1,17 @@
1
- import { DatasetResourceId } from "@sanity/workflow-react";
1
+ import { DatasetResourceId } from "@sanity/workflow-react/observer";
2
2
  import type { DocumentStore } from "sanity";
3
- import type { Engine } from "@sanity/workflow-engine";
3
+ import { Engine } from "@sanity/workflow-engine";
4
+ import type { GdrUri } from "@sanity/workflow-engine";
5
+ import type { InstancesQueryFilter } from "@sanity/workflow-engine";
4
6
  import { MutationGuardDoc } from "@sanity/workflow-react";
5
- import { SanityInstance } from "@sanity/sdk";
7
+ import { ResourceClientResolver } from "@sanity/workflow-engine";
8
+ import type { SanityInstance } from "@sanity/sdk";
9
+ import { useClient } from "sanity";
6
10
  import type { WorkflowAccessOverride } from "@sanity/workflow-engine";
7
- import { WorkflowInstanceController } from "@sanity/workflow-react";
8
- import { WorkflowObserver } from "@sanity/workflow-react";
11
+ import { WorkflowInstanceList } from "@sanity/workflow-react";
12
+ import { WorkflowObserver } from "@sanity/workflow-react/observer";
13
+ import { WorkflowResource } from "@sanity/workflow-engine";
14
+ import { WorkflowSession } from "@sanity/workflow-react";
9
15
 
10
16
  /**
11
17
  * A {@link WorkflowObserver} that routes every stream to the store that can
@@ -13,7 +19,8 @@ import { WorkflowObserver } from "@sanity/workflow-react";
13
19
  *
14
20
  * - Anything in the **mounted workspace dataset** → `documentStore`
15
21
  * (`pair.editState` for docs — including Studio's local uncommitted edits,
16
- * the one stream only Studio has — and `listenQuery` for guards).
22
+ * the one stream only Studio has — and `listenQuery` for guards and
23
+ * instance lists).
17
24
  * - Anything else (foreign content refs; the engine's state dataset when it
18
25
  * isn't the workspace) → the App SDK store via {@link sdkInstanceDocStore} /
19
26
  * {@link sdkContentDocStore} / {@link sdkGuardStore} when
@@ -27,6 +34,8 @@ export declare function makeStudioObserver(
27
34
 
28
35
  export { MutationGuardDoc };
29
36
 
37
+ declare type StudioClient = ReturnType<typeof useClient>;
38
+
30
39
  export declare interface StudioObserverOptions {
31
40
  /** The workspace's project + dataset — the only resource Studio's
32
41
  * `documentStore` can address. */
@@ -42,6 +51,66 @@ export declare interface StudioObserverOptions {
42
51
  sdk?: SanityInstance;
43
52
  }
44
53
 
54
+ /**
55
+ * The default Studio {@link ResourceClientResolver}: any fully-addressed
56
+ * `dataset:` GDR resolves to the workspace client rebound to that
57
+ * project/dataset; other schemes (media-library, canvas) return `undefined` so
58
+ * the engine falls back to its own client. A custom resolver can delegate to
59
+ * this for the dataset arm and handle other schemes itself.
60
+ */
61
+ export declare function studioResourceClients(
62
+ client: StudioClient,
63
+ ): ResourceClientResolver;
64
+
65
+ /** Which in-flight instances reference this document (GDR URI) — live,
66
+ * observed from Sanity Studio. Same routing and `sdk` rules as
67
+ * {@link useWorkflowInstances}. */
68
+ export declare function useDocumentWorkflows({
69
+ engine,
70
+ document,
71
+ sdk,
72
+ }: {
73
+ engine: Engine;
74
+ document: GdrUri;
75
+ sdk?: SanityInstance;
76
+ }): WorkflowInstanceList;
77
+
78
+ /**
79
+ * Build the Studio-side engine once: the workspace client rebound to the
80
+ * engine's state dataset, plus {@link studioResourceClients} as the default
81
+ * per-resource routing — so subjects and refs across many content datasets
82
+ * resolve without call sites assembling clients. Pass `resourceClients` to
83
+ * override the routing (compose with {@link studioResourceClients} to keep
84
+ * dataset-GDR routing). Memoized on the config's content, so inline
85
+ * `workflowResource` literals are fine; a custom `resourceClients` function
86
+ * should still be stable. Must render inside Studio source context.
87
+ */
88
+ export declare function useWorkflowEngine({
89
+ workflowResource,
90
+ tag,
91
+ resourceClients,
92
+ apiVersion,
93
+ }: {
94
+ workflowResource: WorkflowResource;
95
+ tag: string;
96
+ resourceClients?: ResourceClientResolver;
97
+ apiVersion?: string;
98
+ }): Engine;
99
+
100
+ /** A live, filterable workflow-instance list observed from Sanity Studio —
101
+ * through `documentStore.listenQuery` when the engine dataset is the
102
+ * workspace's own, else through an App SDK instance (auto-bootstrapped, or
103
+ * supply `sdk`). Must render inside Studio source context. */
104
+ export declare function useWorkflowInstances({
105
+ engine,
106
+ filter,
107
+ sdk,
108
+ }: {
109
+ engine: Engine;
110
+ filter?: InstancesQueryFilter;
111
+ sdk?: SanityInstance;
112
+ }): WorkflowInstanceList;
113
+
45
114
  /**
46
115
  * Drive a workflow instance reactively from Sanity Studio. Watched docs in the
47
116
  * workspace's own dataset observe through `documentStore.pair.editState`
@@ -49,22 +118,27 @@ export declare interface StudioObserverOptions {
49
118
  * dataset (routinely a dedicated `workflows` dataset) and foreign content
50
119
  * refs — observes through the App SDK store. When the engine dataset differs
51
120
  * from the workspace, an SDK instance is bootstrapped automatically from the
52
- * Studio session (pass `opts.sdk` to supply your own — required for
121
+ * Studio session (pass `sdk` to supply your own — required for
53
122
  * cookie-authenticated studios, where no session token is readable). Returns
54
123
  * `{evaluation, ready, guards, tick, fireAction, editField}`; the consumer decides when
55
- * to advance. Must render inside Studio source context (so
56
- * `useDocumentStore`/`useSource` resolve).
124
+ * to advance. Must render inside Studio source context.
57
125
  */
58
- export declare function useWorkflowInstance(
59
- engine: Engine,
60
- instanceId: string,
61
- opts?: {
62
- access?: WorkflowAccessOverride;
63
- grantsFromPath?: string;
64
- sdk?: SanityInstance;
65
- },
66
- ): WorkflowInstanceController;
126
+ export declare function useWorkflowSession({
127
+ engine,
128
+ instanceId,
129
+ access,
130
+ grantsFromPath,
131
+ sdk,
132
+ }: {
133
+ engine: Engine;
134
+ instanceId: string;
135
+ access?: WorkflowAccessOverride;
136
+ grantsFromPath?: string;
137
+ sdk?: SanityInstance;
138
+ }): WorkflowSession;
139
+
140
+ export { WorkflowInstanceList };
67
141
 
68
- export { WorkflowInstanceController };
142
+ export { WorkflowSession };
69
143
 
70
144
  export {};
package/dist/index.d.ts CHANGED
@@ -1,11 +1,17 @@
1
- import { DatasetResourceId } from "@sanity/workflow-react";
1
+ import { DatasetResourceId } from "@sanity/workflow-react/observer";
2
2
  import type { DocumentStore } from "sanity";
3
- import type { Engine } from "@sanity/workflow-engine";
3
+ import { Engine } from "@sanity/workflow-engine";
4
+ import type { GdrUri } from "@sanity/workflow-engine";
5
+ import type { InstancesQueryFilter } from "@sanity/workflow-engine";
4
6
  import { MutationGuardDoc } from "@sanity/workflow-react";
5
- import { SanityInstance } from "@sanity/sdk";
7
+ import { ResourceClientResolver } from "@sanity/workflow-engine";
8
+ import type { SanityInstance } from "@sanity/sdk";
9
+ import { useClient } from "sanity";
6
10
  import type { WorkflowAccessOverride } from "@sanity/workflow-engine";
7
- import { WorkflowInstanceController } from "@sanity/workflow-react";
8
- import { WorkflowObserver } from "@sanity/workflow-react";
11
+ import { WorkflowInstanceList } from "@sanity/workflow-react";
12
+ import { WorkflowObserver } from "@sanity/workflow-react/observer";
13
+ import { WorkflowResource } from "@sanity/workflow-engine";
14
+ import { WorkflowSession } from "@sanity/workflow-react";
9
15
 
10
16
  /**
11
17
  * A {@link WorkflowObserver} that routes every stream to the store that can
@@ -13,7 +19,8 @@ import { WorkflowObserver } from "@sanity/workflow-react";
13
19
  *
14
20
  * - Anything in the **mounted workspace dataset** → `documentStore`
15
21
  * (`pair.editState` for docs — including Studio's local uncommitted edits,
16
- * the one stream only Studio has — and `listenQuery` for guards).
22
+ * the one stream only Studio has — and `listenQuery` for guards and
23
+ * instance lists).
17
24
  * - Anything else (foreign content refs; the engine's state dataset when it
18
25
  * isn't the workspace) → the App SDK store via {@link sdkInstanceDocStore} /
19
26
  * {@link sdkContentDocStore} / {@link sdkGuardStore} when
@@ -27,6 +34,8 @@ export declare function makeStudioObserver(
27
34
 
28
35
  export { MutationGuardDoc };
29
36
 
37
+ declare type StudioClient = ReturnType<typeof useClient>;
38
+
30
39
  export declare interface StudioObserverOptions {
31
40
  /** The workspace's project + dataset — the only resource Studio's
32
41
  * `documentStore` can address. */
@@ -42,6 +51,66 @@ export declare interface StudioObserverOptions {
42
51
  sdk?: SanityInstance;
43
52
  }
44
53
 
54
+ /**
55
+ * The default Studio {@link ResourceClientResolver}: any fully-addressed
56
+ * `dataset:` GDR resolves to the workspace client rebound to that
57
+ * project/dataset; other schemes (media-library, canvas) return `undefined` so
58
+ * the engine falls back to its own client. A custom resolver can delegate to
59
+ * this for the dataset arm and handle other schemes itself.
60
+ */
61
+ export declare function studioResourceClients(
62
+ client: StudioClient,
63
+ ): ResourceClientResolver;
64
+
65
+ /** Which in-flight instances reference this document (GDR URI) — live,
66
+ * observed from Sanity Studio. Same routing and `sdk` rules as
67
+ * {@link useWorkflowInstances}. */
68
+ export declare function useDocumentWorkflows({
69
+ engine,
70
+ document,
71
+ sdk,
72
+ }: {
73
+ engine: Engine;
74
+ document: GdrUri;
75
+ sdk?: SanityInstance;
76
+ }): WorkflowInstanceList;
77
+
78
+ /**
79
+ * Build the Studio-side engine once: the workspace client rebound to the
80
+ * engine's state dataset, plus {@link studioResourceClients} as the default
81
+ * per-resource routing — so subjects and refs across many content datasets
82
+ * resolve without call sites assembling clients. Pass `resourceClients` to
83
+ * override the routing (compose with {@link studioResourceClients} to keep
84
+ * dataset-GDR routing). Memoized on the config's content, so inline
85
+ * `workflowResource` literals are fine; a custom `resourceClients` function
86
+ * should still be stable. Must render inside Studio source context.
87
+ */
88
+ export declare function useWorkflowEngine({
89
+ workflowResource,
90
+ tag,
91
+ resourceClients,
92
+ apiVersion,
93
+ }: {
94
+ workflowResource: WorkflowResource;
95
+ tag: string;
96
+ resourceClients?: ResourceClientResolver;
97
+ apiVersion?: string;
98
+ }): Engine;
99
+
100
+ /** A live, filterable workflow-instance list observed from Sanity Studio —
101
+ * through `documentStore.listenQuery` when the engine dataset is the
102
+ * workspace's own, else through an App SDK instance (auto-bootstrapped, or
103
+ * supply `sdk`). Must render inside Studio source context. */
104
+ export declare function useWorkflowInstances({
105
+ engine,
106
+ filter,
107
+ sdk,
108
+ }: {
109
+ engine: Engine;
110
+ filter?: InstancesQueryFilter;
111
+ sdk?: SanityInstance;
112
+ }): WorkflowInstanceList;
113
+
45
114
  /**
46
115
  * Drive a workflow instance reactively from Sanity Studio. Watched docs in the
47
116
  * workspace's own dataset observe through `documentStore.pair.editState`
@@ -49,22 +118,27 @@ export declare interface StudioObserverOptions {
49
118
  * dataset (routinely a dedicated `workflows` dataset) and foreign content
50
119
  * refs — observes through the App SDK store. When the engine dataset differs
51
120
  * from the workspace, an SDK instance is bootstrapped automatically from the
52
- * Studio session (pass `opts.sdk` to supply your own — required for
121
+ * Studio session (pass `sdk` to supply your own — required for
53
122
  * cookie-authenticated studios, where no session token is readable). Returns
54
123
  * `{evaluation, ready, guards, tick, fireAction, editField}`; the consumer decides when
55
- * to advance. Must render inside Studio source context (so
56
- * `useDocumentStore`/`useSource` resolve).
124
+ * to advance. Must render inside Studio source context.
57
125
  */
58
- export declare function useWorkflowInstance(
59
- engine: Engine,
60
- instanceId: string,
61
- opts?: {
62
- access?: WorkflowAccessOverride;
63
- grantsFromPath?: string;
64
- sdk?: SanityInstance;
65
- },
66
- ): WorkflowInstanceController;
126
+ export declare function useWorkflowSession({
127
+ engine,
128
+ instanceId,
129
+ access,
130
+ grantsFromPath,
131
+ sdk,
132
+ }: {
133
+ engine: Engine;
134
+ instanceId: string;
135
+ access?: WorkflowAccessOverride;
136
+ grantsFromPath?: string;
137
+ sdk?: SanityInstance;
138
+ }): WorkflowSession;
139
+
140
+ export { WorkflowInstanceList };
67
141
 
68
- export { WorkflowInstanceController };
142
+ export { WorkflowSession };
69
143
 
70
144
  export {};
package/dist/index.js CHANGED
@@ -1,10 +1,15 @@
1
- import { instanceGuardQuery, contentReleaseName, WORKFLOW_INSTANCE_TYPE } from "@sanity/workflow-engine";
2
- import { sameDataset, combineGuardStores, combineDocStores, classifyRef, NO_GUARDS, datasetResourceId, useWorkflowSession } from "@sanity/workflow-react";
3
- import { sdkGuardStore, sdkContentDocStore, sdkInstanceDocStore } from "@sanity/workflow-sdk";
4
- import { createSanityInstance } from "@sanity/sdk";
1
+ import { instanceGuardQuery, contentReleaseName, WORKFLOW_INSTANCE_TYPE, ENGINE_API_VERSION, createEngine } from "@sanity/workflow-engine";
2
+ import { sameDataset, combineGuardStores, combineDocStores, classifyRef, useKeyed, datasetResourceId } from "@sanity/workflow-react/observer";
3
+ import { sdkInstancesStore, sdkGuardStore, sdkContentDocStore, sdkInstanceDocStore } from "@sanity/workflow-sdk/observer";
5
4
  import { useMemo, useRef, useEffect } from "react";
6
- import { useDocumentStore, useSource } from "sanity";
7
- function observableStore(observable, resolve, initial) {
5
+ import { useClient, useDocumentStore, useSource } from "sanity";
6
+ import { useWorkflowSession as useWorkflowSession$1, useDocumentWorkflows as useDocumentWorkflows$1, useWorkflowInstances as useWorkflowInstances$1 } from "@sanity/workflow-react";
7
+ import { createSanityInstance } from "@sanity/sdk";
8
+ function observableStore({
9
+ observable,
10
+ resolve,
11
+ initial
12
+ }) {
8
13
  let latest = initial;
9
14
  return {
10
15
  subscribe(onChange) {
@@ -17,74 +22,121 @@ function observableStore(observable, resolve, initial) {
17
22
  };
18
23
  }
19
24
  function makeStudioObserver(documentStore, options) {
20
- const { mounted, engineResource, sdk } = options, engineIsMounted = sameDataset(engineResource, mounted), editStateStore = (id, type, release) => observableStore(
21
- documentStore.pair.editState(id, type, release),
22
- resolveEditState,
23
- void 0
24
- ), requireSdk = (what) => {
25
+ const { mounted, engineResource, sdk } = options, engineIsMounted = sameDataset(engineResource, mounted), editStateStore = ({
26
+ id,
27
+ type,
28
+ release
29
+ }) => observableStore({
30
+ observable: documentStore.pair.editState(id, type, release),
31
+ resolve: resolveEditState,
32
+ initial: void 0
33
+ }), requireSdk = (what) => {
25
34
  if (sdk !== void 0) return sdk;
26
35
  throw new Error(
27
- `@sanity/workflow-studio: ${what} \u2014 Studio's document store is bound to ${mounted.projectId}.${mounted.dataset} and cannot observe it. Pass an App SDK instance (\`useWorkflowInstance(engine, id, {sdk})\`) to observe foreign resources.`
36
+ `@sanity/workflow-studio: ${what} \u2014 Studio's document store is bound to ${mounted.projectId}.${mounted.dataset} and cannot observe it. Pass an App SDK instance via the hook's \`sdk\` option (e.g. \`useWorkflowSession({engine, instanceId, sdk})\`) to observe foreign resources.`
28
37
  );
29
38
  };
30
39
  return {
31
- observeInstance: (instanceId) => engineIsMounted ? editStateStore(instanceId, WORKFLOW_INSTANCE_TYPE, void 0) : sdkInstanceDocStore(
32
- requireSdk(
40
+ observeInstance: (instanceId) => engineIsMounted ? editStateStore({ id: instanceId, type: WORKFLOW_INSTANCE_TYPE, release: void 0 }) : sdkInstanceDocStore({
41
+ sdk: requireSdk(
33
42
  `instance "${instanceId}" lives in the engine dataset ${engineResource.projectId}.${engineResource.dataset}`
34
43
  ),
35
44
  instanceId,
36
45
  engineResource
37
- ),
46
+ }),
38
47
  observeDocs: (documents, perspective) => combineDocStores(
39
48
  documents.map((ref) => classifyRef(ref, mounted) === "mounted-dataset" ? {
40
49
  key: ref.globalDocumentId,
41
- store: editStateStore(
42
- ref.documentId,
43
- ref.type,
44
- contentReleaseName({ ref, perspective })
45
- )
50
+ store: editStateStore({
51
+ id: ref.documentId,
52
+ type: ref.type,
53
+ release: contentReleaseName({ ref, perspective })
54
+ })
46
55
  } : {
47
56
  key: ref.globalDocumentId,
48
- store: sdkContentDocStore(
49
- requireSdk(`"${ref.globalDocumentId}" lives outside this workspace`),
57
+ store: sdkContentDocStore({
58
+ sdk: requireSdk(`"${ref.globalDocumentId}" lives outside this workspace`),
50
59
  ref,
51
60
  perspective
52
- )
61
+ })
53
62
  })
54
63
  ),
55
64
  observeGuards: (instanceId, resources) => combineGuardStores(
56
65
  resources.map((resource) => {
57
66
  if (sameDataset(resource, mounted)) {
58
67
  const { query, params } = instanceGuardQuery(instanceId);
59
- return observableStore(
60
- documentStore.listenQuery(query, params, {}),
61
- (docs) => docs,
62
- NO_GUARDS
63
- );
68
+ return observableStore({
69
+ observable: documentStore.listenQuery(query, params, {}),
70
+ resolve: (docs) => docs,
71
+ initial: void 0
72
+ });
64
73
  }
65
- return sdkGuardStore(
66
- requireSdk(
74
+ return sdkGuardStore({
75
+ sdk: requireSdk(
67
76
  `guards for "${instanceId}" live in ${resource.projectId}.${resource.dataset}`
68
77
  ),
69
78
  instanceId,
70
79
  resource
71
- );
80
+ });
72
81
  })
73
- )
82
+ ),
83
+ observeInstances: (query) => engineIsMounted ? observableStore({
84
+ observable: documentStore.listenQuery(query.query, query.params, {}),
85
+ resolve: (docs) => docs,
86
+ initial: void 0
87
+ }) : sdkInstancesStore({
88
+ sdk: requireSdk(
89
+ `the instance list lives in the engine dataset ${engineResource.projectId}.${engineResource.dataset}`
90
+ ),
91
+ query,
92
+ resource: engineResource
93
+ })
74
94
  };
75
95
  }
76
96
  function resolveEditState(editState) {
77
97
  if (editState.ready)
78
98
  return editState.version ?? editState.draft ?? editState.published;
79
99
  }
80
- function useWorkflowInstance(engine, instanceId, opts) {
81
- const documentStore = useDocumentStore(), { projectId, dataset } = useSource(), mounted = useMemo(() => ({ projectId, dataset }), [projectId, dataset]), engineResource = useMemo(() => datasetResourceId(engine.workflowResource), [engine]), sdk = useEmbeddedSdk(
82
- opts?.sdk,
100
+ function useWorkflowEngine({
101
+ workflowResource,
102
+ tag,
103
+ resourceClients,
104
+ apiVersion
105
+ }) {
106
+ const client = useClient({ apiVersion: apiVersion ?? ENGINE_API_VERSION }), resource = useKeyed([workflowResource.type, workflowResource.id], () => workflowResource);
107
+ return useMemo(() => {
108
+ const engineDataset = datasetResourceId(resource), engineClient = client.withConfig({
109
+ projectId: engineDataset.projectId,
110
+ dataset: engineDataset.dataset,
111
+ useCdn: !1
112
+ });
113
+ return createEngine({
114
+ client: engineClient,
115
+ workflowResource: resource,
116
+ tag,
117
+ resourceClients: resourceClients ?? studioResourceClients(client)
118
+ });
119
+ }, [client, resource, tag, resourceClients]);
120
+ }
121
+ function studioResourceClients(client) {
122
+ return (parsed) => parsed.scheme === "dataset" && parsed.projectId !== void 0 && parsed.dataset !== void 0 ? client.withConfig({ projectId: parsed.projectId, dataset: parsed.dataset, useCdn: !1 }) : void 0;
123
+ }
124
+ const SESSION_CLIENT_OPTIONS = { apiVersion: ENGINE_API_VERSION };
125
+ function useStudioObserver({
126
+ engine,
127
+ sdk: suppliedSdk
128
+ }) {
129
+ const documentStore = useDocumentStore(), source = useSource(), { projectId, dataset } = source, mounted = useMemo(() => ({ projectId, dataset }), [projectId, dataset]), engineResource = useMemo(() => datasetResourceId(engine.workflowResource), [engine]), sdk = useEmbeddedSdk({
130
+ supplied: suppliedSdk,
83
131
  // The engine dataset being foreign is the signal an SDK instance is
84
132
  // needed; foreign content refs without one still fail loud per ref.
85
- !sameDataset(engineResource, mounted),
86
- mounted
87
- ), observer = useMemo(
133
+ needed: !sameDataset(engineResource, mounted),
134
+ mounted,
135
+ // The session's token rides the source client's config (absent under
136
+ // cookie auth) — the public read; Studio's token storage is private.
137
+ sessionToken: source.getClient(SESSION_CLIENT_OPTIONS).config().token
138
+ });
139
+ return useMemo(
88
140
  () => makeStudioObserver(documentStore, {
89
141
  mounted,
90
142
  engineResource,
@@ -92,23 +144,27 @@ function useWorkflowInstance(engine, instanceId, opts) {
92
144
  }),
93
145
  [documentStore, mounted, engineResource, sdk]
94
146
  );
95
- return useWorkflowSession(engine, instanceId, observer, sessionOptions(opts));
96
- }
97
- function sessionOptions(opts = {}) {
98
- const { sdk: _, ...session } = opts;
99
- return session;
100
147
  }
101
- function useEmbeddedSdk(supplied, needed, mounted) {
148
+ function useEmbeddedSdk({
149
+ supplied,
150
+ needed,
151
+ mounted,
152
+ sessionToken
153
+ }) {
102
154
  const wanted = supplied === void 0 && needed, held = useRef(void 0), key = `${mounted.projectId}.${mounted.dataset}`;
103
- wanted && (held.current?.key !== key || held.current.instance.isDisposed()) && (held.current = { key, instance: bootstrapInstance(mounted) });
155
+ wanted && (held.current?.key !== key || held.current.instance.isDisposed()) && (held.current = { key, instance: bootstrapInstance(mounted, sessionToken) });
104
156
  const embedded = wanted ? held.current?.instance : void 0;
105
157
  return useDeferredDispose(embedded), supplied ?? embedded;
106
158
  }
107
- function bootstrapInstance(mounted) {
159
+ function bootstrapInstance(mounted, sessionToken) {
160
+ if (sessionToken === void 0)
161
+ throw new Error(
162
+ "@sanity/workflow-studio: the engine's state dataset is not this workspace's dataset, and no Studio session token is readable to bootstrap an App SDK instance (cookie-authenticated studio?). Pass one explicitly via the hook's `sdk` option, e.g. useWorkflowSession({engine, instanceId, sdk})."
163
+ );
108
164
  return createSanityInstance({
109
165
  projectId: mounted.projectId,
110
166
  dataset: mounted.dataset,
111
- auth: { token: studioSessionToken(mounted.projectId) }
167
+ auth: { token: sessionToken }
112
168
  });
113
169
  }
114
170
  function useDeferredDispose(instance) {
@@ -125,26 +181,38 @@ function useDeferredDispose(instance) {
125
181
  };
126
182
  }, [instance]);
127
183
  }
128
- function studioSessionToken(projectId) {
129
- const token = persistedToken(projectId);
130
- if (typeof token != "string")
131
- throw new Error(
132
- "@sanity/workflow-studio: the engine's state dataset is not this workspace's dataset, and no Studio session token is readable to bootstrap an App SDK instance (cookie-authenticated studio?). Pass one explicitly: useWorkflowInstance(engine, id, {sdk})."
133
- );
134
- return token;
184
+ function useWorkflowSession({
185
+ engine,
186
+ instanceId,
187
+ access,
188
+ grantsFromPath,
189
+ sdk
190
+ }) {
191
+ const observer = useStudioObserver({ engine, sdk });
192
+ return useWorkflowSession$1({ engine, instanceId, observer, opts: { access, grantsFromPath } });
193
+ }
194
+ function useWorkflowInstances({
195
+ engine,
196
+ filter,
197
+ sdk
198
+ }) {
199
+ const observer = useStudioObserver({ engine, sdk });
200
+ return useWorkflowInstances$1({ engine, observer, ...filter !== void 0 ? { filter } : {} });
135
201
  }
136
- function persistedToken(projectId) {
137
- const raw = window.localStorage.getItem(`__studio_auth_token_${projectId}`);
138
- if (raw !== null)
139
- try {
140
- const parsed = JSON.parse(raw);
141
- return typeof parsed != "object" || parsed === null ? void 0 : parsed.token;
142
- } catch {
143
- return;
144
- }
202
+ function useDocumentWorkflows({
203
+ engine,
204
+ document,
205
+ sdk
206
+ }) {
207
+ const observer = useStudioObserver({ engine, sdk });
208
+ return useDocumentWorkflows$1({ engine, observer, document });
145
209
  }
146
210
  export {
147
211
  makeStudioObserver,
148
- useWorkflowInstance
212
+ studioResourceClients,
213
+ useDocumentWorkflows,
214
+ useWorkflowEngine,
215
+ useWorkflowInstances,
216
+ useWorkflowSession
149
217
  };
150
218
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/studio-observer.ts","../src/use-workflow-instance.tsx"],"sourcesContent":["import type {SanityInstance} from '@sanity/sdk'\nimport {\n contentReleaseName,\n instanceGuardQuery,\n WORKFLOW_INSTANCE_TYPE,\n type MutationGuardDoc,\n} from '@sanity/workflow-engine'\nimport {\n classifyRef,\n combineDocStores,\n combineGuardStores,\n NO_GUARDS,\n sameDataset,\n type DatasetResourceId,\n type DocStore,\n type ObservedDoc,\n type WorkflowObserver,\n} from '@sanity/workflow-react'\nimport {sdkContentDocStore, sdkGuardStore, sdkInstanceDocStore} from '@sanity/workflow-sdk'\nimport type {DocumentStore, EditStateFor} from 'sanity'\n\n/** The slice of an RxJS observable this module needs — kept structural so the\n * file doesn't take an `rxjs` import just to name the type. */\ninterface ObservableLike<E> {\n subscribe: (next: (value: E) => void) => {unsubscribe: () => void}\n}\n\n/** Bridge a replayed RxJS observable into a {@link DocStore}: cache the latest\n * resolved value and notify `useSyncExternalStore` on each emission. Both the\n * per-doc `editState` stream and the guard `listenQuery` stream share it. */\nfunction observableStore<E, T>(\n observable: ObservableLike<E>,\n resolve: (value: E) => T,\n initial: T,\n): DocStore<T> {\n let latest = initial\n return {\n subscribe(onChange) {\n const subscription = observable.subscribe((value) => {\n latest = resolve(value)\n onChange()\n })\n return () => subscription.unsubscribe()\n },\n getSnapshot: () => latest,\n }\n}\n\nexport interface StudioObserverOptions {\n /** The workspace's project + dataset — the only resource Studio's\n * `documentStore` can address. */\n mounted: DatasetResourceId\n /** The engine's state dataset (`datasetResourceId(engine.workflowResource)`)\n * — where the instance doc and its verdict-relevant guards live. Routinely\n * a dedicated `workflows` dataset distinct from the workspace. */\n engineResource: DatasetResourceId\n /** Observe resources OUTSIDE the mounted dataset (foreign content refs, and\n * the engine dataset when it differs from the workspace) through the App\n * SDK store. Omit ⇒ any foreign observation throws an actionable\n * config-mismatch error instead of being silently misread. */\n sdk?: SanityInstance\n}\n\n/**\n * A {@link WorkflowObserver} that routes every stream to the store that can\n * actually address it:\n *\n * - Anything in the **mounted workspace dataset** → `documentStore`\n * (`pair.editState` for docs — including Studio's local uncommitted edits,\n * the one stream only Studio has — and `listenQuery` for guards).\n * - Anything else (foreign content refs; the engine's state dataset when it\n * isn't the workspace) → the App SDK store via {@link sdkInstanceDocStore} /\n * {@link sdkContentDocStore} / {@link sdkGuardStore} when\n * {@link StudioObserverOptions.sdk} is supplied; otherwise an actionable\n * error. Never a silent wrong-dataset read.\n */\nexport function makeStudioObserver(\n documentStore: DocumentStore,\n options: StudioObserverOptions,\n): WorkflowObserver {\n const {mounted, engineResource, sdk} = options\n const engineIsMounted = sameDataset(engineResource, mounted)\n\n const editStateStore = (\n id: string,\n type: string,\n release: string | undefined,\n ): DocStore<ObservedDoc> =>\n observableStore<EditStateFor, ObservedDoc>(\n documentStore.pair.editState(id, type, release),\n resolveEditState,\n undefined,\n )\n\n const requireSdk = (what: string): SanityInstance => {\n if (sdk !== undefined) return sdk\n throw new Error(\n `@sanity/workflow-studio: ${what} — Studio's document store is bound to ${mounted.projectId}.${mounted.dataset} and cannot observe it. Pass an App SDK instance (\\`useWorkflowInstance(engine, id, {sdk})\\`) to observe foreign resources.`,\n )\n }\n\n return {\n observeInstance: (instanceId) => {\n if (engineIsMounted) return editStateStore(instanceId, WORKFLOW_INSTANCE_TYPE, undefined)\n return sdkInstanceDocStore(\n requireSdk(\n `instance \"${instanceId}\" lives in the engine dataset ${engineResource.projectId}.${engineResource.dataset}`,\n ),\n instanceId,\n engineResource,\n )\n },\n observeDocs: (documents, perspective) =>\n combineDocStores(\n documents.map((ref) => {\n if (classifyRef(ref, mounted) === 'mounted-dataset') {\n return {\n key: ref.globalDocumentId,\n store: editStateStore(\n ref.documentId,\n ref.type,\n contentReleaseName({ref, perspective}),\n ),\n }\n }\n return {\n key: ref.globalDocumentId,\n store: sdkContentDocStore(\n requireSdk(`\"${ref.globalDocumentId}\" lives outside this workspace`),\n ref,\n perspective,\n ),\n }\n }),\n ),\n observeGuards: (instanceId, resources) =>\n combineGuardStores(\n resources.map((resource) => {\n if (sameDataset(resource, mounted)) {\n const {query, params} = instanceGuardQuery(instanceId)\n return observableStore<MutationGuardDoc[], readonly MutationGuardDoc[]>(\n documentStore.listenQuery(query, params, {}),\n (docs) => docs,\n NO_GUARDS,\n )\n }\n return sdkGuardStore(\n requireSdk(\n `guards for \"${instanceId}\" live in ${resource.projectId}.${resource.dataset}`,\n ),\n instanceId,\n resource,\n )\n }),\n ),\n }\n}\n\n/**\n * Resolve an `editState` emission to a single observed value: `undefined` while\n * not ready (gates the session feed), otherwise the version / draft / published\n * doc in precedence order (`null` when the doc doesn't exist).\n */\nfunction resolveEditState(editState: EditStateFor): ObservedDoc {\n if (!editState.ready) return undefined\n return (editState.version ?? editState.draft ?? editState.published) as ObservedDoc\n}\n","import {createSanityInstance, type SanityInstance} from '@sanity/sdk'\nimport type {Engine, WorkflowAccessOverride} from '@sanity/workflow-engine'\nimport {\n datasetResourceId,\n sameDataset,\n useWorkflowSession,\n type DatasetResourceId,\n type WorkflowInstanceController,\n} from '@sanity/workflow-react'\nimport {useEffect, useMemo, useRef} from 'react'\nimport {useDocumentStore, useSource} from 'sanity'\n\nimport {makeStudioObserver} from './studio-observer.ts'\n\n/**\n * Drive a workflow instance reactively from Sanity Studio. Watched docs in the\n * workspace's own dataset observe through `documentStore.pair.editState`\n * (including local uncommitted edits); everything else — the engine's state\n * dataset (routinely a dedicated `workflows` dataset) and foreign content\n * refs — observes through the App SDK store. When the engine dataset differs\n * from the workspace, an SDK instance is bootstrapped automatically from the\n * Studio session (pass `opts.sdk` to supply your own — required for\n * cookie-authenticated studios, where no session token is readable). Returns\n * `{evaluation, ready, guards, tick, fireAction, editField}`; the consumer decides when\n * to advance. Must render inside Studio source context (so\n * `useDocumentStore`/`useSource` resolve).\n */\nexport function useWorkflowInstance(\n engine: Engine,\n instanceId: string,\n opts?: {access?: WorkflowAccessOverride; grantsFromPath?: string; sdk?: SanityInstance},\n): WorkflowInstanceController {\n const documentStore = useDocumentStore()\n const {projectId, dataset} = useSource()\n const mounted = useMemo(() => ({projectId, dataset}), [projectId, dataset])\n const engineResource = useMemo(() => datasetResourceId(engine.workflowResource), [engine])\n const sdk = useEmbeddedSdk(\n opts?.sdk,\n // The engine dataset being foreign is the signal an SDK instance is\n // needed; foreign content refs without one still fail loud per ref.\n !sameDataset(engineResource, mounted),\n mounted,\n )\n const observer = useMemo(\n () =>\n makeStudioObserver(documentStore, {\n mounted,\n engineResource,\n ...(sdk !== undefined ? {sdk} : {}),\n }),\n [documentStore, mounted, engineResource, sdk],\n )\n return useWorkflowSession(engine, instanceId, observer, sessionOptions(opts))\n}\n\n/** Only the session options cross into the engine — `sdk` is a store handle\n * for the observer, never an `engine.instance(...)` argument. */\nfunction sessionOptions(\n opts: {access?: WorkflowAccessOverride; grantsFromPath?: string; sdk?: SanityInstance} = {},\n): {access?: WorkflowAccessOverride; grantsFromPath?: string} {\n const {sdk: _, ...session} = opts\n return session\n}\n\n/**\n * The supplied SDK instance, or one bootstrapped from the Studio session's\n * token when foreign observation is needed. Lifecycle mirrors the SDK's own\n * `ResourceProvider`: created in render (the observer needs it synchronously),\n * recreated when a previous unmount disposed it, and released via\n * {@link useDeferredDispose} — so StrictMode's unmount/remount probe neither\n * leaks an instance nor hands the remount a dead one. Exported for tests only.\n */\nexport function useEmbeddedSdk(\n supplied: SanityInstance | undefined,\n needed: boolean,\n mounted: DatasetResourceId,\n): SanityInstance | undefined {\n const wanted = supplied === undefined && needed\n const held = useRef<{key: string; instance: SanityInstance} | undefined>(undefined)\n const key = `${mounted.projectId}.${mounted.dataset}`\n if (wanted && (held.current?.key !== key || held.current.instance.isDisposed())) {\n held.current = {key, instance: bootstrapInstance(mounted)}\n }\n const embedded = wanted ? held.current?.instance : undefined\n useDeferredDispose(embedded)\n return supplied ?? embedded\n}\n\n/** An SDK instance over the workspace's own project, authed with the Studio\n * session's token — the engine dataset is reached per-resource from here. */\nfunction bootstrapInstance(mounted: DatasetResourceId): SanityInstance {\n return createSanityInstance({\n projectId: mounted.projectId,\n dataset: mounted.dataset,\n auth: {token: studioSessionToken(mounted.projectId)},\n })\n}\n\n/**\n * Dispose `instance` after unmount via a zero-delay timer that an immediate\n * remount of the same instance cancels — the SDK `ResourceProvider`'s own\n * idiom for surviving StrictMode's unmount/remount probe.\n */\nfunction useDeferredDispose(instance: SanityInstance | undefined): void {\n const disposal = useRef<\n {instance: SanityInstance; timeoutId: ReturnType<typeof setTimeout>} | undefined\n >(undefined)\n useEffect(() => {\n if (instance === undefined) return undefined\n if (disposal.current?.instance === instance) {\n clearTimeout(disposal.current.timeoutId)\n disposal.current = undefined\n }\n return () => {\n disposal.current = {\n instance,\n timeoutId: setTimeout(() => {\n if (!instance.isDisposed()) instance.dispose()\n }, 0),\n }\n }\n }, [instance])\n}\n\n/** The Studio session's token, as persisted by Studio's own auth store.\n * Exported for tests only. */\nexport function studioSessionToken(projectId: string): string {\n const token = persistedToken(projectId)\n if (typeof token !== 'string') {\n throw new Error(\n `@sanity/workflow-studio: the engine's state dataset is not this workspace's dataset, and no Studio session token is readable to bootstrap an App SDK instance (cookie-authenticated studio?). Pass one explicitly: useWorkflowInstance(engine, id, {sdk}).`,\n )\n }\n return token\n}\n\n/** Best-effort read of Studio's persisted `{token}` localStorage entry — any\n * unreadable shape (absent key, malformed JSON, non-object value) resolves to\n * `undefined` so every failure mode funnels into the one actionable error. */\nfunction persistedToken(projectId: string): unknown {\n const raw = window.localStorage.getItem(`__studio_auth_token_${projectId}`)\n if (raw === null) return undefined\n try {\n const parsed: unknown = JSON.parse(raw)\n if (typeof parsed !== 'object' || parsed === null) return undefined\n return (parsed as {token?: unknown}).token\n } catch {\n return undefined\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/studio-observer.ts","../src/use-workflow-engine.ts","../src/use-studio-observer.ts","../src/use-workflow-session.tsx","../src/use-workflow-instances.tsx"],"sourcesContent":["import type {SanityInstance} from '@sanity/sdk'\nimport {\n contentReleaseName,\n instanceGuardQuery,\n WORKFLOW_INSTANCE_TYPE,\n type MutationGuardDoc,\n type WorkflowInstance,\n} from '@sanity/workflow-engine'\nimport {\n classifyRef,\n combineDocStores,\n combineGuardStores,\n sameDataset,\n type DatasetResourceId,\n type DocStore,\n type ObservedDoc,\n type WorkflowObserver,\n} from '@sanity/workflow-react/observer'\nimport {\n sdkContentDocStore,\n sdkGuardStore,\n sdkInstanceDocStore,\n sdkInstancesStore,\n} from '@sanity/workflow-sdk/observer'\nimport type {DocumentStore, EditStateFor} from 'sanity'\n\n/** The slice of an RxJS observable this module needs — kept structural so the\n * file doesn't take an `rxjs` import just to name the type. */\ninterface ObservableLike<E> {\n subscribe: (next: (value: E) => void) => {unsubscribe: () => void}\n}\n\n/** Bridge a replayed RxJS observable into a {@link DocStore}: cache the latest\n * resolved value and notify `useSyncExternalStore` on each emission. Both the\n * per-doc `editState` stream and the guard `listenQuery` stream share it. */\nfunction observableStore<E, T>({\n observable,\n resolve,\n initial,\n}: {\n observable: ObservableLike<E>\n resolve: (value: E) => T\n initial: T\n}): DocStore<T> {\n let latest = initial\n return {\n subscribe(onChange) {\n const subscription = observable.subscribe((value) => {\n latest = resolve(value)\n onChange()\n })\n return () => subscription.unsubscribe()\n },\n getSnapshot: () => latest,\n }\n}\n\nexport interface StudioObserverOptions {\n /** The workspace's project + dataset — the only resource Studio's\n * `documentStore` can address. */\n mounted: DatasetResourceId\n /** The engine's state dataset (`datasetResourceId(engine.workflowResource)`)\n * — where the instance doc and its verdict-relevant guards live. Routinely\n * a dedicated `workflows` dataset distinct from the workspace. */\n engineResource: DatasetResourceId\n /** Observe resources OUTSIDE the mounted dataset (foreign content refs, and\n * the engine dataset when it differs from the workspace) through the App\n * SDK store. Omit ⇒ any foreign observation throws an actionable\n * config-mismatch error instead of being silently misread. */\n sdk?: SanityInstance\n}\n\n/**\n * A {@link WorkflowObserver} that routes every stream to the store that can\n * actually address it:\n *\n * - Anything in the **mounted workspace dataset** → `documentStore`\n * (`pair.editState` for docs — including Studio's local uncommitted edits,\n * the one stream only Studio has — and `listenQuery` for guards and\n * instance lists).\n * - Anything else (foreign content refs; the engine's state dataset when it\n * isn't the workspace) → the App SDK store via {@link sdkInstanceDocStore} /\n * {@link sdkContentDocStore} / {@link sdkGuardStore} when\n * {@link StudioObserverOptions.sdk} is supplied; otherwise an actionable\n * error. Never a silent wrong-dataset read.\n */\nexport function makeStudioObserver(\n documentStore: DocumentStore,\n options: StudioObserverOptions,\n): WorkflowObserver {\n const {mounted, engineResource, sdk} = options\n const engineIsMounted = sameDataset(engineResource, mounted)\n\n const editStateStore = ({\n id,\n type,\n release,\n }: {\n id: string\n type: string\n release: string | undefined\n }): DocStore<ObservedDoc> =>\n observableStore<EditStateFor, ObservedDoc>({\n observable: documentStore.pair.editState(id, type, release),\n resolve: resolveEditState,\n initial: undefined,\n })\n\n const requireSdk = (what: string): SanityInstance => {\n if (sdk !== undefined) return sdk\n throw new Error(\n `@sanity/workflow-studio: ${what} — Studio's document store is bound to ${mounted.projectId}.${mounted.dataset} and cannot observe it. Pass an App SDK instance via the hook's \\`sdk\\` option (e.g. \\`useWorkflowSession({engine, instanceId, sdk})\\`) to observe foreign resources.`,\n )\n }\n\n return {\n observeInstance: (instanceId) => {\n if (engineIsMounted)\n return editStateStore({id: instanceId, type: WORKFLOW_INSTANCE_TYPE, release: undefined})\n return sdkInstanceDocStore({\n sdk: requireSdk(\n `instance \"${instanceId}\" lives in the engine dataset ${engineResource.projectId}.${engineResource.dataset}`,\n ),\n instanceId,\n engineResource,\n })\n },\n observeDocs: (documents, perspective) =>\n combineDocStores(\n documents.map((ref) => {\n if (classifyRef(ref, mounted) === 'mounted-dataset') {\n return {\n key: ref.globalDocumentId,\n store: editStateStore({\n id: ref.documentId,\n type: ref.type,\n release: contentReleaseName({ref, perspective}),\n }),\n }\n }\n return {\n key: ref.globalDocumentId,\n store: sdkContentDocStore({\n sdk: requireSdk(`\"${ref.globalDocumentId}\" lives outside this workspace`),\n ref,\n perspective,\n }),\n }\n }),\n ),\n observeGuards: (instanceId, resources) =>\n combineGuardStores(\n resources.map((resource) => {\n if (sameDataset(resource, mounted)) {\n const {query, params} = instanceGuardQuery(instanceId)\n // `undefined` until the first `listenQuery` emission — the loading\n // signal the guard merge and the hook's `ready` gate distinguish\n // from a resolved-empty guard list.\n return observableStore<MutationGuardDoc[], readonly MutationGuardDoc[] | undefined>({\n observable: documentStore.listenQuery(query, params, {}),\n resolve: (docs) => docs,\n initial: undefined,\n })\n }\n return sdkGuardStore({\n sdk: requireSdk(\n `guards for \"${instanceId}\" live in ${resource.projectId}.${resource.dataset}`,\n ),\n instanceId,\n resource,\n })\n }),\n ),\n observeInstances: (query) => {\n if (engineIsMounted) {\n // `initial: undefined` (not a shared empty) — a pending first read and\n // a confirmed empty list must stay distinguishable for `loading`.\n return observableStore<WorkflowInstance[], readonly WorkflowInstance[] | undefined>({\n observable: documentStore.listenQuery(query.query, query.params, {}),\n resolve: (docs) => docs,\n initial: undefined,\n })\n }\n return sdkInstancesStore({\n sdk: requireSdk(\n `the instance list lives in the engine dataset ${engineResource.projectId}.${engineResource.dataset}`,\n ),\n query,\n resource: engineResource,\n })\n },\n }\n}\n\n/**\n * Resolve an `editState` emission to a single observed value: `undefined` while\n * not ready (gates the session feed), otherwise the version / draft / published\n * doc in precedence order (`null` when the doc doesn't exist).\n */\nfunction resolveEditState(editState: EditStateFor): ObservedDoc {\n if (!editState.ready) return undefined\n return (editState.version ?? editState.draft ?? editState.published) as ObservedDoc\n}\n","import {\n createEngine,\n ENGINE_API_VERSION,\n type Engine,\n type ResourceClientResolver,\n type WorkflowClient,\n type WorkflowResource,\n} from '@sanity/workflow-engine'\nimport {datasetResourceId, useKeyed} from '@sanity/workflow-react/observer'\nimport {useMemo} from 'react'\nimport {useClient} from 'sanity'\n\n/**\n * Build the Studio-side engine once: the workspace client rebound to the\n * engine's state dataset, plus {@link studioResourceClients} as the default\n * per-resource routing — so subjects and refs across many content datasets\n * resolve without call sites assembling clients. Pass `resourceClients` to\n * override the routing (compose with {@link studioResourceClients} to keep\n * dataset-GDR routing). Memoized on the config's content, so inline\n * `workflowResource` literals are fine; a custom `resourceClients` function\n * should still be stable. Must render inside Studio source context.\n */\nexport function useWorkflowEngine({\n workflowResource,\n tag,\n resourceClients,\n apiVersion,\n}: {\n workflowResource: WorkflowResource\n tag: string\n resourceClients?: ResourceClientResolver\n apiVersion?: string\n}): Engine {\n const client = useClient({apiVersion: apiVersion ?? ENGINE_API_VERSION})\n const resource = useKeyed([workflowResource.type, workflowResource.id], () => workflowResource)\n return useMemo(() => {\n const engineDataset = datasetResourceId(resource)\n const engineClient = asWorkflowClient(\n client.withConfig({\n projectId: engineDataset.projectId,\n dataset: engineDataset.dataset,\n useCdn: false,\n }),\n )\n return createEngine({\n client: engineClient,\n workflowResource: resource,\n tag,\n resourceClients: resourceClients ?? studioResourceClients(client),\n })\n }, [client, resource, tag, resourceClients])\n}\n\n/**\n * The default Studio {@link ResourceClientResolver}: any fully-addressed\n * `dataset:` GDR resolves to the workspace client rebound to that\n * project/dataset; other schemes (media-library, canvas) return `undefined` so\n * the engine falls back to its own client. A custom resolver can delegate to\n * this for the dataset arm and handle other schemes itself.\n */\nexport function studioResourceClients(client: StudioClient): ResourceClientResolver {\n return (parsed) =>\n parsed.scheme === 'dataset' && parsed.projectId !== undefined && parsed.dataset !== undefined\n ? asWorkflowClient(\n client.withConfig({projectId: parsed.projectId, dataset: parsed.dataset, useCdn: false}),\n )\n : undefined\n}\n\ntype StudioClient = ReturnType<typeof useClient>\n\n/** The Studio client is structurally assignable to the engine's narrower\n * client — a typed identity, so drift breaks loudly at compile time. */\nfunction asWorkflowClient(client: StudioClient): WorkflowClient {\n return client\n}\n","import {createSanityInstance, type SanityInstance} from '@sanity/sdk'\nimport {ENGINE_API_VERSION, type Engine} from '@sanity/workflow-engine'\nimport {\n datasetResourceId,\n sameDataset,\n type DatasetResourceId,\n type WorkflowObserver,\n} from '@sanity/workflow-react/observer'\nimport {useEffect, useMemo, useRef} from 'react'\nimport {useDocumentStore, useSource} from 'sanity'\n\nimport {makeStudioObserver} from './studio-observer.ts'\n\n/** The client is only read for its auth config — no request is ever issued\n * with it — but `getClient` requires an apiVersion to hand one out. */\nconst SESSION_CLIENT_OPTIONS = {apiVersion: ENGINE_API_VERSION}\n\n/**\n * The Studio observer for this engine — the shared wiring under every\n * reactive hook in this package. Workspace-dataset streams ride\n * `documentStore`; anything foreign (routinely the engine's dedicated\n * `workflows` dataset) rides an App SDK instance, bootstrapped from the\n * Studio session when the caller doesn't supply one. Must run inside Studio\n * source context (so `useDocumentStore`/`useSource` resolve).\n */\nexport function useStudioObserver({\n engine,\n sdk: suppliedSdk,\n}: {\n engine: Engine\n sdk?: SanityInstance | undefined\n}): WorkflowObserver {\n const documentStore = useDocumentStore()\n const source = useSource()\n const {projectId, dataset} = source\n const mounted = useMemo(() => ({projectId, dataset}), [projectId, dataset])\n const engineResource = useMemo(() => datasetResourceId(engine.workflowResource), [engine])\n const sdk = useEmbeddedSdk({\n supplied: suppliedSdk,\n // The engine dataset being foreign is the signal an SDK instance is\n // needed; foreign content refs without one still fail loud per ref.\n needed: !sameDataset(engineResource, mounted),\n mounted,\n // The session's token rides the source client's config (absent under\n // cookie auth) — the public read; Studio's token storage is private.\n sessionToken: source.getClient(SESSION_CLIENT_OPTIONS).config().token,\n })\n return useMemo(\n () =>\n makeStudioObserver(documentStore, {\n mounted,\n engineResource,\n ...(sdk !== undefined ? {sdk} : {}),\n }),\n [documentStore, mounted, engineResource, sdk],\n )\n}\n\n/**\n * The supplied SDK instance, or one bootstrapped from the Studio session's\n * token when foreign observation is needed. Lifecycle mirrors the SDK's own\n * `ResourceProvider`: created in render (the observer needs it synchronously),\n * recreated when a previous unmount disposed it, and released via\n * {@link useDeferredDispose} — so StrictMode's unmount/remount probe neither\n * leaks an instance nor hands the remount a dead one. `sessionToken` is\n * captured at bootstrap, deliberately not part of the recreate key: a rotated\n * token surfaces as auth failures until remount, and keying on it would churn\n * the instance on every rotation. Exported for tests only.\n */\nexport function useEmbeddedSdk({\n supplied,\n needed,\n mounted,\n sessionToken,\n}: {\n supplied: SanityInstance | undefined\n needed: boolean\n mounted: DatasetResourceId\n sessionToken: string | undefined\n}): SanityInstance | undefined {\n const wanted = supplied === undefined && needed\n const held = useRef<{key: string; instance: SanityInstance} | undefined>(undefined)\n const key = `${mounted.projectId}.${mounted.dataset}`\n if (wanted && (held.current?.key !== key || held.current.instance.isDisposed())) {\n held.current = {key, instance: bootstrapInstance(mounted, sessionToken)}\n }\n const embedded = wanted ? held.current?.instance : undefined\n useDeferredDispose(embedded)\n return supplied ?? embedded\n}\n\n/** An SDK instance over the workspace's own project, authed with the Studio\n * session's token — the engine dataset is reached per-resource from here. */\nfunction bootstrapInstance(\n mounted: DatasetResourceId,\n sessionToken: string | undefined,\n): SanityInstance {\n if (sessionToken === undefined) {\n throw new Error(\n `@sanity/workflow-studio: the engine's state dataset is not this workspace's dataset, and no Studio session token is readable to bootstrap an App SDK instance (cookie-authenticated studio?). Pass one explicitly via the hook's \\`sdk\\` option, e.g. useWorkflowSession({engine, instanceId, sdk}).`,\n )\n }\n return createSanityInstance({\n projectId: mounted.projectId,\n dataset: mounted.dataset,\n auth: {token: sessionToken},\n })\n}\n\n/**\n * Dispose `instance` after unmount via a zero-delay timer that an immediate\n * remount of the same instance cancels — the SDK `ResourceProvider`'s own\n * idiom for surviving StrictMode's unmount/remount probe.\n */\nfunction useDeferredDispose(instance: SanityInstance | undefined): void {\n const disposal = useRef<\n {instance: SanityInstance; timeoutId: ReturnType<typeof setTimeout>} | undefined\n >(undefined)\n useEffect(() => {\n if (instance === undefined) return undefined\n if (disposal.current?.instance === instance) {\n clearTimeout(disposal.current.timeoutId)\n disposal.current = undefined\n }\n return () => {\n disposal.current = {\n instance,\n timeoutId: setTimeout(() => {\n if (!instance.isDisposed()) instance.dispose()\n }, 0),\n }\n }\n }, [instance])\n}\n","import type {SanityInstance} from '@sanity/sdk'\nimport type {Engine, WorkflowAccessOverride} from '@sanity/workflow-engine'\nimport {\n useWorkflowSession as useWorkflowSessionCore,\n type WorkflowSession,\n} from '@sanity/workflow-react'\n\nimport {useStudioObserver} from './use-studio-observer.ts'\n\n/**\n * Drive a workflow instance reactively from Sanity Studio. Watched docs in the\n * workspace's own dataset observe through `documentStore.pair.editState`\n * (including local uncommitted edits); everything else — the engine's state\n * dataset (routinely a dedicated `workflows` dataset) and foreign content\n * refs — observes through the App SDK store. When the engine dataset differs\n * from the workspace, an SDK instance is bootstrapped automatically from the\n * Studio session (pass `sdk` to supply your own — required for\n * cookie-authenticated studios, where no session token is readable). Returns\n * `{evaluation, ready, guards, tick, fireAction, editField}`; the consumer decides when\n * to advance. Must render inside Studio source context.\n */\nexport function useWorkflowSession({\n engine,\n instanceId,\n access,\n grantsFromPath,\n sdk,\n}: {\n engine: Engine\n instanceId: string\n access?: WorkflowAccessOverride\n grantsFromPath?: string\n sdk?: SanityInstance\n}): WorkflowSession {\n const observer = useStudioObserver({engine, sdk})\n // `sdk` is a store handle for the observer, never an `engine.session(...)`\n // argument — only the session options cross into the engine.\n return useWorkflowSessionCore({engine, instanceId, observer, opts: {access, grantsFromPath}})\n}\n","import type {SanityInstance} from '@sanity/sdk'\nimport type {Engine, GdrUri, InstancesQueryFilter} from '@sanity/workflow-engine'\nimport {\n useDocumentWorkflows as useDocumentWorkflowsCore,\n useWorkflowInstances as useWorkflowInstancesCore,\n type WorkflowInstanceList,\n} from '@sanity/workflow-react'\n\nimport {useStudioObserver} from './use-studio-observer.ts'\n\n/** A live, filterable workflow-instance list observed from Sanity Studio —\n * through `documentStore.listenQuery` when the engine dataset is the\n * workspace's own, else through an App SDK instance (auto-bootstrapped, or\n * supply `sdk`). Must render inside Studio source context. */\nexport function useWorkflowInstances({\n engine,\n filter,\n sdk,\n}: {\n engine: Engine\n filter?: InstancesQueryFilter\n sdk?: SanityInstance\n}): WorkflowInstanceList {\n const observer = useStudioObserver({engine, sdk})\n return useWorkflowInstancesCore({engine, observer, ...(filter !== undefined ? {filter} : {})})\n}\n\n/** Which in-flight instances reference this document (GDR URI) — live,\n * observed from Sanity Studio. Same routing and `sdk` rules as\n * {@link useWorkflowInstances}. */\nexport function useDocumentWorkflows({\n engine,\n document,\n sdk,\n}: {\n engine: Engine\n document: GdrUri\n sdk?: SanityInstance\n}): WorkflowInstanceList {\n const observer = useStudioObserver({engine, sdk})\n return useDocumentWorkflowsCore({engine, observer, document})\n}\n"],"names":["useWorkflowSessionCore","useWorkflowInstancesCore","useDocumentWorkflowsCore"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/workflow-studio",
3
- "version": "0.4.2",
3
+ "version": "0.6.0",
4
4
  "description": "React adapter that drives the @sanity/workflow-engine reactive session from the Sanity Studio document store.",
5
5
  "keywords": [
6
6
  "sanity",
@@ -40,14 +40,13 @@
40
40
  "access": "restricted"
41
41
  },
42
42
  "dependencies": {
43
- "@sanity/workflow-react": "0.7.0",
44
- "@sanity/workflow-sdk": "0.4.2",
45
- "@sanity/workflow-engine": "0.12.0"
43
+ "@sanity/workflow-engine": "0.14.0",
44
+ "@sanity/workflow-react": "0.9.0",
45
+ "@sanity/workflow-sdk": "0.6.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@sanity/pkg-utils": "^10.5.2",
49
49
  "@sanity/sdk": "^2.12.0",
50
- "@sanity/sdk-react": "^2.12.0",
51
50
  "@testing-library/react": "^16.3.2",
52
51
  "@types/react": "^19.2.17",
53
52
  "jsdom": "^29.1.1",
@@ -58,7 +57,6 @@
58
57
  },
59
58
  "peerDependencies": {
60
59
  "@sanity/sdk": "^2.12.0",
61
- "@sanity/sdk-react": "^2.12.0",
62
60
  "react": "^19.2.7",
63
61
  "sanity": "^5.30.0"
64
62
  },