Merge domain-task repo into this one

This commit is contained in:
SteveSandersonMS
2016-02-09 10:39:48 -08:00
parent 32349fa85b
commit 047d14a6cd
15 changed files with 289 additions and 10 deletions

View File

@@ -1,10 +0,0 @@
// TODO: Move this on to definitelytyped, and take a dependency on whatwg-fetch
// so that the 'fetch' function can have the correct type args
declare module 'domain-task' {
function addTask(task: PromiseLike<any>): void;
}
declare module 'domain-task/fetch' {
function fetch(url, options?): Promise<any>;
}

View File

@@ -37,6 +37,9 @@
},
"redux-thunk/redux-thunk.d.ts": {
"commit": "e69fe60f2d6377ea4fae539493997b098f52cad1"
},
"whatwg-fetch/whatwg-fetch.d.ts": {
"commit": "f4b1797c1201b6c575668f5d7ea12d9b1ab21846"
}
}
}

View File

@@ -9,3 +9,4 @@
/// <reference path="react-router-bootstrap/react-router-bootstrap.d.ts" />
/// <reference path="react-router-redux/react-router-redux.d.ts" />
/// <reference path="redux-thunk/redux-thunk.d.ts" />
/// <reference path="whatwg-fetch/whatwg-fetch.d.ts" />

View File

@@ -0,0 +1,85 @@
// Type definitions for fetch API
// Project: https://github.com/github/fetch
// Definitions by: Ryan Graham <https://github.com/ryan-codingintrigue>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
declare class Request extends Body {
constructor(input: string|Request, init?:RequestInit);
method: string;
url: string;
headers: Headers;
context: string|RequestContext;
referrer: string;
mode: string|RequestMode;
credentials: string|RequestCredentials;
cache: string|RequestCache;
}
interface RequestInit {
method?: string;
headers?: HeaderInit|{ [index: string]: string };
body?: BodyInit;
mode?: string|RequestMode;
credentials?: string|RequestCredentials;
cache?: string|RequestCache;
}
declare enum RequestContext {
"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"
}
declare enum RequestMode { "same-origin", "no-cors", "cors" }
declare enum RequestCredentials { "omit", "same-origin", "include" }
declare enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached" }
declare class Headers {
append(name: string, value: string): void;
delete(name: string):void;
get(name: string): string;
getAll(name: string): Array<string>;
has(name: string): boolean;
set(name: string, value: string): void;
}
declare class Body {
bodyUsed: boolean;
arrayBuffer(): Promise<ArrayBuffer>;
blob(): Promise<Blob>;
formData(): Promise<FormData>;
json(): Promise<any>;
json<T>(): Promise<T>;
text(): Promise<string>;
}
declare class Response extends Body {
constructor(body?: BodyInit, init?: ResponseInit);
error(): Response;
redirect(url: string, status: number): Response;
type: string|ResponseType;
url: string;
status: number;
ok: boolean;
statusText: string;
headers: Headers;
clone(): Response;
}
declare enum ResponseType { "basic", "cors", "default", "error", "opaque" }
interface ResponseInit {
status: number;
statusText?: string;
headers?: HeaderInit;
}
declare type HeaderInit = Headers|Array<string>;
declare type BodyInit = Blob|FormData|string;
declare type RequestInfo = Request|string;
interface Window {
fetch(url: string|Request, init?: RequestInit): Promise<Response>;
}
declare var fetch: typeof window.fetch;

View File

@@ -0,0 +1,4 @@
/typings/
/node_modules/
/*.js
/*.d.ts

View File

@@ -0,0 +1,3 @@
!/*.js
!/*.d.ts
/typings/

View File

@@ -0,0 +1,12 @@
Copyright (c) .NET Foundation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
these files except in compliance with the License. You may obtain a copy of the
License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

View File

@@ -0,0 +1 @@
TODO

View File

@@ -0,0 +1,16 @@
{
"name": "domain-task",
"version": "1.0.0",
"description": "Tracks outstanding operations for a logical thread of execution",
"main": "main.js",
"scripts": {
"prepublish": "tsd update && tsc && echo 'Finished building NPM package \"domain-task\"'",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Microsoft",
"license": "Apache-2.0",
"dependencies": {
"domain-context": "^0.5.1",
"isomorphic-fetch": "^2.2.1"
}
}

View File

@@ -0,0 +1,9 @@
declare module 'domain' {
var active: Domain;
}
declare module 'domain-context' {
function get(key: string): any;
function set(key: string, value: any): void;
function runInNewDomain(code: () => void): void;
}

View File

@@ -0,0 +1,62 @@
import * as url from 'url';
import * as domain from 'domain';
import * as domainContext from 'domain-context';
import { addTask } from './main';
const isomorphicFetch = require('isomorphic-fetch');
const isBrowser: boolean = (new Function('try { return this === window; } catch (e) { return false; }'))();
// Not using a symbol, because this may need to run in a version of Node.js that doesn't support them
const domainTaskStateKey = '__DOMAIN_TASK_INTERNAL_FETCH_BASEURL__DO_NOT_REFERENCE_THIS__';
let noDomainBaseUrl: string;
function issueRequest(baseUrl: string, req: string | Request, init?: RequestInit): Promise<any> {
// Resolve relative URLs
if (baseUrl) {
if (req instanceof Request) {
const reqAsRequest = req as Request;
reqAsRequest.url = url.resolve(baseUrl, reqAsRequest.url);
} else {
req = url.resolve(baseUrl, req as string);
}
} else if (!isBrowser) {
// TODO: Consider only throwing if it's a relative URL, since absolute ones would work fine
throw new Error(`
When running outside the browser (e.g., in Node.js), you must specify a base URL
before invoking domain-task's 'fetch' wrapper.
Example:
import { baseUrl } from 'domain-task/fetch';
baseUrl('http://example.com'); // Relative URLs will be resolved against this
`);
}
// Currently, some part of ASP.NET (perhaps just Kestrel on Mac - unconfirmed) doesn't complete
// its responses if we send 'Connection: close', which is the default. So if no 'Connection' header
// has been specified explicitly, use 'Connection: keep-alive'.
init = init || {};
init.headers = init.headers || {};
if (!init.headers['Connection']) {
init.headers['Connection'] = 'keep-alive';
}
return isomorphicFetch(req, init);
}
export function fetch(url: string | Request, init?: RequestInit): Promise<any> {
const promise = issueRequest(baseUrl(), url, init);
addTask(promise);
return promise;
}
export function baseUrl(url?: string): string {
if (url) {
if (domain.active) {
// There's an active domain (e.g., in Node.js), so associate the base URL with it
domainContext.set(domainTaskStateKey, url);
} else {
// There's no active domain (e.g., in browser), so there's just one shared base URL
noDomainBaseUrl = url;
}
}
return domain.active ? domainContext.get(domainTaskStateKey) : noDomainBaseUrl;
}

View File

@@ -0,0 +1,4 @@
declare module 'isomorphic-fetch' {
var fetch: (url: string | Request, init?: RequestInit) => Promise<any>;
export default fetch;
}

View File

@@ -0,0 +1,59 @@
import * as domain from 'domain';
import * as domainContext from 'domain-context';
const domainTasksStateKey = '__DOMAIN_TASKS';
export function addTask(task: PromiseLike<any>) {
if (task && domain.active) {
const state = domainContext.get(domainTasksStateKey) as DomainTasksState;
if (state) {
state.numRemainingTasks++;
task.then(() => {
// The application may have other listeners chained to this promise *after*
// this listener. Since we don't want the combined task to complete until
// all the handlers for child tasks have finished, delay the following by
// one tick.
setTimeout(() => {
state.numRemainingTasks--;
if (state.numRemainingTasks === 0 && !state.hasIssuedSuccessCallback) {
state.hasIssuedSuccessCallback = true;
state.completionCallback(/* error */ null);
}
}, 0);
}, (error) => {
state.completionCallback(error);
});
}
}
}
export function run<T>(codeToRun: () => T, completionCallback: (error: any) => void): T {
let synchronousResult: T;
domainContext.runInNewDomain(() => {
const state: DomainTasksState = {
numRemainingTasks: 0,
hasIssuedSuccessCallback: false,
completionCallback: domain.active.bind(completionCallback)
};
try {
domainContext.set(domainTasksStateKey, state);
synchronousResult = codeToRun();
// If no tasks were registered synchronously, then we're done already
if (state.numRemainingTasks === 0 && !state.hasIssuedSuccessCallback) {
state.hasIssuedSuccessCallback = true;
state.completionCallback(/* error */ null);
}
} catch(ex) {
state.completionCallback(ex);
}
});
return synchronousResult;
}
interface DomainTasksState {
numRemainingTasks: number;
hasIssuedSuccessCallback: boolean;
completionCallback: (error: any) => void;
}

View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"moduleResolution": "node",
"module": "commonjs",
"target": "es5",
"declaration": true,
"outDir": "."
},
"exclude": [
"node_modules"
]
}

View File

@@ -0,0 +1,18 @@
{
"version": "v4",
"repo": "borisyankov/DefinitelyTyped",
"ref": "master",
"path": "typings",
"bundle": "typings/tsd.d.ts",
"installed": {
"node/node.d.ts": {
"commit": "3030a4be536b6530c06b80081f1333dc0de4d703"
},
"es6-promise/es6-promise.d.ts": {
"commit": "3030a4be536b6530c06b80081f1333dc0de4d703"
},
"whatwg-fetch/whatwg-fetch.d.ts": {
"commit": "3030a4be536b6530c06b80081f1333dc0de4d703"
}
}
}