@pantheon-systems/nextjs-cache-handler 0.1.0 → 0.2.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 +110 -0
- package/dist/handlers/base.d.ts +9 -0
- package/dist/handlers/base.d.ts.map +1 -1
- package/dist/handlers/base.js +22 -0
- package/dist/handlers/base.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/middleware/index.d.ts +2 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +2 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/surrogate-key.d.ts +29 -0
- package/dist/middleware/surrogate-key.d.ts.map +1 -0
- package/dist/middleware/surrogate-key.js +66 -0
- package/dist/middleware/surrogate-key.js.map +1 -0
- package/dist/use-cache/file-handler.d.ts +71 -0
- package/dist/use-cache/file-handler.d.ts.map +1 -0
- package/dist/use-cache/file-handler.js +366 -0
- package/dist/use-cache/file-handler.js.map +1 -0
- package/dist/use-cache/gcs-handler.d.ts +64 -0
- package/dist/use-cache/gcs-handler.d.ts.map +1 -0
- package/dist/use-cache/gcs-handler.js +406 -0
- package/dist/use-cache/gcs-handler.js.map +1 -0
- package/dist/use-cache/index.d.ts +58 -0
- package/dist/use-cache/index.d.ts.map +1 -0
- package/dist/use-cache/index.js +87 -0
- package/dist/use-cache/index.js.map +1 -0
- package/dist/use-cache/stream-serialization.d.ts +34 -0
- package/dist/use-cache/stream-serialization.d.ts.map +1 -0
- package/dist/use-cache/stream-serialization.js +96 -0
- package/dist/use-cache/stream-serialization.js.map +1 -0
- package/dist/use-cache/types.d.ts +202 -0
- package/dist/use-cache/types.d.ts.map +1 -0
- package/dist/use-cache/types.js +9 -0
- package/dist/use-cache/types.js.map +1 -0
- package/dist/utils/cache-tag-context.d.ts +66 -0
- package/dist/utils/cache-tag-context.d.ts.map +1 -0
- package/dist/utils/cache-tag-context.js +126 -0
- package/dist/utils/cache-tag-context.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/request-context.d.ts +28 -0
- package/dist/utils/request-context.d.ts.map +1 -0
- package/dist/utils/request-context.js +54 -0
- package/dist/utils/request-context.js.map +1 -0
- package/dist/utils/request-context.test.d.ts +2 -0
- package/dist/utils/request-context.test.d.ts.map +1 -0
- package/dist/utils/request-context.test.js +65 -0
- package/dist/utils/request-context.test.js.map +1 -0
- package/dist/utils/with-surrogate-key.d.ts +32 -0
- package/dist/utils/with-surrogate-key.d.ts.map +1 -0
- package/dist/utils/with-surrogate-key.js +113 -0
- package/dist/utils/with-surrogate-key.js.map +1 -0
- package/package.json +15 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert a ReadableStream<Uint8Array> to a Uint8Array.
|
|
3
|
+
* Consumes the entire stream and concatenates all chunks.
|
|
4
|
+
*
|
|
5
|
+
* @param stream - The stream to consume
|
|
6
|
+
* @returns A single Uint8Array containing all the stream data
|
|
7
|
+
*/
|
|
8
|
+
export async function streamToBytes(stream) {
|
|
9
|
+
const reader = stream.getReader();
|
|
10
|
+
const chunks = [];
|
|
11
|
+
while (true) {
|
|
12
|
+
const { done, value } = await reader.read();
|
|
13
|
+
if (done)
|
|
14
|
+
break;
|
|
15
|
+
if (value) {
|
|
16
|
+
chunks.push(value);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// Handle empty stream
|
|
20
|
+
if (chunks.length === 0) {
|
|
21
|
+
return new Uint8Array(0);
|
|
22
|
+
}
|
|
23
|
+
// Single chunk optimization
|
|
24
|
+
if (chunks.length === 1) {
|
|
25
|
+
return chunks[0];
|
|
26
|
+
}
|
|
27
|
+
// Concatenate all chunks
|
|
28
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
29
|
+
const result = new Uint8Array(totalLength);
|
|
30
|
+
let offset = 0;
|
|
31
|
+
for (const chunk of chunks) {
|
|
32
|
+
result.set(chunk, offset);
|
|
33
|
+
offset += chunk.length;
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Convert a Uint8Array to a ReadableStream<Uint8Array>.
|
|
39
|
+
* Creates a stream that emits the entire array as a single chunk.
|
|
40
|
+
*
|
|
41
|
+
* @param bytes - The bytes to convert to a stream
|
|
42
|
+
* @returns A ReadableStream that emits the bytes
|
|
43
|
+
*/
|
|
44
|
+
export function bytesToStream(bytes) {
|
|
45
|
+
return new ReadableStream({
|
|
46
|
+
start(controller) {
|
|
47
|
+
controller.enqueue(bytes);
|
|
48
|
+
controller.close();
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Serialize a UseCacheEntry for persistent storage.
|
|
54
|
+
* Converts the ReadableStream value to a base64-encoded string.
|
|
55
|
+
*
|
|
56
|
+
* @param entry - The cache entry to serialize
|
|
57
|
+
* @returns A serialized entry suitable for JSON storage
|
|
58
|
+
*/
|
|
59
|
+
export async function serializeUseCacheEntry(entry) {
|
|
60
|
+
// Convert stream to bytes
|
|
61
|
+
const bytes = await streamToBytes(entry.value);
|
|
62
|
+
// Convert bytes to base64 for JSON-safe storage
|
|
63
|
+
const base64Value = Buffer.from(bytes).toString('base64');
|
|
64
|
+
return {
|
|
65
|
+
value: base64Value,
|
|
66
|
+
tags: entry.tags,
|
|
67
|
+
stale: entry.stale,
|
|
68
|
+
timestamp: entry.timestamp,
|
|
69
|
+
expire: entry.expire,
|
|
70
|
+
revalidate: entry.revalidate,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Deserialize a stored cache entry back to UseCacheEntry.
|
|
75
|
+
* Converts the base64-encoded value back to a ReadableStream.
|
|
76
|
+
*
|
|
77
|
+
* @param stored - The serialized entry from storage
|
|
78
|
+
* @returns A UseCacheEntry with a ReadableStream value
|
|
79
|
+
*/
|
|
80
|
+
export function deserializeUseCacheEntry(stored) {
|
|
81
|
+
// Convert base64 back to bytes
|
|
82
|
+
const bytes = stored.value ? Buffer.from(stored.value, 'base64') : new Uint8Array(0);
|
|
83
|
+
// Convert bytes to Uint8Array (Buffer is a subclass, but be explicit)
|
|
84
|
+
const uint8Array = new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
85
|
+
// Create stream from bytes
|
|
86
|
+
const stream = bytesToStream(uint8Array);
|
|
87
|
+
return {
|
|
88
|
+
value: stream,
|
|
89
|
+
tags: stored.tags,
|
|
90
|
+
stale: stored.stale,
|
|
91
|
+
timestamp: stored.timestamp,
|
|
92
|
+
expire: stored.expire,
|
|
93
|
+
revalidate: stored.revalidate,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=stream-serialization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-serialization.js","sourceRoot":"","sources":["../../src/use-cache/stream-serialization.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache entry structure for 'use cache' directive.
|
|
3
|
+
*
|
|
4
|
+
* Key differences from the legacy cacheHandler interface:
|
|
5
|
+
* - value is a ReadableStream<Uint8Array> instead of a serializable object
|
|
6
|
+
* - Includes timing metadata: stale, expire, revalidate
|
|
7
|
+
* - timestamp is in milliseconds (creation time)
|
|
8
|
+
*/
|
|
9
|
+
export interface UseCacheEntry {
|
|
10
|
+
/**
|
|
11
|
+
* The cached value as a stream.
|
|
12
|
+
* Must be consumed or tee()'d if needed for multiple reads.
|
|
13
|
+
*/
|
|
14
|
+
value: ReadableStream<Uint8Array>;
|
|
15
|
+
/**
|
|
16
|
+
* Cache tags for on-demand revalidation.
|
|
17
|
+
* Used with cacheTag() and revalidateTag().
|
|
18
|
+
*/
|
|
19
|
+
tags: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Duration in seconds for client-side staleness.
|
|
22
|
+
* Controls how long the entry can be served as stale while revalidating.
|
|
23
|
+
*/
|
|
24
|
+
stale: number;
|
|
25
|
+
/**
|
|
26
|
+
* Creation timestamp in milliseconds.
|
|
27
|
+
* Used to determine cache age.
|
|
28
|
+
*/
|
|
29
|
+
timestamp: number;
|
|
30
|
+
/**
|
|
31
|
+
* How long (in seconds) the entry is allowed to be used.
|
|
32
|
+
* After this time, the entry should not be served.
|
|
33
|
+
*/
|
|
34
|
+
expire: number;
|
|
35
|
+
/**
|
|
36
|
+
* How long (in seconds) until the entry should be revalidated.
|
|
37
|
+
* After this time, the entry is considered stale but may still be served.
|
|
38
|
+
*/
|
|
39
|
+
revalidate: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Serialized format for storing UseCacheEntry to persistent storage.
|
|
43
|
+
* The ReadableStream is converted to a base64 string.
|
|
44
|
+
*/
|
|
45
|
+
export interface SerializedUseCacheEntry {
|
|
46
|
+
/**
|
|
47
|
+
* Base64-encoded bytes from the stream.
|
|
48
|
+
*/
|
|
49
|
+
value: string;
|
|
50
|
+
/**
|
|
51
|
+
* Cache tags for on-demand revalidation.
|
|
52
|
+
*/
|
|
53
|
+
tags: string[];
|
|
54
|
+
/**
|
|
55
|
+
* Duration in seconds for client-side staleness.
|
|
56
|
+
*/
|
|
57
|
+
stale: number;
|
|
58
|
+
/**
|
|
59
|
+
* Creation timestamp in milliseconds.
|
|
60
|
+
*/
|
|
61
|
+
timestamp: number;
|
|
62
|
+
/**
|
|
63
|
+
* How long (in seconds) the entry is allowed to be used.
|
|
64
|
+
*/
|
|
65
|
+
expire: number;
|
|
66
|
+
/**
|
|
67
|
+
* How long (in seconds) until the entry should be revalidated.
|
|
68
|
+
*/
|
|
69
|
+
revalidate: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Cache handler interface for 'use cache' directive (cacheHandlers plural).
|
|
73
|
+
*
|
|
74
|
+
* Key differences from the legacy CacheHandler interface:
|
|
75
|
+
* - get() receives softTags parameter
|
|
76
|
+
* - set() receives a Promise that must be awaited
|
|
77
|
+
* - Includes refreshTags(), getExpiration(), and updateTags() methods
|
|
78
|
+
*/
|
|
79
|
+
export interface UseCacheHandler {
|
|
80
|
+
/**
|
|
81
|
+
* Retrieve a cache entry.
|
|
82
|
+
*
|
|
83
|
+
* @param cacheKey - The cache key to look up
|
|
84
|
+
* @param softTags - Framework-provided tags for additional filtering
|
|
85
|
+
* @returns The cache entry if found and valid, undefined otherwise
|
|
86
|
+
*
|
|
87
|
+
* Implementation notes:
|
|
88
|
+
* - Check expiration based on entry.revalidate and entry.timestamp
|
|
89
|
+
* - Return undefined if missing or expired
|
|
90
|
+
*/
|
|
91
|
+
get(cacheKey: string, softTags: string[]): Promise<UseCacheEntry | undefined>;
|
|
92
|
+
/**
|
|
93
|
+
* Store a cache entry.
|
|
94
|
+
*
|
|
95
|
+
* @param cacheKey - The cache key to store under
|
|
96
|
+
* @param pendingEntry - A Promise that MUST be awaited before storage
|
|
97
|
+
*
|
|
98
|
+
* Implementation notes:
|
|
99
|
+
* - CRITICAL: Must await pendingEntry before storing
|
|
100
|
+
* - entry.value is a ReadableStream that may need .tee() if reading
|
|
101
|
+
* - For persistent storage, convert stream to bytes before storing
|
|
102
|
+
*/
|
|
103
|
+
set(cacheKey: string, pendingEntry: Promise<UseCacheEntry>): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Synchronize tag state from external source.
|
|
106
|
+
*
|
|
107
|
+
* Implementation notes:
|
|
108
|
+
* - Can be no-op for in-memory caches
|
|
109
|
+
* - For distributed caches, sync tag invalidation state from shared storage
|
|
110
|
+
*/
|
|
111
|
+
refreshTags(): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Return maximum revalidation timestamp for given tags.
|
|
114
|
+
*
|
|
115
|
+
* @param tags - Array of tag names to check
|
|
116
|
+
* @returns The most recent invalidation timestamp, or 0 if no cached info
|
|
117
|
+
*
|
|
118
|
+
* Implementation notes:
|
|
119
|
+
* - Used to determine if cached entries are still valid
|
|
120
|
+
* - Return 0 if no tags have been invalidated
|
|
121
|
+
*/
|
|
122
|
+
getExpiration(tags: string[]): Promise<number>;
|
|
123
|
+
/**
|
|
124
|
+
* Invalidate cache entries with matching tags.
|
|
125
|
+
*
|
|
126
|
+
* @param tags - Array of tag names to invalidate
|
|
127
|
+
* @param durations - Corresponding invalidation durations (currently unused by Next.js)
|
|
128
|
+
*
|
|
129
|
+
* Implementation notes:
|
|
130
|
+
* - Store invalidation timestamps for each tag
|
|
131
|
+
* - Entries with matching tags should be considered invalid
|
|
132
|
+
*/
|
|
133
|
+
updateTags(tags: string[], durations: number[]): Promise<void>;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Configuration options for creating a use cache handler instance.
|
|
137
|
+
*/
|
|
138
|
+
export interface UseCacheHandlerConfig {
|
|
139
|
+
/**
|
|
140
|
+
* Handler type selection:
|
|
141
|
+
* - 'auto': Automatically detect based on environment (GCS if CACHE_BUCKET is set, otherwise file)
|
|
142
|
+
* - 'file': Use file-based caching (local development)
|
|
143
|
+
* - 'gcs': Use Google Cloud Storage (production/Pantheon)
|
|
144
|
+
*/
|
|
145
|
+
type?: 'auto' | 'file' | 'gcs';
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Information about a single use-cache entry for stats reporting.
|
|
149
|
+
*/
|
|
150
|
+
export interface UseCacheEntryInfo {
|
|
151
|
+
/**
|
|
152
|
+
* The cache key.
|
|
153
|
+
*/
|
|
154
|
+
key: string;
|
|
155
|
+
/**
|
|
156
|
+
* Cache tags associated with this entry.
|
|
157
|
+
*/
|
|
158
|
+
tags: string[];
|
|
159
|
+
/**
|
|
160
|
+
* Type identifier for stats reporting.
|
|
161
|
+
* Always 'use-cache' for entries from cacheHandlers (plural).
|
|
162
|
+
*/
|
|
163
|
+
type: 'use-cache';
|
|
164
|
+
/**
|
|
165
|
+
* When the entry was created (ISO timestamp).
|
|
166
|
+
*/
|
|
167
|
+
lastModified?: string;
|
|
168
|
+
/**
|
|
169
|
+
* Approximate size in bytes (if available).
|
|
170
|
+
*/
|
|
171
|
+
size?: number;
|
|
172
|
+
/**
|
|
173
|
+
* Revalidation time in seconds.
|
|
174
|
+
*/
|
|
175
|
+
revalidate?: number;
|
|
176
|
+
/**
|
|
177
|
+
* Stale time in seconds.
|
|
178
|
+
*/
|
|
179
|
+
stale?: number;
|
|
180
|
+
/**
|
|
181
|
+
* Expire time in seconds.
|
|
182
|
+
*/
|
|
183
|
+
expire?: number;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Cache statistics for use-cache entries.
|
|
187
|
+
*/
|
|
188
|
+
export interface UseCacheStats {
|
|
189
|
+
/**
|
|
190
|
+
* Number of cache entries.
|
|
191
|
+
*/
|
|
192
|
+
size: number;
|
|
193
|
+
/**
|
|
194
|
+
* Detailed information about each entry.
|
|
195
|
+
*/
|
|
196
|
+
entries: UseCacheEntryInfo[];
|
|
197
|
+
/**
|
|
198
|
+
* List of cache keys.
|
|
199
|
+
*/
|
|
200
|
+
keys: string[];
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/use-cache/types.ts"],"names":[],"mappings":"AAQA;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,KAAK,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IAElC;;;OAGG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;;;;;OAUG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IAE9E;;;;;;;;;;OAUG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3E;;;;;;OAMG;IACH,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7B;;;;;;;;;OASG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/C;;;;;;;;;OASG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;CAChC;AAMD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf;;;OAGG;IACH,IAAI,EAAE,WAAW,CAAC;IAElB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAE7B;;OAEG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Next.js 16 'use cache' Handler Types
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// These types implement the cacheHandlers (plural) interface for the
|
|
5
|
+
// 'use cache' directive introduced in Next.js 16.
|
|
6
|
+
// See: https://nextjs.org/docs/app/api-reference/config/next-config-js/cacheHandlers
|
|
7
|
+
// ============================================================================
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/use-cache/types.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,uCAAuC;AACvC,+EAA+E;AAC/E,qEAAqE;AACrE,kDAAkD;AAClD,qFAAqF;AACrF,+EAA+E"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
interface CacheTagContextData {
|
|
2
|
+
tags: string[];
|
|
3
|
+
requestId: string;
|
|
4
|
+
startTime: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Request-scoped context for tracking cache tags using the Symbol.for pattern.
|
|
8
|
+
*
|
|
9
|
+
* This approach mirrors Next.js's @next/request-context pattern, which is known
|
|
10
|
+
* to propagate through Next.js's internal mechanisms (used by `after()`).
|
|
11
|
+
*
|
|
12
|
+
* The key difference from direct AsyncLocalStorage usage is that cache handlers
|
|
13
|
+
* can access this context via `globalThis[Symbol.for('@nextjs-cache-handler/tag-context')]`
|
|
14
|
+
* rather than importing a module directly.
|
|
15
|
+
*/
|
|
16
|
+
export declare class CacheTagContext {
|
|
17
|
+
/**
|
|
18
|
+
* Add tags to the current request context.
|
|
19
|
+
* Called by cache handler during cache hits.
|
|
20
|
+
*/
|
|
21
|
+
static addTags(tags: string[]): void;
|
|
22
|
+
/**
|
|
23
|
+
* Get all unique tags accumulated during the current request.
|
|
24
|
+
* Called by middleware before sending response.
|
|
25
|
+
*/
|
|
26
|
+
static getTags(): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Get the current request ID.
|
|
29
|
+
*/
|
|
30
|
+
static getRequestId(): string | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Run a callback within a cache tag context.
|
|
33
|
+
* Must be called by withSurrogateKey to initialize tracking.
|
|
34
|
+
*/
|
|
35
|
+
static run<T>(callback: () => T): T;
|
|
36
|
+
/**
|
|
37
|
+
* Check if running within a cache tag context.
|
|
38
|
+
*/
|
|
39
|
+
static isActive(): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Get the well-known symbol for accessing this context from cache handlers.
|
|
42
|
+
* Cache handlers should use: globalThis[CacheTagContext.symbol]?.get()
|
|
43
|
+
*/
|
|
44
|
+
static get symbol(): symbol;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Helper function for cache handlers to access the cache tag context.
|
|
48
|
+
* This can be called from any module without importing CacheTagContext directly.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* // In cache handler (use-cache-handler.mjs)
|
|
53
|
+
* const context = globalThis[Symbol.for('@nextjs-cache-handler/tag-context')]?.get();
|
|
54
|
+
* if (context) {
|
|
55
|
+
* context.tags.push(...entry.tags);
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare function getCacheTagContextFromGlobal(): CacheTagContextData | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Helper function for cache handlers to add tags to the cache tag context.
|
|
62
|
+
* This can be called from any module without importing CacheTagContext directly.
|
|
63
|
+
*/
|
|
64
|
+
export declare function addTagsToCacheTagContext(tags: string[]): boolean;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=cache-tag-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-tag-context.d.ts","sourceRoot":"","sources":["../../src/utils/cache-tag-context.ts"],"names":[],"mappings":"AAYA,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AA0BD;;;;;;;;;GASG;AACH,qBAAa,eAAe;IAC1B;;;OAGG;IACH,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAUpC;;;OAGG;IACH,MAAM,CAAC,OAAO,IAAI,MAAM,EAAE;IAQ1B;;OAEG;IACH,MAAM,CAAC,YAAY,IAAI,MAAM,GAAG,SAAS;IAIzC;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC;IAcnC;;OAEG;IACH,MAAM,CAAC,QAAQ,IAAI,OAAO;IAI1B;;;OAGG;IACH,MAAM,KAAK,MAAM,IAAI,MAAM,CAE1B;CACF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,4BAA4B,IAAI,mBAAmB,GAAG,SAAS,CAK9E;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAQhE"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
2
|
+
import { createLogger } from './logger.js';
|
|
3
|
+
const log = createLogger('CacheTagContext');
|
|
4
|
+
/**
|
|
5
|
+
* Symbol used to store the cache tag context accessor on globalThis.
|
|
6
|
+
* Uses Symbol.for() to ensure the same symbol is used across module boundaries,
|
|
7
|
+
* similar to Next.js's @next/request-context pattern.
|
|
8
|
+
*/
|
|
9
|
+
const CACHE_TAG_CONTEXT_SYMBOL = Symbol.for('@nextjs-cache-handler/tag-context');
|
|
10
|
+
/**
|
|
11
|
+
* AsyncLocalStorage instance for request-scoped context.
|
|
12
|
+
* This is created once and registered globally via Symbol.for().
|
|
13
|
+
*/
|
|
14
|
+
const cacheTagContextStorage = new AsyncLocalStorage();
|
|
15
|
+
/**
|
|
16
|
+
* The accessor object that will be stored on globalThis.
|
|
17
|
+
* Cache handlers can retrieve this via globalThis[Symbol.for('@nextjs-cache-handler/tag-context')].
|
|
18
|
+
*/
|
|
19
|
+
const cacheTagContextAccessor = {
|
|
20
|
+
get() {
|
|
21
|
+
return cacheTagContextStorage.getStore();
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
// Register the accessor on globalThis using the well-known symbol
|
|
25
|
+
// This allows cache handlers (which run in a different module context) to access it
|
|
26
|
+
globalThis[CACHE_TAG_CONTEXT_SYMBOL] = cacheTagContextAccessor;
|
|
27
|
+
/**
|
|
28
|
+
* Request-scoped context for tracking cache tags using the Symbol.for pattern.
|
|
29
|
+
*
|
|
30
|
+
* This approach mirrors Next.js's @next/request-context pattern, which is known
|
|
31
|
+
* to propagate through Next.js's internal mechanisms (used by `after()`).
|
|
32
|
+
*
|
|
33
|
+
* The key difference from direct AsyncLocalStorage usage is that cache handlers
|
|
34
|
+
* can access this context via `globalThis[Symbol.for('@nextjs-cache-handler/tag-context')]`
|
|
35
|
+
* rather than importing a module directly.
|
|
36
|
+
*/
|
|
37
|
+
export class CacheTagContext {
|
|
38
|
+
/**
|
|
39
|
+
* Add tags to the current request context.
|
|
40
|
+
* Called by cache handler during cache hits.
|
|
41
|
+
*/
|
|
42
|
+
static addTags(tags) {
|
|
43
|
+
const context = cacheTagContextStorage.getStore();
|
|
44
|
+
if (context) {
|
|
45
|
+
context.tags.push(...tags);
|
|
46
|
+
log.debug(`Added ${tags.length} tags to CacheTagContext: ${tags.join(', ')}`);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
log.debug('No CacheTagContext available - tags will not be captured via Symbol.for');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get all unique tags accumulated during the current request.
|
|
54
|
+
* Called by middleware before sending response.
|
|
55
|
+
*/
|
|
56
|
+
static getTags() {
|
|
57
|
+
const context = cacheTagContextStorage.getStore();
|
|
58
|
+
if (!context) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
return [...new Set(context.tags)];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get the current request ID.
|
|
65
|
+
*/
|
|
66
|
+
static getRequestId() {
|
|
67
|
+
return cacheTagContextStorage.getStore()?.requestId;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Run a callback within a cache tag context.
|
|
71
|
+
* Must be called by withSurrogateKey to initialize tracking.
|
|
72
|
+
*/
|
|
73
|
+
static run(callback) {
|
|
74
|
+
const requestId = crypto.randomUUID();
|
|
75
|
+
log.debug(`Starting CacheTagContext with requestId: ${requestId}`);
|
|
76
|
+
return cacheTagContextStorage.run({
|
|
77
|
+
tags: [],
|
|
78
|
+
requestId,
|
|
79
|
+
startTime: Date.now(),
|
|
80
|
+
}, callback);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if running within a cache tag context.
|
|
84
|
+
*/
|
|
85
|
+
static isActive() {
|
|
86
|
+
return cacheTagContextStorage.getStore() !== undefined;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the well-known symbol for accessing this context from cache handlers.
|
|
90
|
+
* Cache handlers should use: globalThis[CacheTagContext.symbol]?.get()
|
|
91
|
+
*/
|
|
92
|
+
static get symbol() {
|
|
93
|
+
return CACHE_TAG_CONTEXT_SYMBOL;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Helper function for cache handlers to access the cache tag context.
|
|
98
|
+
* This can be called from any module without importing CacheTagContext directly.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* // In cache handler (use-cache-handler.mjs)
|
|
103
|
+
* const context = globalThis[Symbol.for('@nextjs-cache-handler/tag-context')]?.get();
|
|
104
|
+
* if (context) {
|
|
105
|
+
* context.tags.push(...entry.tags);
|
|
106
|
+
* }
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export function getCacheTagContextFromGlobal() {
|
|
110
|
+
const accessor = globalThis[CACHE_TAG_CONTEXT_SYMBOL];
|
|
111
|
+
return accessor?.get();
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Helper function for cache handlers to add tags to the cache tag context.
|
|
115
|
+
* This can be called from any module without importing CacheTagContext directly.
|
|
116
|
+
*/
|
|
117
|
+
export function addTagsToCacheTagContext(tags) {
|
|
118
|
+
const context = getCacheTagContextFromGlobal();
|
|
119
|
+
if (context && tags.length > 0) {
|
|
120
|
+
context.tags.push(...tags);
|
|
121
|
+
log.debug(`Added ${tags.length} tags via global accessor: ${tags.join(', ')}`);
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=cache-tag-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-tag-context.js","sourceRoot":"","sources":["../../src/utils/cache-tag-context.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -2,4 +2,7 @@ export { serializeForStorage, deserializeFromStorage } from './serialization.js'
|
|
|
2
2
|
export { getBuildId, isBuildPhase } from './build-detection.js';
|
|
3
3
|
export { getStaticRoutes } from './static-routes.js';
|
|
4
4
|
export { createLogger, type Logger } from './logger.js';
|
|
5
|
+
export { RequestContext } from './request-context.js';
|
|
6
|
+
export { CacheTagContext, getCacheTagContextFromGlobal, addTagsToCacheTagContext, } from './cache-tag-context.js';
|
|
7
|
+
export { withSurrogateKey, type SurrogateKeyOptions } from './with-surrogate-key.js';
|
|
5
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,eAAe,EACf,4BAA4B,EAC5B,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/utils/index.js
CHANGED
|
@@ -2,4 +2,7 @@ export { serializeForStorage, deserializeFromStorage } from './serialization.js'
|
|
|
2
2
|
export { getBuildId, isBuildPhase } from './build-detection.js';
|
|
3
3
|
export { getStaticRoutes } from './static-routes.js';
|
|
4
4
|
export { createLogger } from './logger.js';
|
|
5
|
+
export { RequestContext } from './request-context.js';
|
|
6
|
+
export { CacheTagContext, getCacheTagContextFromGlobal, addTagsToCacheTagContext, } from './cache-tag-context.js';
|
|
7
|
+
export { withSurrogateKey } from './with-surrogate-key.js';
|
|
5
8
|
//# sourceMappingURL=index.js.map
|
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,eAAe,EACf,4BAA4B,EAC5B,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAA4B,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-scoped context for tracking cache tags during SSR.
|
|
3
|
+
* Uses AsyncLocalStorage to maintain isolation between concurrent requests.
|
|
4
|
+
*/
|
|
5
|
+
export declare class RequestContext {
|
|
6
|
+
private static storage;
|
|
7
|
+
/**
|
|
8
|
+
* Add tags to the current request context.
|
|
9
|
+
* Called by cache handler during cache hits.
|
|
10
|
+
*/
|
|
11
|
+
static addTags(tags: string[]): void;
|
|
12
|
+
/**
|
|
13
|
+
* Get all unique tags accumulated during the current request.
|
|
14
|
+
* Called by middleware before sending response.
|
|
15
|
+
*/
|
|
16
|
+
static getTags(): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Run a callback within a request context.
|
|
19
|
+
* Must be called by middleware to initialize tracking.
|
|
20
|
+
*/
|
|
21
|
+
static run<T>(callback: () => T): T;
|
|
22
|
+
/**
|
|
23
|
+
* Check if running within a request context.
|
|
24
|
+
* Useful for debugging and conditional logic.
|
|
25
|
+
*/
|
|
26
|
+
static isActive(): boolean;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=request-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/utils/request-context.ts"],"names":[],"mappings":"AAUA;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,OAAO,CAA+C;IAErE;;;OAGG;IACH,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAUpC;;;OAGG;IACH,MAAM,CAAC,OAAO,IAAI,MAAM,EAAE;IAS1B;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC;IAUnC;;;OAGG;IACH,MAAM,CAAC,QAAQ,IAAI,OAAO;CAG3B"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
2
|
+
import { createLogger } from './logger.js';
|
|
3
|
+
const log = createLogger('RequestContext');
|
|
4
|
+
/**
|
|
5
|
+
* Request-scoped context for tracking cache tags during SSR.
|
|
6
|
+
* Uses AsyncLocalStorage to maintain isolation between concurrent requests.
|
|
7
|
+
*/
|
|
8
|
+
export class RequestContext {
|
|
9
|
+
/**
|
|
10
|
+
* Add tags to the current request context.
|
|
11
|
+
* Called by cache handler during cache hits.
|
|
12
|
+
*/
|
|
13
|
+
static addTags(tags) {
|
|
14
|
+
const context = this.storage.getStore();
|
|
15
|
+
if (context) {
|
|
16
|
+
context.tags.push(...tags);
|
|
17
|
+
log.debug(`Added ${tags.length} tags to request context: ${tags.join(', ')}`);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
log.warn('No request context available - tags will not be captured');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get all unique tags accumulated during the current request.
|
|
25
|
+
* Called by middleware before sending response.
|
|
26
|
+
*/
|
|
27
|
+
static getTags() {
|
|
28
|
+
const context = this.storage.getStore();
|
|
29
|
+
if (!context) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
// Remove duplicates using Set
|
|
33
|
+
return [...new Set(context.tags)];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Run a callback within a request context.
|
|
37
|
+
* Must be called by middleware to initialize tracking.
|
|
38
|
+
*/
|
|
39
|
+
static run(callback) {
|
|
40
|
+
return this.storage.run({
|
|
41
|
+
tags: [],
|
|
42
|
+
startTime: Date.now(),
|
|
43
|
+
}, callback);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Check if running within a request context.
|
|
47
|
+
* Useful for debugging and conditional logic.
|
|
48
|
+
*/
|
|
49
|
+
static isActive() {
|
|
50
|
+
return this.storage.getStore() !== undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
RequestContext.storage = new AsyncLocalStorage();
|
|
54
|
+
//# sourceMappingURL=request-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-context.js","sourceRoot":"","sources":["../../src/utils/request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,GAAG,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC;AAO3C;;;GAGG;AACH,MAAM,OAAO,cAAc;IAGzB;;;OAGG;IACH,MAAM,CAAC,OAAO,CAAC,IAAc;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACxC,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;YAC3B,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,MAAM,6BAA6B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChF,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,OAAO;QACZ,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,8BAA8B;QAC9B,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACpC,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAI,QAAiB;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CACrB;YACE,IAAI,EAAE,EAAE;YACR,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,EACD,QAAQ,CACT,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,QAAQ;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,SAAS,CAAC;IAC/C,CAAC;;AAjDc,sBAAO,GAAG,IAAI,iBAAiB,EAAsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-context.test.d.ts","sourceRoot":"","sources":["../../src/utils/request-context.test.ts"],"names":[],"mappings":""}
|