@studiocms/cfetch 0.2.1 → 0.3.1
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 +59 -8
- package/dist/{cache.d.ts → cache.d.mts} +28 -31
- package/dist/cache.mjs +176 -0
- package/dist/consts.d.mts +17 -0
- package/dist/consts.mjs +23 -0
- package/dist/index.d.mts +32 -0
- package/dist/index.mjs +61 -0
- package/dist/stub.d.mts +22 -0
- package/dist/{stub.js → stub.mjs} +77 -20
- package/dist/{types.d.ts → types.d.mts} +9 -5
- package/dist/types.mjs +1 -0
- package/dist/utils/{integration.d.ts → integration.d.mts} +31 -32
- package/dist/utils/integration.mjs +276 -0
- package/dist/{wrappers.d.ts → wrappers.d.mts} +77 -40
- package/dist/wrappers.mjs +364 -0
- package/package.json +14 -11
- package/dist/cache.js +0 -89
- package/dist/consts.d.ts +0 -8
- package/dist/consts.js +0 -11
- package/dist/index.d.ts +0 -66
- package/dist/index.js +0 -42
- package/dist/stub.d.ts +0 -6
- package/dist/types.js +0 -0
- package/dist/utils/integration.js +0 -266
- package/dist/wrappers.js +0 -87
|
@@ -1,42 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* cache layer.
|
|
10
|
-
*/
|
|
11
|
-
import { type Duration, Effect } from 'effect';
|
|
12
|
-
export type { CacheConfig } from './types.js';
|
|
13
|
-
export { Duration } from 'effect';
|
|
14
|
-
declare const FetchError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
15
|
-
readonly _tag: "FetchError";
|
|
1
|
+
import { CacheConfig } from "./types.mjs";
|
|
2
|
+
import { Duration, Duration as Duration$1, Effect } from "effect";
|
|
3
|
+
import * as effect_Types0 from "effect/Types";
|
|
4
|
+
import * as effect_Cause0 from "effect/Cause";
|
|
5
|
+
|
|
6
|
+
//#region src/wrappers.d.ts
|
|
7
|
+
declare const FetchError_base: new <A extends Record<string, any> = {}>(args: effect_Types0.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }) => effect_Cause0.YieldableError & {
|
|
8
|
+
readonly _tag: "FetchError";
|
|
16
9
|
} & Readonly<A>;
|
|
17
10
|
/**
|
|
18
11
|
* Custom error type for fetch-related errors.
|
|
19
12
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}> {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
headers: Record<string, string>;
|
|
13
|
+
declare class FetchError extends FetchError_base<{
|
|
14
|
+
message: string;
|
|
15
|
+
cause?: unknown;
|
|
16
|
+
}> {}
|
|
17
|
+
interface CachedResponse<T> {
|
|
18
|
+
data: T;
|
|
19
|
+
ok: boolean;
|
|
20
|
+
status: number;
|
|
21
|
+
statusText: string;
|
|
22
|
+
headers: Record<string, string>;
|
|
31
23
|
}
|
|
32
24
|
/**
|
|
33
25
|
* Configuration options for cached fetch requests.
|
|
34
26
|
*/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
27
|
+
interface CFetchConfig {
|
|
28
|
+
ttl?: Duration$1.DurationInput;
|
|
29
|
+
tags?: string[];
|
|
30
|
+
key?: string;
|
|
31
|
+
verbose?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Options for cache invalidation.
|
|
35
|
+
*/
|
|
36
|
+
interface InvalidateCacheOptions {
|
|
37
|
+
keys?: string[];
|
|
38
|
+
tags?: string[];
|
|
40
39
|
}
|
|
41
40
|
/**
|
|
42
41
|
* No-op parser for HEAD requests.
|
|
@@ -45,7 +44,7 @@ export interface CFetchConfig {
|
|
|
45
44
|
* @param _ - The Response object (ignored)
|
|
46
45
|
* @returns A Promise that resolves to undefined
|
|
47
46
|
*/
|
|
48
|
-
|
|
47
|
+
declare const noOpParser: <U>(_: Response) => Promise<U>;
|
|
49
48
|
/**
|
|
50
49
|
* Fetches data from a URL with caching capabilities using Effect.
|
|
51
50
|
*
|
|
@@ -87,7 +86,25 @@ export declare const noOpParser: <U>(_: Response) => Promise<U>;
|
|
|
87
86
|
* );
|
|
88
87
|
* ```
|
|
89
88
|
*/
|
|
90
|
-
|
|
89
|
+
declare const cFetchEffect: <T>(url: string | URL, parser: (response: Response) => Promise<T>, options?: RequestInit, cacheConfig?: CFetchConfig) => Effect.Effect<CachedResponse<T>, FetchError, never>;
|
|
90
|
+
/**
|
|
91
|
+
* Invalidates cache entries based on specified keys or tags.
|
|
92
|
+
*
|
|
93
|
+
* @param opts - An object containing optional keys and tags for cache invalidation
|
|
94
|
+
* @param opts.keys - An array of specific cache keys to invalidate
|
|
95
|
+
* @param opts.tags - An array of tags; all cache entries associated with these tags will be invalidated
|
|
96
|
+
*
|
|
97
|
+
* @returns An Effect that performs the cache invalidation when executed
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* yield* invalidateCacheEffect({
|
|
102
|
+
* tags: ['user'],
|
|
103
|
+
* keys: ['user:123', 'user:456']
|
|
104
|
+
* });
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
declare const invalidateCacheEffect: (opts: InvalidateCacheOptions) => Effect.Effect<void, never, never>;
|
|
91
108
|
/**
|
|
92
109
|
* Creates an Effect that fetches JSON data from a URL with caching support.
|
|
93
110
|
*
|
|
@@ -109,7 +126,7 @@ export declare const cFetchEffect: <T>(url: string | URL, parser: (response: Res
|
|
|
109
126
|
* );
|
|
110
127
|
* ```
|
|
111
128
|
*/
|
|
112
|
-
|
|
129
|
+
declare const cFetchEffectJson: <T>(url: string | URL, options?: RequestInit, cacheConfig?: CFetchConfig) => Effect.Effect<CachedResponse<T>, FetchError, never>;
|
|
113
130
|
/**
|
|
114
131
|
* Fetches a resource from the specified URL and returns the response body as text.
|
|
115
132
|
*
|
|
@@ -132,7 +149,7 @@ export declare const cFetchEffectJson: <T>(url: string | URL, options?: RequestI
|
|
|
132
149
|
* );
|
|
133
150
|
* ```
|
|
134
151
|
*/
|
|
135
|
-
|
|
152
|
+
declare const cFetchEffectText: (url: string | URL, options?: RequestInit, cacheConfig?: CFetchConfig) => Effect.Effect<CachedResponse<string>, FetchError, never>;
|
|
136
153
|
/**
|
|
137
154
|
* Fetches a resource and returns it as a Blob with caching support.
|
|
138
155
|
*
|
|
@@ -154,7 +171,7 @@ export declare const cFetchEffectText: (url: string | URL, options?: RequestInit
|
|
|
154
171
|
* );
|
|
155
172
|
* ```
|
|
156
173
|
*/
|
|
157
|
-
|
|
174
|
+
declare const cFetchEffectBlob: (url: string | URL, options?: RequestInit, cacheConfig?: CFetchConfig) => Effect.Effect<CachedResponse<Blob>, FetchError, never>;
|
|
158
175
|
/**
|
|
159
176
|
* Executes a cached fetch request with configurable caching behavior.
|
|
160
177
|
*
|
|
@@ -178,7 +195,7 @@ export declare const cFetchEffectBlob: (url: string | URL, options?: RequestInit
|
|
|
178
195
|
* );
|
|
179
196
|
* ```
|
|
180
197
|
*/
|
|
181
|
-
|
|
198
|
+
declare const cFetch: <T>(url: string | URL, parser: (response: Response) => Promise<T>, options?: RequestInit, cacheConfig?: CFetchConfig) => Promise<CachedResponse<T>>;
|
|
182
199
|
/**
|
|
183
200
|
* Fetches and parses JSON data from the specified URL with caching support.
|
|
184
201
|
*
|
|
@@ -202,7 +219,7 @@ export declare const cFetch: <T>(url: string | URL, parser: (response: Response)
|
|
|
202
219
|
* });
|
|
203
220
|
* ```
|
|
204
221
|
*/
|
|
205
|
-
|
|
222
|
+
declare const cFetchJson: <T>(url: string | URL, options?: RequestInit, cacheConfig?: CFetchConfig) => Promise<CachedResponse<T>>;
|
|
206
223
|
/**
|
|
207
224
|
* Fetches a URL and returns the response as text with caching support.
|
|
208
225
|
*
|
|
@@ -224,7 +241,7 @@ export declare const cFetchJson: <T>(url: string | URL, options?: RequestInit, c
|
|
|
224
241
|
* });
|
|
225
242
|
* ```
|
|
226
243
|
*/
|
|
227
|
-
|
|
244
|
+
declare const cFetchText: (url: string | URL, options?: RequestInit, cacheConfig?: CFetchConfig) => Promise<CachedResponse<string>>;
|
|
228
245
|
/**
|
|
229
246
|
* Fetches a Blob resource from the specified URL with optional caching configuration.
|
|
230
247
|
*
|
|
@@ -242,4 +259,24 @@ export declare const cFetchText: (url: string | URL, options?: RequestInit, cach
|
|
|
242
259
|
* const blob = response.data;
|
|
243
260
|
* ```
|
|
244
261
|
*/
|
|
245
|
-
|
|
262
|
+
declare const cFetchBlob: (url: string | URL, options?: RequestInit, cacheConfig?: CFetchConfig) => Promise<CachedResponse<Blob>>;
|
|
263
|
+
/**
|
|
264
|
+
* Invalidates cache entries based on specified keys or tags.
|
|
265
|
+
*
|
|
266
|
+
* @param opts - An object containing optional keys and tags for cache invalidation
|
|
267
|
+
* @param opts.keys - An array of specific cache keys to invalidate
|
|
268
|
+
* @param opts.tags - An array of tags; all cache entries associated with these tags will be invalidated
|
|
269
|
+
*
|
|
270
|
+
* @returns A Promise that resolves when the cache invalidation is complete
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```typescript
|
|
274
|
+
* await invalidateCache({
|
|
275
|
+
* tags: ['user'],
|
|
276
|
+
* keys: ['user:123', 'user:456']
|
|
277
|
+
* });
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
declare const invalidateCache: (opts: InvalidateCacheOptions) => Promise<void>;
|
|
281
|
+
//#endregion
|
|
282
|
+
export { CFetchConfig, type CacheConfig, CachedResponse, Duration, FetchError, InvalidateCacheOptions, cFetch, cFetchBlob, cFetchEffect, cFetchEffectBlob, cFetchEffectJson, cFetchEffectText, cFetchJson, cFetchText, invalidateCache, invalidateCacheEffect, noOpParser };
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { CacheMaps, CacheService } from "./cache.mjs";
|
|
2
|
+
import { Data, Duration, Effect, Layer, Pretty, Schema } from "effect";
|
|
3
|
+
|
|
4
|
+
//#region src/wrappers.ts
|
|
5
|
+
/**
|
|
6
|
+
* @module cfetch/wrappers
|
|
7
|
+
*
|
|
8
|
+
* Provides cached fetch wrappers using Effect for caching capabilities.
|
|
9
|
+
*
|
|
10
|
+
* This module exports functions to perform HTTP requests with built-in caching logic.
|
|
11
|
+
* It defines types for cached responses and errors, and implements functions to fetch
|
|
12
|
+
* data with various parsing options (JSON, text, Blob) while leveraging an in-memory
|
|
13
|
+
* cache layer.
|
|
14
|
+
*/
|
|
15
|
+
const store = /* @__PURE__ */ new Map();
|
|
16
|
+
const tagIndex = /* @__PURE__ */ new Map();
|
|
17
|
+
const CacheMapLayer = Layer.succeed(CacheMaps, {
|
|
18
|
+
store,
|
|
19
|
+
tagIndex
|
|
20
|
+
});
|
|
21
|
+
const CacheLive = CacheService.Default.pipe(Layer.provide(CacheMapLayer));
|
|
22
|
+
/**
|
|
23
|
+
* Custom error type for fetch-related errors.
|
|
24
|
+
*/
|
|
25
|
+
var FetchError = class extends Data.TaggedError("FetchError") {};
|
|
26
|
+
/**
|
|
27
|
+
* Schema for validating cache-relevant fetch options.
|
|
28
|
+
*
|
|
29
|
+
* This schema is used to ensure that only serializable and relevant options are considered
|
|
30
|
+
* when generating cache keys. It includes the HTTP method, headers (as a record of key-value pairs),
|
|
31
|
+
* and body (only if it's a string). Non-serializable options are excluded to prevent cache key collisions.
|
|
32
|
+
*/
|
|
33
|
+
const CacheRelevantOptionsSchema = Schema.Struct({
|
|
34
|
+
method: Schema.optional(Schema.Union(Schema.String, Schema.Undefined)),
|
|
35
|
+
headers: Schema.optional(Schema.Union(Schema.Record({
|
|
36
|
+
key: Schema.String,
|
|
37
|
+
value: Schema.Any
|
|
38
|
+
}), Schema.Undefined)),
|
|
39
|
+
body: Schema.optional(Schema.Union(Schema.String, Schema.Undefined))
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Utility to stringify cache-relevant options for generating cache keys.
|
|
43
|
+
*/
|
|
44
|
+
const stringifyRelevantOptions = Pretty.make(CacheRelevantOptionsSchema);
|
|
45
|
+
/**
|
|
46
|
+
* Helper to run an Effect and return a Promise.
|
|
47
|
+
*/
|
|
48
|
+
const runEffect = (effect) => Effect.runPromise(effect);
|
|
49
|
+
const cacheableMethods = ["GET", "HEAD"];
|
|
50
|
+
/**
|
|
51
|
+
* No-op parser for HEAD requests.
|
|
52
|
+
*
|
|
53
|
+
* @template U - The type of the parsed data
|
|
54
|
+
* @param _ - The Response object (ignored)
|
|
55
|
+
* @returns A Promise that resolves to undefined
|
|
56
|
+
*/
|
|
57
|
+
const noOpParser = (_) => Promise.resolve(void 0);
|
|
58
|
+
/**
|
|
59
|
+
* Fetches data from a URL and parses the response using a provided parser function.
|
|
60
|
+
*
|
|
61
|
+
* @template T - The type of the parsed data
|
|
62
|
+
* @param url - The URL to fetch from, either as a string or URL object
|
|
63
|
+
* @param parser - An async function that takes a Response and returns the parsed data of type T
|
|
64
|
+
* @param options - Optional fetch configuration options (headers, method, body, etc.)
|
|
65
|
+
* @returns An Effect that yields a CachedResponse containing the parsed data, response status, headers, and metadata
|
|
66
|
+
* @throws {FetchError} When the fetch operation fails or when response parsing fails
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* const result = await Effect.runPromise(
|
|
71
|
+
* fetchAndParse('https://api.example.com/data', (res) => res.json())
|
|
72
|
+
* );
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
const fetchAndParse = (url, parser, options) => Effect.gen(function* () {
|
|
76
|
+
const response = yield* Effect.tryPromise({
|
|
77
|
+
try: () => fetch(url, options),
|
|
78
|
+
catch: (cause) => new FetchError({
|
|
79
|
+
message: "Failed to fetch",
|
|
80
|
+
cause
|
|
81
|
+
})
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
data: yield* Effect.tryPromise({
|
|
85
|
+
try: () => parser(response),
|
|
86
|
+
catch: (cause) => new FetchError({
|
|
87
|
+
message: "Failed to parse response",
|
|
88
|
+
cause
|
|
89
|
+
})
|
|
90
|
+
}),
|
|
91
|
+
ok: response.ok,
|
|
92
|
+
status: response.status,
|
|
93
|
+
statusText: response.statusText,
|
|
94
|
+
headers: Object.fromEntries(response.headers.entries())
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
/**
|
|
98
|
+
* Fetches data from a URL with caching capabilities using Effect.
|
|
99
|
+
*
|
|
100
|
+
* This function performs an HTTP request with built-in caching logic. It first checks
|
|
101
|
+
* the cache for existing data, and if not found, fetches from the network, parses the
|
|
102
|
+
* response, and caches successful responses for future use.
|
|
103
|
+
*
|
|
104
|
+
* @template T - The type of the parsed response data
|
|
105
|
+
*
|
|
106
|
+
* @param url - The URL to fetch data from
|
|
107
|
+
* @param parser - A function that parses the Response object into type T
|
|
108
|
+
* @param options - Optional RequestInit configuration for the fetch request
|
|
109
|
+
* @param cacheConfig - Optional cache configuration object
|
|
110
|
+
* @param cacheConfig.ttl - Time-to-live duration for the cached entry
|
|
111
|
+
* @param cacheConfig.tags - Tags to associate with the cached entry for invalidation
|
|
112
|
+
* @param cacheConfig.key - Custom cache key (defaults to URL and options hash)
|
|
113
|
+
*
|
|
114
|
+
* @returns An Effect that yields a CachedResponse containing the parsed data and response metadata
|
|
115
|
+
*
|
|
116
|
+
* @throws {FetchError} When the network request fails or response parsing fails
|
|
117
|
+
*
|
|
118
|
+
* @remarks
|
|
119
|
+
* - Cache keys are automatically generated from the URL and options if not provided
|
|
120
|
+
* - Only successful responses (response.ok === true) are cached
|
|
121
|
+
* - Only 2xx responses are cached
|
|
122
|
+
* - Users needing negative caching should implement custom logic
|
|
123
|
+
* - The effect is provided with CacheLive layer automatically
|
|
124
|
+
* - Non-cacheable HTTP methods (e.g. POST, PUT) bypass the cache entirely
|
|
125
|
+
* - The parser function is bypassed for HEAD requests, returning undefined data
|
|
126
|
+
* - Verbose logging can be enabled via cacheConfig.verbose
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```typescript
|
|
130
|
+
* const effect = cFetchEffect(
|
|
131
|
+
* 'https://api.example.com/data',
|
|
132
|
+
* (res) => res.json(),
|
|
133
|
+
* { method: 'GET' },
|
|
134
|
+
* { ttl: Duration.minutes(5), tags: ['api-data'] }
|
|
135
|
+
* );
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
const cFetchEffect = (url, parser, options, cacheConfig) => Effect.gen(function* () {
|
|
139
|
+
const cache = yield* CacheService;
|
|
140
|
+
const { key, verbose = false, ...cacheOpts } = cacheConfig || {};
|
|
141
|
+
const method = options?.method?.toUpperCase() || "GET";
|
|
142
|
+
if (!cacheableMethods.includes(method)) {
|
|
143
|
+
if (verbose) console.log(`[c:fetch] Bypassing cache for non-cacheable method: ${method}`);
|
|
144
|
+
return yield* fetchAndParse(url, parser, options);
|
|
145
|
+
}
|
|
146
|
+
const urlString = typeof url === "string" ? url : url.href;
|
|
147
|
+
const cacheRelevantOptions = options ? {
|
|
148
|
+
method: options.method,
|
|
149
|
+
headers: options.headers instanceof Headers ? Object.fromEntries([...options.headers.entries()].sort(([a], [b]) => a.localeCompare(b))) : options.headers ? Object.fromEntries(Object.entries(options.headers).sort(([a], [b]) => a.localeCompare(b))) : void 0,
|
|
150
|
+
body: typeof options.body === "string" ? options.body : void 0
|
|
151
|
+
} : {};
|
|
152
|
+
if (options?.body && typeof options.body !== "string" && !key && verbose) console.warn(`[c:fetch] Non-serializable request body detected for ${urlString}. Consider providing an explicit cache key via cacheConfig.key to avoid collisions.`);
|
|
153
|
+
const cacheKey = key ?? `${urlString}-${stringifyRelevantOptions(cacheRelevantOptions)}`;
|
|
154
|
+
const cached = yield* cache.get(cacheKey);
|
|
155
|
+
if (cached) {
|
|
156
|
+
if (verbose) console.log(`[c:fetch] Cache hit for: ${cacheKey}, returning cached response.`);
|
|
157
|
+
return cached;
|
|
158
|
+
}
|
|
159
|
+
if (verbose) console.log(`[c:fetch] Cache miss for: ${cacheKey}, fetching from network.`);
|
|
160
|
+
const cachedResponse = yield* fetchAndParse(url, method === "HEAD" ? noOpParser : parser, options);
|
|
161
|
+
if (cachedResponse.ok) {
|
|
162
|
+
yield* cache.set(cacheKey, cachedResponse, cacheOpts);
|
|
163
|
+
if (verbose) console.log(`[c:fetch] Cached response for: ${cacheKey}`);
|
|
164
|
+
}
|
|
165
|
+
return cachedResponse;
|
|
166
|
+
}).pipe(Effect.provide(CacheLive));
|
|
167
|
+
/**
|
|
168
|
+
* Invalidates cache entries based on specified keys or tags.
|
|
169
|
+
*
|
|
170
|
+
* @param opts - An object containing optional keys and tags for cache invalidation
|
|
171
|
+
* @param opts.keys - An array of specific cache keys to invalidate
|
|
172
|
+
* @param opts.tags - An array of tags; all cache entries associated with these tags will be invalidated
|
|
173
|
+
*
|
|
174
|
+
* @returns An Effect that performs the cache invalidation when executed
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* yield* invalidateCacheEffect({
|
|
179
|
+
* tags: ['user'],
|
|
180
|
+
* keys: ['user:123', 'user:456']
|
|
181
|
+
* });
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
const invalidateCacheEffect = (opts) => Effect.gen(function* () {
|
|
185
|
+
const cache = yield* CacheService;
|
|
186
|
+
if (opts.keys) for (const key of opts.keys) yield* cache.delete(key);
|
|
187
|
+
if (opts.tags) yield* cache.invalidateTags(opts.tags);
|
|
188
|
+
}).pipe(Effect.provide(CacheLive));
|
|
189
|
+
/**
|
|
190
|
+
* Creates an Effect that fetches JSON data from a URL with caching support.
|
|
191
|
+
*
|
|
192
|
+
* @template T - The expected type of the JSON response data
|
|
193
|
+
* @param url - The URL to fetch data from
|
|
194
|
+
* @param options - Optional fetch configuration options (headers, method, etc.)
|
|
195
|
+
* @param cacheConfig - Optional cache configuration
|
|
196
|
+
* @param cacheConfig.ttl - Time-to-live duration for the cached response
|
|
197
|
+
* @param cacheConfig.tags - Tags to associate with the cached entry for invalidation
|
|
198
|
+
* @param cacheConfig.key - Custom cache key to use instead of the default URL-based key
|
|
199
|
+
* @returns An Effect that yields a CachedResponse containing the parsed JSON data, or fails with a FetchError
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```typescript
|
|
203
|
+
* const effect = cFetchEffectJson<User>(
|
|
204
|
+
* 'https://api.example.com/user/123',
|
|
205
|
+
* { method: 'GET' },
|
|
206
|
+
* { ttl: Duration.minutes(5), tags: ['user'] }
|
|
207
|
+
* );
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
const cFetchEffectJson = (url, options, cacheConfig) => cFetchEffect(url, (res) => res.json(), options, cacheConfig);
|
|
211
|
+
/**
|
|
212
|
+
* Fetches a resource from the specified URL and returns the response body as text.
|
|
213
|
+
*
|
|
214
|
+
* @param url - The URL to fetch the resource from.
|
|
215
|
+
* @param options - Optional fetch configuration including method, headers, body, etc.
|
|
216
|
+
* @param cacheConfig - Optional cache configuration.
|
|
217
|
+
* @param cacheConfig.ttl - Time-to-live duration for the cached response.
|
|
218
|
+
* @param cacheConfig.tags - Array of tags to associate with the cached entry for invalidation purposes.
|
|
219
|
+
* @param cacheConfig.key - Custom cache key. If not provided, the URL will be used as the key.
|
|
220
|
+
*
|
|
221
|
+
* @returns An Effect that resolves to a CachedResponse containing the response text,
|
|
222
|
+
* or fails with a FetchError if the request fails.
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```typescript
|
|
226
|
+
* const textEffect = cFetchEffectText(
|
|
227
|
+
* 'https://api.example.com/data',
|
|
228
|
+
* { method: 'GET' },
|
|
229
|
+
* { ttl: Duration.minutes(5), tags: ['api', 'data'] }
|
|
230
|
+
* );
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
const cFetchEffectText = (url, options, cacheConfig) => cFetchEffect(url, (res) => res.text(), options, cacheConfig);
|
|
234
|
+
/**
|
|
235
|
+
* Fetches a resource and returns it as a Blob with caching support.
|
|
236
|
+
*
|
|
237
|
+
* @param url - The URL of the resource to fetch
|
|
238
|
+
* @param options - Optional fetch request configuration (headers, method, etc.)
|
|
239
|
+
* @param cacheConfig - Optional cache configuration object
|
|
240
|
+
* @param cacheConfig.ttl - Time-to-live duration for the cached response
|
|
241
|
+
* @param cacheConfig.tags - Array of tags to associate with the cached entry
|
|
242
|
+
* @param cacheConfig.key - Custom cache key to use instead of the default
|
|
243
|
+
*
|
|
244
|
+
* @returns An Effect that resolves to a CachedResponse containing a Blob, or fails with a FetchError
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```typescript
|
|
248
|
+
* const imageEffect = cFetchEffectBlob(
|
|
249
|
+
* 'https://example.com/image.png',
|
|
250
|
+
* { method: 'GET' },
|
|
251
|
+
* { ttl: Duration.hours(1), tags: ['images'] }
|
|
252
|
+
* );
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
const cFetchEffectBlob = (url, options, cacheConfig) => cFetchEffect(url, (res) => res.blob(), options, cacheConfig);
|
|
256
|
+
/**
|
|
257
|
+
* Executes a cached fetch request with configurable caching behavior.
|
|
258
|
+
*
|
|
259
|
+
* @template T - The type of data returned after parsing the response
|
|
260
|
+
* @param url - The URL to fetch from
|
|
261
|
+
* @param parser - A function that parses the Response object into the desired type T
|
|
262
|
+
* @param options - Optional fetch request configuration (headers, method, body, etc.)
|
|
263
|
+
* @param cacheConfig - Optional cache configuration object
|
|
264
|
+
* @param cacheConfig.ttl - Time-to-live duration for the cached response
|
|
265
|
+
* @param cacheConfig.tags - Array of tags to associate with the cached entry for invalidation purposes
|
|
266
|
+
* @param cacheConfig.key - Custom cache key; if not provided, a key will be generated from the URL and options
|
|
267
|
+
* @returns A Promise that resolves to a CachedResponse containing the parsed data
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```typescript
|
|
271
|
+
* const result = await cFetch(
|
|
272
|
+
* 'https://api.example.com/data',
|
|
273
|
+
* (res) => res.json(),
|
|
274
|
+
* { method: 'GET' },
|
|
275
|
+
* { ttl: Duration.minutes(5), tags: ['api-data'] }
|
|
276
|
+
* );
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
const cFetch = (url, parser, options, cacheConfig) => runEffect(cFetchEffect(url, parser, options, cacheConfig));
|
|
280
|
+
/**
|
|
281
|
+
* Fetches and parses JSON data from the specified URL with caching support.
|
|
282
|
+
*
|
|
283
|
+
* @template T - The expected type of the parsed JSON response data
|
|
284
|
+
* @param url - The URL to fetch data from
|
|
285
|
+
* @param options - Optional fetch configuration options (headers, method, body, etc.)
|
|
286
|
+
* @param cacheConfig - Optional cache configuration object
|
|
287
|
+
* @param cacheConfig.ttl - Time-to-live duration for the cached response
|
|
288
|
+
* @param cacheConfig.tags - Array of tags to associate with the cached entry for invalidation purposes
|
|
289
|
+
* @param cacheConfig.key - Custom cache key to use instead of the default
|
|
290
|
+
* @returns A Promise that resolves to a CachedResponse containing the parsed JSON data of type T
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```typescript
|
|
294
|
+
* const result = await cFetchJson<User>('https://api.example.com/user/123', {
|
|
295
|
+
* method: 'GET',
|
|
296
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
297
|
+
* }, {
|
|
298
|
+
* ttl: Duration.minutes(5),
|
|
299
|
+
* tags: ['user-data']
|
|
300
|
+
* });
|
|
301
|
+
* ```
|
|
302
|
+
*/
|
|
303
|
+
const cFetchJson = (url, options, cacheConfig) => runEffect(cFetchEffectJson(url, options, cacheConfig));
|
|
304
|
+
/**
|
|
305
|
+
* Fetches a URL and returns the response as text with caching support.
|
|
306
|
+
*
|
|
307
|
+
* @param url - The URL to fetch
|
|
308
|
+
* @param options - Optional fetch configuration options (headers, method, etc.)
|
|
309
|
+
* @param cacheConfig - Optional cache configuration
|
|
310
|
+
* @param cacheConfig.ttl - Time-to-live duration for the cached response
|
|
311
|
+
* @param cacheConfig.tags - Tags to associate with the cached response for invalidation
|
|
312
|
+
* @param cacheConfig.key - Custom cache key (defaults to URL if not provided)
|
|
313
|
+
* @returns A promise that resolves to a CachedResponse containing the response text
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```typescript
|
|
317
|
+
* const result = await cFetchText('https://api.example.com/data', {
|
|
318
|
+
* method: 'GET'
|
|
319
|
+
* }, {
|
|
320
|
+
* ttl: Duration.hours(1),
|
|
321
|
+
* tags: ['api', 'data']
|
|
322
|
+
* });
|
|
323
|
+
* ```
|
|
324
|
+
*/
|
|
325
|
+
const cFetchText = (url, options, cacheConfig) => runEffect(cFetchEffectText(url, options, cacheConfig));
|
|
326
|
+
/**
|
|
327
|
+
* Fetches a Blob resource from the specified URL with optional caching configuration.
|
|
328
|
+
*
|
|
329
|
+
* @param url - The URL to fetch the Blob resource from
|
|
330
|
+
* @param options - Optional fetch request configuration (headers, method, etc.)
|
|
331
|
+
* @param cacheConfig - Optional cache configuration object
|
|
332
|
+
* @param cacheConfig.ttl - Time-to-live duration for the cached response
|
|
333
|
+
* @param cacheConfig.tags - Array of cache tags for cache invalidation
|
|
334
|
+
* @param cacheConfig.key - Custom cache key for storing the response
|
|
335
|
+
* @returns A Promise that resolves to a CachedResponse containing a Blob
|
|
336
|
+
*
|
|
337
|
+
* @example
|
|
338
|
+
* ```typescript
|
|
339
|
+
* const response = await cFetchBlob('https://example.com/image.png', {}, { ttl: Duration.minutes(5) });
|
|
340
|
+
* const blob = response.data;
|
|
341
|
+
* ```
|
|
342
|
+
*/
|
|
343
|
+
const cFetchBlob = (url, options, cacheConfig) => runEffect(cFetchEffectBlob(url, options, cacheConfig));
|
|
344
|
+
/**
|
|
345
|
+
* Invalidates cache entries based on specified keys or tags.
|
|
346
|
+
*
|
|
347
|
+
* @param opts - An object containing optional keys and tags for cache invalidation
|
|
348
|
+
* @param opts.keys - An array of specific cache keys to invalidate
|
|
349
|
+
* @param opts.tags - An array of tags; all cache entries associated with these tags will be invalidated
|
|
350
|
+
*
|
|
351
|
+
* @returns A Promise that resolves when the cache invalidation is complete
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* ```typescript
|
|
355
|
+
* await invalidateCache({
|
|
356
|
+
* tags: ['user'],
|
|
357
|
+
* keys: ['user:123', 'user:456']
|
|
358
|
+
* });
|
|
359
|
+
* ```
|
|
360
|
+
*/
|
|
361
|
+
const invalidateCache = (opts) => runEffect(invalidateCacheEffect(opts));
|
|
362
|
+
|
|
363
|
+
//#endregion
|
|
364
|
+
export { Duration, FetchError, cFetch, cFetchBlob, cFetchEffect, cFetchEffectBlob, cFetchEffectJson, cFetchEffectText, cFetchJson, cFetchText, invalidateCache, invalidateCacheEffect, noOpParser };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@studiocms/cfetch",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Astro integration that allows you to have a cached fetch function in your Astro SSR project.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://studiocms.dev",
|
|
@@ -41,29 +41,32 @@
|
|
|
41
41
|
],
|
|
42
42
|
"exports": {
|
|
43
43
|
".": {
|
|
44
|
-
"types": "./dist/index.d.
|
|
45
|
-
"default": "./dist/index.
|
|
44
|
+
"types": "./dist/index.d.mts",
|
|
45
|
+
"default": "./dist/index.mjs"
|
|
46
46
|
},
|
|
47
47
|
"./types": {
|
|
48
|
-
"types": "./dist/types.d.
|
|
49
|
-
"default": "./dist/types.
|
|
48
|
+
"types": "./dist/types.d.mts",
|
|
49
|
+
"default": "./dist/types.mjs"
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@inox-tools/astro-tests": "^0.8.1",
|
|
54
54
|
"@types/node": "^22.0.0",
|
|
55
55
|
"jest-extended": "^7.0.0",
|
|
56
|
-
"astro": "^5.
|
|
57
|
-
"effect": "^3.19.
|
|
56
|
+
"astro": "^5.17.1",
|
|
57
|
+
"effect": "^3.19.16"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
|
-
"astro": "
|
|
60
|
+
"astro": ">=5.16.6",
|
|
61
61
|
"effect": "^3.19.13",
|
|
62
62
|
"vite": "^6.4.1"
|
|
63
63
|
},
|
|
64
64
|
"scripts": {
|
|
65
|
-
"build": "
|
|
66
|
-
"dev": "
|
|
67
|
-
"
|
|
65
|
+
"build": "tsdown --config ./tsdown.config.ts --clean",
|
|
66
|
+
"dev": "tsdown --config ./tsdown.config.ts --watch",
|
|
67
|
+
"effect-check": "pnpm effect-language-service diagnostics --project tsconfig.tspc.json",
|
|
68
|
+
"typecheck": "tspc -p tsconfig.tspc.json",
|
|
69
|
+
"jsr": "jsr publish",
|
|
70
|
+
"ci:effect-check": "pnpm effect-check --format github-actions"
|
|
68
71
|
}
|
|
69
72
|
}
|