diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj
index b262c50d3..52a85dee5 100644
--- a/CrossPlatformModules.csproj
+++ b/CrossPlatformModules.csproj
@@ -288,6 +288,10 @@
+
+
+
+
file-name-resolver.d.ts
@@ -1670,6 +1674,8 @@
PreserveNewest
+
+
diff --git a/fetch/README.md b/fetch/README.md
new file mode 100644
index 000000000..fb27fc900
--- /dev/null
+++ b/fetch/README.md
@@ -0,0 +1,17 @@
+# Isomorphic Fetch Implementation
+
+## status
+
+WIP
+
+## motivation
+
+implementation of [fetch API](https://fetch.spec.whatwg.org/) in pure javascript.
+polyfill for browser, and implemnt for node.js.
+make network http access isomorphic.
+
+
+## License
+
+The MIT License (MIT)
+Copyright (c) 2013 Jxck
diff --git a/fetch/body.ts b/fetch/body.ts
new file mode 100644
index 000000000..1070006b5
--- /dev/null
+++ b/fetch/body.ts
@@ -0,0 +1,100 @@
+// https://fetch.spec.whatwg.org/#json
+type object = JSON;
+
+type body = Object; // byte stream
+
+// https://fetch.spec.whatwg.org/#bodyinit
+type BodyInit = Blob | BufferSource | FormData | URLSearchParams | USVString
+
+// https://fetch.spec.whatwg.org/#body
+interface IBody {
+ // readonly property
+ bodyUsed: boolean;
+
+ // method
+ arrayBuffer(): Promise;
+ blob(): Promise;
+ formData(): Promise;
+ json(): Promise;
+ text(): Promise;
+};
+
+// https://fetch.spec.whatwg.org/#body
+class Body implements IBody {
+ private _bodyUsed: boolean;
+ private _body: body;
+ private _usedFlag: boolean;
+ private _mimeType: string;
+
+ get bodyUsed(): boolean {
+ return this._bodyUsed;
+ }
+
+ get body(): body {
+ return this._body;
+ }
+
+ get usedFlag(): boolean {
+ return this._usedFlag;
+ }
+
+ get mimeType(): string {
+ return this._mimeType;
+ }
+
+ consumeBody(_type: string): any {
+ // step 1
+ var p = new Promise((resolve, reject) => {
+ // step 2
+ if (this._bodyUsed == true) {
+ return reject(new TypeError("body was already used"));
+ }
+
+ // step 3
+ this._bodyUsed = true;
+
+ // step 3-1
+ var stream = this._body;
+
+ // step 3-2
+ if (stream == null) {
+ stream = [];
+ }
+
+ // step 3-3
+ // TODO: Let bytes be the result of reading from stream until it returns end-of-stream.
+ var bytes;
+
+ // step 4
+ // TODO: implement me
+ switch(_type) {
+ case "ArrayBuffer":
+ case "Blob":
+ case "FormData":
+ case "JSON":
+ case "text":
+ }
+ });
+ return p;
+ }
+
+ arrayBuffer(): Promise {
+ return this.consumeBody("ArrayBuffer");
+ }
+
+ blob(): Promise {
+ return this.consumeBody("Blob");
+ }
+
+ formData(): Promise {
+ return this.consumeBody("FormData");
+ }
+
+ json(): Promise {
+ return this.consumeBody("JSON");
+ }
+
+ text(): Promise {
+ return this.consumeBody("text");
+ }
+}
diff --git a/fetch/fetch.ts b/fetch/fetch.ts
new file mode 100644
index 000000000..c2dbc6f75
--- /dev/null
+++ b/fetch/fetch.ts
@@ -0,0 +1,677 @@
+///
+///
+///
+
+// http://heycam.github.io/webidl/#common-BufferSource
+class BufferSource {
+}
+
+// https://url.spec.whatwg.org/#urlsearchparams
+class URLSearchParams {
+}
+
+// https://fetch.spec.whatwg.org/#concept-method
+enum MethodEnum {
+ OPTIONS,
+ GET,
+ HEAD,
+ POST,
+ PUT,
+ DELETE,
+ TRACE,
+ CONNECT,
+}
+
+// https://fetch.spec.whatwg.org/#simple-method
+enum SimpleMethodEnum {
+ GET,
+ HEAD,
+ POST
+}
+
+function isSimpleMethod(method: ByteString): boolean {
+ if (SimpleMethodEnum[method] !== undefined) {
+ return true;
+ }
+ return false;
+}
+
+// https://fetch.spec.whatwg.org/#forbidden-method
+enum ForbiddenMethodEnum {
+ CONNECT,
+ TRACE,
+ TRACK
+}
+
+function isForbiddenMethod(method: ByteString): boolean {
+ if (ForbiddenMethodEnum[method] !== undefined) {
+ return true;
+ }
+ return false
+}
+
+// https://fetch.spec.whatwg.org/#requestcontext
+type RequestContext = string;
+enum RequestContextEnum {
+ "audio", "beacon",
+ "cspreport", "download",
+ "embed", "eventsource",
+ "favicon", "fetch",
+ "font", "form",
+ "frame", "hyperlink",
+ "iframe", "image",
+ "imageset", "import",
+ "internal", "location",
+ "manifest", "object",
+ "ping", "plugin",
+ "prefetch", "script",
+ "serviceworker", "sharedworker",
+ "subresource", "style",
+ "track", "video",
+ "worker", "xmlhttprequest",
+ "xslt"
+};
+
+// https://fetch.spec.whatwg.org/#concept-request-context-frame-type
+enum ContextFrameTypeEnum {
+ "auxiliary",
+ "top-level",
+ "nested",
+ "none"
+}
+
+// https://fetch.spec.whatwg.org/#concept-request-mode
+type RequestMode = string;
+enum RequestModeEnum {
+ "same-origin",
+ "no-cors",
+ "cors"
+};
+
+// https://fetch.spec.whatwg.org/#concept-request-credentials-mode
+type RequestCredentials = string;
+enum RequestCredentialsEnum {
+ "omit",
+ "same-origin",
+ "include"
+};
+
+// https://fetch.spec.whatwg.org/#concept-request-cache-mode
+type RequestCache = string;
+enum RequestCacheEnum {
+ "default",
+ "bypass",
+ "reload",
+ "revalidate",
+ "force-cache",
+ "offline"
+};
+
+// https://fetch.spec.whatwg.org/#concept-response-type
+type ResponseType = string;
+enum ResponseTypeEnum {
+ "basic",
+ "cors",
+ "default",
+ "error",
+ "opaque"
+};
+
+/////////////////////////////
+/// Request
+/////////////////////////////
+
+// https://fetch.spec.whatwg.org/#requestinfo
+type RequestInfo = Request | USVString;
+
+// https://fetch.spec.whatwg.org/#request
+interface IRequest extends IBody {
+ // readonly property
+ method: ByteString;
+ url: USVString;
+ headers: Headers;
+ context: RequestContext;
+ referrer: DOMString;
+ mode: RequestMode;
+ credentials: RequestCredentials;
+ cache: RequestCache;
+
+ // method
+ clone(): IRequest;
+};
+
+// https://fetch.spec.whatwg.org/#requestinit
+// dictionary RequestInit
+interface RequestInit {
+ method: ByteString;
+ headers: HeadersInit;
+ body: BodyInit;
+ mode: RequestMode;
+ credentials: RequestCredentials;
+ cache: RequestCache;
+};
+
+type Client = Object;
+type Referrer = Object;
+type Context = Object;
+
+type request = {
+ method: string;
+ url: string;
+ headerList: Header[];
+ unsafeRequestFlag: boolean;
+ body: body;
+ //TODO: client: Client;
+ context: Context;
+ //TODO: origin: string;
+ forceOriginHeaderFlag: boolean;
+ sameOriginDataURLFlag: boolean;
+ //TODO: referrer: Referrer;
+ mode: string;
+ credentialsMode: string;
+ cacheMode: string;
+}
+
+class Request implements IRequest {
+ // readonly property on IRequest
+ private _method: ByteString;
+ private _url: USVString;
+ private _headers: Headers;
+ private _context: RequestContext;
+ private _referrer: DOMString;
+ private _mode: ByteString;
+ private _credentials: RequestCredentials;
+ private _cache: RequestCache;
+
+ // readonly property on IBody
+ private _bodyUsed: boolean;
+
+ // https://fetch.spec.whatwg.org/#dom-request-method
+ get method(): ByteString {
+ return this._method;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-request-url
+ get url(): USVString {
+ return this._url;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-request-headers
+ get headers(): Headers {
+ return this._headers;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-request-context
+ get context(): RequestContext {
+ return this._context;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-request-referrer
+ get referrer(): DOMString {
+ return null;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-request-mode
+ get mode(): RequestMode {
+ return null;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-request-credentials
+ get credentials(): RequestCredentials {
+ return this._credentials;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-request-cache
+ get cache(): RequestCache {
+ return null;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-body-bodyused
+ get bodyUsed(): boolean {
+ return this._bodyUsed;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-request-clone
+ // method on IRequest
+ public clone(): IRequest {
+ return null;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-body-arraybuffer
+ // method on IBody
+ public arrayBuffer(): Promise {
+ return null;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-body-blob
+ public blob(): Promise {
+ return null;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-body-formdata
+ public formData(): Promise {
+ return null;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-body-json
+ public json(): Promise {
+ return null;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-body-text
+ public text(): Promise {
+ return null;
+ }
+
+ // Request
+ public request: request;
+ public body: body;
+ public usedFlag: boolean;
+ public mimeType: string;
+
+ // https://fetch.spec.whatwg.org/#dom-request
+ constructor(input: RequestInfo, init?: RequestInit) {
+ // can't detect class by instanceof
+ // if (input instanceof Request) { }
+
+ var request: request;
+ // step 1
+ if (typeof input === "object" && input.body !== null) { // Request
+ // step 1-1
+ if (input.usedFlag) {
+ throw new TypeError("Request already used");
+ }
+ // step 1-2
+ input.usedFlag = true;
+
+ // step 2
+ request = input.request;
+ } else {
+ // step 2
+ // new request otherwise
+ request = {
+ url: null,
+ method: MethodEnum[MethodEnum.GET],
+ headerList: [],
+ unsafeRequestFlag: false,
+ body: null,
+ //TODO: client: entry settings object,
+ //TODO: origin: entry settings object.origin,
+ forceOriginHeaderFlag: false,
+ sameOriginDataURLFlag: false,
+ referrer: null,
+ context: null,
+ mode: RequestModeEnum[RequestModeEnum["no-cors"]],
+ credentialsMode: RequestCredentialsEnum[RequestCredentialsEnum.omit],
+ cacheMode: RequestCacheEnum[RequestCacheEnum.default],
+ }
+ }
+
+ // step 3
+ request = {
+ url: request.url,
+ method: request.method,
+ headerList: request.headerList,
+ unsafeRequestFlag: true,
+ body: request.body,
+ //TODO: client: entry settings object,
+ //TODO: origin: entry settings object.origin,
+ forceOriginHeaderFlag: true,
+ sameOriginDataURLFlag: true,
+ //TODO: referrer : request.client,
+ context: 'fetch',
+ mode: request.mode,
+ credentialsMode: request.credentialsMode,
+ cacheMode: request.cacheMode
+ }
+
+ // step 4, 5, 6
+ var fallbackMode: RequestMode = null;
+ var fallbackCredentials: RequestCredentials = null;
+ var fallbackCache: RequestCache = null;
+
+ //TODO:
+ function parseURL(url: string): string {
+ return url;
+ }
+ // step 7
+ if (typeof input === "string") {
+ // step 7-1
+ var parsedURL;
+
+ try {
+ parsedURL = parseURL(input);
+ } catch(err) {
+ // step 7-2
+ throw new TypeError(err);
+ }
+
+ // step 7-3
+ request.url = parsedURL;
+
+ // step 7-4, 7-5, 7-6
+ fallbackMode = RequestModeEnum[RequestModeEnum.cors];
+ fallbackCredentials = RequestCredentialsEnum[RequestCredentialsEnum.omit];
+ fallbackCache = RequestCacheEnum[RequestCacheEnum.default];
+ }
+
+ // step 8
+ var mode = init.mode? init.mode: fallbackMode;
+
+ // step 9
+ if (mode !== null) request.mode = mode;
+
+ // step 10
+ var credentials = init.credentials? init.credentials: fallbackCredentials;
+
+ // step 11
+ if (credentials !== null) request.credentialsMode = credentials;
+
+ // step 12
+ var cache = init.cache? init.cache: fallbackCache;
+
+ // step 13
+ if (cache !== null) request.cacheMode = cache;
+
+ // step 14
+ if (init.method) {
+ var method = init.method;
+
+ // step 14-1
+ if(isForbiddenMethod(method)) {
+ throw new TypeError("forbidden method " + method);
+ }
+
+ // step 14-2
+ method = method.toUpperCase();
+
+ // step 14-3
+ request.method = method;
+ }
+
+ // step 15
+ var r = this;
+ r.request = request;
+ r._headers = new Headers();
+
+ // step 16
+ var headers = r.headers;
+
+ // step 17
+ if (init.headers) {
+ headers = init.headers;
+ }
+
+ // step 18
+ r.request.headerList = [];
+
+ // step 19
+ if (r.request.mode === "no-cors") {
+ // 19-1
+ if (!isSimpleMethod(this.request.method)) {
+ throw new TypeError("not simple method" + method);
+ }
+ // 19-2
+ r.headers.guard = "request-no-CORS";
+ }
+
+ // step 20
+ r._headers = headers;
+
+ // step 21
+ if (init.body) {
+ // step 21-1
+ var result = extract(init.body);
+
+ // step 21-2
+ r.request.body = result.stream;
+
+ // step 21-3
+ if (result.contentType !== null) {
+ var hasContentType = request.headerList.some((header) => {
+ return header.name === "Content-Type";
+ });
+
+ if (!hasContentType) {
+ r._headers.append("Content-Type", result.contentType);
+ }
+ }
+ }
+
+ // step 22
+ // FIXME implement mime type extract
+ r.mimeType = null;
+ }
+}
+
+// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
+function extract(object: any): any {
+ // step 1
+ var stream = [];
+
+ // step 2
+ var contentType = null;
+
+ // step 3
+ switch(object.constructor) {
+ // Blob
+ case Blob:
+ stream = object.contents;
+
+ if (object.type) {
+ contentType = object.type;
+ }
+ case BufferSource:
+ // TODO: stream = copy(object);
+ case FormData:
+ // TODO:
+ case URLSearchParams:
+ stream = object.list.toString();
+ contentType = "application/x-www-form-urlencoded;charset=UTF-8";
+ case String: // USVString
+ // stream = encode(object);
+ contentType = "text/plain;charset=UTF-8";
+ }
+
+ // step 4
+ return { stream: stream, contentType: contentType };
+}
+
+
+
+// https://fetch.spec.whatwg.org/#response
+interface IResponse extends IBody { // Response implements Body;
+ // static Response error();
+ // static Response redirect(USVString url, optional unsigned short status = 302);
+
+ type: ResponseType; // readonly
+ url: USVString; // readonly
+ status: number; // readonly
+ statusText: ByteString; // readonly
+ headers: Headers; // readonly
+ // Response clone();
+};
+
+// https://fetch.spec.whatwg.org/#responseinit
+class ResponseInit {
+ status: number = 200;
+ statusText: ByteString = "OK";
+ headers: HeadersInit;
+};
+
+class response {
+ type: string;
+ terminationReason: string;
+ url: string;
+ status: number;
+ statusMessage: string;
+ headerList: Header[];
+ body: Body;
+ cacheState: string;
+ TLSState: string;
+
+ constructor() {
+ this.type = "default";
+ this.terminationReason = "timeout";
+ this.url = null;
+ this.status = 200;
+ this.statusMessage = "OK";
+ this.headerList = [];
+ this.body = null;
+ this.cacheState = "none";
+ this.TLSState = "unauthenticated";
+ }
+}
+
+// https://fetch.spec.whatwg.org/#response
+class Response implements IResponse {
+ // implements body
+ _bodyUsed: boolean;
+
+ _type: ResponseType; // readonly
+ _url: USVString; // readonly
+ _status: number; // readonly
+ _statusText: ByteString; // readonly
+ _headers: Headers; // readonly
+
+ _response: response;
+
+ // https://fetch.spec.whatwg.org/#dom-response
+ // [Constructor(optional BodyInit body, optional ResponseInit init), Exposed=(Window,Worker)]
+ constructor(body?: BodyInit, init?: ResponseInit) {
+ if (init !== undefined) {
+ // step 1
+ if (init.status < 200 && init.status > 599) {
+ throw new RangeError("status is not in the range 200 to 599");
+ }
+
+ // step 2
+ if (init.statusText.indexOf("\r") < 0 || init.statusText.indexOf("\n") < 0) {
+ throw new TypeError("Invalid Reason-Phrase token production");
+ }
+ }
+
+ // step 3
+ var r = this;
+ r._response = new response();
+ r._headers = new Headers();
+
+ if (init !== undefined) {
+ // step 4
+ r._response.status = init.status;
+ // step 5
+ r._response.statusMessage = init.statusText;
+ }
+
+ // step 6
+ if (init.headers) {
+ // step 6-1
+ r._response.headerList = [];
+
+ // step 6-2
+ // TODO: implements fill
+ // r._response.headerList =;
+ }
+
+ // step 7
+ if (body) {
+ // step 7-1
+ var extracted = extract(body);
+ var stream = extracted.stream;
+ var contentType = extracted.contentType;
+
+ // step 7-2
+ r._response.body = stream;
+
+ // step 7-3
+ if (contentType !== null) {
+ var hasContentType = r._response.headerList.some((header) => {
+ return header.name === "Content-Type";
+ });
+
+ if (!hasContentType) {
+ // TODO: append
+ // r._response.headerList.append(Header("Content-Type", contentType));
+ }
+ }
+
+ // step 8
+ // TODO: extracting MIME type
+
+ // step 9
+ // TODO: TLS state
+
+ // step 10
+ return r;
+ }
+ }
+
+ get bodyUsed(): boolean {
+ return this._bodyUsed;
+ }
+
+ get type(): ResponseType {
+ return this._type;
+ }
+
+ get url(): USVString {
+ return this._url;
+ }
+
+ get status(): number {
+ return this._status;
+ }
+
+ get statusText(): ByteString {
+ return this._statusText;
+ }
+
+ get headers(): Headers {
+ return this._headers;
+ }
+
+ arrayBuffer(): Promise {
+ return null;
+ }
+
+ blob(): Promise {
+ return null;
+ }
+
+ formData(): Promise {
+ return null;
+ }
+
+ json(): Promise {
+ return null;
+ }
+
+ text(): Promise {
+ return null;
+ }
+}
+
+
+// https://fetch.spec.whatwg.org/#globalfetch
+// Window implements GlobalFetch;
+interface Window {
+ fetch(input: RequestInfo, init?: RequestInit): Promise;
+};
+
+// WorkerGlobalScope implements GlobalFetch;
+this.fetch = function(input: RequestInfo, init?: RequestInit): Promise {
+ // step 1
+ var p = new Promise((resolve, reject) => {
+ try {
+ // step 2
+ var r = (new Request(input, init)).request;
+ } catch(e) {
+ reject(e);
+ }
+ });
+
+ // step 4
+ return p
+}
+
+// WorkerGlobalScope implements GlobalFetch;
diff --git a/fetch/headers.ts b/fetch/headers.ts
new file mode 100644
index 000000000..33e3d2bf6
--- /dev/null
+++ b/fetch/headers.ts
@@ -0,0 +1,341 @@
+///
+
+// https://fetch.spec.whatwg.org/#forbidden-header-name
+var ForbiddenHeaderName = {
+ "Accept-Charset": "Accept-Charset",
+ "Accept-Encoding": "Accept-Encoding",
+ "Access-Control-Request-Headers": "Access-Control-Request-Headers",
+ "Access-Control-Request-Method": "Access-Control-Request-Method",
+ "Connection": "Connection",
+ "Content-Length": "Content-Length",
+ "Cookie": "Cookie",
+ "Cookie2": "Cookie2",
+ "Date": "Date",
+ "DNT": "DNT",
+ "Expect": "Expect",
+ "Host": "Host",
+ "Keep-Alive": "Keep-Alive",
+ "Origin": "Origin",
+ "Referer": "Referer",
+ "TE": "TE",
+ "Trailer": "Trailer",
+ "Transfer-Encoding": "Transfer-Encoding",
+ "Upgrade": "Upgrade",
+ "User-Agent": "User-Agent",
+ "Via": "Via"
+};
+function isForbiddenHeaderName(name: ByteString): boolean {
+ if (ForbiddenHeaderName[name] !== undefined) {
+ return true;
+ }
+ var reg = /^(Proxy\-|Sec\-)/
+ if (reg.exec(name)) {
+ return true;
+ }
+
+ return false;
+}
+
+// https://fetch.spec.whatwg.org/#forbidden-response-header-name
+var ForbiddenResponseHeaderName = {
+ "Set-Cookie": "Set-Cookie",
+ "Set-Cookie2": "Set-Cookie2"
+}
+function isForbiddenResponseHeaderName(name: ByteString): boolean {
+ return ForbiddenResponseHeaderName[name] !== undefined;
+}
+
+// https://fetch.spec.whatwg.org/#simple-header
+var SimpleHeaderName = {
+ "Accept": "Accept",
+ "Accept-Language": "Accept-Language",
+ "Content-Language": "Content-Language"
+}
+var SimpleHeaderValue = {
+ "application/x-www-form-urlencoded": "application/x-www-form-urlencoded",
+ "multipart/form-data": "multipart/form-data",
+ "text/plain": "text/plain"
+}
+function isSimpleHeader(name, value: ByteString): boolean {
+ if (SimpleHeaderName[name] !== undefined) {
+ return true;
+ }
+
+ if (name === "Content-Type") {
+ // TODO: parse value https://fetch.spec.whatwg.org/#concept-header-parse
+ if (SimpleHeaderValue[value] !== undefined) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// https://fetch.spec.whatwg.org/#headersinit
+// typedef (Headers or sequence> or OpenEndedDictionary) HeadersInit;
+type HeadersInit = Headers | ByteString[][] | OpenEndedDictionary;
+
+// https://fetch.spec.whatwg.org/#headers
+interface IHeaders {
+ append(name, value: ByteString): void;
+ delete(name: ByteString): void;
+ get(name: ByteString): ByteString;
+ getAll(name: ByteString): ByteString[];
+ has(name: ByteString): boolean;
+ set(name, value: ByteString): void;
+ // iterable;
+};
+
+// https://fetch.spec.whatwg.org/#concept-header
+class Header {
+ name: ByteString;
+ value: ByteString;
+ constructor(name, value: ByteString) {
+ // TODO: validation
+ this.name = name;
+ this.value = value;
+ }
+}
+
+var Guard = {
+ "immutable": "immutable",
+ "request": "request",
+ "request-no-CORS": "request-no-CORS",
+ "response": "response",
+ "none": "none",
+}
+
+function copy(obj: T): T {
+ return JSON.parse(JSON.stringify(obj));
+}
+
+// https://fetch.spec.whatwg.org/#headers
+class Headers implements IHeaders{
+ public headerList: Header[] = [];
+ public guard: string = Guard.none;
+
+ // https://fetch.spec.whatwg.org/#dom-headers
+ constructor(init?: HeadersInit) {
+ // step 1
+ var headers = this; // new Headers object
+
+ // step 2
+ if (init !== undefined) {
+ this.fill(headers, init);
+ }
+
+ // step 3
+ return headers;
+ }
+
+ // https://fetch.spec.whatwg.org/#concept-headers-fill
+ private fill(headers: Headers, object: HeadersInit) {
+ // step 1 Headers
+ if (object instanceof Headers) {
+ var headerListCopy: Header[] = copy(object.headerList);
+ headerListCopy.forEach((header: Header) => {
+ headers.append(header.name, header.value);
+ });
+ return;
+ }
+
+ // step 2 ByteString[][]
+ if (Array.isArray(object)) {
+ var headerSequence = object;
+ headerSequence.forEach((header) => {
+ if(header.length !== 2) {
+ throw new TypeError("init for Headers was incorrect BytesString Sequence");
+ }
+ headers.append(header[0], header[1]);
+ });
+ return;
+ }
+
+ // step 3 OpenEndedDictionary
+ if (typeof object === "object") {
+ Object.keys(object).forEach((key) => {
+ headers.append(key.toString(), object[key]);
+ });
+ }
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-headers-append
+ append(name, value: ByteString): void {
+ // https://fetch.spec.whatwg.org/#concept-headers-append
+ // step 1
+ if (!name || !value) {
+ // TODO name/value validation
+ throw new TypeError("invalid name/value");
+ }
+
+ // step 2, 3, 4, 5
+ switch(this.guard) {
+ case Guard.immutable:
+ throw new TypeError("operation to immutable headers");
+ case Guard.request:
+ if (isForbiddenHeaderName(name)) {
+ return;
+ }
+ case Guard["request-no-CORS"]:
+ if (!isSimpleHeader(name, value)) {
+ return;
+ }
+ case Guard.response:
+ if (isForbiddenResponseHeaderName(name)) {
+ return;
+ }
+ }
+
+ // step 6
+ name = name.toLowerCase();
+ this.headerList.push(new Header(name, value));
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-headers-delete
+ delete(name: ByteString): void {
+ // step 1
+ if (!name) {
+ throw new TypeError("invalid name");
+ }
+
+ // step 2, 3, 4, 5
+ switch(this.guard) {
+ case Guard.immutable:
+ throw new TypeError("operation to immutable headers");
+ case Guard.request:
+ if (isForbiddenHeaderName(name)) {
+ return;
+ }
+ case Guard["request-no-CORS"]:
+ if (!isSimpleHeader(name, "invalid")) {
+ return;
+ }
+ case Guard.response:
+ if (isForbiddenResponseHeaderName(name)) {
+ return;
+ }
+ }
+
+ name = name.toLowerCase();
+ // step 6
+ this.headerList = this.headerList.filter((header: Header) => {
+ return header.name !== name;
+ });
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-headers-get
+ get(name: ByteString) :ByteString {
+ // step 1
+ if (!name) {
+ throw new TypeError("invalid name");
+ }
+
+ // step 2
+ var value: ByteString = null;
+ this.headerList.forEach((header: Header) => {
+ if (header.name === name) {
+ value = header.value;
+ return;
+ }
+ });
+
+ return value;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-headers-getall
+ getAll(name: ByteString) :ByteString[] {
+ // step 1
+ if (!name) {
+ throw new TypeError("invalid name");
+ }
+
+ // step 2
+ var result: ByteString[] = this.headerList.reduce((acc: ByteString[], header: Header) => {
+ if (header.name === name) {
+ acc.push(header.value);
+ }
+ return acc;
+ }, []);
+
+ return result;
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-headers-has
+ has(name: ByteString) :boolean {
+ // step 1
+ if (!name) {
+ throw new TypeError("invalid name");
+ }
+
+ // step 2
+ return this.headerList.some((header: Header) => {
+ return header.name === name;
+ });
+ }
+
+ // https://fetch.spec.whatwg.org/#dom-headers-set
+ set(name, value: ByteString): void {
+ // step 1
+ if (!name || !value) {
+ throw new TypeError("invalid name/value");
+ }
+
+ switch(this.guard) {
+ // step 2
+ case Guard.immutable:
+ throw new TypeError("operation to immutable headers");
+ // step 3
+ case Guard.request:
+ if (isForbiddenHeaderName(name)) {
+ return;
+ }
+ // step 4
+ case Guard["request-no-CORS"]:
+ if (!isSimpleHeader(name, "invalid")) {
+ return;
+ }
+ // step 5
+ case Guard.response:
+ if (isForbiddenResponseHeaderName(name)) {
+ return;
+ }
+ }
+
+ // step 6
+ // see https://fetch.spec.whatwg.org/#concept-header-list-set
+
+ // step 6-1
+ name = name.toLowerCase();
+
+ // find the all indexes of headers whos key is supplyed key
+ var indexes: number[] = this.headerList.reduce((acc: number[], header: Header, index: number) => {
+ if (header.name === name) {
+ acc.push(index);
+ }
+ return acc;
+ }, []);
+
+ // count of existing headers
+ var len = indexes.length;
+
+ // step 6-3
+ // if there are no key
+ if (len === 0) {
+ // append to last and return
+ return this.append(name, value);
+ }
+
+ // step 6-2
+ // remove the headers in indexes from the last(because splice chenges index)
+ // and change first header value
+ indexes.reverse().forEach((e, i: number) => {
+ if(i === len - 1) {
+ // only replace first entry
+ this.headerList[e].value = value;
+ } else {
+ // remove duplicate from last
+ this.headerList.splice(e, 1);
+ }
+ });
+ }
+}
diff --git a/fetch/package.json b/fetch/package.json
new file mode 100644
index 000000000..f19e3419b
--- /dev/null
+++ b/fetch/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "fetch-standard",
+ "author": "Jxck",
+ "license": "MIT",
+ "version": "0.0.0",
+ "description": "implementaion of https://fetch.spec.whatwg.org/",
+ "homepage": "https://github.com/Jxck/fetch",
+ "bugs": {
+ "url": "https://github.com/Jxck/fetch/issues"
+ },
+ "keywords": [
+ "fetch",
+ "whatwg"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/Jxck/fetch"
+ },
+ "main": "fetch.ts",
+ "scripts": {
+ "clean": "rm *.js",
+ "build": "tsc fetch.ts --target ES5",
+ "test": "npm run build && node fetch.js"
+ },
+ "devDependencies": {
+ "typescript": "^1.4.1"
+ }
+}
diff --git a/fetch/webidl.d.ts b/fetch/webidl.d.ts
new file mode 100644
index 000000000..62bae8bca
--- /dev/null
+++ b/fetch/webidl.d.ts
@@ -0,0 +1,14 @@
+// http://heycam.github.io/webidl/#idl-ByteString
+declare type ByteString = string;
+
+// http://heycam.github.io/webidl/#idl-USVString
+declare type USVString = string;
+
+// http://heycam.github.io/webidl/#idl-DOMString
+declare type DOMString = string;
+
+// see: https://fetch.spec.whatwg.org/#headersinit
+declare type OpenEndedDictionary = Object;
+
+declare class FormData { }
+declare class Blob { }
\ No newline at end of file