@spoosh/plugin-optimistic 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +9 -11
- package/dist/index.d.ts +9 -11
- package/dist/index.js +140 -34
- package/dist/index.mjs +140 -34
- package/package.json +5 -5
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FindMatchingKey, HasParams, ExtractParamNames, Simplify, ExtractData, SpooshPlugin } from '@spoosh/core';
|
|
1
|
+
import { ReadPaths, FindMatchingKey, HasParams, ExtractParamNames, Simplify, ExtractData, SpooshPlugin } from '@spoosh/core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Check if query exists in the method config.
|
|
@@ -94,22 +94,20 @@ type OptimisticBuilder<TData = unknown, TMethodConfig = unknown, TUserPath exten
|
|
|
94
94
|
*/
|
|
95
95
|
ON_ERROR: IfNotUsed<"ON_ERROR", TUsed, (callback: (error: unknown) => void) => OptimisticBuilder<TData, TMethodConfig, TUserPath, TResponse, TTiming, TUsed | "ON_ERROR", TCompleted>>;
|
|
96
96
|
};
|
|
97
|
-
/**
|
|
98
|
-
* Extract paths that have GET methods.
|
|
99
|
-
*/
|
|
100
|
-
type ReadPaths<TSchema> = {
|
|
101
|
-
[K in keyof TSchema & string]: "GET" extends keyof TSchema[K] ? K : never;
|
|
102
|
-
}[keyof TSchema & string];
|
|
103
97
|
/**
|
|
104
98
|
* Path methods proxy for optimistic API - only GET.
|
|
99
|
+
* Resolves literal paths (e.g., "posts/1") to schema keys (e.g., "posts/:id") using FindMatchingKey.
|
|
100
|
+
* Uses TPath for param extraction to preserve user's param names.
|
|
105
101
|
*/
|
|
106
102
|
type OptimisticPathMethods<TSchema, TPath extends string, TResponse> = FindMatchingKey<TSchema, TPath> extends infer TKey ? TKey extends keyof TSchema ? TSchema[TKey] extends infer TRoute ? "GET" extends keyof TRoute ? TRoute["GET"] extends infer TGetConfig ? {
|
|
107
103
|
GET: () => OptimisticBuilder<ExtractData<TGetConfig>, TGetConfig, TPath, TResponse, "immediate", never, false>;
|
|
108
104
|
} : never : never : never : never : never;
|
|
109
105
|
/**
|
|
110
106
|
* Helper type for creating the optimistic API proxy.
|
|
107
|
+
* Accepts both schema-defined paths (e.g., "posts/:id") and literal paths (e.g., "posts/1").
|
|
108
|
+
* Uses union with (string & {}) to allow any string while preserving autocomplete.
|
|
111
109
|
*/
|
|
112
|
-
type OptimisticApiHelper<TSchema, TResponse = unknown> = <TPath extends ReadPaths<TSchema
|
|
110
|
+
type OptimisticApiHelper<TSchema, TResponse = unknown> = <TPath extends ReadPaths<TSchema> | (string & {})>(path: TPath) => OptimisticPathMethods<TSchema, TPath, TResponse>;
|
|
113
111
|
/**
|
|
114
112
|
* A generic OptimisticTarget that accepts any data/response types.
|
|
115
113
|
* Used for the return type of the callback.
|
|
@@ -205,7 +203,7 @@ interface OptimisticWriteTriggerOptions<TSchema = unknown, TResponse = unknown>
|
|
|
205
203
|
optimistic?: OptimisticCallbackFn<TSchema, TResponse>;
|
|
206
204
|
}
|
|
207
205
|
type OptimisticReadOptions = object;
|
|
208
|
-
type
|
|
206
|
+
type OptimisticPagesOptions = object;
|
|
209
207
|
interface OptimisticReadResult {
|
|
210
208
|
isOptimistic: boolean;
|
|
211
209
|
}
|
|
@@ -220,9 +218,9 @@ declare function optimisticPlugin(): SpooshPlugin<{
|
|
|
220
218
|
readOptions: OptimisticReadOptions;
|
|
221
219
|
writeOptions: OptimisticWriteOptions;
|
|
222
220
|
writeTriggerOptions: OptimisticWriteTriggerOptions;
|
|
223
|
-
|
|
221
|
+
pagesOptions: OptimisticPagesOptions;
|
|
224
222
|
readResult: OptimisticReadResult;
|
|
225
223
|
writeResult: OptimisticWriteResult;
|
|
226
224
|
}>;
|
|
227
225
|
|
|
228
|
-
export { type AnyOptimisticTarget, type OptimisticApiHelper, type OptimisticBuilder, type OptimisticCallbackFn, type
|
|
226
|
+
export { type AnyOptimisticTarget, type OptimisticApiHelper, type OptimisticBuilder, type OptimisticCallbackFn, type OptimisticPagesOptions, type OptimisticPluginConfig, type OptimisticReadOptions, type OptimisticReadResult, type OptimisticTarget, type OptimisticWriteOptions, type OptimisticWriteResult, type OptimisticWriteTriggerOptions, optimisticPlugin };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FindMatchingKey, HasParams, ExtractParamNames, Simplify, ExtractData, SpooshPlugin } from '@spoosh/core';
|
|
1
|
+
import { ReadPaths, FindMatchingKey, HasParams, ExtractParamNames, Simplify, ExtractData, SpooshPlugin } from '@spoosh/core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Check if query exists in the method config.
|
|
@@ -94,22 +94,20 @@ type OptimisticBuilder<TData = unknown, TMethodConfig = unknown, TUserPath exten
|
|
|
94
94
|
*/
|
|
95
95
|
ON_ERROR: IfNotUsed<"ON_ERROR", TUsed, (callback: (error: unknown) => void) => OptimisticBuilder<TData, TMethodConfig, TUserPath, TResponse, TTiming, TUsed | "ON_ERROR", TCompleted>>;
|
|
96
96
|
};
|
|
97
|
-
/**
|
|
98
|
-
* Extract paths that have GET methods.
|
|
99
|
-
*/
|
|
100
|
-
type ReadPaths<TSchema> = {
|
|
101
|
-
[K in keyof TSchema & string]: "GET" extends keyof TSchema[K] ? K : never;
|
|
102
|
-
}[keyof TSchema & string];
|
|
103
97
|
/**
|
|
104
98
|
* Path methods proxy for optimistic API - only GET.
|
|
99
|
+
* Resolves literal paths (e.g., "posts/1") to schema keys (e.g., "posts/:id") using FindMatchingKey.
|
|
100
|
+
* Uses TPath for param extraction to preserve user's param names.
|
|
105
101
|
*/
|
|
106
102
|
type OptimisticPathMethods<TSchema, TPath extends string, TResponse> = FindMatchingKey<TSchema, TPath> extends infer TKey ? TKey extends keyof TSchema ? TSchema[TKey] extends infer TRoute ? "GET" extends keyof TRoute ? TRoute["GET"] extends infer TGetConfig ? {
|
|
107
103
|
GET: () => OptimisticBuilder<ExtractData<TGetConfig>, TGetConfig, TPath, TResponse, "immediate", never, false>;
|
|
108
104
|
} : never : never : never : never : never;
|
|
109
105
|
/**
|
|
110
106
|
* Helper type for creating the optimistic API proxy.
|
|
107
|
+
* Accepts both schema-defined paths (e.g., "posts/:id") and literal paths (e.g., "posts/1").
|
|
108
|
+
* Uses union with (string & {}) to allow any string while preserving autocomplete.
|
|
111
109
|
*/
|
|
112
|
-
type OptimisticApiHelper<TSchema, TResponse = unknown> = <TPath extends ReadPaths<TSchema
|
|
110
|
+
type OptimisticApiHelper<TSchema, TResponse = unknown> = <TPath extends ReadPaths<TSchema> | (string & {})>(path: TPath) => OptimisticPathMethods<TSchema, TPath, TResponse>;
|
|
113
111
|
/**
|
|
114
112
|
* A generic OptimisticTarget that accepts any data/response types.
|
|
115
113
|
* Used for the return type of the callback.
|
|
@@ -205,7 +203,7 @@ interface OptimisticWriteTriggerOptions<TSchema = unknown, TResponse = unknown>
|
|
|
205
203
|
optimistic?: OptimisticCallbackFn<TSchema, TResponse>;
|
|
206
204
|
}
|
|
207
205
|
type OptimisticReadOptions = object;
|
|
208
|
-
type
|
|
206
|
+
type OptimisticPagesOptions = object;
|
|
209
207
|
interface OptimisticReadResult {
|
|
210
208
|
isOptimistic: boolean;
|
|
211
209
|
}
|
|
@@ -220,9 +218,9 @@ declare function optimisticPlugin(): SpooshPlugin<{
|
|
|
220
218
|
readOptions: OptimisticReadOptions;
|
|
221
219
|
writeOptions: OptimisticWriteOptions;
|
|
222
220
|
writeTriggerOptions: OptimisticWriteTriggerOptions;
|
|
223
|
-
|
|
221
|
+
pagesOptions: OptimisticPagesOptions;
|
|
224
222
|
readResult: OptimisticReadResult;
|
|
225
223
|
writeResult: OptimisticWriteResult;
|
|
226
224
|
}>;
|
|
227
225
|
|
|
228
|
-
export { type AnyOptimisticTarget, type OptimisticApiHelper, type OptimisticBuilder, type OptimisticCallbackFn, type
|
|
226
|
+
export { type AnyOptimisticTarget, type OptimisticApiHelper, type OptimisticBuilder, type OptimisticCallbackFn, type OptimisticPagesOptions, type OptimisticPluginConfig, type OptimisticReadOptions, type OptimisticReadResult, type OptimisticTarget, type OptimisticWriteOptions, type OptimisticWriteResult, type OptimisticWriteTriggerOptions, optimisticPlugin };
|
package/dist/index.js
CHANGED
|
@@ -25,7 +25,6 @@ __export(src_exports, {
|
|
|
25
25
|
module.exports = __toCommonJS(src_exports);
|
|
26
26
|
|
|
27
27
|
// src/plugin.ts
|
|
28
|
-
var import_core = require("@spoosh/core");
|
|
29
28
|
var import_plugin_invalidation = require("@spoosh/plugin-invalidation");
|
|
30
29
|
function createBuilder(state) {
|
|
31
30
|
return {
|
|
@@ -58,12 +57,50 @@ function createOptimisticProxy() {
|
|
|
58
57
|
});
|
|
59
58
|
return ((path) => createMethodsProxy(path));
|
|
60
59
|
}
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
return (0, import_core.generateTags)(pathSegments);
|
|
60
|
+
function isParameterSegment(segment) {
|
|
61
|
+
return segment.startsWith(":");
|
|
64
62
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
63
|
+
function pathMatchesPattern(actualPath, pattern) {
|
|
64
|
+
const actualSegments = actualPath.split("/").filter(Boolean);
|
|
65
|
+
const patternSegments = pattern.split("/").filter(Boolean);
|
|
66
|
+
if (actualSegments.length !== patternSegments.length) {
|
|
67
|
+
return { matches: false, params: {}, paramMapping: {} };
|
|
68
|
+
}
|
|
69
|
+
const params = {};
|
|
70
|
+
const paramMapping = {};
|
|
71
|
+
for (let i = 0; i < patternSegments.length; i++) {
|
|
72
|
+
const patternSeg = patternSegments[i];
|
|
73
|
+
const actualSeg = actualSegments[i];
|
|
74
|
+
if (isParameterSegment(patternSeg)) {
|
|
75
|
+
const targetParamName = patternSeg.slice(1);
|
|
76
|
+
if (isParameterSegment(actualSeg)) {
|
|
77
|
+
const actualParamName = actualSeg.slice(1);
|
|
78
|
+
paramMapping[targetParamName] = actualParamName;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
params[targetParamName] = actualSeg;
|
|
82
|
+
} else if (isParameterSegment(actualSeg)) {
|
|
83
|
+
continue;
|
|
84
|
+
} else if (patternSeg !== actualSeg) {
|
|
85
|
+
return { matches: false, params: {}, paramMapping: {} };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { matches: true, params, paramMapping };
|
|
89
|
+
}
|
|
90
|
+
function hasPatternParams(path) {
|
|
91
|
+
return path.split("/").some(isParameterSegment);
|
|
92
|
+
}
|
|
93
|
+
function extractPathFromKey(key) {
|
|
94
|
+
try {
|
|
95
|
+
const parsed = JSON.parse(key);
|
|
96
|
+
const path = parsed.path;
|
|
97
|
+
if (typeof path === "string") {
|
|
98
|
+
return path;
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
} catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
67
104
|
}
|
|
68
105
|
function extractOptionsFromKey(key) {
|
|
69
106
|
try {
|
|
@@ -82,6 +119,21 @@ function extractOptionsFromKey(key) {
|
|
|
82
119
|
return null;
|
|
83
120
|
}
|
|
84
121
|
}
|
|
122
|
+
function mapParamsToTargetNames(actualParams, paramMapping) {
|
|
123
|
+
if (!actualParams) return {};
|
|
124
|
+
const result = {};
|
|
125
|
+
for (const [targetName, actualName] of Object.entries(paramMapping)) {
|
|
126
|
+
if (actualName in actualParams) {
|
|
127
|
+
result[targetName] = actualParams[actualName];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
for (const [key, value] of Object.entries(actualParams)) {
|
|
131
|
+
if (!Object.values(paramMapping).includes(key)) {
|
|
132
|
+
result[key] = value;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
85
137
|
function resolveOptimisticTargets(context) {
|
|
86
138
|
const pluginOptions = context.pluginOptions;
|
|
87
139
|
if (!pluginOptions?.optimistic) return [];
|
|
@@ -90,27 +142,70 @@ function resolveOptimisticTargets(context) {
|
|
|
90
142
|
const targets = Array.isArray(result) ? result : [result];
|
|
91
143
|
return targets;
|
|
92
144
|
}
|
|
145
|
+
function getMatchingEntries(stateManager, targetPath, targetMethod) {
|
|
146
|
+
const results = [];
|
|
147
|
+
if (hasPatternParams(targetPath)) {
|
|
148
|
+
const allEntries = stateManager.getAllCacheEntries();
|
|
149
|
+
for (const { key, entry } of allEntries) {
|
|
150
|
+
if (!key.includes(`"method":"${targetMethod}"`)) continue;
|
|
151
|
+
const actualPath = extractPathFromKey(key);
|
|
152
|
+
if (!actualPath) continue;
|
|
153
|
+
const { matches, params, paramMapping } = pathMatchesPattern(
|
|
154
|
+
actualPath,
|
|
155
|
+
targetPath
|
|
156
|
+
);
|
|
157
|
+
if (matches) {
|
|
158
|
+
results.push({ key, entry, extractedParams: params, paramMapping });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
const allEntries = stateManager.getAllCacheEntries();
|
|
163
|
+
for (const { key, entry } of allEntries) {
|
|
164
|
+
if (!key.includes(`"method":"${targetMethod}"`)) continue;
|
|
165
|
+
const actualPath = extractPathFromKey(key);
|
|
166
|
+
if (!actualPath) continue;
|
|
167
|
+
if (actualPath === targetPath) {
|
|
168
|
+
results.push({ key, entry, extractedParams: {}, paramMapping: {} });
|
|
169
|
+
} else if (hasPatternParams(actualPath)) {
|
|
170
|
+
const { matches, params, paramMapping } = pathMatchesPattern(
|
|
171
|
+
targetPath,
|
|
172
|
+
actualPath
|
|
173
|
+
);
|
|
174
|
+
if (matches) {
|
|
175
|
+
results.push({ key, entry, extractedParams: params, paramMapping });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return results;
|
|
181
|
+
}
|
|
93
182
|
function applyOptimisticUpdate(stateManager, target, t) {
|
|
94
183
|
if (!target.updater) return [];
|
|
95
|
-
const tags = extractTagsFromPath(target.path);
|
|
96
|
-
const targetSelfTag = getExactMatchPath(tags);
|
|
97
|
-
if (!targetSelfTag) return [];
|
|
98
184
|
const snapshots = [];
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
185
|
+
const matchingEntries = getMatchingEntries(
|
|
186
|
+
stateManager,
|
|
187
|
+
target.path,
|
|
188
|
+
target.method
|
|
189
|
+
);
|
|
190
|
+
for (const { key, entry, extractedParams, paramMapping } of matchingEntries) {
|
|
107
191
|
if (target.where) {
|
|
108
|
-
const options = extractOptionsFromKey(key);
|
|
109
|
-
|
|
192
|
+
const options = extractOptionsFromKey(key) ?? {};
|
|
193
|
+
const mappedParams = mapParamsToTargetNames(
|
|
194
|
+
options.params,
|
|
195
|
+
paramMapping
|
|
196
|
+
);
|
|
197
|
+
const mergedOptions = {
|
|
198
|
+
...options,
|
|
199
|
+
params: {
|
|
200
|
+
...extractedParams,
|
|
201
|
+
...mappedParams
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
if (!target.where(mergedOptions)) {
|
|
110
205
|
continue;
|
|
111
206
|
}
|
|
112
207
|
}
|
|
113
|
-
if (entry
|
|
208
|
+
if (entry?.state.data === void 0) {
|
|
114
209
|
continue;
|
|
115
210
|
}
|
|
116
211
|
const afterData = target.updater(entry.state.data, void 0);
|
|
@@ -233,24 +328,35 @@ function optimisticPlugin() {
|
|
|
233
328
|
const onSuccessSnapshots = [];
|
|
234
329
|
for (const target of onSuccessTargets) {
|
|
235
330
|
if (!target.updater) continue;
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
331
|
+
const matchingEntries = getMatchingEntries(
|
|
332
|
+
stateManager,
|
|
333
|
+
target.path,
|
|
334
|
+
target.method
|
|
335
|
+
);
|
|
336
|
+
for (const {
|
|
337
|
+
key,
|
|
338
|
+
entry,
|
|
339
|
+
extractedParams,
|
|
340
|
+
paramMapping
|
|
341
|
+
} of matchingEntries) {
|
|
247
342
|
if (target.where) {
|
|
248
|
-
const options = extractOptionsFromKey(key);
|
|
249
|
-
|
|
343
|
+
const options = extractOptionsFromKey(key) ?? {};
|
|
344
|
+
const mappedParams = mapParamsToTargetNames(
|
|
345
|
+
options.params,
|
|
346
|
+
paramMapping
|
|
347
|
+
);
|
|
348
|
+
const mergedOptions = {
|
|
349
|
+
...options,
|
|
350
|
+
params: {
|
|
351
|
+
...extractedParams,
|
|
352
|
+
...mappedParams
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
if (!target.where(mergedOptions)) {
|
|
250
356
|
continue;
|
|
251
357
|
}
|
|
252
358
|
}
|
|
253
|
-
if (entry
|
|
359
|
+
if (entry?.state.data === void 0) {
|
|
254
360
|
continue;
|
|
255
361
|
}
|
|
256
362
|
const afterData = target.updater(entry.state.data, response.data);
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// src/plugin.ts
|
|
2
|
-
import { generateTags } from "@spoosh/core";
|
|
3
2
|
import "@spoosh/plugin-invalidation";
|
|
4
3
|
function createBuilder(state) {
|
|
5
4
|
return {
|
|
@@ -32,12 +31,50 @@ function createOptimisticProxy() {
|
|
|
32
31
|
});
|
|
33
32
|
return ((path) => createMethodsProxy(path));
|
|
34
33
|
}
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
return generateTags(pathSegments);
|
|
34
|
+
function isParameterSegment(segment) {
|
|
35
|
+
return segment.startsWith(":");
|
|
38
36
|
}
|
|
39
|
-
function
|
|
40
|
-
|
|
37
|
+
function pathMatchesPattern(actualPath, pattern) {
|
|
38
|
+
const actualSegments = actualPath.split("/").filter(Boolean);
|
|
39
|
+
const patternSegments = pattern.split("/").filter(Boolean);
|
|
40
|
+
if (actualSegments.length !== patternSegments.length) {
|
|
41
|
+
return { matches: false, params: {}, paramMapping: {} };
|
|
42
|
+
}
|
|
43
|
+
const params = {};
|
|
44
|
+
const paramMapping = {};
|
|
45
|
+
for (let i = 0; i < patternSegments.length; i++) {
|
|
46
|
+
const patternSeg = patternSegments[i];
|
|
47
|
+
const actualSeg = actualSegments[i];
|
|
48
|
+
if (isParameterSegment(patternSeg)) {
|
|
49
|
+
const targetParamName = patternSeg.slice(1);
|
|
50
|
+
if (isParameterSegment(actualSeg)) {
|
|
51
|
+
const actualParamName = actualSeg.slice(1);
|
|
52
|
+
paramMapping[targetParamName] = actualParamName;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
params[targetParamName] = actualSeg;
|
|
56
|
+
} else if (isParameterSegment(actualSeg)) {
|
|
57
|
+
continue;
|
|
58
|
+
} else if (patternSeg !== actualSeg) {
|
|
59
|
+
return { matches: false, params: {}, paramMapping: {} };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return { matches: true, params, paramMapping };
|
|
63
|
+
}
|
|
64
|
+
function hasPatternParams(path) {
|
|
65
|
+
return path.split("/").some(isParameterSegment);
|
|
66
|
+
}
|
|
67
|
+
function extractPathFromKey(key) {
|
|
68
|
+
try {
|
|
69
|
+
const parsed = JSON.parse(key);
|
|
70
|
+
const path = parsed.path;
|
|
71
|
+
if (typeof path === "string") {
|
|
72
|
+
return path;
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
41
78
|
}
|
|
42
79
|
function extractOptionsFromKey(key) {
|
|
43
80
|
try {
|
|
@@ -56,6 +93,21 @@ function extractOptionsFromKey(key) {
|
|
|
56
93
|
return null;
|
|
57
94
|
}
|
|
58
95
|
}
|
|
96
|
+
function mapParamsToTargetNames(actualParams, paramMapping) {
|
|
97
|
+
if (!actualParams) return {};
|
|
98
|
+
const result = {};
|
|
99
|
+
for (const [targetName, actualName] of Object.entries(paramMapping)) {
|
|
100
|
+
if (actualName in actualParams) {
|
|
101
|
+
result[targetName] = actualParams[actualName];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
for (const [key, value] of Object.entries(actualParams)) {
|
|
105
|
+
if (!Object.values(paramMapping).includes(key)) {
|
|
106
|
+
result[key] = value;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
59
111
|
function resolveOptimisticTargets(context) {
|
|
60
112
|
const pluginOptions = context.pluginOptions;
|
|
61
113
|
if (!pluginOptions?.optimistic) return [];
|
|
@@ -64,27 +116,70 @@ function resolveOptimisticTargets(context) {
|
|
|
64
116
|
const targets = Array.isArray(result) ? result : [result];
|
|
65
117
|
return targets;
|
|
66
118
|
}
|
|
119
|
+
function getMatchingEntries(stateManager, targetPath, targetMethod) {
|
|
120
|
+
const results = [];
|
|
121
|
+
if (hasPatternParams(targetPath)) {
|
|
122
|
+
const allEntries = stateManager.getAllCacheEntries();
|
|
123
|
+
for (const { key, entry } of allEntries) {
|
|
124
|
+
if (!key.includes(`"method":"${targetMethod}"`)) continue;
|
|
125
|
+
const actualPath = extractPathFromKey(key);
|
|
126
|
+
if (!actualPath) continue;
|
|
127
|
+
const { matches, params, paramMapping } = pathMatchesPattern(
|
|
128
|
+
actualPath,
|
|
129
|
+
targetPath
|
|
130
|
+
);
|
|
131
|
+
if (matches) {
|
|
132
|
+
results.push({ key, entry, extractedParams: params, paramMapping });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
const allEntries = stateManager.getAllCacheEntries();
|
|
137
|
+
for (const { key, entry } of allEntries) {
|
|
138
|
+
if (!key.includes(`"method":"${targetMethod}"`)) continue;
|
|
139
|
+
const actualPath = extractPathFromKey(key);
|
|
140
|
+
if (!actualPath) continue;
|
|
141
|
+
if (actualPath === targetPath) {
|
|
142
|
+
results.push({ key, entry, extractedParams: {}, paramMapping: {} });
|
|
143
|
+
} else if (hasPatternParams(actualPath)) {
|
|
144
|
+
const { matches, params, paramMapping } = pathMatchesPattern(
|
|
145
|
+
targetPath,
|
|
146
|
+
actualPath
|
|
147
|
+
);
|
|
148
|
+
if (matches) {
|
|
149
|
+
results.push({ key, entry, extractedParams: params, paramMapping });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return results;
|
|
155
|
+
}
|
|
67
156
|
function applyOptimisticUpdate(stateManager, target, t) {
|
|
68
157
|
if (!target.updater) return [];
|
|
69
|
-
const tags = extractTagsFromPath(target.path);
|
|
70
|
-
const targetSelfTag = getExactMatchPath(tags);
|
|
71
|
-
if (!targetSelfTag) return [];
|
|
72
158
|
const snapshots = [];
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
159
|
+
const matchingEntries = getMatchingEntries(
|
|
160
|
+
stateManager,
|
|
161
|
+
target.path,
|
|
162
|
+
target.method
|
|
163
|
+
);
|
|
164
|
+
for (const { key, entry, extractedParams, paramMapping } of matchingEntries) {
|
|
81
165
|
if (target.where) {
|
|
82
|
-
const options = extractOptionsFromKey(key);
|
|
83
|
-
|
|
166
|
+
const options = extractOptionsFromKey(key) ?? {};
|
|
167
|
+
const mappedParams = mapParamsToTargetNames(
|
|
168
|
+
options.params,
|
|
169
|
+
paramMapping
|
|
170
|
+
);
|
|
171
|
+
const mergedOptions = {
|
|
172
|
+
...options,
|
|
173
|
+
params: {
|
|
174
|
+
...extractedParams,
|
|
175
|
+
...mappedParams
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
if (!target.where(mergedOptions)) {
|
|
84
179
|
continue;
|
|
85
180
|
}
|
|
86
181
|
}
|
|
87
|
-
if (entry
|
|
182
|
+
if (entry?.state.data === void 0) {
|
|
88
183
|
continue;
|
|
89
184
|
}
|
|
90
185
|
const afterData = target.updater(entry.state.data, void 0);
|
|
@@ -207,24 +302,35 @@ function optimisticPlugin() {
|
|
|
207
302
|
const onSuccessSnapshots = [];
|
|
208
303
|
for (const target of onSuccessTargets) {
|
|
209
304
|
if (!target.updater) continue;
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
305
|
+
const matchingEntries = getMatchingEntries(
|
|
306
|
+
stateManager,
|
|
307
|
+
target.path,
|
|
308
|
+
target.method
|
|
309
|
+
);
|
|
310
|
+
for (const {
|
|
311
|
+
key,
|
|
312
|
+
entry,
|
|
313
|
+
extractedParams,
|
|
314
|
+
paramMapping
|
|
315
|
+
} of matchingEntries) {
|
|
221
316
|
if (target.where) {
|
|
222
|
-
const options = extractOptionsFromKey(key);
|
|
223
|
-
|
|
317
|
+
const options = extractOptionsFromKey(key) ?? {};
|
|
318
|
+
const mappedParams = mapParamsToTargetNames(
|
|
319
|
+
options.params,
|
|
320
|
+
paramMapping
|
|
321
|
+
);
|
|
322
|
+
const mergedOptions = {
|
|
323
|
+
...options,
|
|
324
|
+
params: {
|
|
325
|
+
...extractedParams,
|
|
326
|
+
...mappedParams
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
if (!target.where(mergedOptions)) {
|
|
224
330
|
continue;
|
|
225
331
|
}
|
|
226
332
|
}
|
|
227
|
-
if (entry
|
|
333
|
+
if (entry?.state.data === void 0) {
|
|
228
334
|
continue;
|
|
229
335
|
}
|
|
230
336
|
const afterData = target.updater(entry.state.data, response.data);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spoosh/plugin-optimistic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Optimistic updates plugin for Spoosh - instant UI updates with automatic rollback",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@spoosh/core": ">=0.
|
|
36
|
+
"@spoosh/core": ">=0.15.0",
|
|
37
37
|
"@spoosh/plugin-invalidation": ">=0.7.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@spoosh/core": "0.
|
|
41
|
-
"@spoosh/plugin-invalidation": "0.
|
|
42
|
-
"@spoosh/test-utils": "0.
|
|
40
|
+
"@spoosh/core": "0.15.0",
|
|
41
|
+
"@spoosh/plugin-invalidation": "0.9.0",
|
|
42
|
+
"@spoosh/test-utils": "0.3.0"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"dev": "tsup --watch",
|