@sanity/workflow-studio 0.7.0 → 0.8.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/CHANGELOG.md +49 -0
- package/README.md +19 -10
- package/dist/index.cjs +20 -13
- package/dist/index.js +22 -15
- package/package.json +6 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
# @sanity/workflow-studio
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5a1a9fe: The observers validate every engine-owned stream (instance doc, guards, instance lists) at the store boundary — the engine's read discipline (data-model gate + shape parse) instead of a cast, with a failure surfaced as the session's explicit `invalid` state. Content-doc streams are foreign and pass through unvalidated, as before.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [f9389e5]
|
|
12
|
+
- Updated dependencies [d0c62ea]
|
|
13
|
+
- Updated dependencies [c3eed2e]
|
|
14
|
+
- Updated dependencies [df4bd80]
|
|
15
|
+
- Updated dependencies [092a0d4]
|
|
16
|
+
- Updated dependencies [5a1a9fe]
|
|
17
|
+
- Updated dependencies [5a1a9fe]
|
|
18
|
+
- Updated dependencies [5a1a9fe]
|
|
19
|
+
- Updated dependencies [e3a7ba2]
|
|
20
|
+
- Updated dependencies [30fed9e]
|
|
21
|
+
- Updated dependencies [c3eed2e]
|
|
22
|
+
- Updated dependencies [1321ba5]
|
|
23
|
+
- Updated dependencies [e683875]
|
|
24
|
+
- Updated dependencies [a8ace4d]
|
|
25
|
+
- @sanity/workflow-engine@0.17.0
|
|
26
|
+
- @sanity/workflow-sdk@0.9.0
|
|
27
|
+
- @sanity/workflow-react@0.12.0
|
|
28
|
+
|
|
29
|
+
## 0.7.1
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- 79c54db: Reactive session reads now match the engine's own reads.
|
|
34
|
+
- The session feeder projects every observed doc onto its watch ref's identity the way the lake's perspective reads do: the published-form id becomes `_id` (Studio/SDK stores resolve drafts and release versions under `drafts.*`/`versions.*` ids, which keyed the session's overlay and snapshot where no transition `when` condition or `$fields.<docref>` deref ever looks — an editor's uncommitted draft could not satisfy a condition the engine's `tick()` would read as satisfied), and a draft/version's stored id rides along as `_originalId`, so conditions like `_originalId in path("versions.**")` read what the engine's own hydration returns. The projection is an engine export (`projectToWatchRef`), one encoding beside `contentDraftFallback`/`contentDocQuery`, and it is strict: only the exact representations a store may legitimately resolve under the instance's perspective are accepted — anything else (another doc's dotted id under a version prefix, a version from an unbound release, a draft the perspective makes invisible) fails loudly instead of being fed under the watched identity.
|
|
35
|
+
- Watches whose perspective makes drafts invisible to the engine no longer fall back to the draft. The engine hydrates a `[release]` stack as version-over-published (and `'published'`/`'raw'` scalars as published/raw) with drafts invisible; the Studio adapter resolved `version ?? draft ?? published` and the SDK's per-doc store has the same fallback built in. The Studio adapter now skips the draft arm unless the effective perspective names `'drafts'`, and the SDK adapter observes such refs through the query store under the instance's exact perspective stack. The SDK's query-store route reads committed state (live), so local uncommitted edits to a release version doc no longer feed the session there — Studio keeps its optimistic `editState` reads. New engine helpers `contentDraftFallback` and `contentDocQuery` are the single encoding of the rule, shared with the engine's own hydration.
|
|
36
|
+
- `useWorkflowSession` resets its surfaced `evaluation` to `undefined` on an `instanceId` swap, as the contract promises, instead of leaving the previous instance's projection visible until the new instance first evaluates.
|
|
37
|
+
|
|
38
|
+
- Updated dependencies [11e2d6f]
|
|
39
|
+
- Updated dependencies [7510fd5]
|
|
40
|
+
- Updated dependencies [01e8042]
|
|
41
|
+
- Updated dependencies [79c54db]
|
|
42
|
+
- Updated dependencies [9dc592d]
|
|
43
|
+
- Updated dependencies [1a4712a]
|
|
44
|
+
- Updated dependencies [6a46db1]
|
|
45
|
+
- Updated dependencies [35b9b85]
|
|
46
|
+
- Updated dependencies [e5f5b77]
|
|
47
|
+
- Updated dependencies [01e8042]
|
|
48
|
+
- @sanity/workflow-engine@0.16.0
|
|
49
|
+
- @sanity/workflow-react@0.11.0
|
|
50
|
+
- @sanity/workflow-sdk@0.8.0
|
|
51
|
+
|
|
3
52
|
## 0.7.0
|
|
4
53
|
|
|
5
54
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -10,14 +10,23 @@ Studio source context (`useDocumentStore` / `useSource` / `useClient`).
|
|
|
10
10
|
exported) as the default per-resource routing that reaches any `dataset:`
|
|
11
11
|
GDR by rebinding the same client, pinned to the engine version too. A custom `resourceClients` replaces the default — delegate
|
|
12
12
|
to `studioResourceClients` for the dataset arm; schemes it returns
|
|
13
|
-
`undefined` for (media-library, canvas) are
|
|
13
|
+
`undefined` for (media-library, canvas) are READ by the engine itself,
|
|
14
14
|
which derives a sibling client from the engine client's credentials.
|
|
15
|
+
Writing a runtime-supplied ref to such a scheme into field state
|
|
16
|
+
additionally needs the resolver to SERVE that resource — serving is what
|
|
17
|
+
declares it on the engine's written-ref surface; an undeclared
|
|
18
|
+
media-library/canvas ref is rejected at the write with
|
|
19
|
+
`RefResourceUndeclaredError`. (Definition content is unaffected — deploy
|
|
20
|
+
vets it.)
|
|
15
21
|
- `useWorkflowSession` — drives an engine session for one instance and
|
|
16
22
|
returns
|
|
17
|
-
`{evaluation, ready, guards, tick, fireAction, editField, previewField, discardFieldPreview}`.
|
|
23
|
+
`{evaluation, ready, invalid, guards, tick, fireAction, editField, previewField, discardFieldPreview}`.
|
|
24
|
+
The observer validates every engine-owned stream, so a model-ahead or
|
|
25
|
+
corrupt doc surfaces as the explicit `invalid` state (never `ready` while
|
|
26
|
+
set) instead of an eternal spinner.
|
|
18
27
|
- `useWorkflowInstances` / `useDocumentWorkflows` — live instance lists
|
|
19
28
|
(filterable collection / per-document discovery), each returning
|
|
20
|
-
`{instances, loading}`. The list observes `documentStore.listenQuery` when
|
|
29
|
+
`{instances, loading, invalid}`. The list observes `documentStore.listenQuery` when
|
|
21
30
|
the engine dataset is the workspace's own, else the App SDK store — same
|
|
22
31
|
routing (and same `sdk` rules) as the instance doc below. On multi-hook
|
|
23
32
|
surfaces (a board with per-row `useWorkflowSession`), pass one shared
|
|
@@ -29,13 +38,13 @@ Studio source context (`useDocumentStore` / `useSource` / `useClient`).
|
|
|
29
38
|
Each ref in the watch-set is classified against the workspace's own
|
|
30
39
|
project + dataset and observed by the store that can actually address it:
|
|
31
40
|
|
|
32
|
-
| Ref
|
|
33
|
-
|
|
|
34
|
-
| **Mounted workspace dataset**
|
|
35
|
-
| **Engine state dataset** (instance doc)
|
|
36
|
-
| **Guards** (`observeGuards`)
|
|
37
|
-
| **Any other dataset / project**
|
|
38
|
-
|
|
|
41
|
+
| Ref | Observed via |
|
|
42
|
+
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
43
|
+
| **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 |
|
|
44
|
+
| **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`) |
|
|
45
|
+
| **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` |
|
|
46
|
+
| **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) |
|
|
47
|
+
| **`canvas:` / `media-library:` schemes** | Routed like foreign resources through the **App SDK store**, observed live via the handle's `resource` field (`{canvasId}` / `{mediaLibraryId}`) — the same path the Canvas app uses. `dashboard:` is the exception: the App SDK store has no dashboard resource, so a `dashboard:` ref **throws** and a workflow with a dashboard subject can't be driven reactively — run it engine-direct instead |
|
|
39
48
|
|
|
40
49
|
There is **no silent fallback**: a ref the configured stores can't address
|
|
41
50
|
throws, instead of being misread from the workspace dataset under the same
|
package/dist/index.cjs
CHANGED
|
@@ -33,9 +33,9 @@ function observableStore({observable: observable, resolve: resolve, initial: ini
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
function makeStudioObserver(documentStore, options) {
|
|
36
|
-
const {mounted: mounted, engineResource: engineResource, sdk: sdk2, client: client} = options, engineIsMounted = observer.sameDataset(engineResource, mounted), editStateStore = ({id: id, type: type, release: release}) => observableStore({
|
|
36
|
+
const {mounted: mounted, engineResource: engineResource, sdk: sdk2, client: client} = options, engineIsMounted = observer.sameDataset(engineResource, mounted), editStateStore = ({id: id, type: type, release: release, draftFallback: draftFallback}) => observableStore({
|
|
37
37
|
observable: documentStore.pair.editState(id, type, release),
|
|
38
|
-
resolve: resolveEditState,
|
|
38
|
+
resolve: editState => resolveEditState(editState, draftFallback),
|
|
39
39
|
initial: void 0,
|
|
40
40
|
name: `the edit state of "${id}"`
|
|
41
41
|
}), requireSdk = what => {
|
|
@@ -43,15 +43,16 @@ function makeStudioObserver(documentStore, options) {
|
|
|
43
43
|
throw new Error(`@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.`);
|
|
44
44
|
};
|
|
45
45
|
return {
|
|
46
|
-
observeInstance: instanceId => engineIsMounted ? editStateStore({
|
|
46
|
+
observeInstance: instanceId => engineIsMounted ? observer.validatedInstanceStore(editStateStore({
|
|
47
47
|
id: instanceId,
|
|
48
48
|
type: workflowEngine.WORKFLOW_INSTANCE_TYPE,
|
|
49
|
-
release: void 0
|
|
50
|
-
|
|
49
|
+
release: void 0,
|
|
50
|
+
draftFallback: !0
|
|
51
|
+
})) : observer.validatedInstanceStore(observer$1.sdkInstanceDocStore({
|
|
51
52
|
sdk: requireSdk(`instance "${instanceId}" lives in the engine dataset ${engineResource.projectId}.${engineResource.dataset}`),
|
|
52
53
|
instanceId: instanceId,
|
|
53
54
|
engineResource: engineResource
|
|
54
|
-
}),
|
|
55
|
+
})),
|
|
55
56
|
observeDocs: (documents, perspective) => observer.combineDocStores(documents.map(ref => observer.classifyRef(ref, mounted) === "mounted-dataset" ? {
|
|
56
57
|
key: ref.globalDocumentId,
|
|
57
58
|
store: editStateStore({
|
|
@@ -60,6 +61,10 @@ function makeStudioObserver(documentStore, options) {
|
|
|
60
61
|
release: workflowEngine.contentReleaseName({
|
|
61
62
|
ref: ref,
|
|
62
63
|
perspective: perspective
|
|
64
|
+
}),
|
|
65
|
+
draftFallback: workflowEngine.contentDraftFallback({
|
|
66
|
+
ref: ref,
|
|
67
|
+
perspective: perspective
|
|
63
68
|
})
|
|
64
69
|
})
|
|
65
70
|
} : {
|
|
@@ -70,7 +75,7 @@ function makeStudioObserver(documentStore, options) {
|
|
|
70
75
|
perspective: perspective
|
|
71
76
|
})
|
|
72
77
|
})),
|
|
73
|
-
observeGuards: (instanceId, resources) => observer.combineGuardStores(resources.map(resource => {
|
|
78
|
+
observeGuards: (instanceId, resources) => observer.validatedGuardsStore(observer.combineGuardStores(resources.map(resource => {
|
|
74
79
|
if (observer.sameDataset(resource, mounted)) {
|
|
75
80
|
const {query: query, params: params} = workflowEngine.instanceGuardQuery(instanceId);
|
|
76
81
|
return observableStore({
|
|
@@ -85,23 +90,25 @@ function makeStudioObserver(documentStore, options) {
|
|
|
85
90
|
instanceId: instanceId,
|
|
86
91
|
resource: resource
|
|
87
92
|
});
|
|
88
|
-
})),
|
|
89
|
-
observeInstances: query => engineIsMounted ? observableStore({
|
|
93
|
+
}))),
|
|
94
|
+
observeInstances: query => engineIsMounted ? observer.validatedInstancesStore(observableStore({
|
|
90
95
|
observable: documentStore.listenQuery(query.query, query.params, {}),
|
|
91
96
|
resolve: docs => docs,
|
|
92
97
|
initial: void 0,
|
|
93
98
|
name: "the instance list"
|
|
94
|
-
}) : observer$1.sdkInstancesStore({
|
|
99
|
+
})) : observer.validatedInstancesStore(observer$1.sdkInstancesStore({
|
|
95
100
|
sdk: requireSdk(`the instance list lives in the engine dataset ${engineResource.projectId}.${engineResource.dataset}`),
|
|
96
101
|
query: query,
|
|
97
102
|
resource: engineResource
|
|
98
|
-
}),
|
|
103
|
+
})),
|
|
99
104
|
ensureDocumentExists: args => observer.ensureDocumentExists(client, args)
|
|
100
105
|
};
|
|
101
106
|
}
|
|
102
107
|
|
|
103
|
-
function resolveEditState(editState) {
|
|
104
|
-
if (editState.ready) return
|
|
108
|
+
function resolveEditState(editState, draftFallback) {
|
|
109
|
+
if (!editState.ready) return;
|
|
110
|
+
const draft = draftFallback ? editState.draft : null;
|
|
111
|
+
return editState.version ?? draft ?? editState.published;
|
|
105
112
|
}
|
|
106
113
|
|
|
107
114
|
const SESSION_CLIENT_OPTIONS = {
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { instanceGuardQuery, contentReleaseName, WORKFLOW_INSTANCE_TYPE, ENGINE_API_VERSION, createEngine, EXECUTION_KINDS } from "@sanity/workflow-engine";
|
|
1
|
+
import { instanceGuardQuery, contentDraftFallback, contentReleaseName, WORKFLOW_INSTANCE_TYPE, ENGINE_API_VERSION, createEngine, EXECUTION_KINDS } from "@sanity/workflow-engine";
|
|
2
2
|
|
|
3
|
-
import { sameDataset, ensureDocumentExists, combineGuardStores, combineDocStores, classifyRef, datasetResourceId, useKeyed } from "@sanity/workflow-react/observer";
|
|
3
|
+
import { sameDataset, ensureDocumentExists, validatedInstancesStore, validatedGuardsStore, combineGuardStores, combineDocStores, classifyRef, validatedInstanceStore, datasetResourceId, useKeyed } from "@sanity/workflow-react/observer";
|
|
4
4
|
|
|
5
5
|
import { sdkInstancesStore, sdkGuardStore, sdkContentDocStore, sdkInstanceDocStore } from "@sanity/workflow-sdk/observer";
|
|
6
6
|
|
|
@@ -39,9 +39,9 @@ function observableStore({observable: observable, resolve: resolve, initial: ini
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function makeStudioObserver(documentStore, options) {
|
|
42
|
-
const {mounted: mounted, engineResource: engineResource, sdk: sdk, client: client} = options, engineIsMounted = sameDataset(engineResource, mounted), editStateStore = ({id: id, type: type, release: release}) => observableStore({
|
|
42
|
+
const {mounted: mounted, engineResource: engineResource, sdk: sdk, client: client} = options, engineIsMounted = sameDataset(engineResource, mounted), editStateStore = ({id: id, type: type, release: release, draftFallback: draftFallback}) => observableStore({
|
|
43
43
|
observable: documentStore.pair.editState(id, type, release),
|
|
44
|
-
resolve: resolveEditState,
|
|
44
|
+
resolve: editState => resolveEditState(editState, draftFallback),
|
|
45
45
|
initial: void 0,
|
|
46
46
|
name: `the edit state of "${id}"`
|
|
47
47
|
}), requireSdk = what => {
|
|
@@ -49,15 +49,16 @@ function makeStudioObserver(documentStore, options) {
|
|
|
49
49
|
throw new Error(`@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.`);
|
|
50
50
|
};
|
|
51
51
|
return {
|
|
52
|
-
observeInstance: instanceId => engineIsMounted ? editStateStore({
|
|
52
|
+
observeInstance: instanceId => engineIsMounted ? validatedInstanceStore(editStateStore({
|
|
53
53
|
id: instanceId,
|
|
54
54
|
type: WORKFLOW_INSTANCE_TYPE,
|
|
55
|
-
release: void 0
|
|
56
|
-
|
|
55
|
+
release: void 0,
|
|
56
|
+
draftFallback: !0
|
|
57
|
+
})) : validatedInstanceStore(sdkInstanceDocStore({
|
|
57
58
|
sdk: requireSdk(`instance "${instanceId}" lives in the engine dataset ${engineResource.projectId}.${engineResource.dataset}`),
|
|
58
59
|
instanceId: instanceId,
|
|
59
60
|
engineResource: engineResource
|
|
60
|
-
}),
|
|
61
|
+
})),
|
|
61
62
|
observeDocs: (documents, perspective) => combineDocStores(documents.map(ref => classifyRef(ref, mounted) === "mounted-dataset" ? {
|
|
62
63
|
key: ref.globalDocumentId,
|
|
63
64
|
store: editStateStore({
|
|
@@ -66,6 +67,10 @@ function makeStudioObserver(documentStore, options) {
|
|
|
66
67
|
release: contentReleaseName({
|
|
67
68
|
ref: ref,
|
|
68
69
|
perspective: perspective
|
|
70
|
+
}),
|
|
71
|
+
draftFallback: contentDraftFallback({
|
|
72
|
+
ref: ref,
|
|
73
|
+
perspective: perspective
|
|
69
74
|
})
|
|
70
75
|
})
|
|
71
76
|
} : {
|
|
@@ -76,7 +81,7 @@ function makeStudioObserver(documentStore, options) {
|
|
|
76
81
|
perspective: perspective
|
|
77
82
|
})
|
|
78
83
|
})),
|
|
79
|
-
observeGuards: (instanceId, resources) => combineGuardStores(resources.map(resource => {
|
|
84
|
+
observeGuards: (instanceId, resources) => validatedGuardsStore(combineGuardStores(resources.map(resource => {
|
|
80
85
|
if (sameDataset(resource, mounted)) {
|
|
81
86
|
const {query: query, params: params} = instanceGuardQuery(instanceId);
|
|
82
87
|
return observableStore({
|
|
@@ -91,23 +96,25 @@ function makeStudioObserver(documentStore, options) {
|
|
|
91
96
|
instanceId: instanceId,
|
|
92
97
|
resource: resource
|
|
93
98
|
});
|
|
94
|
-
})),
|
|
95
|
-
observeInstances: query => engineIsMounted ? observableStore({
|
|
99
|
+
}))),
|
|
100
|
+
observeInstances: query => engineIsMounted ? validatedInstancesStore(observableStore({
|
|
96
101
|
observable: documentStore.listenQuery(query.query, query.params, {}),
|
|
97
102
|
resolve: docs => docs,
|
|
98
103
|
initial: void 0,
|
|
99
104
|
name: "the instance list"
|
|
100
|
-
}) : sdkInstancesStore({
|
|
105
|
+
})) : validatedInstancesStore(sdkInstancesStore({
|
|
101
106
|
sdk: requireSdk(`the instance list lives in the engine dataset ${engineResource.projectId}.${engineResource.dataset}`),
|
|
102
107
|
query: query,
|
|
103
108
|
resource: engineResource
|
|
104
|
-
}),
|
|
109
|
+
})),
|
|
105
110
|
ensureDocumentExists: args => ensureDocumentExists(client, args)
|
|
106
111
|
};
|
|
107
112
|
}
|
|
108
113
|
|
|
109
|
-
function resolveEditState(editState) {
|
|
110
|
-
if (editState.ready) return
|
|
114
|
+
function resolveEditState(editState, draftFallback) {
|
|
115
|
+
if (!editState.ready) return;
|
|
116
|
+
const draft = draftFallback ? editState.draft : null;
|
|
117
|
+
return editState.version ?? draft ?? editState.published;
|
|
111
118
|
}
|
|
112
119
|
|
|
113
120
|
const SESSION_CLIENT_OPTIONS = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/workflow-studio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@sanity/workflow-engine": "0.
|
|
45
|
-
"@sanity/workflow-react": "0.
|
|
46
|
-
"@sanity/workflow-sdk": "0.
|
|
44
|
+
"@sanity/workflow-engine": "0.17.0",
|
|
45
|
+
"@sanity/workflow-react": "0.12.0",
|
|
46
|
+
"@sanity/workflow-sdk": "0.9.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@sanity/pkg-utils": "^10.5.2",
|
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
"react": "^19.2.7",
|
|
55
55
|
"react-dom": "^19.2.7",
|
|
56
56
|
"sanity": "^5.30.0",
|
|
57
|
-
"vitest": "^4.1.8"
|
|
57
|
+
"vitest": "^4.1.8",
|
|
58
|
+
"@sanity/workflow-engine-test": "0.12.0"
|
|
58
59
|
},
|
|
59
60
|
"peerDependencies": {
|
|
60
61
|
"@sanity/sdk": "^2.12.0",
|