@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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lewebsimple/nuxt-graphql",
3
3
  "configKey": "graphql",
4
- "version": "0.6.16",
4
+ "version": "0.6.18",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
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.16";
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
- enumsAsTypes: true,
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
- enumsAsTypes: true,
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((result) => {
24
- if (result.error) {
25
- throw result.error;
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
- if (cacheConfig.policy === "no-cache") {
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 { clearNuxtData, useNuxtData, useRuntimeConfig } from "#imports";
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
- clearNuxtData((k) => k.startsWith(prefix));
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
- clearNuxtData((k) => k.startsWith(opPrefix));
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
- clearNuxtData(key);
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.16",
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.5",
31
- "@graphql-codegen/typescript": "^5.0.7",
32
- "@graphql-codegen/typescript-operations": "^5.0.7",
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",