@spoosh/plugin-invalidation 0.5.1 → 0.5.3
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 +71 -8
- package/dist/index.d.mts +14 -7
- package/dist/index.d.ts +14 -7
- package/dist/index.js +20 -30
- package/dist/index.mjs +20 -32
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Cache invalidation plugin for Spoosh - auto-invalidates related queries after mutations.
|
|
4
4
|
|
|
5
|
-
**[Documentation](https://spoosh.dev/docs/plugins/invalidation)** · **Requirements:** TypeScript >= 5.0 · **Peer Dependencies:** `@spoosh/core`
|
|
5
|
+
**[Documentation](https://spoosh.dev/react/docs/plugins/invalidation)** · **Requirements:** TypeScript >= 5.0 · **Peer Dependencies:** `@spoosh/core`
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -81,7 +81,13 @@ await trigger({
|
|
|
81
81
|
invalidate: "none", // No invalidation
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
//
|
|
84
|
+
// Single tag (string)
|
|
85
|
+
await trigger({
|
|
86
|
+
body: { title: "New Post" },
|
|
87
|
+
invalidate: "posts", // Invalidate only "posts" tag
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Multiple tags (array without mode keyword)
|
|
85
91
|
await trigger({
|
|
86
92
|
body: { title: "New Post" },
|
|
87
93
|
invalidate: ["posts", "users", "custom-tag"],
|
|
@@ -106,6 +112,18 @@ await trigger({
|
|
|
106
112
|
invalidate: ["dashboard", "stats", "all"],
|
|
107
113
|
// → 'all' mode + explicit tags (mode can be anywhere)
|
|
108
114
|
});
|
|
115
|
+
|
|
116
|
+
// Wildcard - global refetch
|
|
117
|
+
await trigger({
|
|
118
|
+
body: { title: "New Post" },
|
|
119
|
+
invalidate: "*", // Triggers ALL queries to refetch
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Combined with clearCache (from @spoosh/plugin-cache)
|
|
123
|
+
await trigger({
|
|
124
|
+
clearCache: true, // Clear all cached data
|
|
125
|
+
invalidate: "*", // Then refetch all queries
|
|
126
|
+
});
|
|
109
127
|
```
|
|
110
128
|
|
|
111
129
|
## Options
|
|
@@ -118,9 +136,9 @@ await trigger({
|
|
|
118
136
|
|
|
119
137
|
### Per-Request Options
|
|
120
138
|
|
|
121
|
-
| Option | Type
|
|
122
|
-
| ------------ |
|
|
123
|
-
| `invalidate` | `"all" \| "self" \| "none" \| string[]` | Mode
|
|
139
|
+
| Option | Type | Description |
|
|
140
|
+
| ------------ | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
141
|
+
| `invalidate` | `"all" \| "self" \| "none" \| "*" \| string \| string[]` | Mode (`"all"`, `"self"`, `"none"`), wildcard (`"*"` for global refetch), single tag, or array of tags with optional mode keyword |
|
|
124
142
|
|
|
125
143
|
### Invalidation Modes
|
|
126
144
|
|
|
@@ -129,6 +147,25 @@ await trigger({
|
|
|
129
147
|
| `"all"` | Invalidate all tags from path hierarchy | `users/123/posts` → `users`, `users/123`, `users/123/posts` |
|
|
130
148
|
| `"self"` | Only invalidate the exact endpoint tag | `users/123/posts` → `users/123/posts` |
|
|
131
149
|
| `"none"` | Disable auto-invalidation (manual only) | No automatic invalidation |
|
|
150
|
+
| `"*"` | Global refetch - triggers all queries | All active queries refetch |
|
|
151
|
+
|
|
152
|
+
### Understanding `"all"` vs `"*"`
|
|
153
|
+
|
|
154
|
+
These two options serve different purposes:
|
|
155
|
+
|
|
156
|
+
- **`"all"`** - Invalidates all tags **from the current endpoint's path hierarchy**. If you're mutating `users/123/posts`, it invalidates `["users", "users/123", "users/123/posts"]`. It's scoped to the mutation's path.
|
|
157
|
+
|
|
158
|
+
- **`"*"`** - Triggers a **global refetch of every active query** in your app, regardless of tags. Use this sparingly for scenarios like "user logged out" or "full data sync from server".
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// "all" - scoped to this mutation's path hierarchy
|
|
162
|
+
await trigger({ invalidate: "all" });
|
|
163
|
+
// If path is users/123/posts → invalidates: users, users/123, users/123/posts
|
|
164
|
+
|
|
165
|
+
// "*" - refetches ALL queries in the entire app
|
|
166
|
+
await trigger({ invalidate: "*" });
|
|
167
|
+
// Every active useRead/injectRead will refetch
|
|
168
|
+
```
|
|
132
169
|
|
|
133
170
|
## Instance API
|
|
134
171
|
|
|
@@ -145,12 +182,38 @@ invalidate(["users", "posts"]);
|
|
|
145
182
|
// Invalidate with single string
|
|
146
183
|
invalidate("users");
|
|
147
184
|
|
|
185
|
+
// Global refetch - triggers ALL queries to refetch
|
|
186
|
+
invalidate("*");
|
|
187
|
+
|
|
148
188
|
// Useful for external events like WebSocket messages
|
|
149
189
|
socket.on("data-changed", (tags) => {
|
|
150
190
|
invalidate(tags);
|
|
151
191
|
});
|
|
192
|
+
|
|
193
|
+
// WebSocket: trigger global refetch
|
|
194
|
+
socket.on("full-sync", () => {
|
|
195
|
+
invalidate("*");
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
| Method | Description |
|
|
200
|
+
| ------------ | ---------------------------------------------------------------------- |
|
|
201
|
+
| `invalidate` | Manually invalidate cache entries by tags, or use `"*"` to refetch all |
|
|
202
|
+
|
|
203
|
+
## Combining with Cache Plugin
|
|
204
|
+
|
|
205
|
+
For scenarios like logout or user switching, combine `invalidate: "*"` with `clearCache` from `@spoosh/plugin-cache`:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
const { trigger } = useWrite((api) => api("auth/logout").POST);
|
|
209
|
+
|
|
210
|
+
// Clear cache + trigger all queries to refetch
|
|
211
|
+
await trigger({
|
|
212
|
+
clearCache: true, // From cache plugin: clear all cached data
|
|
213
|
+
invalidate: "*", // From invalidation plugin: trigger all queries to refetch
|
|
214
|
+
});
|
|
152
215
|
```
|
|
153
216
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
217
|
+
This ensures both:
|
|
218
|
+
1. All cached data is cleared (no stale data from previous session)
|
|
219
|
+
2. All active queries refetch with fresh data
|
package/dist/index.d.mts
CHANGED
|
@@ -15,7 +15,7 @@ type ReadPaths<TSchema> = {
|
|
|
15
15
|
* - Otherwise, it's tags only with mode defaulting to 'none'
|
|
16
16
|
* - 'none' keyword should NOT be used in arrays (use string 'none' instead)
|
|
17
17
|
*/
|
|
18
|
-
type InvalidateOption<TSchema = unknown> = InvalidationMode | (ReadPaths<TSchema> | "all" | "self" | (string & {}))[];
|
|
18
|
+
type InvalidateOption<TSchema = unknown> = InvalidationMode | "*" | ReadPaths<TSchema> | (ReadPaths<TSchema> | "all" | "self" | (string & {}))[];
|
|
19
19
|
interface InvalidationPluginConfig {
|
|
20
20
|
/**
|
|
21
21
|
* Default invalidation mode when invalidate option is not specified
|
|
@@ -32,13 +32,12 @@ type InvalidationInfiniteReadOptions = object;
|
|
|
32
32
|
type InvalidationReadResult = object;
|
|
33
33
|
type InvalidationWriteResult = object;
|
|
34
34
|
/**
|
|
35
|
-
* Manual invalidation - tags only
|
|
35
|
+
* Manual invalidation - tags only, or "*" for global refetch
|
|
36
36
|
*/
|
|
37
37
|
type InvalidateFn<TSchema> = {
|
|
38
|
-
(tag:
|
|
39
|
-
(
|
|
40
|
-
(
|
|
41
|
-
(tags: string[]): void;
|
|
38
|
+
(tag: "*"): void;
|
|
39
|
+
(tag: ReadPaths<TSchema> | (string & {})): void;
|
|
40
|
+
(tags: (ReadPaths<TSchema> | (string & {}))[]): void;
|
|
42
41
|
};
|
|
43
42
|
interface InvalidationInstanceApi {
|
|
44
43
|
/** Manually invalidate cache entries by tags. Useful for external events like WebSocket messages. */
|
|
@@ -85,12 +84,20 @@ declare module "@spoosh/core" {
|
|
|
85
84
|
* });
|
|
86
85
|
*
|
|
87
86
|
* trigger({
|
|
88
|
-
* invalidate:
|
|
87
|
+
* invalidate: "posts", // Single tag
|
|
88
|
+
* });
|
|
89
|
+
*
|
|
90
|
+
* trigger({
|
|
91
|
+
* invalidate: ["posts", "users"], // Multiple tags
|
|
89
92
|
* });
|
|
90
93
|
*
|
|
91
94
|
* trigger({
|
|
92
95
|
* invalidate: ["all", "posts", "custom-tag"], // Mode + tags
|
|
93
96
|
* });
|
|
97
|
+
*
|
|
98
|
+
* trigger({
|
|
99
|
+
* invalidate: "*", // Global refetch - triggers all queries to refetch
|
|
100
|
+
* });
|
|
94
101
|
* ```
|
|
95
102
|
*/
|
|
96
103
|
declare function invalidationPlugin(config?: InvalidationPluginConfig): SpooshPlugin<{
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ type ReadPaths<TSchema> = {
|
|
|
15
15
|
* - Otherwise, it's tags only with mode defaulting to 'none'
|
|
16
16
|
* - 'none' keyword should NOT be used in arrays (use string 'none' instead)
|
|
17
17
|
*/
|
|
18
|
-
type InvalidateOption<TSchema = unknown> = InvalidationMode | (ReadPaths<TSchema> | "all" | "self" | (string & {}))[];
|
|
18
|
+
type InvalidateOption<TSchema = unknown> = InvalidationMode | "*" | ReadPaths<TSchema> | (ReadPaths<TSchema> | "all" | "self" | (string & {}))[];
|
|
19
19
|
interface InvalidationPluginConfig {
|
|
20
20
|
/**
|
|
21
21
|
* Default invalidation mode when invalidate option is not specified
|
|
@@ -32,13 +32,12 @@ type InvalidationInfiniteReadOptions = object;
|
|
|
32
32
|
type InvalidationReadResult = object;
|
|
33
33
|
type InvalidationWriteResult = object;
|
|
34
34
|
/**
|
|
35
|
-
* Manual invalidation - tags only
|
|
35
|
+
* Manual invalidation - tags only, or "*" for global refetch
|
|
36
36
|
*/
|
|
37
37
|
type InvalidateFn<TSchema> = {
|
|
38
|
-
(tag:
|
|
39
|
-
(
|
|
40
|
-
(
|
|
41
|
-
(tags: string[]): void;
|
|
38
|
+
(tag: "*"): void;
|
|
39
|
+
(tag: ReadPaths<TSchema> | (string & {})): void;
|
|
40
|
+
(tags: (ReadPaths<TSchema> | (string & {}))[]): void;
|
|
42
41
|
};
|
|
43
42
|
interface InvalidationInstanceApi {
|
|
44
43
|
/** Manually invalidate cache entries by tags. Useful for external events like WebSocket messages. */
|
|
@@ -85,12 +84,20 @@ declare module "@spoosh/core" {
|
|
|
85
84
|
* });
|
|
86
85
|
*
|
|
87
86
|
* trigger({
|
|
88
|
-
* invalidate:
|
|
87
|
+
* invalidate: "posts", // Single tag
|
|
88
|
+
* });
|
|
89
|
+
*
|
|
90
|
+
* trigger({
|
|
91
|
+
* invalidate: ["posts", "users"], // Multiple tags
|
|
89
92
|
* });
|
|
90
93
|
*
|
|
91
94
|
* trigger({
|
|
92
95
|
* invalidate: ["all", "posts", "custom-tag"], // Mode + tags
|
|
93
96
|
* });
|
|
97
|
+
*
|
|
98
|
+
* trigger({
|
|
99
|
+
* invalidate: "*", // Global refetch - triggers all queries to refetch
|
|
100
|
+
* });
|
|
94
101
|
* ```
|
|
95
102
|
*/
|
|
96
103
|
declare function invalidationPlugin(config?: InvalidationPluginConfig): SpooshPlugin<{
|
package/dist/index.js
CHANGED
|
@@ -25,39 +25,30 @@ __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 INVALIDATION_DEFAULT_KEY = "invalidation:defaultMode";
|
|
30
|
-
function resolveModeTags(context, mode
|
|
29
|
+
function resolveModeTags(context, mode) {
|
|
31
30
|
switch (mode) {
|
|
32
31
|
case "all":
|
|
33
|
-
if (stripTagPrefix) {
|
|
34
|
-
return context.tags.map((tag) => {
|
|
35
|
-
const segments = tag.split("/").filter(Boolean);
|
|
36
|
-
return (0, import_core.stripPrefixFromPath)(segments, stripTagPrefix).join("/");
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
32
|
return context.tags;
|
|
40
|
-
case "self":
|
|
41
|
-
|
|
42
|
-
if (stripTagPrefix) {
|
|
43
|
-
return [(0, import_core.stripPrefixFromPath)(context.path, stripTagPrefix).join("/")];
|
|
44
|
-
}
|
|
45
|
-
return [selfPath];
|
|
46
|
-
}
|
|
33
|
+
case "self":
|
|
34
|
+
return [context.path.join("/")];
|
|
47
35
|
case "none":
|
|
48
36
|
return [];
|
|
49
37
|
}
|
|
50
38
|
}
|
|
51
|
-
function resolveInvalidateTags(context, defaultMode
|
|
39
|
+
function resolveInvalidateTags(context, defaultMode) {
|
|
52
40
|
const pluginOptions = context.pluginOptions;
|
|
53
41
|
const invalidateOption = pluginOptions?.invalidate;
|
|
54
42
|
if (!invalidateOption) {
|
|
55
43
|
const overrideDefault = context.metadata.get(INVALIDATION_DEFAULT_KEY);
|
|
56
44
|
const effectiveDefault = overrideDefault ?? defaultMode;
|
|
57
|
-
return resolveModeTags(context, effectiveDefault
|
|
45
|
+
return resolveModeTags(context, effectiveDefault);
|
|
58
46
|
}
|
|
59
47
|
if (typeof invalidateOption === "string") {
|
|
60
|
-
|
|
48
|
+
if (invalidateOption === "all" || invalidateOption === "self" || invalidateOption === "none") {
|
|
49
|
+
return resolveModeTags(context, invalidateOption);
|
|
50
|
+
}
|
|
51
|
+
return [invalidateOption];
|
|
61
52
|
}
|
|
62
53
|
if (Array.isArray(invalidateOption)) {
|
|
63
54
|
const tags = [];
|
|
@@ -66,15 +57,10 @@ function resolveInvalidateTags(context, defaultMode, stripTagPrefix) {
|
|
|
66
57
|
if (item === "all" || item === "self") {
|
|
67
58
|
mode = item;
|
|
68
59
|
} else if (typeof item === "string") {
|
|
69
|
-
|
|
70
|
-
const segments = item.split("/").filter(Boolean);
|
|
71
|
-
tags.push((0, import_core.stripPrefixFromPath)(segments, stripTagPrefix).join("/"));
|
|
72
|
-
} else {
|
|
73
|
-
tags.push(item);
|
|
74
|
-
}
|
|
60
|
+
tags.push(item);
|
|
75
61
|
}
|
|
76
62
|
}
|
|
77
|
-
tags.push(...resolveModeTags(context, mode
|
|
63
|
+
tags.push(...resolveModeTags(context, mode));
|
|
78
64
|
return [...new Set(tags)];
|
|
79
65
|
}
|
|
80
66
|
return [];
|
|
@@ -93,11 +79,11 @@ function invalidationPlugin(config = {}) {
|
|
|
93
79
|
},
|
|
94
80
|
afterResponse(context, response) {
|
|
95
81
|
if (!response.error) {
|
|
96
|
-
const tags = resolveInvalidateTags(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
82
|
+
const tags = resolveInvalidateTags(context, defaultMode);
|
|
83
|
+
if (tags.includes("*")) {
|
|
84
|
+
context.eventEmitter.emit("refetchAll", void 0);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
101
87
|
if (tags.length > 0) {
|
|
102
88
|
context.stateManager.markStale(tags);
|
|
103
89
|
context.eventEmitter.emit("invalidate", tags);
|
|
@@ -108,6 +94,10 @@ function invalidationPlugin(config = {}) {
|
|
|
108
94
|
const { stateManager, eventEmitter } = context;
|
|
109
95
|
const invalidate = (input) => {
|
|
110
96
|
const tags = Array.isArray(input) ? input : [input];
|
|
97
|
+
if (tags.includes("*")) {
|
|
98
|
+
eventEmitter.emit("refetchAll", void 0);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
111
101
|
if (tags.length > 0) {
|
|
112
102
|
stateManager.markStale(tags);
|
|
113
103
|
eventEmitter.emit("invalidate", tags);
|
package/dist/index.mjs
CHANGED
|
@@ -1,39 +1,28 @@
|
|
|
1
1
|
// src/plugin.ts
|
|
2
|
-
import {
|
|
3
|
-
stripPrefixFromPath
|
|
4
|
-
} from "@spoosh/core";
|
|
5
2
|
var INVALIDATION_DEFAULT_KEY = "invalidation:defaultMode";
|
|
6
|
-
function resolveModeTags(context, mode
|
|
3
|
+
function resolveModeTags(context, mode) {
|
|
7
4
|
switch (mode) {
|
|
8
5
|
case "all":
|
|
9
|
-
if (stripTagPrefix) {
|
|
10
|
-
return context.tags.map((tag) => {
|
|
11
|
-
const segments = tag.split("/").filter(Boolean);
|
|
12
|
-
return stripPrefixFromPath(segments, stripTagPrefix).join("/");
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
6
|
return context.tags;
|
|
16
|
-
case "self":
|
|
17
|
-
|
|
18
|
-
if (stripTagPrefix) {
|
|
19
|
-
return [stripPrefixFromPath(context.path, stripTagPrefix).join("/")];
|
|
20
|
-
}
|
|
21
|
-
return [selfPath];
|
|
22
|
-
}
|
|
7
|
+
case "self":
|
|
8
|
+
return [context.path.join("/")];
|
|
23
9
|
case "none":
|
|
24
10
|
return [];
|
|
25
11
|
}
|
|
26
12
|
}
|
|
27
|
-
function resolveInvalidateTags(context, defaultMode
|
|
13
|
+
function resolveInvalidateTags(context, defaultMode) {
|
|
28
14
|
const pluginOptions = context.pluginOptions;
|
|
29
15
|
const invalidateOption = pluginOptions?.invalidate;
|
|
30
16
|
if (!invalidateOption) {
|
|
31
17
|
const overrideDefault = context.metadata.get(INVALIDATION_DEFAULT_KEY);
|
|
32
18
|
const effectiveDefault = overrideDefault ?? defaultMode;
|
|
33
|
-
return resolveModeTags(context, effectiveDefault
|
|
19
|
+
return resolveModeTags(context, effectiveDefault);
|
|
34
20
|
}
|
|
35
21
|
if (typeof invalidateOption === "string") {
|
|
36
|
-
|
|
22
|
+
if (invalidateOption === "all" || invalidateOption === "self" || invalidateOption === "none") {
|
|
23
|
+
return resolveModeTags(context, invalidateOption);
|
|
24
|
+
}
|
|
25
|
+
return [invalidateOption];
|
|
37
26
|
}
|
|
38
27
|
if (Array.isArray(invalidateOption)) {
|
|
39
28
|
const tags = [];
|
|
@@ -42,15 +31,10 @@ function resolveInvalidateTags(context, defaultMode, stripTagPrefix) {
|
|
|
42
31
|
if (item === "all" || item === "self") {
|
|
43
32
|
mode = item;
|
|
44
33
|
} else if (typeof item === "string") {
|
|
45
|
-
|
|
46
|
-
const segments = item.split("/").filter(Boolean);
|
|
47
|
-
tags.push(stripPrefixFromPath(segments, stripTagPrefix).join("/"));
|
|
48
|
-
} else {
|
|
49
|
-
tags.push(item);
|
|
50
|
-
}
|
|
34
|
+
tags.push(item);
|
|
51
35
|
}
|
|
52
36
|
}
|
|
53
|
-
tags.push(...resolveModeTags(context, mode
|
|
37
|
+
tags.push(...resolveModeTags(context, mode));
|
|
54
38
|
return [...new Set(tags)];
|
|
55
39
|
}
|
|
56
40
|
return [];
|
|
@@ -69,11 +53,11 @@ function invalidationPlugin(config = {}) {
|
|
|
69
53
|
},
|
|
70
54
|
afterResponse(context, response) {
|
|
71
55
|
if (!response.error) {
|
|
72
|
-
const tags = resolveInvalidateTags(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
56
|
+
const tags = resolveInvalidateTags(context, defaultMode);
|
|
57
|
+
if (tags.includes("*")) {
|
|
58
|
+
context.eventEmitter.emit("refetchAll", void 0);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
77
61
|
if (tags.length > 0) {
|
|
78
62
|
context.stateManager.markStale(tags);
|
|
79
63
|
context.eventEmitter.emit("invalidate", tags);
|
|
@@ -84,6 +68,10 @@ function invalidationPlugin(config = {}) {
|
|
|
84
68
|
const { stateManager, eventEmitter } = context;
|
|
85
69
|
const invalidate = (input) => {
|
|
86
70
|
const tags = Array.isArray(input) ? input : [input];
|
|
71
|
+
if (tags.includes("*")) {
|
|
72
|
+
eventEmitter.emit("refetchAll", void 0);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
87
75
|
if (tags.length > 0) {
|
|
88
76
|
stateManager.markStale(tags);
|
|
89
77
|
eventEmitter.emit("invalidate", tags);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spoosh/plugin-invalidation",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "Cache invalidation plugin for Spoosh - auto-invalidates after mutations",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@spoosh/core": ">=0.
|
|
36
|
+
"@spoosh/core": ">=0.8.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@spoosh/core": "0.9.
|
|
39
|
+
"@spoosh/core": "0.9.3",
|
|
40
40
|
"@spoosh/test-utils": "0.1.5"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|