mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
initial commit
This commit is contained in:
@@ -288,6 +288,10 @@
|
||||
</TypeScriptCompile>
|
||||
<TypeScriptCompile Include="es-collections.d.ts" />
|
||||
<TypeScriptCompile Include="es6-promise.d.ts" />
|
||||
<TypeScriptCompile Include="fetch\body.ts" />
|
||||
<TypeScriptCompile Include="fetch\fetch.ts" />
|
||||
<TypeScriptCompile Include="fetch\headers.ts" />
|
||||
<TypeScriptCompile Include="fetch\webidl.d.ts" />
|
||||
<TypeScriptCompile Include="file-system\file-name-resolver.d.ts" />
|
||||
<TypeScriptCompile Include="file-system\file-name-resolver.ts">
|
||||
<DependentUpon>file-name-resolver.d.ts</DependentUpon>
|
||||
@@ -1670,6 +1674,8 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="apps\action-bar-demo\package.json" />
|
||||
<Content Include="fetch\package.json" />
|
||||
<Content Include="fetch\README.md" />
|
||||
<None Include="js-libs\esprima\LICENSE.BSD" />
|
||||
<Content Include="source-control.md" />
|
||||
<Content Include="ui\segmented-bar\package.json">
|
||||
|
||||
17
fetch/README.md
Normal file
17
fetch/README.md
Normal file
@@ -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
|
||||
100
fetch/body.ts
Normal file
100
fetch/body.ts
Normal file
@@ -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<ArrayBuffer>;
|
||||
blob(): Promise<Blob>;
|
||||
formData(): Promise<FormData>;
|
||||
json(): Promise<JSON>;
|
||||
text(): Promise<USVString>;
|
||||
};
|
||||
|
||||
// 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<ArrayBuffer> {
|
||||
return this.consumeBody("ArrayBuffer");
|
||||
}
|
||||
|
||||
blob(): Promise<Blob> {
|
||||
return this.consumeBody("Blob");
|
||||
}
|
||||
|
||||
formData(): Promise<FormData> {
|
||||
return this.consumeBody("FormData");
|
||||
}
|
||||
|
||||
json(): Promise<JSON> {
|
||||
return this.consumeBody("JSON");
|
||||
}
|
||||
|
||||
text(): Promise<USVString> {
|
||||
return this.consumeBody("text");
|
||||
}
|
||||
}
|
||||
677
fetch/fetch.ts
Normal file
677
fetch/fetch.ts
Normal file
@@ -0,0 +1,677 @@
|
||||
/// <reference path="webidl.d.ts" />
|
||||
/// <reference path="headers.ts" />
|
||||
/// <reference path="body.ts" />
|
||||
|
||||
// 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<ArrayBuffer> {
|
||||
return null;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#dom-body-blob
|
||||
public blob(): Promise<Blob> {
|
||||
return null;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#dom-body-formdata
|
||||
public formData(): Promise<FormData> {
|
||||
return null;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#dom-body-json
|
||||
public json(): Promise<JSON> {
|
||||
return null;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#dom-body-text
|
||||
public text(): Promise<USVString> {
|
||||
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 = <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<ArrayBuffer> {
|
||||
return null;
|
||||
}
|
||||
|
||||
blob(): Promise<Blob> {
|
||||
return null;
|
||||
}
|
||||
|
||||
formData(): Promise<FormData> {
|
||||
return null;
|
||||
}
|
||||
|
||||
json(): Promise<JSON> {
|
||||
return null;
|
||||
}
|
||||
|
||||
text(): Promise<USVString> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// https://fetch.spec.whatwg.org/#globalfetch
|
||||
// Window implements GlobalFetch;
|
||||
interface Window {
|
||||
fetch(input: RequestInfo, init?: RequestInit): Promise<IResponse>;
|
||||
};
|
||||
|
||||
// WorkerGlobalScope implements GlobalFetch;
|
||||
this.fetch = function(input: RequestInfo, init?: RequestInit): Promise<IResponse> {
|
||||
// step 1
|
||||
var p = new Promise<IResponse>((resolve, reject) => {
|
||||
try {
|
||||
// step 2
|
||||
var r = (new Request(input, init)).request;
|
||||
} catch(e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
|
||||
// step 4
|
||||
return p
|
||||
}
|
||||
|
||||
// WorkerGlobalScope implements GlobalFetch;
|
||||
341
fetch/headers.ts
Normal file
341
fetch/headers.ts
Normal file
@@ -0,0 +1,341 @@
|
||||
/// <reference path="webidl.d.ts" />
|
||||
|
||||
// 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<sequence<ByteString>> or OpenEndedDictionary<ByteString>) 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<ByteString, ByteString>;
|
||||
};
|
||||
|
||||
// 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<T>(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 = <ByteString[][]> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
28
fetch/package.json
Normal file
28
fetch/package.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
14
fetch/webidl.d.ts
vendored
Normal file
14
fetch/webidl.d.ts
vendored
Normal file
@@ -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 { }
|
||||
Reference in New Issue
Block a user