@spoosh/plugin-optimistic 0.7.2 → 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/README.md +40 -39
- package/dist/index.d.mts +103 -103
- package/dist/index.d.ts +103 -103
- package/dist/index.js +157 -164
- package/dist/index.mjs +155 -162
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -32,40 +32,41 @@ const { trigger } = useWrite((api) => api("posts/:id").DELETE());
|
|
|
32
32
|
trigger({
|
|
33
33
|
params: { id },
|
|
34
34
|
// Optimistic delete - instantly remove item from list
|
|
35
|
-
optimistic: (
|
|
36
|
-
|
|
37
|
-
.GET()
|
|
38
|
-
.UPDATE_CACHE((posts) => posts.filter((p) => p.id !== id)),
|
|
35
|
+
optimistic: (cache) =>
|
|
36
|
+
cache("posts").set((posts) => posts.filter((p) => p.id !== id)),
|
|
39
37
|
});
|
|
40
38
|
|
|
41
|
-
//
|
|
39
|
+
// Confirmed update - update cache after successful response
|
|
42
40
|
trigger({
|
|
43
|
-
optimistic: (
|
|
44
|
-
|
|
45
|
-
.
|
|
46
|
-
.
|
|
47
|
-
|
|
41
|
+
optimistic: (cache) =>
|
|
42
|
+
cache("posts")
|
|
43
|
+
.confirmed()
|
|
44
|
+
.set((posts, newPost) => [newPost, ...posts]),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Both immediate and confirmed updates
|
|
48
|
+
trigger({
|
|
49
|
+
optimistic: (cache) =>
|
|
50
|
+
cache("posts")
|
|
51
|
+
.set((posts) => [...posts, { id: -1, title: "Saving..." }])
|
|
52
|
+
.confirmed()
|
|
53
|
+
.set((posts, newPost) => posts.map((p) => (p.id === -1 ? newPost : p))),
|
|
48
54
|
});
|
|
49
55
|
|
|
50
56
|
// Multiple targets
|
|
51
57
|
trigger({
|
|
52
|
-
optimistic: (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
.UPDATE_CACHE((posts) => posts.filter((p) => p.id !== id)),
|
|
56
|
-
api("stats")
|
|
57
|
-
.GET()
|
|
58
|
-
.UPDATE_CACHE((stats) => ({ ...stats, count: stats.count - 1 })),
|
|
58
|
+
optimistic: (cache) => [
|
|
59
|
+
cache("posts").set((posts) => posts.filter((p) => p.id !== id)),
|
|
60
|
+
cache("stats").set((stats) => ({ ...stats, count: stats.count - 1 })),
|
|
59
61
|
],
|
|
60
62
|
});
|
|
61
63
|
|
|
62
64
|
// Filter by request params
|
|
63
65
|
trigger({
|
|
64
|
-
optimistic: (
|
|
65
|
-
|
|
66
|
-
.
|
|
67
|
-
.
|
|
68
|
-
.UPDATE_CACHE((posts, newPost) => [newPost!, ...posts]),
|
|
66
|
+
optimistic: (cache) =>
|
|
67
|
+
cache("posts/:id")
|
|
68
|
+
.filter((entry) => entry.params.id === "1")
|
|
69
|
+
.set((post) => ({ ...post, title: "Updated" })),
|
|
69
70
|
});
|
|
70
71
|
```
|
|
71
72
|
|
|
@@ -73,22 +74,21 @@ trigger({
|
|
|
73
74
|
|
|
74
75
|
### Per-Request Options
|
|
75
76
|
|
|
76
|
-
| Option | Type
|
|
77
|
-
| ------------ |
|
|
78
|
-
| `optimistic` | `(
|
|
77
|
+
| Option | Type | Description |
|
|
78
|
+
| ------------ | --------------------------------- | ------------------------------------- |
|
|
79
|
+
| `optimistic` | `(cache) => builder \| builder[]` | Callback to define optimistic updates |
|
|
79
80
|
|
|
80
|
-
### Builder Methods
|
|
81
|
+
### Builder Methods
|
|
81
82
|
|
|
82
83
|
Chain methods to configure optimistic updates:
|
|
83
84
|
|
|
84
|
-
| Method
|
|
85
|
-
|
|
|
86
|
-
| `.
|
|
87
|
-
| `.
|
|
88
|
-
| `.
|
|
89
|
-
| `.
|
|
90
|
-
| `.
|
|
91
|
-
| `.ON_ERROR(fn)` | Error callback |
|
|
85
|
+
| Method | Description |
|
|
86
|
+
| -------------------- | ----------------------------------------------- |
|
|
87
|
+
| `.filter(fn)` | Filter which cache entries to update |
|
|
88
|
+
| `.set(fn)` | Update cache (immediate before `.confirmed()`) |
|
|
89
|
+
| `.confirmed()` | Switch to confirmed mode (update after success) |
|
|
90
|
+
| `.disableRollback()` | Disable automatic rollback on error |
|
|
91
|
+
| `.onError(fn)` | Error callback |
|
|
92
92
|
|
|
93
93
|
### Result
|
|
94
94
|
|
|
@@ -96,9 +96,10 @@ Chain methods to configure optimistic updates:
|
|
|
96
96
|
| -------------- | --------- | --------------------------------------------------- |
|
|
97
97
|
| `isOptimistic` | `boolean` | `true` if current data is from an optimistic update |
|
|
98
98
|
|
|
99
|
-
###
|
|
99
|
+
### Update Modes
|
|
100
100
|
|
|
101
|
-
| Usage
|
|
102
|
-
|
|
|
103
|
-
| `.
|
|
104
|
-
| `.
|
|
101
|
+
| Usage | Description |
|
|
102
|
+
| ------------------------------ | --------------------------------------------------------------------------------------------------- |
|
|
103
|
+
| `.set(fn)` | **Immediate** - Update cache instantly before request completes. Rollback on error. |
|
|
104
|
+
| `.confirmed().set(fn)` | **Confirmed** - Wait for successful response, then update cache. `fn` receives response as 2nd arg. |
|
|
105
|
+
| `.set(fn).confirmed().set(fn)` | **Both** - Immediate update, then replace with confirmed data on success. |
|
package/dist/index.d.mts
CHANGED
|
@@ -14,112 +14,113 @@ type ExtractQuery<T> = T extends {
|
|
|
14
14
|
query?: infer Q;
|
|
15
15
|
} ? Q : never;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
* @internal
|
|
17
|
+
* Filter options for filtering cache entries.
|
|
19
18
|
*/
|
|
20
|
-
type
|
|
21
|
-
|
|
22
|
-
method: string;
|
|
23
|
-
where?: (options: unknown) => boolean;
|
|
24
|
-
updater?: (data: unknown, response?: unknown) => unknown;
|
|
25
|
-
timing: "immediate" | "onSuccess";
|
|
26
|
-
rollbackOnError: boolean;
|
|
27
|
-
onError?: (error: unknown) => void;
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* WHERE options for filtering cache entries.
|
|
31
|
-
*/
|
|
32
|
-
type WhereOptions<TMethodConfig, TUserPath extends string> = HasParams<TUserPath> extends true ? HasQuery<TMethodConfig> extends true ? {
|
|
33
|
-
params: Record<ExtractParamNames<TUserPath>, string | number>;
|
|
19
|
+
type FilterOptions<TMethodConfig, TUserPath extends string> = HasParams<TUserPath> extends true ? HasQuery<TMethodConfig> extends true ? {
|
|
20
|
+
params: Record<ExtractParamNames<TUserPath>, string>;
|
|
34
21
|
query: ExtractQuery<TMethodConfig>;
|
|
35
22
|
} : {
|
|
36
|
-
params: Record<ExtractParamNames<TUserPath>, string
|
|
23
|
+
params: Record<ExtractParamNames<TUserPath>, string>;
|
|
37
24
|
} : HasQuery<TMethodConfig> extends true ? {
|
|
38
25
|
query: ExtractQuery<TMethodConfig>;
|
|
39
26
|
} : never;
|
|
40
27
|
/**
|
|
41
|
-
*
|
|
42
|
-
*/
|
|
43
|
-
type IfNotUsed<TMethod extends string, TUsed extends string, TType> = TMethod extends TUsed ? never : TType;
|
|
44
|
-
/**
|
|
45
|
-
* Brand for completed builders (UPDATE_CACHE was called).
|
|
28
|
+
* Brand for completed builders (at least one set() was called).
|
|
46
29
|
* @internal
|
|
47
30
|
*/
|
|
48
31
|
declare const COMPLETED_BRAND: unique symbol;
|
|
49
32
|
/**
|
|
50
|
-
* Chainable builder
|
|
51
|
-
*
|
|
52
|
-
* Internal properties are hidden from autocomplete.
|
|
33
|
+
* Chainable builder for cache operations.
|
|
34
|
+
* Uses conditional intersections to completely hide unavailable methods from IDE suggestions.
|
|
53
35
|
*
|
|
54
|
-
* @typeParam
|
|
55
|
-
* @typeParam
|
|
56
|
-
* @typeParam
|
|
36
|
+
* @typeParam TData - The data type of the cache entry
|
|
37
|
+
* @typeParam TMethodConfig - The method configuration from schema
|
|
38
|
+
* @typeParam TUserPath - The user's path string
|
|
39
|
+
* @typeParam TResponse - The mutation response type
|
|
40
|
+
* @typeParam TError - The error type
|
|
41
|
+
* @typeParam TConfirmed - Whether we're in confirmed mode
|
|
42
|
+
* @typeParam THasImmediate - Whether immediate set() was called
|
|
43
|
+
* @typeParam THasConfirmed - Whether confirmed set() was called
|
|
44
|
+
* @typeParam THasFilter - Whether filter() was called
|
|
45
|
+
* @typeParam THasDisableRollback - Whether disableRollback() was called
|
|
46
|
+
* @typeParam THasOnError - Whether onError() was called
|
|
57
47
|
*/
|
|
58
|
-
type
|
|
48
|
+
type CacheBuilder<TData = unknown, TMethodConfig = unknown, TUserPath extends string = string, TResponse = unknown, TError = unknown, TConfirmed extends boolean = false, THasImmediate extends boolean = false, THasConfirmed extends boolean = false, THasFilter extends boolean = false, THasDisableRollback extends boolean = false, THasOnError extends boolean = false> = (THasImmediate extends true ? {
|
|
59
49
|
readonly [COMPLETED_BRAND]: true;
|
|
60
|
-
} :
|
|
50
|
+
} : THasConfirmed extends true ? {
|
|
51
|
+
readonly [COMPLETED_BRAND]: true;
|
|
52
|
+
} : unknown) & (THasFilter extends true ? unknown : THasImmediate extends true ? unknown : THasConfirmed extends true ? unknown : FilterOptions<TMethodConfig, TUserPath> extends never ? unknown : {
|
|
61
53
|
/**
|
|
62
54
|
* Filter which cache entries to update based on query/params.
|
|
55
|
+
* Must be called before any `set()`.
|
|
63
56
|
*
|
|
64
57
|
* @param predicate - Function that receives cache entry info and returns true to match
|
|
65
58
|
*
|
|
66
59
|
* @example
|
|
67
60
|
* ```ts
|
|
68
|
-
* .
|
|
61
|
+
* .filter(entry => entry.params.id === "1")
|
|
69
62
|
* ```
|
|
70
63
|
*/
|
|
71
|
-
|
|
64
|
+
filter: (predicate: (entry: Simplify<FilterOptions<TMethodConfig, TUserPath>>) => boolean) => CacheBuilder<TData, TMethodConfig, TUserPath, TResponse, TError, TConfirmed, THasImmediate, THasConfirmed, true, THasDisableRollback, THasOnError>;
|
|
65
|
+
}) & (TConfirmed extends true ? THasConfirmed extends true ? unknown : {
|
|
72
66
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
67
|
+
* Set the cache data (confirmed mode).
|
|
68
|
+
* Receives current data and the mutation response.
|
|
75
69
|
*
|
|
76
|
-
*
|
|
77
|
-
|
|
70
|
+
* @param updater - Function that receives current data and response, returns updated data
|
|
71
|
+
*/
|
|
72
|
+
set: (updater: (data: TData, response: TResponse) => TData) => CacheBuilder<TData, TMethodConfig, TUserPath, TResponse, TError, TConfirmed, THasImmediate, true, THasFilter, THasDisableRollback, THasOnError>;
|
|
73
|
+
} : THasImmediate extends true ? unknown : {
|
|
74
|
+
/**
|
|
75
|
+
* Set the cache data (immediate mode).
|
|
76
|
+
* Receives only the current data.
|
|
78
77
|
*
|
|
79
|
-
* @param updater - Function that receives current data
|
|
78
|
+
* @param updater - Function that receives current data, returns updated data
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* .set(data => ({ ...data, pending: true }))
|
|
83
|
+
* ```
|
|
80
84
|
*/
|
|
81
|
-
|
|
85
|
+
set: (updater: (data: TData) => TData) => CacheBuilder<TData, TMethodConfig, TUserPath, TResponse, TError, TConfirmed, true, THasConfirmed, THasFilter, false, false>;
|
|
86
|
+
}) & (TConfirmed extends true ? unknown : {
|
|
82
87
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
88
|
+
* Switch to confirmed mode. The next set() will be applied after mutation succeeds.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* .set(data => ({ ...data, pending: true })) // immediate
|
|
93
|
+
* .confirmed()
|
|
94
|
+
* .set((data, response) => response) // after success
|
|
95
|
+
* ```
|
|
86
96
|
*/
|
|
87
|
-
|
|
97
|
+
confirmed: () => CacheBuilder<TData, TMethodConfig, TUserPath, TResponse, TError, true, THasImmediate, THasConfirmed, THasFilter, THasDisableRollback, THasOnError>;
|
|
98
|
+
}) & (THasImmediate extends true ? THasDisableRollback extends true ? unknown : {
|
|
88
99
|
/**
|
|
89
100
|
* Disable automatic rollback when mutation fails.
|
|
90
101
|
* By default, optimistic updates are rolled back on error.
|
|
91
102
|
*/
|
|
92
|
-
|
|
103
|
+
disableRollback: () => CacheBuilder<TData, TMethodConfig, TUserPath, TResponse, TError, TConfirmed, THasImmediate, THasConfirmed, THasFilter, true, THasOnError>;
|
|
104
|
+
} : unknown) & (THasImmediate extends true ? THasOnError extends true ? unknown : {
|
|
93
105
|
/**
|
|
94
106
|
* Callback when mutation fails.
|
|
95
107
|
*/
|
|
96
|
-
|
|
97
|
-
};
|
|
108
|
+
onError: (callback: (error: TError) => void) => CacheBuilder<TData, TMethodConfig, TUserPath, TResponse, TError, TConfirmed, THasImmediate, THasConfirmed, THasFilter, THasDisableRollback, true>;
|
|
109
|
+
} : unknown);
|
|
98
110
|
/**
|
|
99
|
-
*
|
|
100
|
-
* Resolves literal paths (e.g., "posts/1") to schema keys (e.g., "posts/:id") using FindMatchingKey.
|
|
101
|
-
* Uses TPath for param extraction to preserve user's param names.
|
|
111
|
+
* Cache selector that resolves paths to their schema definitions.
|
|
102
112
|
*/
|
|
103
|
-
type
|
|
104
|
-
GET: () => OptimisticBuilder<ExtractData<TGetConfig>, TGetConfig, TPath, TResponse, TError, "immediate", never, false>;
|
|
105
|
-
} : never : never : never : never : never;
|
|
113
|
+
type CacheSelector<TSchema, TPath extends string, TResponse, TError> = FindMatchingKey<TSchema, TPath> extends infer TKey ? TKey extends keyof TSchema ? TSchema[TKey] extends infer TRoute ? "GET" extends keyof TRoute ? TRoute["GET"] extends infer TGetConfig ? CacheBuilder<ExtractData<TGetConfig>, TGetConfig, TPath, TResponse, TError, false, false, false, false, false, false> : never : never : never : never : never;
|
|
106
114
|
/**
|
|
107
|
-
* Helper type for creating the
|
|
115
|
+
* Helper type for creating the cache selector.
|
|
108
116
|
* Accepts both schema-defined paths (e.g., "posts/:id") and literal paths (e.g., "posts/1").
|
|
109
|
-
* Uses union with (string & {}) to allow any string while preserving autocomplete.
|
|
110
117
|
*/
|
|
111
|
-
type
|
|
118
|
+
type CacheHelper<TSchema, TResponse = unknown, TError = unknown> = <TPath extends ReadPaths<TSchema> | (string & {})>(path: TPath) => CacheSelector<TSchema, TPath, TResponse, TError>;
|
|
112
119
|
/**
|
|
113
|
-
* A
|
|
114
|
-
* Used for the return type of the callback.
|
|
115
|
-
*/
|
|
116
|
-
type AnyOptimisticTarget = OptimisticTarget;
|
|
117
|
-
/**
|
|
118
|
-
* A completed builder that has UPDATE_CACHE called.
|
|
119
|
-
* Uses the brand to ensure UPDATE_CACHE was called.
|
|
120
|
+
* A completed builder that has at least one set() called.
|
|
120
121
|
* @internal
|
|
121
122
|
*/
|
|
122
|
-
type
|
|
123
|
+
type CompletedCacheBuilder = {
|
|
123
124
|
readonly [COMPLETED_BRAND]: true;
|
|
124
125
|
};
|
|
125
126
|
/**
|
|
@@ -127,41 +128,52 @@ type CompletedOptimisticBuilder = {
|
|
|
127
128
|
*
|
|
128
129
|
* @example
|
|
129
130
|
* ```ts
|
|
130
|
-
* //
|
|
131
|
-
* optimistic: (
|
|
132
|
-
* .
|
|
133
|
-
* .UPDATE_CACHE(posts => posts.filter(p => p.id !== deletedId))
|
|
131
|
+
* // Optimistic only
|
|
132
|
+
* optimistic: (cache) => cache("posts")
|
|
133
|
+
* .set(posts => posts.filter(p => p.id !== deletedId))
|
|
134
134
|
* ```
|
|
135
135
|
*
|
|
136
136
|
* @example
|
|
137
137
|
* ```ts
|
|
138
|
-
* //
|
|
139
|
-
* optimistic: (
|
|
140
|
-
* .
|
|
141
|
-
* .
|
|
142
|
-
* .NO_ROLLBACK()
|
|
143
|
-
* .UPDATE_CACHE(posts => [...posts, newPost])
|
|
138
|
+
* // Confirmed only (post-success)
|
|
139
|
+
* optimistic: (cache) => cache("posts")
|
|
140
|
+
* .confirmed()
|
|
141
|
+
* .set((posts, newPost) => [...posts, newPost])
|
|
144
142
|
* ```
|
|
145
143
|
*
|
|
146
144
|
* @example
|
|
147
145
|
* ```ts
|
|
148
|
-
* //
|
|
149
|
-
* optimistic: (
|
|
150
|
-
* .
|
|
151
|
-
* .
|
|
152
|
-
* .
|
|
146
|
+
* // Both optimistic and confirmed
|
|
147
|
+
* optimistic: (cache) => cache("posts/:id")
|
|
148
|
+
* .filter(e => e.params.id === "1")
|
|
149
|
+
* .set(post => ({ ...post, pending: true }))
|
|
150
|
+
* .confirmed()
|
|
151
|
+
* .set((post, response) => response)
|
|
153
152
|
* ```
|
|
154
153
|
*
|
|
155
154
|
* @example
|
|
156
155
|
* ```ts
|
|
157
156
|
* // Multiple targets
|
|
158
|
-
* optimistic: (
|
|
159
|
-
*
|
|
160
|
-
*
|
|
157
|
+
* optimistic: (cache) => [
|
|
158
|
+
* cache("posts").set(posts => posts.filter(p => p.id !== id)),
|
|
159
|
+
* cache("stats").set(stats => ({ ...stats, count: stats.count - 1 })),
|
|
161
160
|
* ]
|
|
162
161
|
* ```
|
|
163
162
|
*/
|
|
164
|
-
type OptimisticCallbackFn<TSchema = unknown, TResponse = unknown, TError = unknown> = (
|
|
163
|
+
type OptimisticCallbackFn<TSchema = unknown, TResponse = unknown, TError = unknown> = (cache: CacheHelper<TSchema, TResponse, TError>) => CompletedCacheBuilder | CompletedCacheBuilder[];
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Internal optimistic target data.
|
|
167
|
+
* @internal
|
|
168
|
+
*/
|
|
169
|
+
type OptimisticTarget = {
|
|
170
|
+
path: string;
|
|
171
|
+
filter?: (options: unknown) => boolean;
|
|
172
|
+
immediateUpdater?: (data: unknown) => unknown;
|
|
173
|
+
confirmedUpdater?: (data: unknown, response: unknown) => unknown;
|
|
174
|
+
rollbackOnError: boolean;
|
|
175
|
+
onError?: (error: unknown) => void;
|
|
176
|
+
};
|
|
165
177
|
type OptimisticPluginConfig = object;
|
|
166
178
|
type OptimisticWriteOptions = object;
|
|
167
179
|
interface OptimisticWriteTriggerOptions<TSchema = unknown, TResponse = unknown, TError = unknown> {
|
|
@@ -170,34 +182,22 @@ interface OptimisticWriteTriggerOptions<TSchema = unknown, TResponse = unknown,
|
|
|
170
182
|
*
|
|
171
183
|
* @example
|
|
172
184
|
* ```ts
|
|
173
|
-
* //
|
|
174
|
-
* trigger({
|
|
175
|
-
* optimistic: (api) => api("posts")
|
|
176
|
-
* .GET()
|
|
177
|
-
* .UPDATE_CACHE(posts => posts.filter(p => p.id !== deletedId)),
|
|
178
|
-
* });
|
|
179
|
-
* ```
|
|
180
|
-
*
|
|
181
|
-
* @example
|
|
182
|
-
* ```ts
|
|
183
|
-
* // With WHERE filter and disable rollback
|
|
185
|
+
* // Optimistic update
|
|
184
186
|
* trigger({
|
|
185
|
-
* optimistic: (
|
|
186
|
-
* .
|
|
187
|
-
* .NO_ROLLBACK()
|
|
188
|
-
* .WHERE(entry => entry.query.page === 1)
|
|
189
|
-
* .UPDATE_CACHE(posts => [newPost, ...posts]),
|
|
187
|
+
* optimistic: (cache) => cache("posts")
|
|
188
|
+
* .set(posts => posts.filter(p => p.id !== deletedId)),
|
|
190
189
|
* });
|
|
191
190
|
* ```
|
|
192
191
|
*
|
|
193
192
|
* @example
|
|
194
193
|
* ```ts
|
|
195
|
-
* //
|
|
194
|
+
* // With filter and confirmed update
|
|
196
195
|
* trigger({
|
|
197
|
-
* optimistic: (
|
|
198
|
-
* .
|
|
199
|
-
* .
|
|
200
|
-
* .
|
|
196
|
+
* optimistic: (cache) => cache("posts/:id")
|
|
197
|
+
* .filter(e => e.params.id === "1")
|
|
198
|
+
* .set(post => ({ ...post, pending: true }))
|
|
199
|
+
* .confirmed()
|
|
200
|
+
* .set((post, response) => response),
|
|
201
201
|
* });
|
|
202
202
|
* ```
|
|
203
203
|
*/
|
|
@@ -224,4 +224,4 @@ declare function optimisticPlugin(): _spoosh_core.SpooshPlugin<{
|
|
|
224
224
|
writeResult: OptimisticWriteResult;
|
|
225
225
|
}>;
|
|
226
226
|
|
|
227
|
-
export { type
|
|
227
|
+
export { type CacheBuilder, type CacheHelper, type OptimisticCallbackFn, type OptimisticPagesOptions, type OptimisticPluginConfig, type OptimisticReadOptions, type OptimisticReadResult, type OptimisticTarget, type OptimisticWriteOptions, type OptimisticWriteResult, type OptimisticWriteTriggerOptions, optimisticPlugin };
|