@lewebsimple/nuxt-graphql 0.6.18 → 0.6.20

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.18",
4
+ "version": "0.6.20",
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.18";
206
+ const version = "0.6.20";
207
207
 
208
208
  async function getDocuments(documentsGlob) {
209
209
  try {
@@ -3,9 +3,10 @@ import type { QueryName, ResultOf, VariablesOf } from "#graphql/registry";
3
3
  import { type MaybeRefOrGetter } from "#imports";
4
4
  import type { NormalizedError } from "../../shared/lib/error.js";
5
5
  import type { CacheConfig, IsEmptyObject } from "../../shared/lib/types.js";
6
- type UseAsyncGraphQLQueryOptions<TName extends QueryName, TTransformed = ResultOf<TName>> = Omit<AsyncDataOptions<ResultOf<TName>>, "transform" | "pick"> & {
6
+ type UseAsyncGraphQLQueryOptions<TName extends QueryName, TTransformed = ResultOf<TName>> = Omit<AsyncDataOptions<ResultOf<TName>, TTransformed, never>, "transform" | "pick"> & {
7
7
  transform?: (input: ResultOf<TName>) => TTransformed;
8
8
  cache?: Partial<CacheConfig>;
9
+ scope?: string;
9
10
  };
10
11
  /**
11
12
  * Async GraphQL query composable with caching support.
@@ -1,5 +1,5 @@
1
1
  import { useAsyncData, useNuxtApp, useNuxtData, useRuntimeConfig } from "#app";
2
- import { computed, toValue } from "#imports";
2
+ import { computed, toValue, watchEffect } from "#imports";
3
3
  import { normalizeError } from "../../shared/lib/error.js";
4
4
  import { getOperationDocument } from "../../shared/lib/registry.js";
5
5
  import { getCacheKeyParts, markCacheKeyRefreshed, registerCacheKey, resolveCacheConfig, shouldBypassCache } from "../lib/cache.js";
@@ -11,10 +11,13 @@ export function useAsyncGraphQLQuery(operationName, ...args) {
11
11
  const document = getOperationDocument(operationName);
12
12
  const isClient = import.meta.client;
13
13
  const { public: { graphql } } = useRuntimeConfig();
14
- const { cache, transform, ...asyncDataOptions } = options ?? {};
14
+ const { cache, transform, scope: scopeOpt, ...asyncDataOptions } = options ?? {};
15
15
  const cacheConfig = resolveCacheConfig(graphql.cacheConfig, cache);
16
- const cacheKey = computed(() => getCacheKeyParts(cacheConfig, operationName, toValue(variables)).key);
17
- registerCacheKey(cacheKey.value);
16
+ const scope = scopeOpt ?? "global";
17
+ const cacheKey = computed(() => getCacheKeyParts(cacheConfig, scope, operationName, toValue(variables)).key);
18
+ watchEffect(() => {
19
+ registerCacheKey(cacheKey.value);
20
+ });
18
21
  const inFlight = getInFlightRequests();
19
22
  async function fetchAndPersist() {
20
23
  const key = cacheKey.value;
@@ -8,11 +8,12 @@ type CacheWriteOptions = {
8
8
  *
9
9
  * @returns Cache manipulation helpers.
10
10
  */
11
- export declare function useGraphQLCache(): {
11
+ export declare function useGraphQLCache(scope?: string): {
12
12
  readonly cacheConfig: import("../../shared/lib/types.js").CacheConfig;
13
13
  readonly read: <TName extends QueryName>(operation: TName, ...args: IsEmptyObject<VariablesOf<TName>> extends true ? [variables?: VariablesOf<TName>] : [variables: VariablesOf<TName>]) => ResultOf<TName> | undefined;
14
14
  readonly write: <TName extends QueryName>(operation: TName, variables: VariablesOf<TName>, value: ResultOf<TName> | ((current: ResultOf<TName> | undefined) => ResultOf<TName>), options?: CacheWriteOptions) => void;
15
15
  readonly update: <TName extends QueryName>(operation: TName, variables: VariablesOf<TName>, value: ResultOf<TName> | ((current: ResultOf<TName> | undefined) => ResultOf<TName>), options?: CacheWriteOptions) => Promise<void>;
16
16
  readonly invalidate: <TName extends QueryName>(operation?: TName | undefined, variables?: VariablesOf<TName> | undefined) => Promise<void>;
17
+ readonly invalidateAllScopes: () => Promise<void>;
17
18
  };
18
19
  export {};
@@ -1,65 +1,89 @@
1
1
  import { refreshNuxtData, useNuxtData, useRuntimeConfig } from "#imports";
2
- import { getCacheKeyParts, getCacheKeysByPrefix, invalidateAllCacheKeys, invalidateCacheKey, invalidateCachePrefix, markCacheKeyRefreshed, registerCacheKey } from "../lib/cache.js";
2
+ import {
3
+ forgetCacheAll,
4
+ forgetCacheByPrefix,
5
+ forgetCacheKey,
6
+ getCacheKeyParts,
7
+ getCacheKeysByPrefix,
8
+ invalidateAllCacheKeys,
9
+ invalidateCacheKey,
10
+ invalidateCachePrefix,
11
+ markCacheKeyRefreshed,
12
+ registerCacheKey
13
+ } from "../lib/cache.js";
3
14
  import { deletePersistedByPrefix, deletePersistedEntry, getPersistedEntry, setPersistedEntry } from "../lib/persisted.js";
4
- export function useGraphQLCache() {
15
+ export function useGraphQLCache(scope = "global") {
5
16
  const { public: { graphql: { cacheConfig } } } = useRuntimeConfig();
17
+ function resolveKeyParts(operation, variables) {
18
+ return getCacheKeyParts(cacheConfig, scope, operation, variables ?? {});
19
+ }
6
20
  function read(operation, ...args) {
7
21
  const [variables] = args;
8
- const { key } = getCacheKeyParts(cacheConfig, operation, variables ?? {});
22
+ const { key } = resolveKeyParts(operation, variables);
9
23
  const nuxtData = useNuxtData(key);
10
24
  return nuxtData.data.value;
11
25
  }
12
- function write(operation, variables, value, options) {
13
- const { key } = getCacheKeyParts(cacheConfig, operation, variables);
26
+ function applyWrite(key, value, current, options) {
14
27
  const nuxtData = useNuxtData(key);
15
- nuxtData.data.value = typeof value === "function" ? value(nuxtData.data.value) : value;
28
+ const next = typeof value === "function" ? value(current) : value;
29
+ nuxtData.data.value = next;
16
30
  registerCacheKey(key);
17
31
  if (options?.markFresh) {
18
32
  markCacheKeyRefreshed(key);
19
33
  }
34
+ return next;
35
+ }
36
+ function write(operation, variables, value, options) {
37
+ const { key } = resolveKeyParts(operation, variables);
38
+ const nuxtData = useNuxtData(key);
39
+ applyWrite(key, value, nuxtData.data.value, options);
20
40
  }
21
41
  async function update(operation, variables, value, options) {
22
- const { key } = getCacheKeyParts(cacheConfig, operation, variables);
42
+ const { key } = resolveKeyParts(operation, variables);
23
43
  const nuxtData = useNuxtData(key);
24
44
  let current = nuxtData.data.value;
25
45
  if (current === void 0 && cacheConfig.ttl !== void 0) {
26
46
  current = await getPersistedEntry(key);
27
47
  }
28
- const updated = typeof value === "function" ? value(current) : value;
29
- nuxtData.data.value = updated;
48
+ const next = applyWrite(key, value, current, options);
30
49
  if (cacheConfig.ttl !== void 0) {
31
- await setPersistedEntry(key, updated, cacheConfig.ttl);
32
- }
33
- registerCacheKey(key);
34
- if (options?.markFresh) {
35
- markCacheKeyRefreshed(key);
50
+ await setPersistedEntry(key, next, cacheConfig.ttl);
36
51
  }
37
52
  }
38
53
  async function invalidate(operation, variables) {
54
+ const { scopePrefix, opPrefix, key } = resolveKeyParts(operation, variables);
39
55
  if (operation === void 0) {
40
- const { keyPrefix, keyVersion } = cacheConfig;
41
- const prefix = `${keyPrefix}:${keyVersion}:`;
42
- invalidateAllCacheKeys();
43
- await deletePersistedByPrefix(prefix);
44
- await refreshNuxtData();
56
+ invalidateCachePrefix(scopePrefix);
57
+ await deletePersistedByPrefix(scopePrefix);
58
+ const keys = getCacheKeysByPrefix(scopePrefix);
59
+ if (keys.length > 0) {
60
+ await refreshNuxtData(keys);
61
+ }
62
+ forgetCacheByPrefix(scopePrefix);
45
63
  return;
46
64
  }
47
65
  if (variables === void 0) {
48
- const { opPrefix } = getCacheKeyParts(cacheConfig, operation, {});
49
66
  invalidateCachePrefix(opPrefix);
50
67
  await deletePersistedByPrefix(opPrefix);
51
68
  const keys = getCacheKeysByPrefix(opPrefix);
52
69
  if (keys.length > 0) {
53
70
  await refreshNuxtData(keys);
54
- } else {
55
- console.warn(`[nuxt-graphql][cache] No cache keys found for operation "${operation}" with prefix "${opPrefix}"`);
56
71
  }
72
+ forgetCacheByPrefix(opPrefix);
57
73
  return;
58
74
  }
59
- const { key } = getCacheKeyParts(cacheConfig, operation, variables);
60
75
  invalidateCacheKey(key);
61
76
  await deletePersistedEntry(key);
62
77
  await refreshNuxtData(key);
78
+ forgetCacheKey(key);
79
+ }
80
+ async function invalidateAllScopes() {
81
+ const { keyPrefix, keyVersion } = cacheConfig;
82
+ const rootPrefix = `${keyPrefix}:${keyVersion}:`;
83
+ invalidateAllCacheKeys();
84
+ await deletePersistedByPrefix(rootPrefix);
85
+ await refreshNuxtData();
86
+ forgetCacheAll();
63
87
  }
64
- return { cacheConfig, read, write, update, invalidate };
88
+ return { cacheConfig, read, write, update, invalidate, invalidateAllScopes };
65
89
  }
@@ -7,21 +7,23 @@ import type { CacheConfig } from "../../shared/lib/types.js";
7
7
  */
8
8
  export declare function resolveCacheConfig(...overrides: Array<Partial<CacheConfig> | undefined>): CacheConfig;
9
9
  type CacheKeyParts = {
10
- key: string;
10
+ rootPrefix: string;
11
+ scopePrefix: string;
11
12
  opPrefix: string;
13
+ key: string;
12
14
  };
13
15
  /**
14
16
  * Build cache key parts from config, operation name, and variables.
15
17
  *
16
- * @param {GraphQLCacheConfig} options Cache configuration.
17
- * @param options.keyPrefix Cache key prefix.
18
- * @param options.keyVersion Cache key version.
18
+ * @param {GraphQLCacheConfig} config Cache configuration.
19
+ * @param config.keyPrefix Cache key prefix.
20
+ * @param config.keyVersion Cache key version.
21
+ * @param scope Cache scope segment.
19
22
  * @param operationName Operation name.
20
23
  * @param variables Operation variables.
21
- * @param scope Optional cache scope segment.
22
24
  * @returns Key parts including full key and operation prefix.
23
25
  */
24
- export declare function getCacheKeyParts({ keyPrefix, keyVersion }: CacheConfig, operationName: string, variables: unknown, scope?: string): CacheKeyParts;
26
+ export declare function getCacheKeyParts({ keyPrefix, keyVersion }: CacheConfig, scope: string, operationName: string, variables: unknown): CacheKeyParts;
25
27
  /**
26
28
  * Register a cache key seen by async GraphQL queries.
27
29
  *
@@ -64,4 +66,17 @@ export declare function markCacheKeyRefreshed(key: string): void;
64
66
  * @returns True when invalidation is newer than the last successful network refresh.
65
67
  */
66
68
  export declare function shouldBypassCache(key: string): boolean;
69
+ /**
70
+ * Remove a single cache key from all internal registries.
71
+ */
72
+ export declare function forgetCacheKey(key: string): void;
73
+ /**
74
+ * Remove all cache keys and related metadata matching a prefix.
75
+ */
76
+ export declare function forgetCacheByPrefix(prefix: string): void;
77
+ /**
78
+ * Clear all in-memory cache tracking metadata.
79
+ * Does NOT delete persisted storage.
80
+ */
81
+ export declare function forgetCacheAll(): void;
67
82
  export {};
@@ -8,13 +8,12 @@ const defaultCacheConfig = {
8
8
  export function resolveCacheConfig(...overrides) {
9
9
  return Object.assign({}, defaultCacheConfig, ...overrides);
10
10
  }
11
- export function getCacheKeyParts({ keyPrefix, keyVersion }, operationName, variables, scope) {
12
- const parts = [keyPrefix, keyVersion];
13
- if (scope) parts.push(scope);
14
- parts.push(operationName);
15
- const opPrefix = parts.join(":") + ":";
16
- const key = opPrefix + hash(variables || {});
17
- return { key, opPrefix };
11
+ export function getCacheKeyParts({ keyPrefix, keyVersion }, scope, operationName, variables) {
12
+ const rootPrefix = `${keyPrefix}:${keyVersion}:`;
13
+ const scopePrefix = `${rootPrefix}${scope}:`;
14
+ const opPrefix = `${scopePrefix}${operationName}:`;
15
+ const key = `${opPrefix}${hash(variables || {})}`;
16
+ return { rootPrefix, scopePrefix, opPrefix, key };
18
17
  }
19
18
  const knownCacheKeys = /* @__PURE__ */ new Set();
20
19
  export function registerCacheKey(key) {
@@ -51,3 +50,25 @@ export function shouldBypassCache(key) {
51
50
  const latestInvalidationAt = Math.max(invalidatedAllAt, exactInvalidatedAt, prefixInvalidatedAt);
52
51
  return latestInvalidationAt > lastRefreshAt;
53
52
  }
53
+ export function forgetCacheKey(key) {
54
+ knownCacheKeys.delete(key);
55
+ invalidatedExactAt.delete(key);
56
+ refreshedAt.delete(key);
57
+ }
58
+ export function forgetCacheByPrefix(prefix) {
59
+ for (const key of knownCacheKeys) {
60
+ if (key.startsWith(prefix)) {
61
+ knownCacheKeys.delete(key);
62
+ invalidatedExactAt.delete(key);
63
+ refreshedAt.delete(key);
64
+ }
65
+ }
66
+ invalidatedPrefixAt.delete(prefix);
67
+ }
68
+ export function forgetCacheAll() {
69
+ knownCacheKeys.clear();
70
+ invalidatedExactAt.clear();
71
+ invalidatedPrefixAt.clear();
72
+ refreshedAt.clear();
73
+ invalidatedAllAt = 0;
74
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lewebsimple/nuxt-graphql",
3
- "version": "0.6.18",
3
+ "version": "0.6.20",
4
4
  "description": "Opinionated Nuxt module for using GraphQL",
5
5
  "repository": "lewebsimple/nuxt-graphql",
6
6
  "license": "AGPL-3.0-only",
@@ -33,10 +33,10 @@
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",
36
- "@graphql-tools/stitch": "^10.1.11",
36
+ "@graphql-tools/stitch": "^10.1.12",
37
37
  "@nuxt/kit": "^4.3.1",
38
38
  "defu": "^6.1.4",
39
- "graphql": "^16.12.0",
39
+ "graphql": "^16.13.0",
40
40
  "graphql-sse": "^2.6.0",
41
41
  "graphql-yoga": "^5.18.0",
42
42
  "jiti": "^2.6.1",
@@ -44,18 +44,18 @@
44
44
  "unstorage": "^1.17.4"
45
45
  },
46
46
  "devDependencies": {
47
- "@nuxt/devtools": "^3.2.1",
48
- "@nuxt/eslint-config": "^1.15.1",
47
+ "@nuxt/devtools": "^3.2.2",
48
+ "@nuxt/eslint-config": "^1.15.2",
49
49
  "@nuxt/module-builder": "^1.0.2",
50
50
  "@nuxt/schema": "^4.3.1",
51
51
  "@nuxt/test-utils": "^3.23.0",
52
52
  "@types/node": "latest",
53
53
  "changelogen": "^0.6.2",
54
- "eslint": "^9.39.2",
54
+ "eslint": "^9.39.3",
55
55
  "nuxt": "^4.3.1",
56
56
  "typescript": "~5.9.3",
57
57
  "vitest": "^4.0.18",
58
- "vue-tsc": "^3.2.4"
58
+ "vue-tsc": "^3.2.5"
59
59
  },
60
60
  "scripts": {
61
61
  "dev": "pnpm run dev:prepare && nuxt dev playground",