@spoosh/plugin-optimistic 0.8.0 → 0.8.2

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.js CHANGED
@@ -32,54 +32,28 @@ var import_plugin_invalidation = require("@spoosh/plugin-invalidation");
32
32
  function isParameterSegment(segment) {
33
33
  return segment.startsWith(":");
34
34
  }
35
- function hasPatternParams(path) {
36
- return path.split("/").some(isParameterSegment);
37
- }
38
- function pathMatchesPattern(actualPath, pattern) {
39
- const actualSegments = actualPath.split("/").filter(Boolean);
35
+ function pathMatchesPattern(resolvedPath, pattern) {
36
+ const resolvedSegments = resolvedPath.split("/").filter(Boolean);
40
37
  const patternSegments = pattern.split("/").filter(Boolean);
41
- if (actualSegments.length !== patternSegments.length) {
42
- return { matches: false, params: {}, paramMapping: {} };
38
+ if (resolvedSegments.length !== patternSegments.length) {
39
+ return { matches: false, params: {} };
43
40
  }
44
41
  const params = {};
45
- const paramMapping = {};
46
42
  for (let i = 0; i < patternSegments.length; i++) {
47
43
  const patternSeg = patternSegments[i];
48
- const actualSeg = actualSegments[i];
44
+ const resolvedSeg = resolvedSegments[i];
49
45
  if (isParameterSegment(patternSeg)) {
50
- const targetParamName = patternSeg.slice(1);
51
- if (isParameterSegment(actualSeg)) {
52
- const actualParamName = actualSeg.slice(1);
53
- paramMapping[targetParamName] = actualParamName;
54
- continue;
55
- }
56
- params[targetParamName] = actualSeg;
57
- } else if (isParameterSegment(actualSeg)) {
58
- continue;
59
- } else if (patternSeg !== actualSeg) {
60
- return { matches: false, params: {}, paramMapping: {} };
46
+ const paramName = patternSeg.slice(1);
47
+ params[paramName] = resolvedSeg;
48
+ } else if (patternSeg !== resolvedSeg) {
49
+ return { matches: false, params: {} };
61
50
  }
62
51
  }
63
- return { matches: true, params, paramMapping };
52
+ return { matches: true, params };
64
53
  }
65
54
 
66
55
  // src/utils/cache-key.ts
67
56
  var import_core = require("@spoosh/core");
68
- function extractPathFromKey(key) {
69
- try {
70
- const parsed = JSON.parse(key);
71
- const path = parsed.path;
72
- if (typeof path === "string") {
73
- return path;
74
- }
75
- if (Array.isArray(path)) {
76
- return path.join("/");
77
- }
78
- return null;
79
- } catch {
80
- return null;
81
- }
82
- }
83
57
  function formatCacheKeyForTrace(key) {
84
58
  const resolvedPath = (0, import_core.generateSelfTagFromKey)(key);
85
59
  if (!resolvedPath) return "unknown";
@@ -122,26 +96,16 @@ function extractOptionsFromKey(key) {
122
96
  return null;
123
97
  }
124
98
  }
125
- function mapParamsToTargetNames(actualParams, paramMapping) {
126
- if (!actualParams) return {};
127
- const result = {};
128
- for (const [targetName, actualName] of Object.entries(paramMapping)) {
129
- if (actualName in actualParams) {
130
- result[targetName] = String(actualParams[actualName]);
131
- }
132
- }
133
- for (const [key, value] of Object.entries(actualParams)) {
134
- if (!Object.values(paramMapping).includes(key)) {
135
- result[key] = String(value);
136
- }
137
- }
138
- return result;
139
- }
99
+
100
+ // src/plugin.ts
101
+ var import_core3 = require("@spoosh/core");
140
102
 
141
103
  // src/builder/index.ts
142
104
  function createBuilder(state, isConfirmed = false) {
143
105
  return {
144
106
  ...state,
107
+ // Expose the internal state so the plugin can access predicates
108
+ __state: state,
145
109
  filter(predicate) {
146
110
  return createBuilder({ ...state, filter: predicate }, isConfirmed);
147
111
  },
@@ -193,27 +157,13 @@ function resolveOptimisticTargets(context) {
193
157
  function getMatchingEntries(stateManager, targetPath) {
194
158
  const results = [];
195
159
  const allEntries = stateManager.getAllCacheEntries();
196
- if (hasPatternParams(targetPath)) {
197
- for (const { key, entry } of allEntries) {
198
- if (!key.includes(`"method":"GET"`)) continue;
199
- const actualPath = extractPathFromKey(key);
200
- if (!actualPath) continue;
201
- const { matches, params, paramMapping } = pathMatchesPattern(
202
- actualPath,
203
- targetPath
204
- );
205
- if (matches) {
206
- results.push({ key, entry, extractedParams: params, paramMapping });
207
- }
208
- }
209
- } else {
210
- for (const { key, entry } of allEntries) {
211
- if (!key.includes(`"method":"GET"`)) continue;
212
- const actualPath = extractPathFromKey(key);
213
- if (!actualPath) continue;
214
- if (actualPath === targetPath) {
215
- results.push({ key, entry, extractedParams: {}, paramMapping: {} });
216
- }
160
+ for (const { key, entry } of allEntries) {
161
+ if (!key.includes(`"method":"GET"`)) continue;
162
+ const resolvedPath = (0, import_core3.generateSelfTagFromKey)(key);
163
+ if (!resolvedPath) continue;
164
+ const { matches, params } = pathMatchesPattern(resolvedPath, targetPath);
165
+ if (matches) {
166
+ results.push({ key, entry, extractedParams: params });
217
167
  }
218
168
  }
219
169
  return results;
@@ -222,28 +172,22 @@ function applyUpdate(stateManager, target, updater, response, t) {
222
172
  const snapshots = [];
223
173
  const matchingEntries = getMatchingEntries(stateManager, target.path);
224
174
  if (matchingEntries.length === 0) {
225
- t?.skip(`Skipped ${target.path} (no cache entry)`);
226
175
  return [];
227
176
  }
228
- for (const { key, entry, extractedParams, paramMapping } of matchingEntries) {
229
- if (target.filter) {
177
+ for (const { key, entry, extractedParams } of matchingEntries) {
178
+ const targetWithState = target;
179
+ const filterPredicate = targetWithState.__state ? targetWithState.__state.filter : target.filter;
180
+ if (filterPredicate) {
230
181
  const options = extractOptionsFromKey(key) ?? {};
231
- const mappedParams = mapParamsToTargetNames(
232
- options.params,
233
- paramMapping
234
- );
235
182
  const mergedOptions = {
236
183
  ...options,
237
184
  params: {
238
- ...extractedParams,
239
- ...mappedParams
185
+ ...options.params,
186
+ ...extractedParams
240
187
  }
241
188
  };
242
189
  try {
243
- if (!target.filter(mergedOptions)) {
244
- t?.skip(
245
- `Skipped ${formatCacheKeyForTrace(key)} (filter not matched)`
246
- );
190
+ if (!filterPredicate(mergedOptions)) {
247
191
  continue;
248
192
  }
249
193
  } catch {
@@ -252,7 +196,6 @@ function applyUpdate(stateManager, target, updater, response, t) {
252
196
  }
253
197
  }
254
198
  if (entry?.state.data === void 0) {
255
- t?.skip(`Skipped ${formatCacheKeyForTrace(key)} (no cached data)`);
256
199
  continue;
257
200
  }
258
201
  const afterData = updater(entry.state.data, response);
@@ -315,11 +258,13 @@ function optimisticPlugin() {
315
258
  context.plugins.get("spoosh:invalidation")?.setDefaultMode("none");
316
259
  const allImmediateSnapshots = [];
317
260
  for (const target of targets) {
318
- if (!target.immediateUpdater) continue;
261
+ const targetWithState = target;
262
+ const immediateUpdater = targetWithState.__state ? targetWithState.__state.immediateUpdater : target.immediateUpdater;
263
+ if (!immediateUpdater) continue;
319
264
  const snapshots = applyUpdate(
320
265
  stateManager,
321
266
  target,
322
- target.immediateUpdater,
267
+ immediateUpdater,
323
268
  void 0,
324
269
  t
325
270
  );
@@ -333,9 +278,12 @@ function optimisticPlugin() {
333
278
  }
334
279
  const response = await next();
335
280
  if (response.error) {
336
- const shouldRollback = targets.some(
337
- (target) => target.rollbackOnError && target.immediateUpdater
338
- );
281
+ const shouldRollback = targets.some((target) => {
282
+ const targetWithState = target;
283
+ const rollbackOnError = targetWithState.__state ? targetWithState.__state.rollbackOnError : target.rollbackOnError;
284
+ const immediateUpdater = targetWithState.__state ? targetWithState.__state.immediateUpdater : target.immediateUpdater;
285
+ return rollbackOnError && immediateUpdater;
286
+ });
339
287
  if (shouldRollback && allImmediateSnapshots.length > 0) {
340
288
  rollbackOptimistic(stateManager, allImmediateSnapshots);
341
289
  for (const snapshot of allImmediateSnapshots) {
@@ -346,8 +294,10 @@ function optimisticPlugin() {
346
294
  }
347
295
  }
348
296
  for (const target of targets) {
349
- if (target.onError) {
350
- target.onError(response.error);
297
+ const targetWithState = target;
298
+ const onError = targetWithState.__state ? targetWithState.__state.onError : target.onError;
299
+ if (onError) {
300
+ onError(response.error);
351
301
  }
352
302
  }
353
303
  } else {
@@ -355,11 +305,13 @@ function optimisticPlugin() {
355
305
  confirmOptimistic(stateManager, allImmediateSnapshots);
356
306
  }
357
307
  for (const target of targets) {
358
- if (!target.confirmedUpdater) continue;
308
+ const targetWithState = target;
309
+ const confirmedUpdater = targetWithState.__state ? targetWithState.__state.confirmedUpdater : target.confirmedUpdater;
310
+ if (!confirmedUpdater) continue;
359
311
  const snapshots = applyUpdate(
360
312
  stateManager,
361
313
  target,
362
- target.confirmedUpdater,
314
+ confirmedUpdater,
363
315
  response.data,
364
316
  t
365
317
  );
package/dist/index.mjs CHANGED
@@ -8,54 +8,28 @@ import "@spoosh/plugin-invalidation";
8
8
  function isParameterSegment(segment) {
9
9
  return segment.startsWith(":");
10
10
  }
11
- function hasPatternParams(path) {
12
- return path.split("/").some(isParameterSegment);
13
- }
14
- function pathMatchesPattern(actualPath, pattern) {
15
- const actualSegments = actualPath.split("/").filter(Boolean);
11
+ function pathMatchesPattern(resolvedPath, pattern) {
12
+ const resolvedSegments = resolvedPath.split("/").filter(Boolean);
16
13
  const patternSegments = pattern.split("/").filter(Boolean);
17
- if (actualSegments.length !== patternSegments.length) {
18
- return { matches: false, params: {}, paramMapping: {} };
14
+ if (resolvedSegments.length !== patternSegments.length) {
15
+ return { matches: false, params: {} };
19
16
  }
20
17
  const params = {};
21
- const paramMapping = {};
22
18
  for (let i = 0; i < patternSegments.length; i++) {
23
19
  const patternSeg = patternSegments[i];
24
- const actualSeg = actualSegments[i];
20
+ const resolvedSeg = resolvedSegments[i];
25
21
  if (isParameterSegment(patternSeg)) {
26
- const targetParamName = patternSeg.slice(1);
27
- if (isParameterSegment(actualSeg)) {
28
- const actualParamName = actualSeg.slice(1);
29
- paramMapping[targetParamName] = actualParamName;
30
- continue;
31
- }
32
- params[targetParamName] = actualSeg;
33
- } else if (isParameterSegment(actualSeg)) {
34
- continue;
35
- } else if (patternSeg !== actualSeg) {
36
- return { matches: false, params: {}, paramMapping: {} };
22
+ const paramName = patternSeg.slice(1);
23
+ params[paramName] = resolvedSeg;
24
+ } else if (patternSeg !== resolvedSeg) {
25
+ return { matches: false, params: {} };
37
26
  }
38
27
  }
39
- return { matches: true, params, paramMapping };
28
+ return { matches: true, params };
40
29
  }
41
30
 
42
31
  // src/utils/cache-key.ts
43
32
  import { generateSelfTagFromKey } from "@spoosh/core";
44
- function extractPathFromKey(key) {
45
- try {
46
- const parsed = JSON.parse(key);
47
- const path = parsed.path;
48
- if (typeof path === "string") {
49
- return path;
50
- }
51
- if (Array.isArray(path)) {
52
- return path.join("/");
53
- }
54
- return null;
55
- } catch {
56
- return null;
57
- }
58
- }
59
33
  function formatCacheKeyForTrace(key) {
60
34
  const resolvedPath = generateSelfTagFromKey(key);
61
35
  if (!resolvedPath) return "unknown";
@@ -98,26 +72,16 @@ function extractOptionsFromKey(key) {
98
72
  return null;
99
73
  }
100
74
  }
101
- function mapParamsToTargetNames(actualParams, paramMapping) {
102
- if (!actualParams) return {};
103
- const result = {};
104
- for (const [targetName, actualName] of Object.entries(paramMapping)) {
105
- if (actualName in actualParams) {
106
- result[targetName] = String(actualParams[actualName]);
107
- }
108
- }
109
- for (const [key, value] of Object.entries(actualParams)) {
110
- if (!Object.values(paramMapping).includes(key)) {
111
- result[key] = String(value);
112
- }
113
- }
114
- return result;
115
- }
75
+
76
+ // src/plugin.ts
77
+ import { generateSelfTagFromKey as generateSelfTagFromKey2 } from "@spoosh/core";
116
78
 
117
79
  // src/builder/index.ts
118
80
  function createBuilder(state, isConfirmed = false) {
119
81
  return {
120
82
  ...state,
83
+ // Expose the internal state so the plugin can access predicates
84
+ __state: state,
121
85
  filter(predicate) {
122
86
  return createBuilder({ ...state, filter: predicate }, isConfirmed);
123
87
  },
@@ -169,27 +133,13 @@ function resolveOptimisticTargets(context) {
169
133
  function getMatchingEntries(stateManager, targetPath) {
170
134
  const results = [];
171
135
  const allEntries = stateManager.getAllCacheEntries();
172
- if (hasPatternParams(targetPath)) {
173
- for (const { key, entry } of allEntries) {
174
- if (!key.includes(`"method":"GET"`)) continue;
175
- const actualPath = extractPathFromKey(key);
176
- if (!actualPath) continue;
177
- const { matches, params, paramMapping } = pathMatchesPattern(
178
- actualPath,
179
- targetPath
180
- );
181
- if (matches) {
182
- results.push({ key, entry, extractedParams: params, paramMapping });
183
- }
184
- }
185
- } else {
186
- for (const { key, entry } of allEntries) {
187
- if (!key.includes(`"method":"GET"`)) continue;
188
- const actualPath = extractPathFromKey(key);
189
- if (!actualPath) continue;
190
- if (actualPath === targetPath) {
191
- results.push({ key, entry, extractedParams: {}, paramMapping: {} });
192
- }
136
+ for (const { key, entry } of allEntries) {
137
+ if (!key.includes(`"method":"GET"`)) continue;
138
+ const resolvedPath = generateSelfTagFromKey2(key);
139
+ if (!resolvedPath) continue;
140
+ const { matches, params } = pathMatchesPattern(resolvedPath, targetPath);
141
+ if (matches) {
142
+ results.push({ key, entry, extractedParams: params });
193
143
  }
194
144
  }
195
145
  return results;
@@ -198,28 +148,22 @@ function applyUpdate(stateManager, target, updater, response, t) {
198
148
  const snapshots = [];
199
149
  const matchingEntries = getMatchingEntries(stateManager, target.path);
200
150
  if (matchingEntries.length === 0) {
201
- t?.skip(`Skipped ${target.path} (no cache entry)`);
202
151
  return [];
203
152
  }
204
- for (const { key, entry, extractedParams, paramMapping } of matchingEntries) {
205
- if (target.filter) {
153
+ for (const { key, entry, extractedParams } of matchingEntries) {
154
+ const targetWithState = target;
155
+ const filterPredicate = targetWithState.__state ? targetWithState.__state.filter : target.filter;
156
+ if (filterPredicate) {
206
157
  const options = extractOptionsFromKey(key) ?? {};
207
- const mappedParams = mapParamsToTargetNames(
208
- options.params,
209
- paramMapping
210
- );
211
158
  const mergedOptions = {
212
159
  ...options,
213
160
  params: {
214
- ...extractedParams,
215
- ...mappedParams
161
+ ...options.params,
162
+ ...extractedParams
216
163
  }
217
164
  };
218
165
  try {
219
- if (!target.filter(mergedOptions)) {
220
- t?.skip(
221
- `Skipped ${formatCacheKeyForTrace(key)} (filter not matched)`
222
- );
166
+ if (!filterPredicate(mergedOptions)) {
223
167
  continue;
224
168
  }
225
169
  } catch {
@@ -228,7 +172,6 @@ function applyUpdate(stateManager, target, updater, response, t) {
228
172
  }
229
173
  }
230
174
  if (entry?.state.data === void 0) {
231
- t?.skip(`Skipped ${formatCacheKeyForTrace(key)} (no cached data)`);
232
175
  continue;
233
176
  }
234
177
  const afterData = updater(entry.state.data, response);
@@ -291,11 +234,13 @@ function optimisticPlugin() {
291
234
  context.plugins.get("spoosh:invalidation")?.setDefaultMode("none");
292
235
  const allImmediateSnapshots = [];
293
236
  for (const target of targets) {
294
- if (!target.immediateUpdater) continue;
237
+ const targetWithState = target;
238
+ const immediateUpdater = targetWithState.__state ? targetWithState.__state.immediateUpdater : target.immediateUpdater;
239
+ if (!immediateUpdater) continue;
295
240
  const snapshots = applyUpdate(
296
241
  stateManager,
297
242
  target,
298
- target.immediateUpdater,
243
+ immediateUpdater,
299
244
  void 0,
300
245
  t
301
246
  );
@@ -309,9 +254,12 @@ function optimisticPlugin() {
309
254
  }
310
255
  const response = await next();
311
256
  if (response.error) {
312
- const shouldRollback = targets.some(
313
- (target) => target.rollbackOnError && target.immediateUpdater
314
- );
257
+ const shouldRollback = targets.some((target) => {
258
+ const targetWithState = target;
259
+ const rollbackOnError = targetWithState.__state ? targetWithState.__state.rollbackOnError : target.rollbackOnError;
260
+ const immediateUpdater = targetWithState.__state ? targetWithState.__state.immediateUpdater : target.immediateUpdater;
261
+ return rollbackOnError && immediateUpdater;
262
+ });
315
263
  if (shouldRollback && allImmediateSnapshots.length > 0) {
316
264
  rollbackOptimistic(stateManager, allImmediateSnapshots);
317
265
  for (const snapshot of allImmediateSnapshots) {
@@ -322,8 +270,10 @@ function optimisticPlugin() {
322
270
  }
323
271
  }
324
272
  for (const target of targets) {
325
- if (target.onError) {
326
- target.onError(response.error);
273
+ const targetWithState = target;
274
+ const onError = targetWithState.__state ? targetWithState.__state.onError : target.onError;
275
+ if (onError) {
276
+ onError(response.error);
327
277
  }
328
278
  }
329
279
  } else {
@@ -331,11 +281,13 @@ function optimisticPlugin() {
331
281
  confirmOptimistic(stateManager, allImmediateSnapshots);
332
282
  }
333
283
  for (const target of targets) {
334
- if (!target.confirmedUpdater) continue;
284
+ const targetWithState = target;
285
+ const confirmedUpdater = targetWithState.__state ? targetWithState.__state.confirmedUpdater : target.confirmedUpdater;
286
+ if (!confirmedUpdater) continue;
335
287
  const snapshots = applyUpdate(
336
288
  stateManager,
337
289
  target,
338
- target.confirmedUpdater,
290
+ confirmedUpdater,
339
291
  response.data,
340
292
  t
341
293
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spoosh/plugin-optimistic",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "Optimistic updates plugin for Spoosh - instant UI updates with automatic rollback",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -37,9 +37,9 @@
37
37
  "@spoosh/plugin-invalidation": ">=0.7.0"
38
38
  },
39
39
  "devDependencies": {
40
- "@spoosh/core": "0.17.0",
41
- "@spoosh/plugin-invalidation": "0.10.0",
42
- "@spoosh/test-utils": "0.3.0"
40
+ "@spoosh/core": "0.17.1",
41
+ "@spoosh/test-utils": "0.3.0",
42
+ "@spoosh/plugin-invalidation": "0.10.0"
43
43
  },
44
44
  "scripts": {
45
45
  "dev": "tsup --watch",