///
///
///
// 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;