-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
135 lines (128 loc) · 5.85 KB
/
index.js
File metadata and controls
135 lines (128 loc) · 5.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { validateRules, sortRulesBySpecificity } from './lib/validator.js'
import { createRouter } from './lib/router.js'
import { createInterceptorFunction } from './lib/interceptor.js'
import abstractLogging from 'abstract-logging'
/**
* Creates an undici interceptor that adds headers based on specified rules.
* The interceptor uses a router to match the request path and applies the corresponding
* headers to the response, but only for GET and HEAD requests, and only if
* those headers don't already exist. It can also add cache tags headers and any other
* dynamic headers based on jq-style rules implemented via fgh.
* Additionally, it can transform response bodies using fgh expressions.
*
* @param {Array<{routeToMatch: string, headers?: Object, responseBodyTransform?: Object}>} rules - Array of rules for headers and body transforms
* @param {string} rules[].routeToMatch - Origin and path pattern to match in format "hostname:port/path" or "hostname/path"
* @param {Object} [rules[].headers] - Object containing headers to set. Values can be strings for static headers
* (e.g., {"cache-control": "public, max-age=3600"}) or objects with an fgh property for dynamic headers based on
* request context (e.g., {"x-cache-tags": { fgh: "'user', 'user-' + .params.userId" }})
* @param {Object} [rules[].responseBodyTransform] - Object with an fgh property containing an expression to transform the response body
* (e.g., { fgh: ". + { cached: true }" })
*
* @param {Object} [options] - Options for the find-my-way router
* @param {boolean} [options.ignoreTrailingSlash=false] - Ignore trailing slashes in routes
* @param {boolean} [options.ignoreDuplicateSlashes=false] - Ignore duplicate slashes in routes
* @param {number} [options.maxParamLength=100] - Maximum length of a parameter
* @param {boolean} [options.caseSensitive=true] - Use case sensitive routing
* @param {boolean} [options.useSemicolonDelimiter=false] - Use semicolon instead of ampersand as query param delimiter
* @param {Object} [options.logger=abstract-logging] - Logger instance (pino compatible)
* The logger can be any Pino-compatible logger. It will log interceptor operations
* such as creation, rule validation, route matching, and header application.
* @returns {Function} - An undici interceptor function that can be composed with a dispatcher
*
* @example
* ```js
* import { Agent } from 'undici'
* import { createInterceptor } from 'make-cacheable-interceptor'
*
* const agent = new Agent()
* const interceptor = createInterceptor(
* {
* rules: [
* {
* routeToMatch: 'localhost:3042/static/*',
* headers: {
* 'cache-control': 'public, max-age=86400',
* 'x-custom-header': 'static-content',
* 'x-cache-tags': { fgh: "'static', 'cdn'" }
* }
* },
* {
* routeToMatch: 'localhost:3042/api/products/:productId',
* headers: {
* 'cache-control': 'public, max-age=3600',
* 'x-product-id': { fgh: '.params.productId' }
* },
* // Add a cached property and timestamp to the response
* responseBodyTransform: { fgh: '. + { cached: true, timestamp: .response.headers["date"] }' }
* },
* {
* routeToMatch: 'localhost:3042/users/:id',
* headers: {
* 'cache-control': 'public, max-age=3600',
* 'x-user-id': { fgh: ".params.id" },
* 'x-cache-tags': { fgh: "'user-' + .params.id, 'type-user'" }
* }
* },
* {
* routeToMatch: 'localhost:3042/api/products',
* headers: {
* 'cache-control': 'public, max-age=3600',
* 'x-api-version': '1.0',
* 'x-cache-tags': { fgh: ".querystring.category, 'products'" }
* }
* },
* {
* routeToMatch: 'api.example.com/api/auth',
* headers: {
* 'cache-control': 'public, max-age=600',
* 'x-security-level': 'high',
* 'x-cache-tags': { fgh: ".headers[\"x-tenant-id\"], 'auth'" },
* 'x-tenant': { fgh: ".headers[\"x-tenant-id\"]" }
* }
* }
* ],
* ignoreTrailingSlash: true,
* caseSensitive: false
* }
* )
*
* // This will add headers to GET and HEAD requests that don't already
* // have those headers. Dynamic headers can use jq-style expressions
* // to generate values based on request context.
* const composedAgent = agent.compose(interceptor)
* setGlobalDispatcher(composedAgent)
* ```
*
* The `responseBodyTransform` property allows you to modify the response body using an FGH expression.
* It only works with JSON responses and requires the response body to be buffered in memory before processing.
* The transformation is applied before the response is sent to the client.
*
* Example response body transformations:
*
* ```js
* // Add properties to response
* responseBodyTransform: { fgh: '. + { cached: true, timestamp: .response.headers["date"] }' }
*
* // Filter an array response
* responseBodyTransform: { fgh: 'map(select(.price > 100))' }
*
* // Add computed properties
* responseBodyTransform: { fgh: '. + { total: (.items | map(.price * .quantity) | add) }' }
* ```
*/
export function createInterceptor (options = {}) {
// Default option for cache tags header name
// Default logger to abstract-logging if not provided
const { rules, logger: optsLogger, ...routeOptions } = options
const logger = optsLogger || abstractLogging
logger.debug('Creating cacheable interceptor with %d rules', rules?.length || 0)
// Validate rules
validateRules(rules, logger)
// Sort rules by specificity
const sortedRules = sortRulesBySpecificity(rules, logger)
// Create and configure router
const router = createRouter(sortedRules, routeOptions, logger)
// Create and return the interceptor function
return createInterceptorFunction(router, logger)
}
export default createInterceptor