@lewebsimple/nuxt-graphql 0.6.16 → 0.6.18
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/module.json +1 -1
- package/dist/module.mjs +3 -3
- package/dist/runtime/app/composables/useAsyncGraphQLQuery.js +8 -6
- package/dist/runtime/app/composables/useGraphQLCache.client.d.ts +6 -2
- package/dist/runtime/app/composables/useGraphQLCache.client.js +23 -7
- package/dist/runtime/app/lib/cache.d.ts +42 -0
- package/dist/runtime/app/lib/cache.js +35 -0
- package/package.json +4 -4
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -203,7 +203,7 @@ function addUniversalTemplate({ filename, getContents, emitTs }) {
|
|
|
203
203
|
return modulePath;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
const version = "0.6.
|
|
206
|
+
const version = "0.6.18";
|
|
207
207
|
|
|
208
208
|
async function getDocuments(documentsGlob) {
|
|
209
209
|
try {
|
|
@@ -238,7 +238,7 @@ async function getOperationsTemplate({ loadSchema, loadDocuments, documentGlob }
|
|
|
238
238
|
defaultValue: false
|
|
239
239
|
},
|
|
240
240
|
defaultScalarType: "never",
|
|
241
|
-
|
|
241
|
+
enumsAsConst: true,
|
|
242
242
|
preResolveTypes: false,
|
|
243
243
|
strictScalars: true,
|
|
244
244
|
useTypeImports: true
|
|
@@ -253,7 +253,7 @@ async function getOperationsTemplate({ loadSchema, loadDocuments, documentGlob }
|
|
|
253
253
|
defaultValue: false
|
|
254
254
|
},
|
|
255
255
|
defaultScalarType: "never",
|
|
256
|
-
|
|
256
|
+
enumsAsConst: true,
|
|
257
257
|
exportFragmentSpreadSubTypes: true,
|
|
258
258
|
inlineFragmentTypes: "combine",
|
|
259
259
|
operationResultSuffix: "Result",
|
|
@@ -2,7 +2,7 @@ import { useAsyncData, useNuxtApp, useNuxtData, useRuntimeConfig } from "#app";
|
|
|
2
2
|
import { computed, toValue } from "#imports";
|
|
3
3
|
import { normalizeError } from "../../shared/lib/error.js";
|
|
4
4
|
import { getOperationDocument } from "../../shared/lib/registry.js";
|
|
5
|
-
import { getCacheKeyParts, resolveCacheConfig } from "../lib/cache.js";
|
|
5
|
+
import { getCacheKeyParts, markCacheKeyRefreshed, registerCacheKey, resolveCacheConfig, shouldBypassCache } from "../lib/cache.js";
|
|
6
6
|
import { getInFlightRequests } from "../lib/in-flight.js";
|
|
7
7
|
import { getPersistedEntry, setPersistedEntry } from "../lib/persisted.js";
|
|
8
8
|
export function useAsyncGraphQLQuery(operationName, ...args) {
|
|
@@ -14,20 +14,21 @@ export function useAsyncGraphQLQuery(operationName, ...args) {
|
|
|
14
14
|
const { cache, transform, ...asyncDataOptions } = options ?? {};
|
|
15
15
|
const cacheConfig = resolveCacheConfig(graphql.cacheConfig, cache);
|
|
16
16
|
const cacheKey = computed(() => getCacheKeyParts(cacheConfig, operationName, toValue(variables)).key);
|
|
17
|
+
registerCacheKey(cacheKey.value);
|
|
17
18
|
const inFlight = getInFlightRequests();
|
|
18
19
|
async function fetchAndPersist() {
|
|
19
20
|
const key = cacheKey.value;
|
|
20
21
|
if (inFlight.has(key)) {
|
|
21
22
|
return inFlight.get(key);
|
|
22
23
|
}
|
|
23
|
-
const promise = $executeGraphQL({ query: document, variables: toValue(variables), operationName }).then((
|
|
24
|
-
if (
|
|
25
|
-
throw
|
|
24
|
+
const promise = $executeGraphQL({ query: document, variables: toValue(variables), operationName }).then(({ data, error }) => {
|
|
25
|
+
if (error) {
|
|
26
|
+
throw error;
|
|
26
27
|
}
|
|
27
|
-
const data = result.data;
|
|
28
28
|
if (isClient && cacheConfig.ttl !== void 0) {
|
|
29
29
|
setPersistedEntry(key, data, cacheConfig.ttl);
|
|
30
30
|
}
|
|
31
|
+
markCacheKeyRefreshed(key);
|
|
31
32
|
return data;
|
|
32
33
|
});
|
|
33
34
|
inFlight.set(key, promise);
|
|
@@ -35,7 +36,8 @@ export function useAsyncGraphQLQuery(operationName, ...args) {
|
|
|
35
36
|
return promise;
|
|
36
37
|
}
|
|
37
38
|
async function asyncDataHandler() {
|
|
38
|
-
|
|
39
|
+
registerCacheKey(cacheKey.value);
|
|
40
|
+
if (cacheConfig.policy === "no-cache" || shouldBypassCache(cacheKey.value)) {
|
|
39
41
|
return await fetchAndPersist();
|
|
40
42
|
}
|
|
41
43
|
const nuxtData = useNuxtData(cacheKey.value);
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { QueryName, ResultOf, VariablesOf } from "#graphql/registry";
|
|
2
2
|
import type { IsEmptyObject } from "../../shared/lib/types.js";
|
|
3
|
+
type CacheWriteOptions = {
|
|
4
|
+
markFresh?: boolean;
|
|
5
|
+
};
|
|
3
6
|
/**
|
|
4
7
|
* GraphQL cache helper composable.
|
|
5
8
|
*
|
|
@@ -8,7 +11,8 @@ import type { IsEmptyObject } from "../../shared/lib/types.js";
|
|
|
8
11
|
export declare function useGraphQLCache(): {
|
|
9
12
|
readonly cacheConfig: import("../../shared/lib/types.js").CacheConfig;
|
|
10
13
|
readonly read: <TName extends QueryName>(operation: TName, ...args: IsEmptyObject<VariablesOf<TName>> extends true ? [variables?: VariablesOf<TName>] : [variables: VariablesOf<TName>]) => ResultOf<TName> | undefined;
|
|
11
|
-
readonly write: <TName extends QueryName>(operation: TName, variables: VariablesOf<TName>, value: ResultOf<TName> | ((current: ResultOf<TName> | undefined) => ResultOf<TName>)) => void;
|
|
12
|
-
readonly update: <TName extends QueryName>(operation: TName, variables: VariablesOf<TName>, value: ResultOf<TName> | ((current: ResultOf<TName> | undefined) => ResultOf<TName>)) => Promise<void>;
|
|
14
|
+
readonly write: <TName extends QueryName>(operation: TName, variables: VariablesOf<TName>, value: ResultOf<TName> | ((current: ResultOf<TName> | undefined) => ResultOf<TName>), options?: CacheWriteOptions) => void;
|
|
15
|
+
readonly update: <TName extends QueryName>(operation: TName, variables: VariablesOf<TName>, value: ResultOf<TName> | ((current: ResultOf<TName> | undefined) => ResultOf<TName>), options?: CacheWriteOptions) => Promise<void>;
|
|
13
16
|
readonly invalidate: <TName extends QueryName>(operation?: TName | undefined, variables?: VariablesOf<TName> | undefined) => Promise<void>;
|
|
14
17
|
};
|
|
18
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getCacheKeyParts } from "../lib/cache.js";
|
|
1
|
+
import { refreshNuxtData, useNuxtData, useRuntimeConfig } from "#imports";
|
|
2
|
+
import { getCacheKeyParts, getCacheKeysByPrefix, invalidateAllCacheKeys, invalidateCacheKey, invalidateCachePrefix, markCacheKeyRefreshed, registerCacheKey } from "../lib/cache.js";
|
|
3
3
|
import { deletePersistedByPrefix, deletePersistedEntry, getPersistedEntry, setPersistedEntry } from "../lib/persisted.js";
|
|
4
4
|
export function useGraphQLCache() {
|
|
5
5
|
const { public: { graphql: { cacheConfig } } } = useRuntimeConfig();
|
|
@@ -9,12 +9,16 @@ export function useGraphQLCache() {
|
|
|
9
9
|
const nuxtData = useNuxtData(key);
|
|
10
10
|
return nuxtData.data.value;
|
|
11
11
|
}
|
|
12
|
-
function write(operation, variables, value) {
|
|
12
|
+
function write(operation, variables, value, options) {
|
|
13
13
|
const { key } = getCacheKeyParts(cacheConfig, operation, variables);
|
|
14
14
|
const nuxtData = useNuxtData(key);
|
|
15
15
|
nuxtData.data.value = typeof value === "function" ? value(nuxtData.data.value) : value;
|
|
16
|
+
registerCacheKey(key);
|
|
17
|
+
if (options?.markFresh) {
|
|
18
|
+
markCacheKeyRefreshed(key);
|
|
19
|
+
}
|
|
16
20
|
}
|
|
17
|
-
async function update(operation, variables, value) {
|
|
21
|
+
async function update(operation, variables, value, options) {
|
|
18
22
|
const { key } = getCacheKeyParts(cacheConfig, operation, variables);
|
|
19
23
|
const nuxtData = useNuxtData(key);
|
|
20
24
|
let current = nuxtData.data.value;
|
|
@@ -26,24 +30,36 @@ export function useGraphQLCache() {
|
|
|
26
30
|
if (cacheConfig.ttl !== void 0) {
|
|
27
31
|
await setPersistedEntry(key, updated, cacheConfig.ttl);
|
|
28
32
|
}
|
|
33
|
+
registerCacheKey(key);
|
|
34
|
+
if (options?.markFresh) {
|
|
35
|
+
markCacheKeyRefreshed(key);
|
|
36
|
+
}
|
|
29
37
|
}
|
|
30
38
|
async function invalidate(operation, variables) {
|
|
31
39
|
if (operation === void 0) {
|
|
32
40
|
const { keyPrefix, keyVersion } = cacheConfig;
|
|
33
41
|
const prefix = `${keyPrefix}:${keyVersion}:`;
|
|
34
|
-
|
|
42
|
+
invalidateAllCacheKeys();
|
|
35
43
|
await deletePersistedByPrefix(prefix);
|
|
44
|
+
await refreshNuxtData();
|
|
36
45
|
return;
|
|
37
46
|
}
|
|
38
47
|
if (variables === void 0) {
|
|
39
48
|
const { opPrefix } = getCacheKeyParts(cacheConfig, operation, {});
|
|
40
|
-
|
|
49
|
+
invalidateCachePrefix(opPrefix);
|
|
41
50
|
await deletePersistedByPrefix(opPrefix);
|
|
51
|
+
const keys = getCacheKeysByPrefix(opPrefix);
|
|
52
|
+
if (keys.length > 0) {
|
|
53
|
+
await refreshNuxtData(keys);
|
|
54
|
+
} else {
|
|
55
|
+
console.warn(`[nuxt-graphql][cache] No cache keys found for operation "${operation}" with prefix "${opPrefix}"`);
|
|
56
|
+
}
|
|
42
57
|
return;
|
|
43
58
|
}
|
|
44
59
|
const { key } = getCacheKeyParts(cacheConfig, operation, variables);
|
|
45
|
-
|
|
60
|
+
invalidateCacheKey(key);
|
|
46
61
|
await deletePersistedEntry(key);
|
|
62
|
+
await refreshNuxtData(key);
|
|
47
63
|
}
|
|
48
64
|
return { cacheConfig, read, write, update, invalidate };
|
|
49
65
|
}
|
|
@@ -22,4 +22,46 @@ type CacheKeyParts = {
|
|
|
22
22
|
* @returns Key parts including full key and operation prefix.
|
|
23
23
|
*/
|
|
24
24
|
export declare function getCacheKeyParts({ keyPrefix, keyVersion }: CacheConfig, operationName: string, variables: unknown, scope?: string): CacheKeyParts;
|
|
25
|
+
/**
|
|
26
|
+
* Register a cache key seen by async GraphQL queries.
|
|
27
|
+
*
|
|
28
|
+
* @param key Cache key.
|
|
29
|
+
*/
|
|
30
|
+
export declare function registerCacheKey(key: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Get known cache keys by prefix.
|
|
33
|
+
*
|
|
34
|
+
* @param prefix Cache key prefix.
|
|
35
|
+
* @returns Matching cache keys.
|
|
36
|
+
*/
|
|
37
|
+
export declare function getCacheKeysByPrefix(prefix: string): string[];
|
|
38
|
+
/**
|
|
39
|
+
* Mark a single cache key as invalidated.
|
|
40
|
+
*
|
|
41
|
+
* @param key Cache key.
|
|
42
|
+
*/
|
|
43
|
+
export declare function invalidateCacheKey(key: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Mark all cache keys matching a prefix as invalidated.
|
|
46
|
+
*
|
|
47
|
+
* @param prefix Cache key prefix.
|
|
48
|
+
*/
|
|
49
|
+
export declare function invalidateCachePrefix(prefix: string): void;
|
|
50
|
+
/**
|
|
51
|
+
* Mark all cache keys as invalidated.
|
|
52
|
+
*/
|
|
53
|
+
export declare function invalidateAllCacheKeys(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Mark a cache key as refreshed from network.
|
|
56
|
+
*
|
|
57
|
+
* @param key Cache key.
|
|
58
|
+
*/
|
|
59
|
+
export declare function markCacheKeyRefreshed(key: string): void;
|
|
60
|
+
/**
|
|
61
|
+
* Determine whether cache should be bypassed for this key.
|
|
62
|
+
*
|
|
63
|
+
* @param key Cache key.
|
|
64
|
+
* @returns True when invalidation is newer than the last successful network refresh.
|
|
65
|
+
*/
|
|
66
|
+
export declare function shouldBypassCache(key: string): boolean;
|
|
25
67
|
export {};
|
|
@@ -16,3 +16,38 @@ export function getCacheKeyParts({ keyPrefix, keyVersion }, operationName, varia
|
|
|
16
16
|
const key = opPrefix + hash(variables || {});
|
|
17
17
|
return { key, opPrefix };
|
|
18
18
|
}
|
|
19
|
+
const knownCacheKeys = /* @__PURE__ */ new Set();
|
|
20
|
+
export function registerCacheKey(key) {
|
|
21
|
+
knownCacheKeys.add(key);
|
|
22
|
+
}
|
|
23
|
+
export function getCacheKeysByPrefix(prefix) {
|
|
24
|
+
return [...knownCacheKeys].filter((key) => key.startsWith(prefix));
|
|
25
|
+
}
|
|
26
|
+
const invalidatedExactAt = /* @__PURE__ */ new Map();
|
|
27
|
+
const invalidatedPrefixAt = /* @__PURE__ */ new Map();
|
|
28
|
+
const refreshedAt = /* @__PURE__ */ new Map();
|
|
29
|
+
let invalidatedAllAt = 0;
|
|
30
|
+
export function invalidateCacheKey(key) {
|
|
31
|
+
invalidatedExactAt.set(key, Date.now());
|
|
32
|
+
}
|
|
33
|
+
export function invalidateCachePrefix(prefix) {
|
|
34
|
+
invalidatedPrefixAt.set(prefix, Date.now());
|
|
35
|
+
}
|
|
36
|
+
export function invalidateAllCacheKeys() {
|
|
37
|
+
invalidatedAllAt = Date.now();
|
|
38
|
+
}
|
|
39
|
+
export function markCacheKeyRefreshed(key) {
|
|
40
|
+
refreshedAt.set(key, Date.now());
|
|
41
|
+
}
|
|
42
|
+
export function shouldBypassCache(key) {
|
|
43
|
+
const lastRefreshAt = refreshedAt.get(key) ?? 0;
|
|
44
|
+
const exactInvalidatedAt = invalidatedExactAt.get(key) ?? 0;
|
|
45
|
+
let prefixInvalidatedAt = 0;
|
|
46
|
+
for (const [prefix, timestamp] of invalidatedPrefixAt) {
|
|
47
|
+
if (key.startsWith(prefix) && timestamp > prefixInvalidatedAt) {
|
|
48
|
+
prefixInvalidatedAt = timestamp;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const latestInvalidationAt = Math.max(invalidatedAllAt, exactInvalidatedAt, prefixInvalidatedAt);
|
|
52
|
+
return latestInvalidationAt > lastRefreshAt;
|
|
53
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lewebsimple/nuxt-graphql",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.18",
|
|
4
4
|
"description": "Opinionated Nuxt module for using GraphQL",
|
|
5
5
|
"repository": "lewebsimple/nuxt-graphql",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@graphql-codegen/core": "^5.0.0",
|
|
30
|
-
"@graphql-codegen/typed-document-node": "^6.1.
|
|
31
|
-
"@graphql-codegen/typescript": "^5.0.
|
|
32
|
-
"@graphql-codegen/typescript-operations": "^5.0.
|
|
30
|
+
"@graphql-codegen/typed-document-node": "^6.1.6",
|
|
31
|
+
"@graphql-codegen/typescript": "^5.0.8",
|
|
32
|
+
"@graphql-codegen/typescript-operations": "^5.0.8",
|
|
33
33
|
"@graphql-tools/graphql-file-loader": "^8.1.9",
|
|
34
34
|
"@graphql-tools/load": "^8.1.8",
|
|
35
35
|
"@graphql-tools/schema": "^10.0.31",
|