diff --git a/types/akamai-edgeworkers/OTHER_FILES.txt b/types/akamai-edgeworkers/OTHER_FILES.txt
new file mode 100644
index 0000000000..42061c01a1
--- /dev/null
+++ b/types/akamai-edgeworkers/OTHER_FILES.txt
@@ -0,0 +1 @@
+README.md
\ No newline at end of file
diff --git a/types/akamai-edgeworkers/README.md b/types/akamai-edgeworkers/README.md
new file mode 100644
index 0000000000..2066a52626
--- /dev/null
+++ b/types/akamai-edgeworkers/README.md
@@ -0,0 +1,58 @@
+Bindings for the Akamai [EdgeWorker API]. This allows you to write
+your EdgeWorkers in TypeScript.
+
+Types are available for the `Request` and `Response` objects, as well as the
+built-in modules.
+
+# User Guide
+
+EdgeWorkers are written in ECMAScript6, so you need to set your
+`tsconfig.json` to use `es6` as the compilation target and module
+code generator:
+
+```json5
+{
+ "compilerOptions": {
+ "module": "es6",
+ "target": "es6",
+//...
+ }
+}
+```
+
+## Using the `Request` and `Response` Objects
+
+The predefined EdgeWorker callbacks take Request and Response objects as
+arguments. After you have installed this package, you can create a `main.ts`
+with the following stubs:
+
+```typescript
+///
+
+export function onClientRequest(request : EW.MutableRequest & EW.HasRespondWith){}
+export function onOriginRequest(request : EW.MutableRequest) {}
+export function onOriginResponse(request : EW.ImmutableRequest & EW.HasRespondWith, response : EW.Response) {}
+export function onClientResponse(request : EW.ImmutableRequest, response : EW.Response) {}
+```
+
+The triple-slashed first line references this package and pulls `EW` into your
+namespace.
+
+## Using Built-In Modules
+
+Bindings are available for the built-in `cookies` and `url-search-params`
+modules. Once you've added the triple-slash reference to `akamai-edgeworkers`
+you can import them normally:
+
+```typescript
+///
+
+import { Cookies } from 'cookies';
+
+function onClientRequest(request: EW.MutableRequest & EW.HasRespondWith) {
+ const cookie = new Cookies(request.getHeader('cookies') || undefined);
+//...
+}
+```
+
+[EdgeWorker API]: https://developer.akamai.com/api/web_performance/edgeworkers/v1.html
\ No newline at end of file
diff --git a/types/akamai-edgeworkers/index.d.ts b/types/akamai-edgeworkers/index.d.ts
new file mode 100644
index 0000000000..1690aca8d4
--- /dev/null
+++ b/types/akamai-edgeworkers/index.d.ts
@@ -0,0 +1,461 @@
+// Type definitions for non-npm package Akamai EdgeWorkers JavaScript API 1.0
+// Project: https://developer.akamai.com/akamai-edgeworkers-overview
+// Definitions by: Evan Hughes
+// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
+
+declare namespace EW {
+ interface ReadsHeaders {
+ /**
+ * Provides header values by header name
+ */
+ getHeader(name: string): string[] | null;
+ }
+
+ interface MutatesHeaders {
+ /**
+ * Sets header value(s), replacing previous one(s)
+ */
+ setHeader(name: string, value: string | string[]): void;
+
+ /**
+ * Add value(s) to header
+ */
+ addHeader(name: string, value: string | string[]): void;
+
+ /**
+ * Removes header
+ */
+ removeHeader(name: string): void;
+ }
+
+ interface ReadsVariables {
+ /**
+ * Get's the value of a request variable
+ */
+ getVariable(name: string): string | undefined;
+ }
+
+ interface HasRespondWith {
+ /**
+ * Indicates that a complete response is being generated for a
+ * request, rather than fetching a response from cache or the origin.
+ *
+ * If called multiple times within an event handler, the last
+ * Response arguments passed in would be the arguments used to
+ * generate a response.
+ *
+ * The maximum response body string length is 2K characters. If
+ * validation of the passed in Response object fails it will throw
+ * an exception. For example, a Response body bigger than the limit
+ * will cause an exception.
+ *
+ * The deny_reason is an optional argument, and only used if the
+ * status code is 403.
+ *
+ * @param status The HTTP status code
+ * @param headers Properties used as key/value pairs for the response
+ * headers
+ * @param body The content of the response body
+ * @param deny_reason The deny reason set if the status code is a 403
+ */
+ respondWith(status: number, headers: object, body: string, deny_reason?: string): void;
+ }
+
+ interface HasStatus {
+ /**
+ * The HTTP status of a response sent to the client.
+ */
+ status: number;
+ }
+
+ interface Request {
+ /**
+ * The Host header value of the incoming request.
+ */
+ readonly host: string;
+
+ /**
+ * The HTTP method of the incoming request.
+ */
+ readonly method: string;
+
+ /**
+ * The URL path of the incoming request, including the filename and
+ * extension, but without any query string.
+ */
+ readonly path: string;
+
+ /**
+ * The scheme of the incoming request ("http" or "https").
+ */
+ readonly scheme: string;
+
+ /**
+ * The query string of the incoming request.
+ */
+ readonly query: string;
+
+ /**
+ * The Relative URL of the incoming request. This includes the path as well
+ * as the query string.
+ */
+ readonly url: string;
+
+ /**
+ * Object containing properties specifying the end user's geographic
+ * location. This value of this property will be null if the contract
+ * associated with the request does not have the appropriate entitlements.
+ */
+ readonly userLocation: UserLocation | undefined;
+
+ /**
+ * Object containing properties specifying the device characteristics. This
+ * value of this property will be null if the contract associated with the
+ * request does not have entitlements for EDC.
+ */
+ readonly device: Device | undefined;
+
+ /**
+ * The cpcode used for reporting.
+ */
+ readonly cpCode: number;
+ }
+
+ interface MutableRequest extends MutatesHeaders, ReadsHeaders, ReadsVariables, Request {
+ }
+
+ interface ImmutableRequest extends ReadsHeaders, ReadsVariables, Request {
+ }
+
+ interface Response extends HasStatus, MutatesHeaders, ReadsHeaders {
+ }
+
+ /**
+ * Notes:
+ * * If the IP address is in the reserved IP space (as designated by the
+ * Internet Assigned Numbers Authority), every property will have the
+ * value of ‘reserved’.
+ * * If user location properties can not be supplied for any reason,
+ * undefined is returned for that property
+ */
+ interface UserLocation {
+ /**
+ * The continent value is a two-letter code for the continent that
+ * the IP address maps to.
+ */
+ continent: string | undefined;
+
+ /**
+ * The country value is an ISO-3166, two-letter code for the country
+ * where the IP address maps to.
+ */
+ country: string | undefined;
+
+ /**
+ * The region value is an ISO-3166, two-letter code for the state,
+ * province, or region where the IP address maps to.
+ */
+ region: string | undefined;
+
+ /**
+ * The city value is the city (within a 50-mile radius) that the IP
+ * address maps to.
+ */
+ city: string | undefined;
+
+ /**
+ * The zipCode value is the zipcode that the IP address maps to
+ * (multiple values possible).
+ *
+ * Contiguous zip codes will be represented as a range of the form
+ * "FirstZipInRange LastZipInRange", and multiple ranges may be
+ * present (each range separated by the plus (+) character).
+ *
+ * For example, the following strings are all valid zipCode values:
+ *
+ * * 10001
+ * * 10001+10003
+ * * 10001-10003+10005
+ * * 10001-10003+10005-10008
+ *
+ * For country = US and country = PR, zip refers to the 5 digit
+ * zipcode.
+ *
+ * For country = CA, zip refers to the forward sortation area (FSA).
+ * For more information on FSA, go to http://www.canadapost.ca and
+ * search for FSA.
+ *
+ * See the EdgeScape Users Guide for more details.
+ */
+ zipCode: string | undefined;
+ }
+
+ /**
+ * Notes:
+ * * If device properties can not be supplied for any reason,
+ * undefined is returned for each property
+ */
+ interface Device {
+ /**
+ * Brand name of the device.
+ */
+ brandName: string | undefined;
+
+ /**
+ * Model name of the device.
+ */
+ modelName: string | undefined;
+
+ /**
+ * Marketing name of the device.
+ */
+ marketingName: string | undefined;
+
+ /**
+ * Indicates if the device is a wireless device.
+ */
+ isWireless: boolean | undefined;
+
+ /**
+ * Indicates if the device is a tablet.
+ */
+ isTablet: boolean | undefined;
+
+ /**
+ * The device operation system.
+ */
+ os: string | undefined;
+
+ /**
+ * The device operating system version.
+ */
+ osVersion: string | undefined;
+
+ /**
+ * The mobile browser name.
+ */
+ mobileBrowser: string | undefined;
+
+ /**
+ * The mobile browser version.
+ */
+ mobileBrowserVersion: string | undefined;
+
+ /**
+ * The screen resolution width, in pixels.
+ */
+ resolutionWidth: number | undefined;
+
+ /**
+ * The screen resolution height, in pixels.
+ */
+ resolutionHeight: number | undefined;
+
+ /**
+ * The physical screen height, in millimeters.
+ */
+ physicalScreenHeight: number | undefined;
+
+ /**
+ * The physical screen width, in millimeters.
+ */
+ physicalScreenWidth: number | undefined;
+
+ /**
+ * Indicates if the browser supports cookies.
+ */
+ hasCookieSupport: boolean | undefined;
+
+ /**
+ * Indicates if the device supports all of the following
+ * JavaScript functions: "alert confirm access form elements
+ * setTimeout setInterval and document.location"
+ */
+ hasAjaxSupport: boolean | undefined;
+
+ /**
+ * Indicates if the browser supports Flash.
+ */
+ hasFlashSupport: boolean | undefined;
+
+ /**
+ * Indicates if the browser accepts third party cookies.
+ */
+ acceptsThirdPartyCookie: boolean | undefined;
+
+ /**
+ * Indicates the level of support for XHTML.
+ */
+ xhtmlSupportLevel: number | undefined;
+
+ /**
+ * Indicates if the device is a mobile device.
+ */
+ isMobile: boolean | undefined;
+ }
+}
+
+/**
+ * Query, add, and remove cookies.
+ */
+declare module "cookies" {
+ /**
+ * Provides access to the Cookies header of a request, allowing the
+ * addition, removal, or modification of cookie values.
+ */
+ class Cookies {
+ /**
+ * Constructor for a new "Cookies" struct to hold cookies.
+ *
+ * @param cookieHeader The raw Cookie header to pass to the constructor
+ * to parse. If an array is passed, the first element must be a
+ * string and that is used as the cookies string to parse. If this
+ * is not passed, an empty cookies object is returned.
+ *
+ * @param options Only used when parsing an existing Cookie header.
+ * Object to override the default decode of the Cookie values. This
+ * object must have a function named 'decode' on it, which should
+ * take a string and return the result of the custom decoding of
+ * that string.
+ */
+ constructor(header?: string | string[], options?: object);
+
+ /**
+ * Returns the string representation to use when setting the Cookie
+ * header, encoding values by default.
+ */
+ toHeader(): string;
+
+ /**
+ * Get the first instance of the cookie matching the given name.
+ *
+ * @param name Cookie name.
+ */
+ get(name: string): string | undefined;
+
+ /**
+ * Get all Instances of the cookie matching the given name.
+ *
+ * @param name cookie name.
+ */
+ getAll(name: string): string[];
+
+ /**
+ * Get all names of existing cookies held by this Cookies object.
+ */
+ names(): string[];
+
+ /**
+ * Adds a cookie.
+ * @param name Name of the cookie
+ * @param value Value of the cookie.
+ */
+ add(name: string, value: string): void;
+
+ /**
+ * Removes all cookies with a given name.
+ *
+ * @param name Cookie name.
+ */
+ delete(name: string): void;
+ }
+
+ /**
+ * Provides access to the SetCookies header of a request.
+ */
+ class SetCookie {
+ /**
+ * Constructor for a new "SetCookie" struct to hold a specific Set-Cookie
+ * header representation.
+ */
+ constructor(opts?: {
+ name?: string;
+ value?: string;
+ maxAge?: number;
+ domain?: string;
+ path?: string;
+ expires?: { toUTCString: () => string };
+ httpOnly?: boolean;
+ secure?: boolean;
+ sameSite?: "Strict" | "Lax" | "None" | true;
+ });
+
+ /**
+ * Returns the string representation to use when setting the Set-Cookie
+ * header, encoding values by default.
+ */
+ toHeader(): string;
+
+ name: string;
+ value: string;
+ maxAge: number;
+ domain: string;
+ path: string;
+ expires: { toUTCString: () => string };
+ httpOnly: boolean;
+ secure: boolean;
+ sameSite: "Strict" | "Lax" | "None" | true;
+ }
+}
+
+/**
+ * Query, add, and remove parameters from the query string.
+ */
+declare module "url-search-params" {
+ export default class URLSearchParams {
+ /**
+ * Create a new URLSearchParams object.
+ */
+ constructor(init?: string | URLSearchParams);
+
+ /**
+ * Add a new name/value to the receiver.
+ */
+ append(name: string, value: string): void;
+
+ /**
+ * Remove the given name/value from the receiver.
+ */
+ delete(name: string): void;
+
+ /**
+ * Return the first value with the specified name.
+ */
+ get(name: string): string | null;
+
+ /**
+ * Check if the given name exists.
+ */
+ has(name: string): boolean;
+
+ /**
+ * Return *all* values association with the specified name.
+ */
+ getAll(name: string): string[];
+
+ /**
+ * Iterate through the name/value pairs.
+ */
+ entries(): IterableIterator<[string, string]>;
+
+ /**
+ * Iterate through the names.
+ */
+ keys(): IterableIterator;
+
+ /**
+ * Iterate through the values.
+ */
+ values(): IterableIterator;
+
+ /**
+ * Replace all instances of `name` with a single name/value pair.
+ */
+ set(name: string, value: string): void;
+
+ /**
+ * Return a query string suitable for use in a URL.
+ */
+ toString(): string;
+ }
+}
diff --git a/types/akamai-edgeworkers/test/akamai-edgeworkers-global.test.ts b/types/akamai-edgeworkers/test/akamai-edgeworkers-global.test.ts
new file mode 100644
index 0000000000..e1caca7180
--- /dev/null
+++ b/types/akamai-edgeworkers/test/akamai-edgeworkers-global.test.ts
@@ -0,0 +1,117 @@
+export function onClientRequest(request: EW.MutableRequest & EW.HasRespondWith) {
+ // Exercise EW.ClientRequest.setHeader()
+ request.setHeader("from-set-header-1", ["value-1", "trailer-1"]);
+
+ // Exercise EW.ClientRequest.addHeader()
+ request.addHeader("from-add-header-3", ["value-3", "trailer-2"]);
+
+ // Exercise EW.ClientRequest.removeHeader()
+ request.removeHeader("to-remove-1");
+
+ // Exercise EW.ClientRequest.getVariable()
+ request.respondWith(505, [], "Missing get-variable-present");
+
+ request.respondWith(505, { no: 'bad' }, 'Expected var to be missing');
+
+ // Exercise respondWith
+ const target = request.getHeader("target");
+ if (target != null && target[0] === 'onClientRequest-respondWith') {
+ request.respondWith(418, { 'from-respond-with': "frw value" }, "frw body");
+ }
+}
+
+export function onOriginRequest(request: EW.MutableRequest) {
+ // getHeader
+ const h = request.getHeader("onOriginRequest-getHeader");
+ if (h == null) {
+ return;
+ }
+
+ // setHeader
+ request.setHeader("onOriginRequest-getHeader-set", h[0]);
+
+ // addHeader
+ request.addHeader("onOriginRequest-addHeader-single", "single");
+ request.addHeader("onOriginRequest-addHeader-multi", ["multi-1", "multi-2"]);
+
+ // removeHeader
+ request.getHeader("onOriginRequest-removeHeader-bye");
+ request.removeHeader("onOriginRequest-removeHeader-bye");
+
+ // getVariable
+ const v = request.getVariable("var") || [];
+ request.setHeader("variable", v);
+}
+
+export function onOriginResponse(request: EW.ImmutableRequest & EW.HasRespondWith, response: EW.Response) {
+ if (response.getHeader("should-respondWith")) {
+ request.respondWith(444, {}, "wanted a respond with");
+ return;
+ }
+
+ if (response.getHeader("should-status")) {
+ response.status = 456;
+ return;
+ }
+
+ // Req - getHeader
+ let h = request.getHeader("onOriginResponse-req-getHeader") || [];
+ response.setHeader("header-from-req", h);
+
+ // getVariable
+ const v = request.getVariable("req-var") || [];
+ response.setHeader("variable", v);
+
+ // Resp - getHeader
+ h = response.getHeader("onOriginResponse-resp-getHeader") || [];
+
+ // Resp- setHeader
+ response.setHeader("onOriginResponse-getHeader-resp-set", h);
+
+ // Resp- addHeader
+ response.addHeader("onOriginResponse-addHeader-resp-single", "single");
+ response.addHeader("onOriginResponse-addHeader-resp-multi", ["multi-1", "multi-2"]);
+
+ // Resp- removeHeader
+ if (!response.getHeader("onOriginResponse-removeHeader-resp-bye")) {
+ return;
+ }
+ response.removeHeader("onOriginResponse-removeHeader-resp-bye");
+
+ // Verify we set status
+ response.status = 189;
+}
+
+export function onClientResponse(request: EW.ImmutableRequest, response: EW.Response) {
+ if (request.getHeader("should-status")) {
+ response.status = 234;
+ return;
+ }
+
+ // Req - getHeader
+ let h = request.getHeader("onClientResponse-req-getHeader") || [];
+ response.setHeader("header-from-req", h);
+
+ // getVariable
+ const v = request.getVariable("req-var") || "";
+ response.setHeader("variable", v);
+
+ // Resp - getHeader
+ h = response.getHeader("onClientResponse-resp-getHeader") || [];
+
+ // Resp- setHeader
+ response.setHeader("onClientResponse-getHeader-resp-set", h);
+
+ // Resp- addHeader
+ response.addHeader("onClientResponse-addHeader-resp-single", "single");
+ response.addHeader("onClientResponse-addHeader-resp-multi", ["multi-1", "multi-2"]);
+
+ // Resp- removeHeader
+ if (!response.getHeader("onClientResponse-removeHeader-resp-bye")) {
+ return;
+ }
+ response.removeHeader("onClientResponse-removeHeader-resp-bye");
+
+ // Verify we set status
+ response.status = 123;
+}
diff --git a/types/akamai-edgeworkers/test/cookies-tests.ts b/types/akamai-edgeworkers/test/cookies-tests.ts
new file mode 100644
index 0000000000..c44183cc6d
--- /dev/null
+++ b/types/akamai-edgeworkers/test/cookies-tests.ts
@@ -0,0 +1,51 @@
+import { Cookies, SetCookie } from 'cookies';
+
+function onClientRequest(request: EW.MutableRequest & EW.HasRespondWith) {
+ // Verify parse constructor
+ const c = new Cookies(request.getHeader('cookies') || undefined);
+
+ // Verify toHeader()
+ const s: string = c.toHeader();
+
+ // Verify get()
+ let v = c.get('first');
+ if (v !== '1st value') {
+ request.respondWith(543, {}, "Wrong value for first cookie. Got " + v);
+ return;
+ }
+
+ v = c.get('m i s s i n g');
+ if (typeof v !== 'undefined') {
+ request.respondWith(544, {}, "Got a value for a missing header: " + v);
+ return;
+ }
+
+ // getAll()
+ const all: string[] = c.getAll('z');
+
+ // names()
+ const names: string[] = c.names();
+
+ c.add('name', 'value');
+
+ c.delete('name');
+}
+
+function onClientRequest2(request: EW.MutableRequest & EW.HasRespondWith) {
+ // The values passed to SetCookie can be ignored - they're just to verify the compiler.
+ const c = new SetCookie({
+ name: "n",
+ sameSite: true
+ });
+ c.name = "le name";
+ c.value = "le value";
+ c.maxAge = 5;
+ c.domain = "le domain";
+ c.path = "le path";
+ c.expires = new Date('2013-02-21T06:55:00');
+
+ c.httpOnly = true;
+ c.secure = false;
+ c.sameSite = "Lax";
+ request.respondWith(234, { SetCookie: c.toHeader() }, "");
+}
diff --git a/types/akamai-edgeworkers/test/url-search-params-tests.ts b/types/akamai-edgeworkers/test/url-search-params-tests.ts
new file mode 100644
index 0000000000..c44233711e
--- /dev/null
+++ b/types/akamai-edgeworkers/test/url-search-params-tests.ts
@@ -0,0 +1,63 @@
+import URLSearchParams from 'url-search-params';
+
+export function onClientRequest(request: EW.MutableRequest & EW.HasRespondWith) {
+ const params = new URLSearchParams(request.query);
+
+ params.append("from-script", "from-value");
+ params.delete("to-delete");
+
+ let gotten = params.get('m i s s i n g');
+ if (gotten != null) {
+ request.respondWith(404, {}, 'busted in get() null check');
+ return;
+ }
+
+ gotten = params.get('from-script');
+ if (gotten !== 'from-value') {
+ request.respondWith(404, {}, 'didn\'t get() value');
+ return;
+ }
+
+ if (params.has('nope')) {
+ request.respondWith(404, {}, 'has() found a non-existent value');
+ return;
+ }
+
+ if (!params.has('from-script')) {
+ request.respondWith(404, {}, 'has() didn\'t find an expected value');
+ return;
+ }
+
+ if (params.getAll('nope').length === 0) {
+ // excelsior
+ } else {
+ request.respondWith(404, {}, 'getAll() test failed');
+ return;
+ }
+
+ let entriesCount = 0;
+ for (const [k, v] of params.entries()) {
+ entriesCount++;
+ }
+
+ let keysCount = 0;
+ for (const [k, v] of params.keys()) {
+ keysCount++;
+ }
+
+ let valuesCount = 0;
+ for (const [k, v] of params.values()) {
+ valuesCount++;
+ }
+
+ if (entriesCount !== keysCount || keysCount !== valuesCount) {
+ request.respondWith(404, {}, 'iteration counts didn\'t add up');
+ return;
+ }
+
+ params.set("setted", "value-setted");
+
+ request.setHeader("foo", params.toString());
+
+ request.respondWith(282, {}, 'succeeded');
+}
diff --git a/types/akamai-edgeworkers/tsconfig.json b/types/akamai-edgeworkers/tsconfig.json
new file mode 100644
index 0000000000..46738167db
--- /dev/null
+++ b/types/akamai-edgeworkers/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es6",
+ "lib": [
+ "es6"
+ ],
+ "noImplicitAny": false,
+ "noImplicitThis": true,
+ "strictNullChecks": true,
+ "strictFunctionTypes": true,
+ "types": [],
+ "forceConsistentCasingInFileNames": true,
+ "baseUrl": "../",
+ "typeRoots": [
+ "../"
+ ],
+ "noEmit": true
+ },
+ "files": [
+ "index.d.ts",
+ "test/akamai-edgeworkers-global.test.ts",
+ "test/cookies-tests.ts",
+ "test/url-search-params-tests.ts"
+ ]
+}
diff --git a/types/akamai-edgeworkers/tslint.json b/types/akamai-edgeworkers/tslint.json
new file mode 100644
index 0000000000..f93cf8562a
--- /dev/null
+++ b/types/akamai-edgeworkers/tslint.json
@@ -0,0 +1,3 @@
+{
+ "extends": "dtslint/dt.json"
+}