@vercel/edge
Advanced tools
Comparing version 0.0.5 to 0.1.0
@@ -0,1 +1,25 @@ | ||
interface ModifiedRequest { | ||
/** | ||
* If set, overwrites the incoming headers to the origin request. | ||
* | ||
* This is useful when you want to pass data between a Middleware and a | ||
* Serverless or Edge Function. | ||
* | ||
* @example | ||
* <caption>Add a `x-user-id` header and remove the `Authorization` header</caption> | ||
* | ||
* ```ts | ||
* import { rewrite } from '@vercel/edge'; | ||
* export default async function middleware(request: Request): Promise<Response> { | ||
* const newHeaders = new Headers(request.headers); | ||
* newHeaders.set('x-user-id', 'user_123'); | ||
* newHeaders.delete('authorization'); | ||
* return rewrite(request.url, { | ||
* request: { headers: newHeaders } | ||
* }) | ||
* } | ||
* ``` | ||
*/ | ||
headers?: Headers; | ||
} | ||
interface ExtraResponseInit extends Omit<ResponseInit, 'headers'> { | ||
@@ -7,2 +31,6 @@ /** | ||
headers?: HeadersInit; | ||
/** | ||
* Fields to rewrite for the upstream request. | ||
*/ | ||
request?: ModifiedRequest; | ||
} | ||
@@ -106,6 +134,12 @@ /** | ||
/** | ||
* Region of the original client IP as calculated by Vercel Proxy. | ||
* Country region of the original client IP calculated by Vercel Proxy. | ||
* | ||
* See [docs](https://vercel.com/docs/concepts/edge-network/headers#x-vercel-ip-country-region). | ||
*/ | ||
declare const REGION_HEADER_NAME = "x-vercel-ip-country-region"; | ||
/** | ||
* The request ID for each request generated by Vercel Proxy. | ||
*/ | ||
declare const REQUEST_ID_HEADER_NAME = "x-vercel-id"; | ||
/** | ||
* We define a new type so this function can be reused with | ||
@@ -129,2 +163,6 @@ * the global `Request`, `node-fetch` and other types. | ||
region?: string; | ||
/** The region part of the ISO 3166-2 code of the client IP. | ||
* See [docs](https://vercel.com/docs/concepts/edge-network/headers#x-vercel-ip-country-region). | ||
*/ | ||
countryRegion?: string; | ||
/** The latitude of the client. */ | ||
@@ -154,2 +192,2 @@ latitude?: string; | ||
export { CITY_HEADER_NAME, COUNTRY_HEADER_NAME, ExtraResponseInit, Geo, IP_HEADER_NAME, LATITUDE_HEADER_NAME, LONGITUDE_HEADER_NAME, REGION_HEADER_NAME, geolocation, ipAddress, next, rewrite }; | ||
export { CITY_HEADER_NAME, COUNTRY_HEADER_NAME, ExtraResponseInit, Geo, IP_HEADER_NAME, LATITUDE_HEADER_NAME, LONGITUDE_HEADER_NAME, ModifiedRequest, REGION_HEADER_NAME, REQUEST_ID_HEADER_NAME, geolocation, ipAddress, next, rewrite }; |
@@ -29,2 +29,3 @@ "use strict"; | ||
REGION_HEADER_NAME: () => REGION_HEADER_NAME, | ||
REQUEST_ID_HEADER_NAME: () => REQUEST_ID_HEADER_NAME, | ||
geolocation: () => geolocation, | ||
@@ -38,5 +39,20 @@ ipAddress: () => ipAddress, | ||
// src/middleware-helpers.ts | ||
function handleMiddlewareField(init, headers) { | ||
var _a; | ||
if ((_a = init == null ? void 0 : init.request) == null ? void 0 : _a.headers) { | ||
if (!(init.request.headers instanceof Headers)) { | ||
throw new Error("request.headers must be an instance of Headers"); | ||
} | ||
const keys = []; | ||
for (const [key, value] of init.request.headers) { | ||
headers.set("x-middleware-request-" + key, value); | ||
keys.push(key); | ||
} | ||
headers.set("x-middleware-override-headers", keys.join(",")); | ||
} | ||
} | ||
function rewrite(destination, init) { | ||
const headers = new Headers((init == null ? void 0 : init.headers) ?? {}); | ||
headers.set("x-middleware-rewrite", String(destination)); | ||
handleMiddlewareField(init, headers); | ||
return new Response(null, { | ||
@@ -50,2 +66,3 @@ ...init, | ||
headers.set("x-middleware-next", "1"); | ||
handleMiddlewareField(init, headers); | ||
return new Response(null, { | ||
@@ -64,2 +81,3 @@ ...init, | ||
var REGION_HEADER_NAME = "x-vercel-ip-country-region"; | ||
var REQUEST_ID_HEADER_NAME = "x-vercel-id"; | ||
function getHeader(request, key) { | ||
@@ -71,2 +89,8 @@ return request.headers.get(key) ?? void 0; | ||
} | ||
function getRegionFromRequestId(requestId) { | ||
if (!requestId) { | ||
return "dev1"; | ||
} | ||
return requestId.split(":")[0]; | ||
} | ||
function geolocation(request) { | ||
@@ -76,3 +100,4 @@ return { | ||
country: getHeader(request, COUNTRY_HEADER_NAME), | ||
region: getHeader(request, REGION_HEADER_NAME), | ||
countryRegion: getHeader(request, REGION_HEADER_NAME), | ||
region: getRegionFromRequestId(getHeader(request, REQUEST_ID_HEADER_NAME)), | ||
latitude: getHeader(request, LATITUDE_HEADER_NAME), | ||
@@ -90,2 +115,3 @@ longitude: getHeader(request, LONGITUDE_HEADER_NAME) | ||
REGION_HEADER_NAME, | ||
REQUEST_ID_HEADER_NAME, | ||
geolocation, | ||
@@ -92,0 +118,0 @@ ipAddress, |
@@ -14,2 +14,3 @@ # Interface: ExtraResponseInit | ||
- [headers](ExtraResponseInit.md#headers) | ||
- [request](ExtraResponseInit.md#request) | ||
- [status](ExtraResponseInit.md#status) | ||
@@ -29,6 +30,18 @@ - [statusText](ExtraResponseInit.md#statustext) | ||
[src/middleware-helpers.ts:6](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L6) | ||
[src/middleware-helpers.ts:31](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L31) | ||
--- | ||
### request | ||
• `Optional` **request**: [`ModifiedRequest`](ModifiedRequest.md) | ||
Fields to rewrite for the upstream request. | ||
#### Defined in | ||
[src/middleware-helpers.ts:35](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L35) | ||
--- | ||
### status | ||
@@ -35,0 +48,0 @@ |
@@ -11,2 +11,3 @@ # Interface: Geo | ||
- [country](Geo.md#country) | ||
- [countryRegion](Geo.md#countryregion) | ||
- [latitude](Geo.md#latitude) | ||
@@ -26,3 +27,3 @@ - [longitude](Geo.md#longitude) | ||
[src/edge-headers.ts:41](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L41) | ||
[src/edge-headers.ts:47](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L47) | ||
@@ -39,6 +40,19 @@ --- | ||
[src/edge-headers.ts:44](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L44) | ||
[src/edge-headers.ts:50](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L50) | ||
--- | ||
### countryRegion | ||
• `Optional` **countryRegion**: `string` | ||
The region part of the ISO 3166-2 code of the client IP. | ||
See [docs](https://vercel.com/docs/concepts/edge-network/headers#x-vercel-ip-country-region). | ||
#### Defined in | ||
[src/edge-headers.ts:58](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L58) | ||
--- | ||
### latitude | ||
@@ -52,3 +66,3 @@ | ||
[src/edge-headers.ts:50](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L50) | ||
[src/edge-headers.ts:61](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L61) | ||
@@ -65,3 +79,3 @@ --- | ||
[src/edge-headers.ts:53](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L53) | ||
[src/edge-headers.ts:64](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L64) | ||
@@ -78,2 +92,2 @@ --- | ||
[src/edge-headers.ts:47](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L47) | ||
[src/edge-headers.ts:53](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L53) |
@@ -9,2 +9,3 @@ # @vercel/edge | ||
- [Geo](interfaces/Geo.md) | ||
- [ModifiedRequest](interfaces/ModifiedRequest.md) | ||
@@ -19,2 +20,3 @@ ### Variables | ||
- [REGION_HEADER_NAME](README.md#region_header_name) | ||
- [REQUEST_ID_HEADER_NAME](README.md#request_id_header_name) | ||
@@ -94,8 +96,22 @@ ### Functions | ||
Region of the original client IP as calculated by Vercel Proxy. | ||
Country region of the original client IP calculated by Vercel Proxy. | ||
See [docs](https://vercel.com/docs/concepts/edge-network/headers#x-vercel-ip-country-region). | ||
#### Defined in | ||
[src/edge-headers.ts:24](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L24) | ||
[src/edge-headers.ts:26](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L26) | ||
--- | ||
### REQUEST_ID_HEADER_NAME | ||
• `Const` **REQUEST_ID_HEADER_NAME**: `"x-vercel-id"` | ||
The request ID for each request generated by Vercel Proxy. | ||
#### Defined in | ||
[src/edge-headers.ts:30](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L30) | ||
## Functions | ||
@@ -129,3 +145,3 @@ | ||
[src/edge-headers.ts:80](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L80) | ||
[src/edge-headers.ts:106](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L106) | ||
@@ -156,3 +172,3 @@ --- | ||
[src/edge-headers.ts:66](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L66) | ||
[src/edge-headers.ts:77](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L77) | ||
@@ -205,3 +221,3 @@ --- | ||
[src/middleware-helpers.ts:94](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L94) | ||
[src/middleware-helpers.ts:145](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L145) | ||
@@ -268,2 +284,2 @@ --- | ||
[src/middleware-helpers.ts:53](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L53) | ||
[src/middleware-helpers.ts:101](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L101) |
{ | ||
"name": "@vercel/edge", | ||
"version": "0.0.5", | ||
"version": "0.1.0", | ||
"license": "MIT", | ||
@@ -15,3 +15,3 @@ "main": "dist/index.js", | ||
"devDependencies": { | ||
"@edge-runtime/jest-environment": "1.1.0-beta.36", | ||
"@edge-runtime/jest-environment": "2.0.0", | ||
"@types/jest": "27.4.1", | ||
@@ -35,3 +35,3 @@ "ts-node": "8.9.1", | ||
}, | ||
"gitHead": "dd94dcab3255839c7c9addbba35026dc88cf0e95" | ||
"gitHead": "a630e1989613052f64411fd7e5468de58c5999ba" | ||
} |
@@ -22,5 +22,11 @@ /** | ||
/** | ||
* Region of the original client IP as calculated by Vercel Proxy. | ||
* Country region of the original client IP calculated by Vercel Proxy. | ||
* | ||
* See [docs](https://vercel.com/docs/concepts/edge-network/headers#x-vercel-ip-country-region). | ||
*/ | ||
export const REGION_HEADER_NAME = 'x-vercel-ip-country-region'; | ||
/** | ||
* The request ID for each request generated by Vercel Proxy. | ||
*/ | ||
export const REQUEST_ID_HEADER_NAME = 'x-vercel-id'; | ||
@@ -50,2 +56,7 @@ /** | ||
/** The region part of the ISO 3166-2 code of the client IP. | ||
* See [docs](https://vercel.com/docs/concepts/edge-network/headers#x-vercel-ip-country-region). | ||
*/ | ||
countryRegion?: string; | ||
/** The latitude of the client. */ | ||
@@ -73,2 +84,17 @@ latitude?: string; | ||
/** | ||
* Extracts the Vercel Edge Network region name from the request ID. | ||
* | ||
* @param requestId The request ID (`x-vercel-id`). | ||
* @returns The first region received the client request. | ||
*/ | ||
function getRegionFromRequestId(requestId?: string): string | undefined { | ||
if (!requestId) { | ||
return 'dev1'; | ||
} | ||
// The request ID is in the format of `region::id` or `region1:region2:...::id`. | ||
return requestId.split(':')[0]; | ||
} | ||
/** | ||
* Returns the location information for the incoming request. | ||
@@ -87,3 +113,4 @@ * | ||
country: getHeader(request, COUNTRY_HEADER_NAME), | ||
region: getHeader(request, REGION_HEADER_NAME), | ||
countryRegion: getHeader(request, REGION_HEADER_NAME), | ||
region: getRegionFromRequestId(getHeader(request, REQUEST_ID_HEADER_NAME)), | ||
latitude: getHeader(request, LATITUDE_HEADER_NAME), | ||
@@ -90,0 +117,0 @@ longitude: getHeader(request, LONGITUDE_HEADER_NAME), |
@@ -0,1 +1,26 @@ | ||
export interface ModifiedRequest { | ||
/** | ||
* If set, overwrites the incoming headers to the origin request. | ||
* | ||
* This is useful when you want to pass data between a Middleware and a | ||
* Serverless or Edge Function. | ||
* | ||
* @example | ||
* <caption>Add a `x-user-id` header and remove the `Authorization` header</caption> | ||
* | ||
* ```ts | ||
* import { rewrite } from '@vercel/edge'; | ||
* export default async function middleware(request: Request): Promise<Response> { | ||
* const newHeaders = new Headers(request.headers); | ||
* newHeaders.set('x-user-id', 'user_123'); | ||
* newHeaders.delete('authorization'); | ||
* return rewrite(request.url, { | ||
* request: { headers: newHeaders } | ||
* }) | ||
* } | ||
* ``` | ||
*/ | ||
headers?: Headers; | ||
} | ||
export interface ExtraResponseInit extends Omit<ResponseInit, 'headers'> { | ||
@@ -7,4 +32,27 @@ /** | ||
headers?: HeadersInit; | ||
/** | ||
* Fields to rewrite for the upstream request. | ||
*/ | ||
request?: ModifiedRequest; | ||
} | ||
function handleMiddlewareField( | ||
init: ExtraResponseInit | undefined, | ||
headers: Headers | ||
) { | ||
if (init?.request?.headers) { | ||
if (!(init.request.headers instanceof Headers)) { | ||
throw new Error('request.headers must be an instance of Headers'); | ||
} | ||
const keys = []; | ||
for (const [key, value] of init.request.headers) { | ||
headers.set('x-middleware-request-' + key, value); | ||
keys.push(key); | ||
} | ||
headers.set('x-middleware-override-headers', keys.join(',')); | ||
} | ||
} | ||
/** | ||
@@ -60,2 +108,5 @@ * Returns a response that rewrites the request to a different URL. | ||
headers.set('x-middleware-rewrite', String(destination)); | ||
handleMiddlewareField(init, headers); | ||
return new Response(null, { | ||
@@ -99,2 +150,5 @@ ...init, | ||
headers.set('x-middleware-next', '1'); | ||
handleMiddlewareField(init, headers); | ||
return new Response(null, { | ||
@@ -101,0 +155,0 @@ ...init, |
@@ -15,2 +15,3 @@ /** | ||
REGION_HEADER_NAME, | ||
REQUEST_ID_HEADER_NAME, | ||
} from '../src'; | ||
@@ -28,5 +29,12 @@ | ||
describe('`geolocation`', () => { | ||
test('returns an empty object if headers are not found', () => { | ||
test('returns an object with lots of undefined if headers are not found', () => { | ||
const req = new Request('https://example.vercel.sh'); | ||
expect(geolocation(req)).toEqual({}); | ||
expect(geolocation(req)).toEqual({ | ||
city: undefined, | ||
country: undefined, | ||
countryRegion: undefined, | ||
latitude: undefined, | ||
longitude: undefined, | ||
region: 'dev1', | ||
}); | ||
}); | ||
@@ -41,3 +49,4 @@ | ||
[LONGITUDE_HEADER_NAME]: '34.855499', | ||
[REGION_HEADER_NAME]: 'fra1', | ||
[REGION_HEADER_NAME]: 'TA', // https://en.wikipedia.org/wiki/ISO_3166-2:IL | ||
[REQUEST_ID_HEADER_NAME]: 'fra1::kpwjx-123455678-c0ffee', | ||
}, | ||
@@ -51,4 +60,46 @@ }); | ||
region: 'fra1', | ||
countryRegion: 'TA', | ||
}); | ||
}); | ||
test('reads values from headers (with a request ID containing two edge regions)', () => { | ||
const req = new Request('https://example.vercel.sh', { | ||
headers: { | ||
[CITY_HEADER_NAME]: 'Tokyo', | ||
[COUNTRY_HEADER_NAME]: 'Japan', | ||
[LATITUDE_HEADER_NAME]: '37.1233', | ||
[LONGITUDE_HEADER_NAME]: '30.733399', | ||
[REGION_HEADER_NAME]: '13', | ||
[REQUEST_ID_HEADER_NAME]: 'hnd1:iad1::kpwjx-123455678-c0ffee', | ||
}, | ||
}); | ||
expect(geolocation(req)).toEqual<Geo>({ | ||
city: 'Tokyo', | ||
country: 'Japan', | ||
latitude: '37.1233', | ||
longitude: '30.733399', | ||
region: 'hnd1', | ||
countryRegion: '13', | ||
}); | ||
}); | ||
test('reads values from headers (without a request ID)', () => { | ||
const req = new Request('https://example.vercel.sh', { | ||
headers: { | ||
[CITY_HEADER_NAME]: 'Tokyo', | ||
[COUNTRY_HEADER_NAME]: 'Japan', | ||
[LATITUDE_HEADER_NAME]: '37.1233', | ||
[LONGITUDE_HEADER_NAME]: '30.733399', | ||
[REGION_HEADER_NAME]: '13', | ||
}, | ||
}); | ||
expect(geolocation(req)).toEqual<Geo>({ | ||
city: 'Tokyo', | ||
country: 'Japan', | ||
latitude: '37.1233', | ||
longitude: '30.733399', | ||
region: 'dev1', | ||
countryRegion: '13', | ||
}); | ||
}); | ||
}); |
@@ -25,2 +25,28 @@ /** | ||
}); | ||
test('receives new request headers', () => { | ||
const headers = new Headers(); | ||
headers.set('x-from-middleware1', 'hello1'); | ||
headers.set('x-from-middleware2', 'hello2'); | ||
const resp = rewrite(new URL('https://example.vercel.sh/'), { | ||
headers: { | ||
'x-custom-header': 'custom-value', | ||
}, | ||
request: { headers }, | ||
}); | ||
expect({ | ||
status: resp.status, | ||
headers: Object.fromEntries(resp.headers), | ||
}).toMatchObject({ | ||
status: 200, | ||
headers: { | ||
'x-middleware-rewrite': 'https://example.vercel.sh/', | ||
'x-custom-header': 'custom-value', | ||
'x-middleware-override-headers': | ||
'x-from-middleware1,x-from-middleware2', | ||
'x-middleware-request-x-from-middleware2': 'hello2', | ||
'x-middleware-request-x-from-middleware1': 'hello1', | ||
}, | ||
}); | ||
}); | ||
}); | ||
@@ -46,2 +72,28 @@ | ||
}); | ||
test('receives new request headers', () => { | ||
const headers = new Headers(); | ||
headers.set('x-from-middleware1', 'hello1'); | ||
headers.set('x-from-middleware2', 'hello2'); | ||
const resp = next({ | ||
headers: { | ||
'x-custom-header': 'custom-value', | ||
}, | ||
request: { headers }, | ||
}); | ||
expect({ | ||
status: resp.status, | ||
headers: Object.fromEntries(resp.headers), | ||
}).toMatchObject({ | ||
status: 200, | ||
headers: { | ||
'x-middleware-next': '1', | ||
'x-custom-header': 'custom-value', | ||
'x-middleware-override-headers': | ||
'x-from-middleware1,x-from-middleware2', | ||
'x-middleware-request-x-from-middleware2': 'hello2', | ||
'x-middleware-request-x-from-middleware1': 'hello1', | ||
}, | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
51129
19
873