@lewebsimple/nuxt-graphql 0.6.19 → 0.7.0
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 +61 -74
- package/dist/module.d.mts +31 -5
- package/dist/module.json +3 -3
- package/dist/module.mjs +442 -341
- package/dist/runtime/app/composables/useAsyncGraphQLQuery.d.ts +14 -10
- package/dist/runtime/app/composables/useAsyncGraphQLQuery.js +71 -80
- package/dist/runtime/app/composables/useGraphQLCache.client.d.ts +9 -14
- package/dist/runtime/app/composables/useGraphQLCache.client.js +40 -61
- package/dist/runtime/app/composables/useGraphQLLoadMore.d.ts +16 -9
- package/dist/runtime/app/composables/useGraphQLLoadMore.js +36 -40
- package/dist/runtime/app/composables/useGraphQLMutation.d.ts +29 -31
- package/dist/runtime/app/composables/useGraphQLMutation.js +17 -32
- package/dist/runtime/app/composables/useGraphQLQuery.d.ts +10 -3
- package/dist/runtime/app/composables/useGraphQLQuery.js +4 -14
- package/dist/runtime/app/composables/useGraphQLSubscription.client.d.ts +4 -5
- package/dist/runtime/app/composables/useGraphQLSubscription.client.js +7 -5
- package/dist/runtime/app/lib/cache-config.d.ts +18 -0
- package/dist/runtime/app/lib/cache-config.js +9 -0
- package/dist/runtime/app/lib/cache.d.ts +81 -49
- package/dist/runtime/app/lib/cache.js +65 -55
- package/dist/runtime/app/lib/persisted.d.ts +18 -12
- package/dist/runtime/app/lib/persisted.js +42 -45
- package/dist/runtime/app/plugins/graphql-sse.client.js +1 -2
- package/dist/runtime/app/plugins/graphql.d.ts +24 -0
- package/dist/runtime/app/plugins/graphql.js +16 -0
- package/dist/runtime/server/api/graphql.d.ts +6 -0
- package/dist/runtime/server/api/graphql.js +1 -1
- package/dist/runtime/server/{utils/defineGraphQLContext.d.ts → lib/context.d.ts} +10 -1
- package/dist/runtime/server/lib/remote-executor.d.ts +42 -19
- package/dist/runtime/server/lib/remote-executor.js +11 -13
- package/dist/runtime/server/lib/yoga.d.ts +0 -1
- package/dist/runtime/server/lib/yoga.js +1 -2
- package/dist/runtime/server/tsconfig.json +1 -1
- package/dist/runtime/server/utils/execute-schema.d.ts +11 -0
- package/dist/runtime/server/utils/execute-schema.js +24 -0
- package/dist/runtime/shared/lib/headers.d.ts +14 -2
- package/dist/runtime/shared/lib/headers.js +18 -1
- package/dist/runtime/shared/utils/error.d.ts +39 -0
- package/dist/runtime/shared/utils/error.js +67 -0
- package/dist/runtime/shared/utils/execute.d.ts +33 -0
- package/dist/runtime/shared/utils/execute.js +26 -0
- package/dist/runtime/shared/utils/registry.d.ts +72 -0
- package/dist/runtime/shared/utils/registry.js +37 -0
- package/package.json +48 -36
- package/dist/runtime/app/lib/in-flight.d.ts +0 -14
- package/dist/runtime/app/lib/in-flight.js +0 -19
- package/dist/runtime/app/plugins/execute-graphql.d.ts +0 -18
- package/dist/runtime/app/plugins/execute-graphql.js +0 -25
- package/dist/runtime/server/lib/execute-graphql-schema.d.ts +0 -3
- package/dist/runtime/server/lib/execute-graphql-schema.js +0 -22
- package/dist/runtime/server/utils/defineRemoteExecutorHooks.d.ts +0 -8
- package/dist/runtime/server/utils/defineRemoteExecutorHooks.js +0 -3
- package/dist/runtime/server/utils/useGraphQLOperation.d.ts +0 -16
- package/dist/runtime/server/utils/useGraphQLOperation.js +0 -12
- package/dist/runtime/shared/lib/error.d.ts +0 -42
- package/dist/runtime/shared/lib/error.js +0 -52
- package/dist/runtime/shared/lib/registry.d.ts +0 -12
- package/dist/runtime/shared/lib/registry.js +0 -8
- package/dist/runtime/shared/lib/types.d.ts +0 -30
- package/dist/runtime/shared/lib/types.js +0 -0
- package/dist/runtime/shared/utils/execute-graphql-http.d.ts +0 -7
- package/dist/runtime/shared/utils/execute-graphql-http.js +0 -31
- /package/dist/runtime/server/{utils/defineGraphQLContext.js → lib/context.js} +0 -0
|
@@ -1,41 +1,26 @@
|
|
|
1
1
|
import { useNuxtApp } from "#app";
|
|
2
|
-
import { ref } from "vue";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const [variables] = args;
|
|
11
|
-
let context;
|
|
12
|
-
if (options?.onMutate) {
|
|
13
|
-
try {
|
|
14
|
-
context = await options.onMutate(variables);
|
|
15
|
-
} catch (error) {
|
|
16
|
-
const normalizedError = normalizeError(error);
|
|
17
|
-
options?.onError?.(normalizedError, variables, context);
|
|
18
|
-
return { data: null, error: normalizedError };
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
pending.value = true;
|
|
2
|
+
import { computed, ref } from "vue";
|
|
3
|
+
export function useGraphQLMutation(operationName, hooks) {
|
|
4
|
+
const { $executeOperation } = useNuxtApp();
|
|
5
|
+
const inFlightCount = ref(0);
|
|
6
|
+
const pending = computed(() => inFlightCount.value > 0);
|
|
7
|
+
async function mutate(variables) {
|
|
8
|
+
let context = void 0;
|
|
9
|
+
inFlightCount.value += 1;
|
|
22
10
|
try {
|
|
23
|
-
|
|
11
|
+
if (hooks?.onMutate) {
|
|
12
|
+
context = await hooks.onMutate(variables);
|
|
13
|
+
}
|
|
14
|
+
const result = await $executeOperation({ operationName, variables });
|
|
24
15
|
if (result.error) {
|
|
25
|
-
|
|
26
|
-
} else
|
|
27
|
-
|
|
16
|
+
hooks?.onError?.(result.error, variables, context);
|
|
17
|
+
} else {
|
|
18
|
+
hooks?.onSuccess?.(result.data, variables, context);
|
|
28
19
|
}
|
|
29
|
-
|
|
20
|
+
hooks?.onSettled?.(result, variables, context);
|
|
30
21
|
return result;
|
|
31
|
-
} catch (error) {
|
|
32
|
-
const normalizedError = normalizeError(error);
|
|
33
|
-
const errorResult = { data: null, error: normalizedError };
|
|
34
|
-
options?.onError?.(normalizedError, variables, context);
|
|
35
|
-
options?.onSettled?.(errorResult, variables, context);
|
|
36
|
-
return errorResult;
|
|
37
22
|
} finally {
|
|
38
|
-
|
|
23
|
+
inFlightCount.value = Math.max(0, inFlightCount.value - 1);
|
|
39
24
|
}
|
|
40
25
|
}
|
|
41
26
|
return { pending, mutate };
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
|
|
1
|
+
import type { ExecuteGraphQLResult } from "../../shared/utils/execute.js";
|
|
2
|
+
import type { QueryName, VariablesInputOf } from "../../shared/utils/registry.js";
|
|
3
|
+
/**
|
|
4
|
+
* Execute a GraphQL query operation once.
|
|
5
|
+
*
|
|
6
|
+
* @param operationName Query operation name.
|
|
7
|
+
* @param variables Query variables.
|
|
8
|
+
* @returns Query execution result.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useGraphQLQuery<TName extends QueryName>(operationName: TName, variables: VariablesInputOf<TName>): Promise<ExecuteGraphQLResult<TName>>;
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
import { useNuxtApp } from "#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const { $executeGraphQL } = useNuxtApp();
|
|
6
|
-
const [variables] = args;
|
|
7
|
-
const document = getOperationDocument(operationName);
|
|
8
|
-
try {
|
|
9
|
-
return await $executeGraphQL(
|
|
10
|
-
{ query: document, variables, operationName }
|
|
11
|
-
);
|
|
12
|
-
} catch (error) {
|
|
13
|
-
return { data: null, error: normalizeError(error) };
|
|
14
|
-
}
|
|
1
|
+
import { useNuxtApp } from "#app";
|
|
2
|
+
export async function useGraphQLQuery(operationName, variables) {
|
|
3
|
+
const { $executeOperation } = useNuxtApp();
|
|
4
|
+
return $executeOperation({ operationName, variables });
|
|
15
5
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { type Ref } from "
|
|
2
|
-
import
|
|
3
|
-
import { type
|
|
4
|
-
import type { IsEmptyObject } from "../../shared/lib/types.js";
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
import { type NormalizedError } from "../../shared/utils/error.js";
|
|
3
|
+
import { type ResultOf, type SubscriptionName, type VariablesInputOf } from "../../shared/utils/registry.js";
|
|
5
4
|
type UseGraphQLSubscriptionReturn<TName extends SubscriptionName> = {
|
|
6
5
|
data: Readonly<Ref<ResultOf<TName> | null>>;
|
|
7
6
|
error: Readonly<Ref<NormalizedError | null>>;
|
|
@@ -15,5 +14,5 @@ type UseGraphQLSubscriptionReturn<TName extends SubscriptionName> = {
|
|
|
15
14
|
* @param args Operation variables (if any).
|
|
16
15
|
* @returns Object with reactive data, error, and start/stop helpers.
|
|
17
16
|
*/
|
|
18
|
-
export declare function useGraphQLSubscription<TName extends SubscriptionName>(operationName: TName,
|
|
17
|
+
export declare function useGraphQLSubscription<TName extends SubscriptionName>(operationName: TName, variables: VariablesInputOf<TName>): UseGraphQLSubscriptionReturn<TName>;
|
|
19
18
|
export {};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { useNuxtApp } from "#app";
|
|
1
2
|
import { print } from "graphql";
|
|
2
|
-
import { onScopeDispose, shallowRef
|
|
3
|
-
import { normalizeError } from "../../shared/
|
|
4
|
-
import {
|
|
5
|
-
|
|
3
|
+
import { onScopeDispose, shallowRef } from "vue";
|
|
4
|
+
import { normalizeError } from "../../shared/utils/error.js";
|
|
5
|
+
import {
|
|
6
|
+
getOperationDocument
|
|
7
|
+
} from "../../shared/utils/registry.js";
|
|
8
|
+
export function useGraphQLSubscription(operationName, variables) {
|
|
6
9
|
const { $getGraphQLSSEClient } = useNuxtApp();
|
|
7
|
-
const [variables] = args;
|
|
8
10
|
const document = getOperationDocument(operationName);
|
|
9
11
|
const query = print(document);
|
|
10
12
|
const data = shallowRef(null);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Cache configuration. */
|
|
2
|
+
export type CacheConfig = {
|
|
3
|
+
/** Cache strategy. */
|
|
4
|
+
policy: "no-cache" | "cache-first" | "network-first" | "swr";
|
|
5
|
+
/** Optional cache time-to-live in seconds. */
|
|
6
|
+
ttl?: number;
|
|
7
|
+
/** Cache key prefix. */
|
|
8
|
+
keyPrefix: string;
|
|
9
|
+
/** Cache key version. */
|
|
10
|
+
keyVersion: string | number;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Merge cache configuration overrides with defaults.
|
|
14
|
+
*
|
|
15
|
+
* @param overrides Partial cache config overrides.
|
|
16
|
+
* @returns Resolved cache configuration.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveCacheConfig(...overrides: Array<Partial<CacheConfig> | undefined>): CacheConfig;
|
|
@@ -1,82 +1,114 @@
|
|
|
1
|
-
import type { CacheConfig } from "
|
|
1
|
+
import type { CacheConfig } from "./cache-config.js";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Build the root cache key prefix shared by all entries.
|
|
4
4
|
*
|
|
5
|
-
* @param
|
|
6
|
-
* @returns
|
|
5
|
+
* @param config Cache configuration.
|
|
6
|
+
* @returns Root cache prefix.
|
|
7
7
|
*/
|
|
8
|
-
export declare function
|
|
9
|
-
type CacheKeyParts = {
|
|
10
|
-
rootPrefix: string;
|
|
11
|
-
scopePrefix: string;
|
|
12
|
-
opPrefix: string;
|
|
13
|
-
key: string;
|
|
14
|
-
};
|
|
8
|
+
export declare function getCacheRootPrefix({ keyPrefix, keyVersion }: CacheConfig): string;
|
|
15
9
|
/**
|
|
16
|
-
* Build cache key
|
|
10
|
+
* Build the cache key prefix for a specific scope.
|
|
17
11
|
*
|
|
18
|
-
* @param
|
|
19
|
-
* @param
|
|
20
|
-
* @
|
|
21
|
-
* @param scope Cache scope segment.
|
|
22
|
-
* @param operationName Operation name.
|
|
23
|
-
* @param variables Operation variables.
|
|
24
|
-
* @returns Key parts including full key and operation prefix.
|
|
12
|
+
* @param config Cache configuration.
|
|
13
|
+
* @param scope Cache scope.
|
|
14
|
+
* @returns Scope cache prefix.
|
|
25
15
|
*/
|
|
26
|
-
export declare function
|
|
16
|
+
export declare function getCacheScopePrefix(config: CacheConfig, scope: string): string;
|
|
27
17
|
/**
|
|
28
|
-
*
|
|
18
|
+
* Build the cache key prefix for a specific operation in a scope.
|
|
29
19
|
*
|
|
30
|
-
* @param
|
|
20
|
+
* @param config Cache configuration.
|
|
21
|
+
* @param scope Cache scope.
|
|
22
|
+
* @param operationName GraphQL operation name.
|
|
23
|
+
* @returns Operation cache prefix.
|
|
31
24
|
*/
|
|
32
|
-
export declare function
|
|
25
|
+
export declare function getCacheOperationPrefix(config: CacheConfig, scope: string, operationName: string): string;
|
|
33
26
|
/**
|
|
34
|
-
*
|
|
27
|
+
* Build the full cache key for a scoped operation and variables.
|
|
35
28
|
*
|
|
36
|
-
* @param
|
|
37
|
-
* @
|
|
29
|
+
* @param config Cache configuration.
|
|
30
|
+
* @param scope Cache scope.
|
|
31
|
+
* @param operationName GraphQL operation name.
|
|
32
|
+
* @param variables Operation variables.
|
|
33
|
+
* @returns Full cache key.
|
|
38
34
|
*/
|
|
39
|
-
export declare function
|
|
35
|
+
export declare function getCacheKey(config: CacheConfig, scope: string, operationName: string, variables: unknown): string;
|
|
40
36
|
/**
|
|
41
|
-
*
|
|
37
|
+
* Invalidate cache entries matching a specific key prefix.
|
|
42
38
|
*
|
|
43
|
-
* @param
|
|
39
|
+
* @param prefix Cache key prefix to invalidate.
|
|
44
40
|
*/
|
|
45
|
-
export declare function
|
|
41
|
+
export declare function invalidateCachePrefix(prefix: string): void;
|
|
42
|
+
/** Invalidate all cache entries regardless of prefix. */
|
|
43
|
+
export declare function invalidateAllCache(): void;
|
|
46
44
|
/**
|
|
47
|
-
*
|
|
45
|
+
* Determine whether a cache entry should be bypassed due to invalidation.
|
|
48
46
|
*
|
|
49
|
-
* @param
|
|
47
|
+
* @param key Full cache key.
|
|
48
|
+
* @param createdAt Entry creation timestamp in milliseconds.
|
|
49
|
+
* @returns `true` when the entry was invalidated by global or prefix invalidation.
|
|
50
50
|
*/
|
|
51
|
-
export declare function
|
|
51
|
+
export declare function shouldBypassCache(key: string, createdAt?: number): boolean;
|
|
52
|
+
/** Metadata stored for a cache entry. */
|
|
53
|
+
export type CacheMeta<T> = {
|
|
54
|
+
/** In-flight promise for the current cache request. */
|
|
55
|
+
promise?: Promise<T>;
|
|
56
|
+
/** Timestamp when this metadata entry was created. */
|
|
57
|
+
createdAt: number;
|
|
58
|
+
/** Expiration timestamp in milliseconds, or `null` when it does not expire. */
|
|
59
|
+
expiresAt: number | null;
|
|
60
|
+
};
|
|
52
61
|
/**
|
|
53
|
-
*
|
|
62
|
+
* Returns metadata for a cache key.
|
|
63
|
+
*
|
|
64
|
+
* @param key Cache key used to look up metadata.
|
|
65
|
+
* @returns Metadata for the key when present, otherwise `undefined`.
|
|
54
66
|
*/
|
|
55
|
-
export declare function
|
|
67
|
+
export declare function getCacheMeta<T>(key: string): CacheMeta<T> | undefined;
|
|
56
68
|
/**
|
|
57
|
-
*
|
|
69
|
+
* Resolves a cache entry and updates both metadata and Nuxt data state.
|
|
58
70
|
*
|
|
59
|
-
* @param key Cache key.
|
|
71
|
+
* @param key Cache key to resolve.
|
|
72
|
+
* @param value Resolved data to store in Nuxt state.
|
|
73
|
+
* @param ttl Optional time-to-live in milliseconds. When omitted or `null`, the entry does not expire.
|
|
74
|
+
* @returns The resolved value.
|
|
60
75
|
*/
|
|
61
|
-
export declare function
|
|
76
|
+
export declare function resolveCacheEntry<T>(key: string, value: T, ttl?: number | null): T;
|
|
62
77
|
/**
|
|
63
|
-
*
|
|
78
|
+
* Returns whether the provided cache metadata is expired.
|
|
64
79
|
*
|
|
65
|
-
* @param
|
|
66
|
-
* @returns
|
|
80
|
+
* @param meta Metadata object to evaluate.
|
|
81
|
+
* @returns `true` when metadata exists and is past its expiration time; otherwise `false`.
|
|
67
82
|
*/
|
|
68
|
-
export declare function
|
|
83
|
+
export declare function isExpired(meta?: CacheMeta<unknown>): boolean;
|
|
69
84
|
/**
|
|
70
|
-
*
|
|
85
|
+
* Returns a promise for a cache key, creating it if it doesn't exist.
|
|
86
|
+
*
|
|
87
|
+
* @param key Cache key to resolve.
|
|
88
|
+
* @param create Function to create a new promise if none exists.
|
|
89
|
+
* @param ttl Optional time-to-live in milliseconds. When omitted or `null`, the entry does not expire.
|
|
90
|
+
* @returns A promise for the cache key.
|
|
71
91
|
*/
|
|
72
|
-
export declare function
|
|
92
|
+
export declare function getOrCreatePromise<T>(key: string, create: () => Promise<T>, ttl?: number | null): Promise<T>;
|
|
73
93
|
/**
|
|
74
|
-
*
|
|
94
|
+
* Determines whether a cache entry should be used based on its presence, invalidation status, and expiration.
|
|
95
|
+
*
|
|
96
|
+
* @param key Cache key to evaluate.
|
|
97
|
+
* @param cached Cached value to check.
|
|
98
|
+
* @param meta Metadata associated with the cache entry.
|
|
99
|
+
* @returns `true` if the cache entry should be used; otherwise `false`.
|
|
75
100
|
*/
|
|
76
|
-
export declare function
|
|
101
|
+
export declare function shouldUseCached<T>(key: string, cached: T | undefined, meta?: CacheMeta<unknown>): cached is T;
|
|
102
|
+
type SWRParams<T> = {
|
|
103
|
+
cached: T | undefined;
|
|
104
|
+
fetch: () => Promise<T>;
|
|
105
|
+
inFlight?: Promise<T>;
|
|
106
|
+
};
|
|
77
107
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
108
|
+
* Return cached data immediately and refresh in background when possible.
|
|
109
|
+
*
|
|
110
|
+
* @param params SWR inputs including cached value, fetcher, and optional in-flight promise.
|
|
111
|
+
* @returns Cached value when available, otherwise a promise resolving fresh data.
|
|
80
112
|
*/
|
|
81
|
-
export declare function
|
|
113
|
+
export declare function staleWhileRevalidate<T>({ cached, fetch, inFlight }: SWRParams<T>): Promise<T> | T;
|
|
82
114
|
export {};
|
|
@@ -1,74 +1,84 @@
|
|
|
1
1
|
import { hash } from "ohash";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
ttl: void 0,
|
|
5
|
-
keyPrefix: "gql",
|
|
6
|
-
keyVersion: "1"
|
|
7
|
-
};
|
|
8
|
-
export function resolveCacheConfig(...overrides) {
|
|
9
|
-
return Object.assign({}, defaultCacheConfig, ...overrides);
|
|
2
|
+
export function getCacheRootPrefix({ keyPrefix, keyVersion }) {
|
|
3
|
+
return `${keyPrefix}:${keyVersion}:`;
|
|
10
4
|
}
|
|
11
|
-
export function
|
|
12
|
-
|
|
13
|
-
const scopePrefix = `${rootPrefix}${scope}:`;
|
|
14
|
-
const opPrefix = `${scopePrefix}${operationName}:`;
|
|
15
|
-
const key = `${opPrefix}${hash(variables || {})}`;
|
|
16
|
-
return { rootPrefix, scopePrefix, opPrefix, key };
|
|
5
|
+
export function getCacheScopePrefix(config, scope) {
|
|
6
|
+
return `${getCacheRootPrefix(config)}${scope}:`;
|
|
17
7
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
knownCacheKeys.add(key);
|
|
8
|
+
export function getCacheOperationPrefix(config, scope, operationName) {
|
|
9
|
+
return `${getCacheScopePrefix(config, scope)}${operationName}:`;
|
|
21
10
|
}
|
|
22
|
-
export function
|
|
23
|
-
return
|
|
11
|
+
export function getCacheKey(config, scope, operationName, variables) {
|
|
12
|
+
return `${getCacheOperationPrefix(config, scope, operationName)}${hash(variables ?? {})}`;
|
|
24
13
|
}
|
|
25
|
-
const invalidatedExactAt = /* @__PURE__ */ new Map();
|
|
26
14
|
const invalidatedPrefixAt = /* @__PURE__ */ new Map();
|
|
27
|
-
const refreshedAt = /* @__PURE__ */ new Map();
|
|
28
15
|
let invalidatedAllAt = 0;
|
|
29
|
-
export function invalidateCacheKey(key) {
|
|
30
|
-
invalidatedExactAt.set(key, Date.now());
|
|
31
|
-
}
|
|
32
16
|
export function invalidateCachePrefix(prefix) {
|
|
33
17
|
invalidatedPrefixAt.set(prefix, Date.now());
|
|
34
18
|
}
|
|
35
|
-
export function
|
|
19
|
+
export function invalidateAllCache() {
|
|
36
20
|
invalidatedAllAt = Date.now();
|
|
37
21
|
}
|
|
38
|
-
export function
|
|
39
|
-
|
|
22
|
+
export function shouldBypassCache(key, createdAt) {
|
|
23
|
+
if (!createdAt) return false;
|
|
24
|
+
if (createdAt < invalidatedAllAt) return true;
|
|
25
|
+
let newest = 0;
|
|
26
|
+
let idx = key.indexOf(":");
|
|
27
|
+
while (idx !== -1) {
|
|
28
|
+
const prefix = key.slice(0, idx + 1);
|
|
29
|
+
const ts = invalidatedPrefixAt.get(prefix);
|
|
30
|
+
if (ts && ts > newest) newest = ts;
|
|
31
|
+
idx = key.indexOf(":", idx + 1);
|
|
32
|
+
}
|
|
33
|
+
return createdAt < newest;
|
|
34
|
+
}
|
|
35
|
+
const metaCache = /* @__PURE__ */ new Map();
|
|
36
|
+
export function getCacheMeta(key) {
|
|
37
|
+
return metaCache.get(key);
|
|
40
38
|
}
|
|
41
|
-
export function
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
export function resolveCacheEntry(key, value, ttl) {
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
const meta = metaCache.get(key);
|
|
42
|
+
const next = meta ?? { createdAt: now, expiresAt: null };
|
|
43
|
+
next.createdAt = now;
|
|
44
|
+
next.expiresAt = ttl ? now + ttl : null;
|
|
45
|
+
next.promise = void 0;
|
|
46
|
+
metaCache.set(key, next);
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
export function isExpired(meta) {
|
|
50
|
+
if (!meta) return false;
|
|
51
|
+
if (meta.expiresAt === null) return false;
|
|
52
|
+
return Date.now() > meta.expiresAt;
|
|
53
|
+
}
|
|
54
|
+
export function getOrCreatePromise(key, create, ttl) {
|
|
55
|
+
let meta = metaCache.get(key);
|
|
56
|
+
if (meta?.promise) return meta.promise;
|
|
57
|
+
const promise = (async () => {
|
|
58
|
+
const value = await create();
|
|
59
|
+
resolveCacheEntry(key, value, ttl);
|
|
60
|
+
return value;
|
|
61
|
+
})();
|
|
62
|
+
if (!meta) {
|
|
63
|
+
meta = { createdAt: Date.now(), expiresAt: null };
|
|
64
|
+
metaCache.set(key, meta);
|
|
49
65
|
}
|
|
50
|
-
|
|
51
|
-
return
|
|
66
|
+
meta.promise = promise;
|
|
67
|
+
return promise;
|
|
52
68
|
}
|
|
53
|
-
export function
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
69
|
+
export function shouldUseCached(key, cached, meta) {
|
|
70
|
+
if (cached === void 0) return false;
|
|
71
|
+
if (shouldBypassCache(key, meta?.createdAt)) return false;
|
|
72
|
+
if (isExpired(meta)) return false;
|
|
73
|
+
return true;
|
|
57
74
|
}
|
|
58
|
-
export function
|
|
59
|
-
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
refreshedAt.delete(key);
|
|
75
|
+
export function staleWhileRevalidate({ cached, fetch, inFlight }) {
|
|
76
|
+
if (cached !== void 0) {
|
|
77
|
+
if (!inFlight) {
|
|
78
|
+
fetch().catch(() => {
|
|
79
|
+
});
|
|
64
80
|
}
|
|
81
|
+
return cached;
|
|
65
82
|
}
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
export function forgetCacheAll() {
|
|
69
|
-
knownCacheKeys.clear();
|
|
70
|
-
invalidatedExactAt.clear();
|
|
71
|
-
invalidatedPrefixAt.clear();
|
|
72
|
-
refreshedAt.clear();
|
|
73
|
-
invalidatedAllAt = 0;
|
|
83
|
+
return inFlight ?? fetch();
|
|
74
84
|
}
|
|
@@ -1,30 +1,36 @@
|
|
|
1
|
+
type PersistedEntry<T> = {
|
|
2
|
+
value: T;
|
|
3
|
+
createdAt: number;
|
|
4
|
+
expiresAt: number | null;
|
|
5
|
+
};
|
|
1
6
|
/**
|
|
2
|
-
*
|
|
7
|
+
* Read a persisted cache entry from local storage.
|
|
3
8
|
*
|
|
4
9
|
* @param key Cache key.
|
|
5
|
-
* @returns
|
|
10
|
+
* @returns Persisted entry when present and not expired.
|
|
6
11
|
*/
|
|
7
|
-
export declare function getPersistedEntry<T>(key: string): Promise<T | undefined>;
|
|
12
|
+
export declare function getPersistedEntry<T>(key: string): Promise<PersistedEntry<T> | undefined>;
|
|
8
13
|
/**
|
|
9
|
-
* Persist a cache entry
|
|
14
|
+
* Persist a cache entry to local storage.
|
|
10
15
|
*
|
|
11
16
|
* @param key Cache key.
|
|
12
|
-
* @param value Value to
|
|
13
|
-
* @param ttl
|
|
14
|
-
* @returns
|
|
17
|
+
* @param value Value to persist.
|
|
18
|
+
* @param ttl Optional time-to-live in milliseconds.
|
|
19
|
+
* @returns Nothing.
|
|
15
20
|
*/
|
|
16
|
-
export declare function setPersistedEntry<T>(key: string, value: T, ttl
|
|
21
|
+
export declare function setPersistedEntry<T>(key: string, value: T, ttl?: number | null): Promise<void>;
|
|
17
22
|
/**
|
|
18
|
-
*
|
|
23
|
+
* Remove a single persisted cache entry.
|
|
19
24
|
*
|
|
20
25
|
* @param key Cache key.
|
|
21
|
-
* @returns
|
|
26
|
+
* @returns Nothing.
|
|
22
27
|
*/
|
|
23
28
|
export declare function deletePersistedEntry(key: string): Promise<void>;
|
|
24
29
|
/**
|
|
25
|
-
*
|
|
30
|
+
* Remove persisted cache entries matching a key prefix.
|
|
26
31
|
*
|
|
27
32
|
* @param prefix Cache key prefix.
|
|
28
|
-
* @returns
|
|
33
|
+
* @returns Nothing.
|
|
29
34
|
*/
|
|
30
35
|
export declare function deletePersistedByPrefix(prefix: string): Promise<void>;
|
|
36
|
+
export {};
|
|
@@ -1,70 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
return import.meta.client && typeof window.localStorage !== "undefined";
|
|
5
|
-
}
|
|
6
|
-
let storage = null;
|
|
7
|
-
function getPersistedStorage() {
|
|
8
|
-
if (!storage && isPersistedStorageAvailable()) {
|
|
9
|
-
try {
|
|
10
|
-
storage = createStorage({
|
|
11
|
-
driver: localStorageDriver({ base: "nuxt-graphql:" })
|
|
12
|
-
});
|
|
13
|
-
} catch {
|
|
14
|
-
storage = null;
|
|
15
|
-
}
|
|
1
|
+
function getStorage() {
|
|
2
|
+
if (import.meta.server) {
|
|
3
|
+
return null;
|
|
16
4
|
}
|
|
17
|
-
|
|
5
|
+
try {
|
|
6
|
+
return window.localStorage;
|
|
7
|
+
} catch {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function buildKey(key) {
|
|
12
|
+
return `cache:${key}`;
|
|
18
13
|
}
|
|
19
14
|
export async function getPersistedEntry(key) {
|
|
20
|
-
const
|
|
21
|
-
if (!
|
|
22
|
-
return void 0;
|
|
23
|
-
}
|
|
15
|
+
const storage = getStorage();
|
|
16
|
+
if (!storage) return void 0;
|
|
24
17
|
try {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
-
|
|
18
|
+
const storageKey = buildKey(key);
|
|
19
|
+
const raw = storage.getItem(storageKey);
|
|
20
|
+
if (!raw) return void 0;
|
|
21
|
+
const entry = JSON.parse(raw);
|
|
22
|
+
if (entry.expiresAt !== null && Date.now() > entry.expiresAt) {
|
|
23
|
+
storage.removeItem(storageKey);
|
|
31
24
|
return void 0;
|
|
32
25
|
}
|
|
33
|
-
return
|
|
26
|
+
return entry;
|
|
34
27
|
} catch {
|
|
35
28
|
return void 0;
|
|
36
29
|
}
|
|
37
30
|
}
|
|
38
31
|
export async function setPersistedEntry(key, value, ttl) {
|
|
39
|
-
const
|
|
40
|
-
if (!
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
const expiresAt = ttl > 0 ? Date.now() + ttl * 1e3 : null;
|
|
32
|
+
const storage = getStorage();
|
|
33
|
+
if (!storage) return;
|
|
44
34
|
try {
|
|
45
|
-
const
|
|
46
|
-
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
const entry = {
|
|
37
|
+
value,
|
|
38
|
+
createdAt: now,
|
|
39
|
+
expiresAt: ttl ? now + ttl : null
|
|
40
|
+
};
|
|
41
|
+
storage.setItem(buildKey(key), JSON.stringify(entry));
|
|
47
42
|
} catch {
|
|
48
43
|
}
|
|
49
44
|
}
|
|
50
45
|
export async function deletePersistedEntry(key) {
|
|
51
|
-
const
|
|
52
|
-
if (!
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
46
|
+
const storage = getStorage();
|
|
47
|
+
if (!storage) return;
|
|
55
48
|
try {
|
|
56
|
-
|
|
49
|
+
storage.removeItem(buildKey(key));
|
|
57
50
|
} catch {
|
|
58
51
|
}
|
|
59
52
|
}
|
|
60
53
|
export async function deletePersistedByPrefix(prefix) {
|
|
61
|
-
const
|
|
62
|
-
if (!
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
54
|
+
const storage = getStorage();
|
|
55
|
+
if (!storage) return;
|
|
65
56
|
try {
|
|
66
|
-
const
|
|
67
|
-
|
|
57
|
+
const fullPrefix = buildKey(prefix);
|
|
58
|
+
for (let i = storage.length - 1; i >= 0; i--) {
|
|
59
|
+
const k = storage.key(i);
|
|
60
|
+
if (!k) continue;
|
|
61
|
+
if (k.startsWith(fullPrefix)) {
|
|
62
|
+
storage.removeItem(k);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
68
65
|
} catch {
|
|
69
66
|
}
|
|
70
67
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineNuxtPlugin, useRequestURL } from "#app";
|
|
2
2
|
import { createClient } from "graphql-sse";
|
|
3
|
-
export default defineNuxtPlugin((
|
|
3
|
+
export default defineNuxtPlugin(() => {
|
|
4
4
|
const { origin } = useRequestURL();
|
|
5
5
|
let sseClient;
|
|
6
6
|
function getGraphQLSSEClient() {
|
|
@@ -10,6 +10,5 @@ export default defineNuxtPlugin((_nuxtApp) => {
|
|
|
10
10
|
if (sseClient) return sseClient;
|
|
11
11
|
return sseClient = createClient({ url: `${origin}/api/graphql` });
|
|
12
12
|
}
|
|
13
|
-
;
|
|
14
13
|
return { provide: { getGraphQLSSEClient } };
|
|
15
14
|
});
|