@klnap/next-proxy-chain 1.0.2 → 1.0.4
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/CHANGELOG.md +55 -0
- package/LICENSE +21 -0
- package/README.md +165 -146
- package/dist/index.cjs +335 -320
- package/dist/index.d.cts +65 -169
- package/dist/index.d.ts +65 -169
- package/dist/index.js +334 -289
- package/package.json +19 -12
package/dist/index.d.cts
CHANGED
|
@@ -4,10 +4,6 @@ import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';
|
|
|
4
4
|
* Unique symbol identifying an explicit continue action in the proxy chain pipeline.
|
|
5
5
|
*/
|
|
6
6
|
declare const NEXT: unique symbol;
|
|
7
|
-
/**
|
|
8
|
-
* Protected request headers that cannot be rewritten unless explicitly permitted by policy.
|
|
9
|
-
*/
|
|
10
|
-
declare const DEFAULT_BLOCKED_REQUEST_HEADERS: readonly string[];
|
|
11
7
|
|
|
12
8
|
/**
|
|
13
9
|
* Configuration options for chain continuation.
|
|
@@ -15,7 +11,7 @@ declare const DEFAULT_BLOCKED_REQUEST_HEADERS: readonly string[];
|
|
|
15
11
|
type NextOptions = {
|
|
16
12
|
/** Request header overrides visible to downstream rules and handlers. */
|
|
17
13
|
request?: HeadersInit;
|
|
18
|
-
/** Response headers merged into the final outgoing response. */
|
|
14
|
+
/** Response headers merged into the final outgoing response. `set-cookie` is extracted as cookies. */
|
|
19
15
|
headers?: HeadersInit;
|
|
20
16
|
};
|
|
21
17
|
/**
|
|
@@ -29,12 +25,7 @@ type NextResult = NextOptions & {
|
|
|
29
25
|
* Backed by Map to eliminate prototype-pollution risks.
|
|
30
26
|
*/
|
|
31
27
|
type ProxyContext = Map<string, unknown>;
|
|
32
|
-
type ProxyResult<TResponse = NextResponse | Response> = TResponse | NextResult | undefined | void;
|
|
33
28
|
type PathMatcher = RegExp | string;
|
|
34
|
-
type PathOptions = {
|
|
35
|
-
/** Optional locale prefixes for path matching. */
|
|
36
|
-
locales?: readonly string[];
|
|
37
|
-
};
|
|
38
29
|
type HostMatcher = string | RegExp | readonly (string | RegExp)[];
|
|
39
30
|
/**
|
|
40
31
|
* Scoping configuration for rule inclusion or exclusion.
|
|
@@ -62,7 +53,6 @@ type ProxyPathFilter = {
|
|
|
62
53
|
include?: RouteScopeList;
|
|
63
54
|
exclude?: RouteScopeList;
|
|
64
55
|
name?: string;
|
|
65
|
-
pathOptions?: PathOptions;
|
|
66
56
|
};
|
|
67
57
|
/**
|
|
68
58
|
* A single proxy execution function.
|
|
@@ -72,39 +62,40 @@ type ProxyRule<TContext = ProxyContext> = BaseProxyRule<ProxyFn<TContext>>;
|
|
|
72
62
|
type DefinedProxy<TContext = ProxyContext> = ProxyFn<TContext> & ((filter: ProxyPathFilter) => ProxyRule<TContext>);
|
|
73
63
|
type Entry<TContext = ProxyContext> = ProxyFn<TContext> | ProxyRule<TContext>;
|
|
74
64
|
type MergeStrategy = "last-write-wins" | "first-write-wins" | ((key: string, existing: string, incoming: string) => string | null);
|
|
75
|
-
interface EntryWrite {
|
|
76
|
-
ruleIndex: number;
|
|
77
|
-
ruleName: string;
|
|
78
|
-
items: Map<string, string>;
|
|
79
|
-
}
|
|
80
|
-
type LogLevel = "debug" | "info" | "warn" | "error";
|
|
81
65
|
interface ChainLogger {
|
|
82
66
|
debug: (metaOrMsg: unknown, maybeMsg?: string) => void;
|
|
83
67
|
info: (metaOrMsg: unknown, maybeMsg?: string) => void;
|
|
84
68
|
warn: (metaOrMsg: unknown, maybeMsg?: string) => void;
|
|
85
69
|
error: (metaOrMsg: unknown, maybeMsg?: string) => void;
|
|
86
70
|
}
|
|
87
|
-
type PipelineOutcome = {
|
|
88
|
-
kind: "passed";
|
|
89
|
-
duration: number;
|
|
90
|
-
} | {
|
|
91
|
-
kind: "next";
|
|
92
|
-
duration: number;
|
|
93
|
-
headers: number;
|
|
94
|
-
} | {
|
|
95
|
-
kind: "stop";
|
|
96
|
-
duration: number;
|
|
97
|
-
status: number;
|
|
98
|
-
};
|
|
99
71
|
interface RequestHeaderPolicy {
|
|
100
72
|
blocked?: readonly string[];
|
|
101
73
|
allowed?: readonly string[];
|
|
102
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Value returned from `onError`.
|
|
77
|
+
*
|
|
78
|
+
* - `Response` — stop the pipeline and return it
|
|
79
|
+
* - `NextResult` (`next()`) — continue to the next rule (rule-level errors only)
|
|
80
|
+
* - `undefined` / `void` / `null` — fail closed (`500`)
|
|
81
|
+
*/
|
|
82
|
+
type OnErrorResult = NextResponse | Response | NextResult | undefined | void | null;
|
|
83
|
+
type OnErrorHandler<TContext = ProxyContext> = (err: unknown,
|
|
84
|
+
/**
|
|
85
|
+
* 0-based index of the rule that threw.
|
|
86
|
+
* `-1` means the error originated in the pipeline engine after context creation.
|
|
87
|
+
* Engine-level errors cannot continue; only a `Response` is honored.
|
|
88
|
+
*/
|
|
89
|
+
ruleIndex: number, req: NextRequest, ctx: TContext) => OnErrorResult | Promise<OnErrorResult>;
|
|
103
90
|
interface ChainConfig<TContext = ProxyContext> {
|
|
104
91
|
createContext: (req: NextRequest, event: NextFetchEvent) => TContext;
|
|
105
92
|
cookieMergeStrategy: MergeStrategy;
|
|
93
|
+
/**
|
|
94
|
+
* How conflicting **response** headers from different rules are resolved.
|
|
95
|
+
* Request header overrides always use last-write-wins and ignore this option.
|
|
96
|
+
*/
|
|
106
97
|
headerMergeStrategy: MergeStrategy;
|
|
107
|
-
onError?:
|
|
98
|
+
onError?: OnErrorHandler<TContext>;
|
|
108
99
|
debug: boolean;
|
|
109
100
|
logger: ChainLogger;
|
|
110
101
|
/** Maximum wall-clock execution time per rule in milliseconds. 0 disables timeout. */
|
|
@@ -112,101 +103,41 @@ interface ChainConfig<TContext = ProxyContext> {
|
|
|
112
103
|
requestHeaderPolicy: RequestHeaderPolicy;
|
|
113
104
|
/** Throw an error on startup if the runtime platform does not support request header overrides. */
|
|
114
105
|
strictOverrideCheck?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Request header name that prefixes rewrites with a physical host folder
|
|
108
|
+
* (multi-tenant `app/(sites)/shop` layouts). Unset = disabled.
|
|
109
|
+
*/
|
|
110
|
+
hostFolderHeader?: string;
|
|
115
111
|
}
|
|
116
112
|
type ProxyChainOptions<TContext = ProxyContext> = Partial<ChainConfig<TContext>>;
|
|
117
113
|
|
|
118
114
|
/**
|
|
119
|
-
* Creates
|
|
120
|
-
*
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Strips sticky and global flags (g, y) from RegExp instances.
|
|
126
|
-
* On Edge runtimes, compiled regex instances can be shared across requests;
|
|
127
|
-
* sticky and global flags mutate stateful indices, causing non-deterministic matching.
|
|
128
|
-
*/
|
|
129
|
-
declare function toStatelessRegExp(re: RegExp): RegExp;
|
|
130
|
-
/**
|
|
131
|
-
* Builds a compiled Host matcher RegExp from domain names, wildcards, or regex patterns.
|
|
132
|
-
*/
|
|
133
|
-
declare function host(matcher: HostMatcher): RegExp;
|
|
134
|
-
/**
|
|
135
|
-
* Compiles a path pattern (glob string, param pattern, or RegExp) into a stateless regular expression.
|
|
136
|
-
*/
|
|
137
|
-
declare function path(pattern: string | RegExp, options?: PathOptions): RegExp;
|
|
138
|
-
declare function toPathRegex(matcher: PathMatcher | PathMatcher[], options?: PathOptions): RegExp;
|
|
139
|
-
/**
|
|
140
|
-
* Compiles route scoping declarations into normalized regex clauses.
|
|
141
|
-
*/
|
|
142
|
-
declare function compileScopes(input: RouteScopeList | undefined, pathOptions?: PathOptions): CompiledScope[] | undefined;
|
|
143
|
-
declare function toRule<TFn>(entry: TFn | BaseProxyRule<TFn>): BaseProxyRule<TFn>;
|
|
144
|
-
declare function ruleLabel<TFn>(rule: BaseProxyRule<TFn>, index: number): string;
|
|
145
|
-
/**
|
|
146
|
-
* Decorates a proxy entry with declarative path and host inclusion/exclusion filters.
|
|
147
|
-
*/
|
|
148
|
-
declare function withPaths<TFn>(entry: TFn | BaseProxyRule<TFn>, filter: ProxyPathFilter): BaseProxyRule<TFn>;
|
|
149
|
-
/**
|
|
150
|
-
* Determines whether a given request matches a rule's scoping constraints.
|
|
151
|
-
*/
|
|
152
|
-
declare function ruleMatches<TFn>(pathname: string, hostname: string | null, rule: BaseProxyRule<TFn>): boolean;
|
|
153
|
-
declare function pathMatches<TFn>(pathname: string, rule: BaseProxyRule<TFn>): boolean;
|
|
154
|
-
/**
|
|
155
|
-
* Normalizes Host or X-Forwarded-Host header values to lower-case hostname without port.
|
|
156
|
-
*/
|
|
157
|
-
declare function normalizeHost(raw: string | null | undefined): string | null;
|
|
158
|
-
/**
|
|
159
|
-
* Validates that an identical scope is not declared simultaneously in include and exclude.
|
|
160
|
-
*/
|
|
161
|
-
declare function assertIncludeExcludeNoOverlap(include: CompiledScope[] | undefined, exclude: CompiledScope[] | undefined, label: string): void;
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Converts HeadersInit or undefined to a standard Web Headers instance.
|
|
165
|
-
*/
|
|
166
|
-
declare function headersInitToHeaders(init?: HeadersInit): Headers;
|
|
167
|
-
/**
|
|
168
|
-
* Compares two Headers instances to determine if overrides contain differences.
|
|
169
|
-
*/
|
|
170
|
-
declare function headersDiffer(base: Headers, overrides: Headers): boolean;
|
|
171
|
-
/**
|
|
172
|
-
* Checks whether a request header is permitted to be mutated under the configured policy.
|
|
173
|
-
*/
|
|
174
|
-
declare function isRequestHeaderAllowed(name: string, policy?: RequestHeaderPolicy): boolean;
|
|
175
|
-
/**
|
|
176
|
-
* Merges new request header overrides into the target Headers instance, filtering prohibited headers.
|
|
177
|
-
*/
|
|
178
|
-
declare function mergeRequestHeaderOverrides(target: Headers, overrides: Headers, policy?: RequestHeaderPolicy): {
|
|
179
|
-
applied: number;
|
|
180
|
-
dropped: string[];
|
|
181
|
-
};
|
|
182
|
-
/**
|
|
183
|
-
* Extracts cookie name from a raw Set-Cookie directive.
|
|
184
|
-
*/
|
|
185
|
-
declare function parseCookieName(setCookie: string): string;
|
|
186
|
-
/**
|
|
187
|
-
* Extracts all Set-Cookie directives using standard getSetCookie() when available.
|
|
188
|
-
*/
|
|
189
|
-
declare function listSetCookie(headers: Headers): string[];
|
|
190
|
-
/**
|
|
191
|
-
* Maps cookie name to Set-Cookie header value from a Headers instance.
|
|
192
|
-
*/
|
|
193
|
-
declare function cookieItemsFromHeaders(headers: Headers): Map<string, string>;
|
|
194
|
-
/**
|
|
195
|
-
* Filters out Set-Cookie and internal transport headers (x-middleware-*) from response headers.
|
|
196
|
-
*/
|
|
197
|
-
declare function responseHeaderItems(headers: Headers): Map<string, string>;
|
|
198
|
-
/**
|
|
199
|
-
* Decodes Next.js request header overrides encoded into response headers.
|
|
200
|
-
*/
|
|
201
|
-
declare function decodeOverriddenRequestHeaders(headers: Headers): Headers;
|
|
202
|
-
/**
|
|
203
|
-
* Deterministically merges writes across rules based on configured strategy.
|
|
115
|
+
* Creates a reusable, scope-aware proxy rule function.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* const authRule = defineProxy("auth", async (req, ctx) => next())
|
|
119
|
+
* const scopedAuthRule = authRule({ include: path("/admin/*") })
|
|
204
120
|
*/
|
|
205
|
-
declare function
|
|
121
|
+
declare function defineProxy<TContext = ProxyContext>(run: ProxyFn<TContext>): DefinedProxy<TContext>;
|
|
122
|
+
declare function defineProxy<TContext = ProxyContext>(name: string, run: ProxyFn<TContext>): DefinedProxy<TContext>;
|
|
123
|
+
type ProxyHandler = (req: NextRequest, event: NextFetchEvent) => Promise<NextResponse | Response>;
|
|
206
124
|
/**
|
|
207
|
-
*
|
|
125
|
+
* Composes independent proxy rules into a single pipeline for Next.js 15 (middleware.ts) and Next.js 16 (proxy.ts).
|
|
126
|
+
*
|
|
127
|
+
* Custom `TContext` requires `createContext`. Without it, context is `ProxyContext` (`Map`).
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* export default proxyChain({ debug: true }, [
|
|
131
|
+
* requestIdRule,
|
|
132
|
+
* authRule({ include: path("/dashboard/*") }),
|
|
133
|
+
* i18nRule,
|
|
134
|
+
* ])
|
|
208
135
|
*/
|
|
209
|
-
declare function
|
|
136
|
+
declare function proxyChain(entries: Entry<ProxyContext>[]): ProxyHandler;
|
|
137
|
+
declare function proxyChain(options: ProxyChainOptions<ProxyContext>, entries: Entry<ProxyContext>[]): ProxyHandler;
|
|
138
|
+
declare function proxyChain<TContext>(options: ProxyChainOptions<TContext> & {
|
|
139
|
+
createContext: (req: NextRequest, event: NextFetchEvent) => TContext;
|
|
140
|
+
}, entries: Entry<TContext>[]): ProxyHandler;
|
|
210
141
|
|
|
211
142
|
/**
|
|
212
143
|
* Creates a continuation action instructing the proxy chain to proceed to subsequent rules.
|
|
@@ -223,63 +154,28 @@ declare function next(options?: NextOptions): NextResult;
|
|
|
223
154
|
*/
|
|
224
155
|
declare function isNextResult(value: unknown): value is NextResult;
|
|
225
156
|
|
|
226
|
-
declare function createConsoleLogger(minLevel?: LogLevel): ChainLogger;
|
|
227
157
|
/**
|
|
228
|
-
*
|
|
158
|
+
* Builds a compiled Host matcher RegExp from domain names, `*.example.com` wildcards, or regex patterns.
|
|
159
|
+
*/
|
|
160
|
+
declare function host(matcher: HostMatcher): RegExp;
|
|
161
|
+
/**
|
|
162
|
+
* Compiles a path pattern (glob string, param pattern, or RegExp) into a stateless regular expression.
|
|
163
|
+
*
|
|
164
|
+
* - `*` — exactly one path segment
|
|
165
|
+
* - `**` — zero or more subsequent segments
|
|
166
|
+
*/
|
|
167
|
+
declare function path(pattern: string | RegExp): RegExp;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Creates an isolated per-request context instance.
|
|
171
|
+
* Backed by Map to eliminate prototype pollution risks.
|
|
229
172
|
*/
|
|
230
|
-
declare function
|
|
231
|
-
method: string;
|
|
232
|
-
path: string;
|
|
233
|
-
name: string;
|
|
234
|
-
traceId?: string;
|
|
235
|
-
outcome: PipelineOutcome;
|
|
236
|
-
}): string;
|
|
173
|
+
declare function createProxyContext(): ProxyContext;
|
|
237
174
|
|
|
238
175
|
declare class RuleTimeoutError extends Error {
|
|
239
176
|
readonly ruleName: string;
|
|
240
177
|
readonly timeoutMs: number;
|
|
241
178
|
constructor(ruleName: string, timeoutMs: number);
|
|
242
179
|
}
|
|
243
|
-
/**
|
|
244
|
-
* Enforces a strict execution time budget on an asynchronous rule operation.
|
|
245
|
-
*/
|
|
246
|
-
declare function withRuleTimeout<T>(promise: Promise<T>, timeoutMs: number | undefined, ruleName: string): Promise<T>;
|
|
247
|
-
|
|
248
|
-
declare function safeInternalError(): NextResponse;
|
|
249
|
-
declare function assertOverrideMechanismSupported(strict?: boolean): boolean;
|
|
250
|
-
type ResolvedOutcome = {
|
|
251
|
-
kind: "continue";
|
|
252
|
-
request?: Headers;
|
|
253
|
-
responseHeaders: Map<string, string>;
|
|
254
|
-
cookies: Map<string, string>;
|
|
255
|
-
rewriteUrl?: string;
|
|
256
|
-
} | {
|
|
257
|
-
kind: "stop";
|
|
258
|
-
response: NextResponse | Response;
|
|
259
|
-
};
|
|
260
|
-
declare function resolveRuleResult(res: NextResponse | Response | NextResult | undefined | void, requestHeaderPolicy: RequestHeaderPolicy, logger: ChainLogger, debug: boolean): ResolvedOutcome;
|
|
261
|
-
declare function finalizeResponse(res: NextResponse | Response, headerWrites: EntryWrite[], cookieWrites: EntryWrite[], headerStrategy: MergeStrategy, cookieStrategy: MergeStrategy, logger: ChainLogger, debug: boolean): NextResponse | Response;
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Creates a reusable, scope-aware proxy rule function.
|
|
265
|
-
*
|
|
266
|
-
* @example
|
|
267
|
-
* const authRule = defineProxy("auth", async (req, ctx) => next())
|
|
268
|
-
* const scopedAuthRule = authRule({ include: path("/admin/*") })
|
|
269
|
-
*/
|
|
270
|
-
declare function defineProxy<TContext = ProxyContext>(run: ProxyFn<TContext>): DefinedProxy<TContext>;
|
|
271
|
-
declare function defineProxy<TContext = ProxyContext>(name: string, run: ProxyFn<TContext>): DefinedProxy<TContext>;
|
|
272
|
-
/**
|
|
273
|
-
* Composes independent proxy rules into a single pipeline for Next.js 15 (middleware.ts) and Next.js 16 (proxy.ts).
|
|
274
|
-
*
|
|
275
|
-
* @example
|
|
276
|
-
* export default proxyChain({ debug: true }, [
|
|
277
|
-
* requestIdRule,
|
|
278
|
-
* authRule({ include: path("/dashboard/*") }),
|
|
279
|
-
* i18nRule,
|
|
280
|
-
* ])
|
|
281
|
-
*/
|
|
282
|
-
declare function proxyChain<TContext = ProxyContext>(entries: Entry<TContext>[]): (req: NextRequest, event: NextFetchEvent) => Promise<NextResponse | Response>;
|
|
283
|
-
declare function proxyChain<TContext = ProxyContext>(options: ProxyChainOptions<TContext>, entries: Entry<TContext>[]): (req: NextRequest, event: NextFetchEvent) => Promise<NextResponse | Response>;
|
|
284
180
|
|
|
285
|
-
export { type
|
|
181
|
+
export { type ChainLogger, type DefinedProxy, type Entry, type HostMatcher, type MergeStrategy, type NextOptions, type NextResult, type OnErrorHandler, type OnErrorResult, type PathMatcher, type ProxyChainOptions, type ProxyContext, type ProxyFn, type ProxyPathFilter, type ProxyRule, type RequestHeaderPolicy, RuleTimeoutError, createProxyContext, defineProxy, host, isNextResult, next, path, proxyChain };
|
package/dist/index.d.ts
CHANGED
|
@@ -4,10 +4,6 @@ import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';
|
|
|
4
4
|
* Unique symbol identifying an explicit continue action in the proxy chain pipeline.
|
|
5
5
|
*/
|
|
6
6
|
declare const NEXT: unique symbol;
|
|
7
|
-
/**
|
|
8
|
-
* Protected request headers that cannot be rewritten unless explicitly permitted by policy.
|
|
9
|
-
*/
|
|
10
|
-
declare const DEFAULT_BLOCKED_REQUEST_HEADERS: readonly string[];
|
|
11
7
|
|
|
12
8
|
/**
|
|
13
9
|
* Configuration options for chain continuation.
|
|
@@ -15,7 +11,7 @@ declare const DEFAULT_BLOCKED_REQUEST_HEADERS: readonly string[];
|
|
|
15
11
|
type NextOptions = {
|
|
16
12
|
/** Request header overrides visible to downstream rules and handlers. */
|
|
17
13
|
request?: HeadersInit;
|
|
18
|
-
/** Response headers merged into the final outgoing response. */
|
|
14
|
+
/** Response headers merged into the final outgoing response. `set-cookie` is extracted as cookies. */
|
|
19
15
|
headers?: HeadersInit;
|
|
20
16
|
};
|
|
21
17
|
/**
|
|
@@ -29,12 +25,7 @@ type NextResult = NextOptions & {
|
|
|
29
25
|
* Backed by Map to eliminate prototype-pollution risks.
|
|
30
26
|
*/
|
|
31
27
|
type ProxyContext = Map<string, unknown>;
|
|
32
|
-
type ProxyResult<TResponse = NextResponse | Response> = TResponse | NextResult | undefined | void;
|
|
33
28
|
type PathMatcher = RegExp | string;
|
|
34
|
-
type PathOptions = {
|
|
35
|
-
/** Optional locale prefixes for path matching. */
|
|
36
|
-
locales?: readonly string[];
|
|
37
|
-
};
|
|
38
29
|
type HostMatcher = string | RegExp | readonly (string | RegExp)[];
|
|
39
30
|
/**
|
|
40
31
|
* Scoping configuration for rule inclusion or exclusion.
|
|
@@ -62,7 +53,6 @@ type ProxyPathFilter = {
|
|
|
62
53
|
include?: RouteScopeList;
|
|
63
54
|
exclude?: RouteScopeList;
|
|
64
55
|
name?: string;
|
|
65
|
-
pathOptions?: PathOptions;
|
|
66
56
|
};
|
|
67
57
|
/**
|
|
68
58
|
* A single proxy execution function.
|
|
@@ -72,39 +62,40 @@ type ProxyRule<TContext = ProxyContext> = BaseProxyRule<ProxyFn<TContext>>;
|
|
|
72
62
|
type DefinedProxy<TContext = ProxyContext> = ProxyFn<TContext> & ((filter: ProxyPathFilter) => ProxyRule<TContext>);
|
|
73
63
|
type Entry<TContext = ProxyContext> = ProxyFn<TContext> | ProxyRule<TContext>;
|
|
74
64
|
type MergeStrategy = "last-write-wins" | "first-write-wins" | ((key: string, existing: string, incoming: string) => string | null);
|
|
75
|
-
interface EntryWrite {
|
|
76
|
-
ruleIndex: number;
|
|
77
|
-
ruleName: string;
|
|
78
|
-
items: Map<string, string>;
|
|
79
|
-
}
|
|
80
|
-
type LogLevel = "debug" | "info" | "warn" | "error";
|
|
81
65
|
interface ChainLogger {
|
|
82
66
|
debug: (metaOrMsg: unknown, maybeMsg?: string) => void;
|
|
83
67
|
info: (metaOrMsg: unknown, maybeMsg?: string) => void;
|
|
84
68
|
warn: (metaOrMsg: unknown, maybeMsg?: string) => void;
|
|
85
69
|
error: (metaOrMsg: unknown, maybeMsg?: string) => void;
|
|
86
70
|
}
|
|
87
|
-
type PipelineOutcome = {
|
|
88
|
-
kind: "passed";
|
|
89
|
-
duration: number;
|
|
90
|
-
} | {
|
|
91
|
-
kind: "next";
|
|
92
|
-
duration: number;
|
|
93
|
-
headers: number;
|
|
94
|
-
} | {
|
|
95
|
-
kind: "stop";
|
|
96
|
-
duration: number;
|
|
97
|
-
status: number;
|
|
98
|
-
};
|
|
99
71
|
interface RequestHeaderPolicy {
|
|
100
72
|
blocked?: readonly string[];
|
|
101
73
|
allowed?: readonly string[];
|
|
102
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Value returned from `onError`.
|
|
77
|
+
*
|
|
78
|
+
* - `Response` — stop the pipeline and return it
|
|
79
|
+
* - `NextResult` (`next()`) — continue to the next rule (rule-level errors only)
|
|
80
|
+
* - `undefined` / `void` / `null` — fail closed (`500`)
|
|
81
|
+
*/
|
|
82
|
+
type OnErrorResult = NextResponse | Response | NextResult | undefined | void | null;
|
|
83
|
+
type OnErrorHandler<TContext = ProxyContext> = (err: unknown,
|
|
84
|
+
/**
|
|
85
|
+
* 0-based index of the rule that threw.
|
|
86
|
+
* `-1` means the error originated in the pipeline engine after context creation.
|
|
87
|
+
* Engine-level errors cannot continue; only a `Response` is honored.
|
|
88
|
+
*/
|
|
89
|
+
ruleIndex: number, req: NextRequest, ctx: TContext) => OnErrorResult | Promise<OnErrorResult>;
|
|
103
90
|
interface ChainConfig<TContext = ProxyContext> {
|
|
104
91
|
createContext: (req: NextRequest, event: NextFetchEvent) => TContext;
|
|
105
92
|
cookieMergeStrategy: MergeStrategy;
|
|
93
|
+
/**
|
|
94
|
+
* How conflicting **response** headers from different rules are resolved.
|
|
95
|
+
* Request header overrides always use last-write-wins and ignore this option.
|
|
96
|
+
*/
|
|
106
97
|
headerMergeStrategy: MergeStrategy;
|
|
107
|
-
onError?:
|
|
98
|
+
onError?: OnErrorHandler<TContext>;
|
|
108
99
|
debug: boolean;
|
|
109
100
|
logger: ChainLogger;
|
|
110
101
|
/** Maximum wall-clock execution time per rule in milliseconds. 0 disables timeout. */
|
|
@@ -112,101 +103,41 @@ interface ChainConfig<TContext = ProxyContext> {
|
|
|
112
103
|
requestHeaderPolicy: RequestHeaderPolicy;
|
|
113
104
|
/** Throw an error on startup if the runtime platform does not support request header overrides. */
|
|
114
105
|
strictOverrideCheck?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Request header name that prefixes rewrites with a physical host folder
|
|
108
|
+
* (multi-tenant `app/(sites)/shop` layouts). Unset = disabled.
|
|
109
|
+
*/
|
|
110
|
+
hostFolderHeader?: string;
|
|
115
111
|
}
|
|
116
112
|
type ProxyChainOptions<TContext = ProxyContext> = Partial<ChainConfig<TContext>>;
|
|
117
113
|
|
|
118
114
|
/**
|
|
119
|
-
* Creates
|
|
120
|
-
*
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Strips sticky and global flags (g, y) from RegExp instances.
|
|
126
|
-
* On Edge runtimes, compiled regex instances can be shared across requests;
|
|
127
|
-
* sticky and global flags mutate stateful indices, causing non-deterministic matching.
|
|
128
|
-
*/
|
|
129
|
-
declare function toStatelessRegExp(re: RegExp): RegExp;
|
|
130
|
-
/**
|
|
131
|
-
* Builds a compiled Host matcher RegExp from domain names, wildcards, or regex patterns.
|
|
132
|
-
*/
|
|
133
|
-
declare function host(matcher: HostMatcher): RegExp;
|
|
134
|
-
/**
|
|
135
|
-
* Compiles a path pattern (glob string, param pattern, or RegExp) into a stateless regular expression.
|
|
136
|
-
*/
|
|
137
|
-
declare function path(pattern: string | RegExp, options?: PathOptions): RegExp;
|
|
138
|
-
declare function toPathRegex(matcher: PathMatcher | PathMatcher[], options?: PathOptions): RegExp;
|
|
139
|
-
/**
|
|
140
|
-
* Compiles route scoping declarations into normalized regex clauses.
|
|
141
|
-
*/
|
|
142
|
-
declare function compileScopes(input: RouteScopeList | undefined, pathOptions?: PathOptions): CompiledScope[] | undefined;
|
|
143
|
-
declare function toRule<TFn>(entry: TFn | BaseProxyRule<TFn>): BaseProxyRule<TFn>;
|
|
144
|
-
declare function ruleLabel<TFn>(rule: BaseProxyRule<TFn>, index: number): string;
|
|
145
|
-
/**
|
|
146
|
-
* Decorates a proxy entry with declarative path and host inclusion/exclusion filters.
|
|
147
|
-
*/
|
|
148
|
-
declare function withPaths<TFn>(entry: TFn | BaseProxyRule<TFn>, filter: ProxyPathFilter): BaseProxyRule<TFn>;
|
|
149
|
-
/**
|
|
150
|
-
* Determines whether a given request matches a rule's scoping constraints.
|
|
151
|
-
*/
|
|
152
|
-
declare function ruleMatches<TFn>(pathname: string, hostname: string | null, rule: BaseProxyRule<TFn>): boolean;
|
|
153
|
-
declare function pathMatches<TFn>(pathname: string, rule: BaseProxyRule<TFn>): boolean;
|
|
154
|
-
/**
|
|
155
|
-
* Normalizes Host or X-Forwarded-Host header values to lower-case hostname without port.
|
|
156
|
-
*/
|
|
157
|
-
declare function normalizeHost(raw: string | null | undefined): string | null;
|
|
158
|
-
/**
|
|
159
|
-
* Validates that an identical scope is not declared simultaneously in include and exclude.
|
|
160
|
-
*/
|
|
161
|
-
declare function assertIncludeExcludeNoOverlap(include: CompiledScope[] | undefined, exclude: CompiledScope[] | undefined, label: string): void;
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Converts HeadersInit or undefined to a standard Web Headers instance.
|
|
165
|
-
*/
|
|
166
|
-
declare function headersInitToHeaders(init?: HeadersInit): Headers;
|
|
167
|
-
/**
|
|
168
|
-
* Compares two Headers instances to determine if overrides contain differences.
|
|
169
|
-
*/
|
|
170
|
-
declare function headersDiffer(base: Headers, overrides: Headers): boolean;
|
|
171
|
-
/**
|
|
172
|
-
* Checks whether a request header is permitted to be mutated under the configured policy.
|
|
173
|
-
*/
|
|
174
|
-
declare function isRequestHeaderAllowed(name: string, policy?: RequestHeaderPolicy): boolean;
|
|
175
|
-
/**
|
|
176
|
-
* Merges new request header overrides into the target Headers instance, filtering prohibited headers.
|
|
177
|
-
*/
|
|
178
|
-
declare function mergeRequestHeaderOverrides(target: Headers, overrides: Headers, policy?: RequestHeaderPolicy): {
|
|
179
|
-
applied: number;
|
|
180
|
-
dropped: string[];
|
|
181
|
-
};
|
|
182
|
-
/**
|
|
183
|
-
* Extracts cookie name from a raw Set-Cookie directive.
|
|
184
|
-
*/
|
|
185
|
-
declare function parseCookieName(setCookie: string): string;
|
|
186
|
-
/**
|
|
187
|
-
* Extracts all Set-Cookie directives using standard getSetCookie() when available.
|
|
188
|
-
*/
|
|
189
|
-
declare function listSetCookie(headers: Headers): string[];
|
|
190
|
-
/**
|
|
191
|
-
* Maps cookie name to Set-Cookie header value from a Headers instance.
|
|
192
|
-
*/
|
|
193
|
-
declare function cookieItemsFromHeaders(headers: Headers): Map<string, string>;
|
|
194
|
-
/**
|
|
195
|
-
* Filters out Set-Cookie and internal transport headers (x-middleware-*) from response headers.
|
|
196
|
-
*/
|
|
197
|
-
declare function responseHeaderItems(headers: Headers): Map<string, string>;
|
|
198
|
-
/**
|
|
199
|
-
* Decodes Next.js request header overrides encoded into response headers.
|
|
200
|
-
*/
|
|
201
|
-
declare function decodeOverriddenRequestHeaders(headers: Headers): Headers;
|
|
202
|
-
/**
|
|
203
|
-
* Deterministically merges writes across rules based on configured strategy.
|
|
115
|
+
* Creates a reusable, scope-aware proxy rule function.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* const authRule = defineProxy("auth", async (req, ctx) => next())
|
|
119
|
+
* const scopedAuthRule = authRule({ include: path("/admin/*") })
|
|
204
120
|
*/
|
|
205
|
-
declare function
|
|
121
|
+
declare function defineProxy<TContext = ProxyContext>(run: ProxyFn<TContext>): DefinedProxy<TContext>;
|
|
122
|
+
declare function defineProxy<TContext = ProxyContext>(name: string, run: ProxyFn<TContext>): DefinedProxy<TContext>;
|
|
123
|
+
type ProxyHandler = (req: NextRequest, event: NextFetchEvent) => Promise<NextResponse | Response>;
|
|
206
124
|
/**
|
|
207
|
-
*
|
|
125
|
+
* Composes independent proxy rules into a single pipeline for Next.js 15 (middleware.ts) and Next.js 16 (proxy.ts).
|
|
126
|
+
*
|
|
127
|
+
* Custom `TContext` requires `createContext`. Without it, context is `ProxyContext` (`Map`).
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* export default proxyChain({ debug: true }, [
|
|
131
|
+
* requestIdRule,
|
|
132
|
+
* authRule({ include: path("/dashboard/*") }),
|
|
133
|
+
* i18nRule,
|
|
134
|
+
* ])
|
|
208
135
|
*/
|
|
209
|
-
declare function
|
|
136
|
+
declare function proxyChain(entries: Entry<ProxyContext>[]): ProxyHandler;
|
|
137
|
+
declare function proxyChain(options: ProxyChainOptions<ProxyContext>, entries: Entry<ProxyContext>[]): ProxyHandler;
|
|
138
|
+
declare function proxyChain<TContext>(options: ProxyChainOptions<TContext> & {
|
|
139
|
+
createContext: (req: NextRequest, event: NextFetchEvent) => TContext;
|
|
140
|
+
}, entries: Entry<TContext>[]): ProxyHandler;
|
|
210
141
|
|
|
211
142
|
/**
|
|
212
143
|
* Creates a continuation action instructing the proxy chain to proceed to subsequent rules.
|
|
@@ -223,63 +154,28 @@ declare function next(options?: NextOptions): NextResult;
|
|
|
223
154
|
*/
|
|
224
155
|
declare function isNextResult(value: unknown): value is NextResult;
|
|
225
156
|
|
|
226
|
-
declare function createConsoleLogger(minLevel?: LogLevel): ChainLogger;
|
|
227
157
|
/**
|
|
228
|
-
*
|
|
158
|
+
* Builds a compiled Host matcher RegExp from domain names, `*.example.com` wildcards, or regex patterns.
|
|
159
|
+
*/
|
|
160
|
+
declare function host(matcher: HostMatcher): RegExp;
|
|
161
|
+
/**
|
|
162
|
+
* Compiles a path pattern (glob string, param pattern, or RegExp) into a stateless regular expression.
|
|
163
|
+
*
|
|
164
|
+
* - `*` — exactly one path segment
|
|
165
|
+
* - `**` — zero or more subsequent segments
|
|
166
|
+
*/
|
|
167
|
+
declare function path(pattern: string | RegExp): RegExp;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Creates an isolated per-request context instance.
|
|
171
|
+
* Backed by Map to eliminate prototype pollution risks.
|
|
229
172
|
*/
|
|
230
|
-
declare function
|
|
231
|
-
method: string;
|
|
232
|
-
path: string;
|
|
233
|
-
name: string;
|
|
234
|
-
traceId?: string;
|
|
235
|
-
outcome: PipelineOutcome;
|
|
236
|
-
}): string;
|
|
173
|
+
declare function createProxyContext(): ProxyContext;
|
|
237
174
|
|
|
238
175
|
declare class RuleTimeoutError extends Error {
|
|
239
176
|
readonly ruleName: string;
|
|
240
177
|
readonly timeoutMs: number;
|
|
241
178
|
constructor(ruleName: string, timeoutMs: number);
|
|
242
179
|
}
|
|
243
|
-
/**
|
|
244
|
-
* Enforces a strict execution time budget on an asynchronous rule operation.
|
|
245
|
-
*/
|
|
246
|
-
declare function withRuleTimeout<T>(promise: Promise<T>, timeoutMs: number | undefined, ruleName: string): Promise<T>;
|
|
247
|
-
|
|
248
|
-
declare function safeInternalError(): NextResponse;
|
|
249
|
-
declare function assertOverrideMechanismSupported(strict?: boolean): boolean;
|
|
250
|
-
type ResolvedOutcome = {
|
|
251
|
-
kind: "continue";
|
|
252
|
-
request?: Headers;
|
|
253
|
-
responseHeaders: Map<string, string>;
|
|
254
|
-
cookies: Map<string, string>;
|
|
255
|
-
rewriteUrl?: string;
|
|
256
|
-
} | {
|
|
257
|
-
kind: "stop";
|
|
258
|
-
response: NextResponse | Response;
|
|
259
|
-
};
|
|
260
|
-
declare function resolveRuleResult(res: NextResponse | Response | NextResult | undefined | void, requestHeaderPolicy: RequestHeaderPolicy, logger: ChainLogger, debug: boolean): ResolvedOutcome;
|
|
261
|
-
declare function finalizeResponse(res: NextResponse | Response, headerWrites: EntryWrite[], cookieWrites: EntryWrite[], headerStrategy: MergeStrategy, cookieStrategy: MergeStrategy, logger: ChainLogger, debug: boolean): NextResponse | Response;
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Creates a reusable, scope-aware proxy rule function.
|
|
265
|
-
*
|
|
266
|
-
* @example
|
|
267
|
-
* const authRule = defineProxy("auth", async (req, ctx) => next())
|
|
268
|
-
* const scopedAuthRule = authRule({ include: path("/admin/*") })
|
|
269
|
-
*/
|
|
270
|
-
declare function defineProxy<TContext = ProxyContext>(run: ProxyFn<TContext>): DefinedProxy<TContext>;
|
|
271
|
-
declare function defineProxy<TContext = ProxyContext>(name: string, run: ProxyFn<TContext>): DefinedProxy<TContext>;
|
|
272
|
-
/**
|
|
273
|
-
* Composes independent proxy rules into a single pipeline for Next.js 15 (middleware.ts) and Next.js 16 (proxy.ts).
|
|
274
|
-
*
|
|
275
|
-
* @example
|
|
276
|
-
* export default proxyChain({ debug: true }, [
|
|
277
|
-
* requestIdRule,
|
|
278
|
-
* authRule({ include: path("/dashboard/*") }),
|
|
279
|
-
* i18nRule,
|
|
280
|
-
* ])
|
|
281
|
-
*/
|
|
282
|
-
declare function proxyChain<TContext = ProxyContext>(entries: Entry<TContext>[]): (req: NextRequest, event: NextFetchEvent) => Promise<NextResponse | Response>;
|
|
283
|
-
declare function proxyChain<TContext = ProxyContext>(options: ProxyChainOptions<TContext>, entries: Entry<TContext>[]): (req: NextRequest, event: NextFetchEvent) => Promise<NextResponse | Response>;
|
|
284
180
|
|
|
285
|
-
export { type
|
|
181
|
+
export { type ChainLogger, type DefinedProxy, type Entry, type HostMatcher, type MergeStrategy, type NextOptions, type NextResult, type OnErrorHandler, type OnErrorResult, type PathMatcher, type ProxyChainOptions, type ProxyContext, type ProxyFn, type ProxyPathFilter, type ProxyRule, type RequestHeaderPolicy, RuleTimeoutError, createProxyContext, defineProxy, host, isNextResult, next, path, proxyChain };
|