├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── examples ├── browser-require │ ├── README.md │ ├── index.html │ ├── index.js │ └── main.py ├── browser │ ├── README.md │ ├── index.html │ ├── index.ts │ ├── main.py │ ├── package.json │ ├── tsconfig.json │ └── webpack.config.js └── node │ ├── README.md │ ├── index.js │ ├── main.py │ └── package.json ├── package.json ├── scripts ├── travis_after_success.sh ├── travis_install.sh └── travis_script.sh ├── src ├── config │ └── index.ts ├── contents │ ├── index.ts │ └── validate.ts ├── index.ts ├── kernel │ ├── comm.ts │ ├── default.ts │ ├── future.ts │ ├── index.ts │ ├── kernel.ts │ ├── manager.ts │ ├── messages.ts │ ├── serialize.ts │ └── validate.ts ├── manager.ts ├── nbformat.ts ├── session │ ├── default.ts │ ├── index.ts │ ├── manager.ts │ ├── session.ts │ └── validate.ts ├── terminal │ ├── default.ts │ ├── index.ts │ ├── manager.ts │ └── terminal.ts ├── tsconfig.json ├── typedoc.d.ts ├── typings.d.ts └── utils.ts ├── test ├── integration_test.py └── src │ ├── config │ └── config.spec.ts │ ├── contents │ ├── index.spec.ts │ └── validate.spec.ts │ ├── integration.ts │ ├── kernel │ ├── comm.spec.ts │ ├── kernel.spec.ts │ ├── manager.spec.ts │ ├── messages.spec.ts │ └── validate.spec.ts │ ├── manager.spec.ts │ ├── mockxhr.ts │ ├── nbformat.spec.ts │ ├── session │ ├── manager.spec.ts │ ├── session.spec.ts │ └── validate.spec.ts │ ├── target.ts │ ├── terminal │ ├── manager.spec.ts │ └── terminal.spec.ts │ ├── tsconfig.json │ ├── typings.d.ts │ ├── utils.spec.ts │ └── utils.ts ├── typedoc.json ├── typings ├── path-posix │ └── path-posix.d.ts ├── url-parse │ └── url-parse.d.ts └── xmlhttprequest │ └── xmlhttprequest.d.ts └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | *.user 3 | .DS_Store 4 | node_modules 5 | typings/tsd.d.ts 6 | npm-debug.log 7 | test/build 8 | test/coverage 9 | lib 10 | dist 11 | build 12 | docs 13 | example/*.js 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.12' 4 | - '5.1' 5 | sudo: false 6 | env: 7 | matrix: 8 | - GROUP= 9 | global: 10 | - GH_REF: github.com/jupyterlab/services.git 11 | - secure: VksMSktfsYS7q60EhjFK+acMlRSgDbMYZ67djRy6Gu17C8e3fBpXgZHT1d9RB9eN1GGji3l889ocUOmvcR5bD/0GgidPwTwQpZnDsrTX5O7TbinKP8K228V/hGfPWhEC5lIViRGISzZkxrQmEA+TIcIqqPY6rRCoXMGzV/EVHztFFLzTeAhLPg/9cp1U24ZlLpz2n/a+rIaOASj9KVLDwwj3gGduzrwszP4idZlkFd6pgvSUn1J/YL+9IseI2+H0G/9ybEmPWPSOdOkrK6yeg7KpKaF9PDoxyik3vAnWfdO6JV/AsyWc2zcZmobf2pJ/m0W8hvsUlFsg0fBzOX6rf7U78orahPt+1o5+9M2mI48G5itazbXeVNi/7hedJM0Zj0g9sEQsnxzCMDcFEUVF8tqd2fShDdgEFAaCTVn2DMGk+kSCjcfXHj0OZs51IHMVZdihlliwYaPxaQrbQYvf0E7RyeDs512RSc5k3ivd/OjwIdFVoL0u4e+aJL3YVGTY4qA51K0wTi++IPVqbnZ7R1GnmtBiTD5Yl1KpyGqdW3fvH4YISAXT8t+O5Bze6AnaNd/zRUsiUQfq4rDabptA1u5P6lEzgnwthKSnh2OLDlMV+XVw92gOfRTTPlL4In8hWoytPJWWQBNaHu1sY7f24TGudX/ERwddkSJzBzYbqb0= 12 | install: 13 | - bash ./scripts/travis_install.sh 14 | script: 15 | - bash ./scripts/travis_script.sh 16 | after_success: 17 | - bash ./scripts/travis_after_success.sh 18 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We follow the [IPython Contributing Guide](https://github.com/ipython/ipython/blob/master/CONTRIBUTING.md). 4 | 5 | All source code is written in [TypeScript](http://www.typescriptlang.org/Handbook). See the [Style Guide](https://github.com/phosphorjs/phosphor/wiki/Style-Guide). 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Project Jupyter 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of jupyter-js-services nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JupyterLab Services 2 | =================== 3 | 4 | Javascript client for the Jupyter services REST APIs 5 | 6 | [API Docs](http://jupyterlab.github.io/services/) 7 | 8 | [REST API Docs](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml) 9 | 10 | Note: All functions and classes using the REST API allow an `ajaxOptions` 11 | parameter to configure requests. 12 | 13 | 14 | Package Install 15 | --------------- 16 | 17 | **Prerequisites** 18 | - [node](http://nodejs.org/) 19 | - [python](https://www.continuum.io/downloads) 20 | 21 | ```bash 22 | npm install --save @jupyterlab/services 23 | conda install notebook # notebook 4.2+ required 24 | ``` 25 | 26 | 27 | Source Build 28 | ------------ 29 | 30 | **Prerequisites** 31 | - [git](http://git-scm.com/) 32 | - [node 0.12+](http://nodejs.org/) 33 | - [python](https://www.continuum.io/downloads) 34 | 35 | ```bash 36 | git clone https://github.com/jupyterlab/services.git 37 | cd services 38 | npm install 39 | npm run build 40 | conda install notebook # notebook 4.2+ required 41 | ``` 42 | 43 | **Rebuild** 44 | ```bash 45 | npm run clean 46 | npm run build 47 | ``` 48 | 49 | 50 | Run Tests 51 | --------- 52 | 53 | Follow the source build instructions first. 54 | 55 | ```bash 56 | npm test 57 | ``` 58 | 59 | 60 | Build Docs 61 | ---------- 62 | 63 | Follow the source build instructions first. 64 | 65 | ```bash 66 | npm run docs 67 | ``` 68 | 69 | Navigate to `docs/index.html`. 70 | 71 | 72 | Supported Runtimes 73 | ------------------ 74 | 75 | The runtime versions which are currently *known to work* are listed below. 76 | Earlier versions may also work, but come with no guarantees. 77 | 78 | - Node 0.12.7+ 79 | - IE 11+ 80 | - Firefox 32+ 81 | - Chrome 38+ 82 | 83 | Note: "requirejs" must be included in a global context for Comm targets. 84 | This can be as a ` 6 | 11 | 12 | 13 | 14 |

Run code!

15 |

16 | Type code in the text area and click run to execute it. 17 | You will see a log of the messages produced by the kernel below. 18 |

19 |
20 | Kernel: 21 |
Starting...
22 |
23 | 24 |
25 | 26 |
27 |
28 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /examples/browser-require/index.js: -------------------------------------------------------------------------------- 1 | 2 | require(['jquery', '@jupyterlab/services'], function ($, services) { 3 | 'use strict'; 4 | var startNewKernel = services.Kernel.startNew; 5 | 6 | var kernelOptions = { 7 | name: 'python', 8 | }; 9 | 10 | // start a single kernel for the page 11 | startNewKernel(kernelOptions).then(function (kernel) { 12 | console.log('Kernel started:', kernel); 13 | kernel.requestKernelInfo().then(function (reply) { 14 | var content = reply.content; 15 | $('#kernel-info').text(content.banner); 16 | console.log('Kernel info:', content); 17 | }) 18 | $('#run').click(function () { 19 | var code = $('#cell').val(); 20 | console.log('Executing:', code); 21 | // clear output 22 | $('#output').text(''); 23 | // Execute and handle replies on the kernel. 24 | var future = kernel.requestExecute({ code: code }); 25 | // record each IOPub message 26 | future.onIOPub = function (msg) { 27 | console.log('Got IOPub:', msg); 28 | $('#output').append( 29 | $('
').text('msg_type: ' + msg.header.msg_type)
30 |         );
31 |         $('#output').append(
32 |           $('
').text(JSON.stringify(msg.content))
33 |         );
34 |       };
35 | 
36 |       future.onReply = function (reply) {
37 |         console.log('Got execute reply');
38 |       };
39 | 
40 |       future.onDone = function () {
41 |         console.log('Future is fulfilled');
42 |         $('#output').append($('
').text('Done!'));
43 |       };
44 |     });
45 |   });
46 | });
47 | 


--------------------------------------------------------------------------------
/examples/browser-require/main.py:
--------------------------------------------------------------------------------
 1 | """
 2 | Copyright (c) Jupyter Development Team.
 3 | Distributed under the terms of the Modified BSD License.
 4 | """
 5 | from notebook.notebookapp import NotebookApp
 6 | import os
 7 | from jinja2 import FileSystemLoader
 8 | from notebook.base.handlers import IPythonHandler, FileFindHandler
 9 | from traitlets import Unicode
10 | 
11 | 
12 | HERE = os.path.dirname(__file__)
13 | LOADER = FileSystemLoader(HERE)
14 | 
15 | 
16 | class ExampleHander(IPythonHandler):
17 |     """Handle requests between the main app page and notebook server."""
18 | 
19 |     def get(self):
20 |         """Get the main page for the application's interface."""
21 |         return self.write(self.render_template("index.html",
22 |             static=self.static_url, base_url=self.base_url,
23 |             token=self.settings['token']))
24 | 
25 |     def get_template(self, name):
26 |         return LOADER.load(self.settings['jinja2_env'], name)
27 | 
28 | 
29 | class ExampleApp(NotebookApp):
30 |     """A notebook app that runs the example."""
31 | 
32 |     default_url = Unicode('/example')
33 | 
34 |     def start(self):
35 |         handlers = [
36 |             (r'/example/?', ExampleHander),
37 |             (r"/example/(.*)", FileFindHandler,
38 |                 {'path': HERE}),
39 |         ]
40 |         self.web_app.add_handlers(".*$", handlers)
41 |         super(ExampleApp, self).start()
42 | 
43 | 
44 | if __name__ == '__main__':
45 |     ExampleApp.launch_instance()
46 | 


--------------------------------------------------------------------------------
/examples/browser/README.md:
--------------------------------------------------------------------------------
 1 | Jupyter JS Services Browser Example
 2 | ===================================
 3 | 
 4 | This example demonstrates using Jupyter JS Services from the browser using
 5 | Webpack. The python script `main.py` is used to start a Jupyter Notebook Server
 6 | and serve the Webpack bundle.  
 7 | 
 8 | The base url of the notebook server is to the HTML template as part of a JSON
 9 | script tag.  The script starts a python notebook session and interacts
10 | with it, printing messages to the browser console.
11 | 
12 | The example can be installed as `npm install` and run as `python main.py`.
13 | 
14 | Notes: 
15 | - The example is written in *TypeScript* using *ES6* syntax.  
16 | - The TypeScript compiler config is in `tsconfig.json`.  
17 | - A typings file and a polyfill are required for ES6 promises.  
18 | - The example requires version 4.1+ of the Jupyter Notebook.
19 | - This example `require`s its dependencies from the root directory of the
20 | repo, but typically the requirements would be directly in `package.json`
21 | 


--------------------------------------------------------------------------------
/examples/browser/index.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |   
 4 |     Jupyter Services Demo
 5 |     
 6 |   
 7 |   
 8 |     
 9 |     
10 |     

11 |   
12 | 
13 | 


--------------------------------------------------------------------------------
/examples/browser/index.ts:
--------------------------------------------------------------------------------
 1 | /*-----------------------------------------------------------------------------
 2 | | Copyright (c) 2014-2015, Jupyter Development Team.
 3 | |
 4 | | Distributed under the terms of the Modified BSD License.
 5 | |----------------------------------------------------------------------------*/
 6 | 'use strict';
 7 | 
 8 | // Polyfill for ES6 Promises
 9 | import 'es6-promise';
10 | 
11 | import {
12 |   Session, utils
13 | } from '@jupyterlab/services';
14 | 
15 | 
16 | function log(text: string): void {
17 |   let el = document.getElementById('output');
18 |   el.textContent = el.textContent + '\n' + text;
19 |   console.log(text);
20 | }
21 | 
22 | function main() {
23 |   // Start a new session.
24 |   let options: Session.IOptions = {
25 |     kernelName: 'python',
26 |     path: 'foo.ipynb'
27 |   };
28 |   let session: Session.ISession;
29 | 
30 |   log('Starting session');
31 |   Session.startNew(options).then(s => {
32 |     log('Session started');
33 |     session = s;
34 |     // Rename the session.
35 |     return session.rename('bar.ipynb');
36 |   }).then(() => {
37 |     log(`Session renamed to ${session.path}`);
38 |     // Execute and handle replies on the kernel.
39 |     let future = session.kernel.requestExecute({ code: 'a = 1' });
40 |     future.onReply = (reply) => {
41 |       log('Got execute reply');
42 |     };
43 |     future.onDone = () => {
44 |       log('Future is fulfilled');
45 |       // Shut down the session.
46 |       session.shutdown().then(() => {
47 |         log('Session shut down');
48 |         log('Test Complete!');
49 |       });
50 |     };
51 |   }).catch(err => {
52 |     console.error(err);
53 |     log('Test Failed! See the console output for details');
54 |   });
55 | }
56 | 
57 | window.onload = main;
58 | 


--------------------------------------------------------------------------------
/examples/browser/main.py:
--------------------------------------------------------------------------------
 1 | """
 2 | Copyright (c) Jupyter Development Team.
 3 | Distributed under the terms of the Modified BSD License.
 4 | """
 5 | from notebook.notebookapp import NotebookApp
 6 | import os
 7 | from jinja2 import FileSystemLoader
 8 | from notebook.base.handlers import IPythonHandler, FileFindHandler
 9 | from traitlets import Unicode
10 | 
11 | 
12 | HERE = os.path.dirname(__file__)
13 | LOADER = FileSystemLoader(HERE)
14 | 
15 | 
16 | class ExampleHander(IPythonHandler):
17 |     """Handle requests between the main app page and notebook server."""
18 | 
19 |     def get(self):
20 |         """Get the main page for the application's interface."""
21 |         return self.write(self.render_template("index.html",
22 |             static=self.static_url, base_url=self.base_url,
23 |             token=self.settings['token']))
24 | 
25 |     def get_template(self, name):
26 |         return LOADER.load(self.settings['jinja2_env'], name)
27 | 
28 | 
29 | class ExampleApp(NotebookApp):
30 |     """A notebook app that runs the example."""
31 | 
32 |     default_url = Unicode('/example')
33 | 
34 |     def start(self):
35 |         handlers = [
36 |             (r'/example/?', ExampleHander),
37 |             (r"/example/(.*)", FileFindHandler,
38 |                 {'path': os.path.join(HERE, 'build')}),
39 |         ]
40 |         self.web_app.add_handlers(".*$", handlers)
41 |         super(ExampleApp, self).start()
42 | 
43 | 
44 | if __name__ == '__main__':
45 |     ExampleApp.launch_instance()
46 | 


--------------------------------------------------------------------------------
/examples/browser/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "private": true,
 3 |   "name": "browser-example",
 4 |   "dependencies": {
 5 |     "es6-promise": "^3.2.1",
 6 |     "@jupyterlab/services": "file:../.."
 7 |   },
 8 |   "scripts": {
 9 |     "build": "tsc && webpack",
10 |     "clean": "rimraf build && rimraf node_modules",
11 |     "prepublish": "npm run build",
12 |     "update": "rimraf node_modules/@jupyterlab/services && npm install"
13 |   },
14 |   "devDependencies": {
15 |     "rimraf": "^2.5.2",
16 |     "webpack": "^1.12.14"
17 |   }
18 | }
19 | 


--------------------------------------------------------------------------------
/examples/browser/tsconfig.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "compilerOptions": {
 3 |     "noImplicitAny": true,
 4 |     "noEmitOnError": true,
 5 |     "module": "commonjs",
 6 |     "moduleResolution": "node",
 7 |     "target": "ES5",
 8 |     "lib": ["dom", "es5", "es2015.collection", "es2015.promise"],
 9 |     "types": [],
10 |     "outDir": "build"
11 |   }
12 | }
13 | 


--------------------------------------------------------------------------------
/examples/browser/webpack.config.js:
--------------------------------------------------------------------------------
1 | 
2 | module.exports = {
3 |   entry: './build/index.js',
4 |   output: {
5 |     filename: './build/bundle.js'
6 |   },
7 |   debug: true
8 | }
9 | 


--------------------------------------------------------------------------------
/examples/node/README.md:
--------------------------------------------------------------------------------
 1 | Jupyter JS Services Node Example
 2 | ================================
 3 | 
 4 | This example demonstrates using Jupyter JS Services from node. The python script `main.py` is used to start a Jupyter Notebook Server
 5 | and run the node script.
 6 | 
 7 | The base url of the notebook server is passed as a command line argument
 8 | to the node script.  The script starts a python notebook session and interacts
 9 | with it.
10 | 
11 | The example can be installed as `npm install` and run as `python main.py`.
12 | 
13 | 
14 | Notes: 
15 | - The example is written using *ES5* syntax.  
16 | - The example requires version 4.1+ of the Jupyter Notebook.
17 | - This example `require`s its dependencies from the root directory of the
18 | repo, but typically the requirements would be directly in `package.json`
19 | 


--------------------------------------------------------------------------------
/examples/node/index.js:
--------------------------------------------------------------------------------
 1 | /*-----------------------------------------------------------------------------
 2 | | Copyright (c) 2014-2015, Jupyter Development Team.
 3 | |
 4 | | Distributed under the terms of the Modified BSD License.
 5 | |----------------------------------------------------------------------------*/
 6 | 'use strict';
 7 | 
 8 | var services = require('@jupyterlab/services');
 9 | var ws = require('ws');
10 | var xhr = require('xmlhttprequest');
11 | 
12 | 
13 | // Override the global request and socket functions.
14 | global.XMLHttpRequest = xhr.XMLHttpRequest;
15 | global.WebSocket = ws;
16 | 
17 | 
18 | // Start a new session.
19 | var options = {
20 |   kernelName: 'python',
21 |   path: 'foo.ipynb'
22 | }
23 | 
24 | 
25 | var session;
26 | 
27 | 
28 | services.Session.startNew(options).then(function(s) {
29 |   // Rename the session.
30 |   session = s;
31 |   return session.rename('bar.ipynb');
32 | }).then(function() {
33 |   console.log('Session renamed to', session.path);
34 |   // Execute and handle replies on the kernel.
35 |   var future = session.kernel.requestExecute({ code: 'a = 1' });
36 |   future.onReply = function(reply) {
37 |     console.log('Got execute reply');
38 |   }
39 |   future.onDone = function() {
40 |     console.log('Future is fulfilled');
41 |     // Shut down the session.
42 |     session.shutdown().then(function() {
43 |       console.log('Session shut down');
44 |       process.exit(0);
45 |     });
46 |   };
47 | }).catch(function(err) {
48 |   console.error(err);
49 |   process.exit(1);
50 | })
51 | 
52 | 


--------------------------------------------------------------------------------
/examples/node/main.py:
--------------------------------------------------------------------------------
 1 | # Copyright (c) Jupyter Development Team.
 2 | # Distributed under the terms of the Modified BSD License.
 3 | 
 4 | from __future__ import print_function, absolute_import
 5 | 
 6 | import atexit
 7 | import shutil
 8 | import subprocess
 9 | import sys
10 | import tempfile
11 | from multiprocessing.pool import ThreadPool
12 | 
13 | 
14 | from tornado import ioloop
15 | from notebook.notebookapp import NotebookApp
16 | from traitlets import Bool, Unicode
17 | 
18 | root_dir = tempfile.mkdtemp(prefix='mock_contents')
19 | atexit.register(lambda: shutil.rmtree(root_dir, True))
20 | 
21 | 
22 | def run_task(func, args=(), kwds={}):
23 |     """Run a task in a thread and exit with the return code."""
24 |     loop = ioloop.IOLoop.instance()
25 |     worker = ThreadPool(1)
26 | 
27 |     def callback(result):
28 |         loop.add_callback(lambda: sys.exit(result))
29 | 
30 |     def start():
31 |         worker.apply_async(func, args, kwds, callback)
32 | 
33 |     loop.call_later(1, start)
34 | 
35 | 
36 | class TestApp(NotebookApp):
37 | 
38 |     open_browser = Bool(False)
39 |     notebook_dir = Unicode(root_dir)
40 | 
41 |     def start(self):
42 |         run_task(run_node, args=(self.connection_url, self.token))
43 |         super(TestApp, self).start()
44 | 
45 | 
46 | def run_node(base_url, token):
47 |     # Run the node script with command arguments.
48 |     node_command = ['node', 'index.js', '--baseUrl', base_url]
49 |     if token:
50 |         node_command.append('--token=%s' % token)
51 | 
52 |     print('*' * 60)
53 |     print(' '.join(node_command))
54 |     return subprocess.check_call(node_command)
55 | 
56 | 
57 | if __name__ == '__main__':
58 |     TestApp.launch_instance()
59 | 


--------------------------------------------------------------------------------
/examples/node/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "private": true,
 3 |   "name": "node-example",
 4 |   "dependencies": {
 5 |     "@jupyterlab/services": "file:../..",
 6 |     "requirejs": "^2.2.0",
 7 |     "ws": "^1.1.1",
 8 |     "xmlhttprequest": "^1.8.0"
 9 |   },
10 |   "scripts": {
11 |     "clean": "rimraf node_modules",
12 |     "update": "rimraf node_modules/@jupyterlab/services && npm install"
13 |   },
14 |   "devDependencies": {
15 |     "rimraf": "^2.5.2"
16 |   }
17 | }
18 | 


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "@jupyterlab/services",
 3 |   "version": "0.40.3",
 4 |   "description": "Client APIs for the Jupyter services REST APIs",
 5 |   "main": "lib/index.js",
 6 |   "typings": "lib/index.d.ts",
 7 |   "dependencies": {
 8 |     "@phosphor/algorithm": "^0.1.1",
 9 |     "@phosphor/coreutils": "^0.1.5",
10 |     "@phosphor/disposable": "^0.1.1",
11 |     "@phosphor/signaling": "^0.1.2",
12 |     "@types/minimist": "^1.2.0",
13 |     "@types/text-encoding": "0.0.30",
14 |     "minimist": "^1.2.0",
15 |     "path-posix": "^1.0.0",
16 |     "url-parse": "^1.1.8"
17 |   },
18 |   "devDependencies": {
19 |     "@types/expect.js": "^0.3.29",
20 |     "@types/mocha": "^2.2.39",
21 |     "@types/ws": "0.0.39",
22 |     "expect.js": "^0.3.1",
23 |     "istanbul": "^0.3.18",
24 |     "mocha": "^2.2.5",
25 |     "rimraf": "^2.4.2",
26 |     "text-encoding": "^0.5.2",
27 |     "typedoc": "^0.5.7",
28 |     "typescript": "~2.1.6",
29 |     "webpack": "^1.13.1",
30 |     "ws": "^1.0.1",
31 |     "xmlhttprequest": "^1.8.0"
32 |   },
33 |   "scripts": {
34 |     "clean": "rimraf docs && rimraf lib && rimraf test/build && rimraf test/coverage",
35 |     "build:src": "tsc --project src",
36 |     "build:test": "tsc --project test/src",
37 |     "build": "npm run build:src && npm run build:test",
38 |     "example:browser": "cd examples/browser && npm run update && npm run build",
39 |     "example:node": "cd examples/node && npm install",
40 |     "build:examples": "npm run example:browser && npm run example:node",
41 |     "docs": "typedoc --options typedoc.json src",
42 |     "prepublish": "npm run build && webpack",
43 |     "test:coverage": "istanbul cover --dir test/coverage _mocha -- --retries 3 test/build/**/*.spec.js --foo bar --terminalsAvailable True",
44 |     "test:integration": "cd test && python integration_test.py",
45 |     "test:devtool": "devtool node_modules/mocha/bin/_mocha -qc test/build/**/**.spec.js --foo bar --terminalsAvailable True",
46 |     "test:debug": "mocha test/build/**/*.spec.js --foo bar --terminalsAvailable True --debug-brk",
47 |     "test": "mocha --retries 3 test/build/**/*.spec.js --foo bar --terminalsAvailable True"
48 |   },
49 |   "repository": {
50 |     "type": "git",
51 |     "url": "https://github.com/jupyter/jupyter-js-services"
52 |   },
53 |   "keywords": [
54 |     "jupyter",
55 |     "services",
56 |     "notebook"
57 |   ],
58 |   "files": [
59 |     "lib/**/*.js",
60 |     "lib/**/*.d.ts",
61 |     "lib/*.js",
62 |     "lib/*.d.ts",
63 |     "dist/*.js",
64 |     "dist/**/*.js"
65 |   ],
66 |   "author": "Project Jupyter",
67 |   "license": "BSD-3-Clause",
68 |   "bugs": {
69 |     "url": "https://github.com/jupyterlab/services/issues"
70 |   },
71 |   "homepage": "https://github.com/jupyterlab/services"
72 | }
73 | 


--------------------------------------------------------------------------------
/scripts/travis_after_success.sh:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | if [[ $TRAVIS_PULL_REQUEST == false && $TRAVIS_BRANCH == "master" ]]
 3 | then
 4 |     echo "-- pushing docs --"
 5 | 
 6 |     ( cd docs 
 7 |     git init
 8 |     git config user.email "travis@travis-ci.com"
 9 |     git config user.name "Travis Bot"
10 | 
11 |     git add .
12 |     git commit -m "Deployed to GitHub Pages"
13 |     git push --force --quiet "https://${GHTOKEN}@${GH_REF}" master:gh-pages > /dev/null 2>&1
14 |     )
15 | else
16 |     echo "-- will only push docs from master --"
17 | fi
18 | 


--------------------------------------------------------------------------------
/scripts/travis_install.sh:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | set -ex
 3 | npm install
 4 | wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
 5 | bash miniconda.sh -b -p $HOME/miniconda
 6 | export PATH="$HOME/miniconda/bin:$PATH"
 7 | hash -r
 8 | conda config --set always_yes yes --set changeps1 no
 9 | conda update -q conda
10 | conda info -a
11 | conda install pip pyzmq
12 | 
13 | if [[ $TRAVIS_NODE_VERSION == "0.12" ]]; then
14 |     conda install notebook
15 | else 
16 |     # Install the development version of the notebook
17 |     git clone https://github.com/jupyter/notebook
18 |     cd notebook
19 |     pip install --pre -v -e .
20 | fi
21 | 
22 | # Create jupyter base dir (needed for config retrieval).
23 | mkdir ~/.jupyter
24 | 


--------------------------------------------------------------------------------
/scripts/travis_script.sh:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | set -ex
 3 | export DISPLAY=:99.0
 4 | sh -e /etc/init.d/xvfb start || true
 5 | 
 6 | npm run clean
 7 | npm run build
 8 | npm run build:examples
 9 | npm test
10 | npm run test:coverage
11 | export PATH="$HOME/miniconda/bin:$PATH"
12 | npm run test:integration
13 | 
14 | pushd examples/node
15 | python main.py 
16 | popd
17 | 
18 | if [[ $TRAVIS_NODE_VERSION == "5.1" ]]; then    
19 |     npm run docs
20 | fi
21 | 


--------------------------------------------------------------------------------
/src/config/index.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import {
  5 |   JSONObject, JSONValue
  6 | } from '@phosphor/coreutils';
  7 | 
  8 | import {
  9 |   IAjaxSettings
 10 | } from '../utils';
 11 | 
 12 | import * as utils
 13 |    from '../utils';
 14 | 
 15 | 
 16 | /**
 17 |  * The url for the config service.
 18 |  */
 19 | let SERVICE_CONFIG_URL = 'api/config';
 20 | 
 21 | 
 22 | /**
 23 |  * A Configurable data section.
 24 |  */
 25 | export
 26 | interface IConfigSection {
 27 |   /**
 28 |    * The data for this section.
 29 |    */
 30 |   readonly data: JSONObject;
 31 | 
 32 |   /**
 33 |    * Modify the stored config values.
 34 |    *
 35 |    * #### Notes
 36 |    * Updates the local data immediately, sends the change to the server,
 37 |    * and updates the local data with the response, and fullfils the promise
 38 |    * with that data.
 39 |    */
 40 |   update(newdata: JSONObject): Promise;
 41 | 
 42 |   /**
 43 |    * Optional default settings for ajax requests, if applicable.
 44 |    */
 45 |   ajaxSettings?: IAjaxSettings;
 46 | }
 47 | 
 48 | 
 49 | /**
 50 |  * The namespace for ConfigSection statics.
 51 |  */
 52 | export
 53 | namespace ConfigSection {
 54 |   /**
 55 |    * Create a config section.
 56 |    *
 57 |    * @returns A Promise that is fulfilled with the config section is loaded.
 58 |    */
 59 |   export
 60 |   function create(options: ConfigSection.IOptions): Promise {
 61 |     let section = new DefaultConfigSection(options);
 62 |     return section.load().then(() => {
 63 |       return section;
 64 |     });
 65 |   }
 66 | 
 67 |   /**
 68 |    * The options used to create a config section.
 69 |    */
 70 |   export
 71 |   interface IOptions {
 72 |     /**
 73 |      * The section name.
 74 |      */
 75 |     name: string;
 76 | 
 77 |     /**
 78 |      * The server base url.
 79 |      */
 80 |     baseUrl?: string;
 81 | 
 82 |     /**
 83 |      * The authentication token for the API.
 84 |      */
 85 |     token?: string;
 86 | 
 87 |     /**
 88 |      * The default ajax settings.
 89 |      */
 90 |     ajaxSettings?: IAjaxSettings;
 91 |   }
 92 | }
 93 | 
 94 | 
 95 | /**
 96 |  * Implementation of the Configurable data section.
 97 |  */
 98 | class DefaultConfigSection implements IConfigSection {
 99 |   /**
100 |    * Construct a new config section.
101 |    */
102 |   constructor(options: ConfigSection.IOptions) {
103 |     let baseUrl = options.baseUrl || utils.getBaseUrl();
104 |     this.ajaxSettings = utils.ajaxSettingsWithToken(options.ajaxSettings, options.token);
105 |     this._url = utils.urlPathJoin(baseUrl, SERVICE_CONFIG_URL,
106 |                                   encodeURIComponent(options.name));
107 |   }
108 | 
109 |   /**
110 |    * Get a copy of the default ajax settings for the section.
111 |    */
112 |   get ajaxSettings(): IAjaxSettings {
113 |     return utils.copy(this._ajaxSettings);
114 |   }
115 |   /**
116 |    * Set the default ajax settings for the section.
117 |    */
118 |   set ajaxSettings(value: IAjaxSettings) {
119 |     this._ajaxSettings = utils.copy(value);
120 |   }
121 | 
122 |   /**
123 |    * Get the data for this section.
124 |    */
125 |   get data(): JSONObject {
126 |     return this._data;
127 |   }
128 | 
129 |   /**
130 |    * Load the initial data for this section.
131 |    *
132 |    * #### Notes
133 |    * Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/config).
134 |    *
135 |    * The promise is fulfilled on a valid response and rejected otherwise.
136 |    */
137 |   load(): Promise {
138 |     let ajaxSettings = this.ajaxSettings;
139 |     ajaxSettings.method = 'GET';
140 |     ajaxSettings.dataType = 'json';
141 |     ajaxSettings.cache = false;
142 |     return utils.ajaxRequest(this._url, ajaxSettings).then(success => {
143 |       if (success.xhr.status !== 200) {
144 |          throw utils.makeAjaxError(success);
145 |       }
146 |       this._data = success.data;
147 |     });
148 |   }
149 | 
150 |   /**
151 |    * Modify the stored config values.
152 |    *
153 |    * #### Notes
154 |    * Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/config).
155 |    *
156 |    * The promise is fulfilled on a valid response and rejected otherwise.
157 |    *
158 |    * Updates the local data immediately, sends the change to the server,
159 |    * and updates the local data with the response, and fulfils the promise
160 |    * with that data.
161 |    */
162 |   update(newdata: JSONObject): Promise {
163 |     this._data = utils.extend(this._data, newdata);
164 |     let ajaxSettings = this.ajaxSettings;
165 |     ajaxSettings.method = 'PATCH';
166 |     ajaxSettings.data = JSON.stringify(newdata);
167 |     ajaxSettings.dataType = 'json';
168 |     ajaxSettings.contentType = 'application/json';
169 | 
170 |     return utils.ajaxRequest(this._url, ajaxSettings).then(success => {
171 |       if (success.xhr.status !== 200) {
172 |        throw utils.makeAjaxError(success);
173 |       }
174 | 
175 |       this._data = success.data;
176 |       return this._data;
177 |     });
178 |   }
179 | 
180 |   private _url = 'unknown';
181 |   private _data: JSONObject = null;
182 |   private _ajaxSettings: IAjaxSettings = null;
183 | }
184 | 
185 | 
186 | /**
187 |  * Configurable object with defaults.
188 |  */
189 | export
190 | class ConfigWithDefaults {
191 |   /**
192 |    * Create a new config with defaults.
193 |    */
194 |   constructor(options: ConfigWithDefaults.IOptions) {
195 |     this._section = options.section;
196 |     this._defaults = options.defaults || {};
197 |     this._className = options.className || '';
198 |   }
199 | 
200 |   /**
201 |    * Get data from the config section or fall back to defaults.
202 |    */
203 |   get(key: string): JSONValue {
204 |     let data = this._classData();
205 |     return key in data ? data[key] : this._defaults[key];
206 |   }
207 | 
208 |   /**
209 |    * Set a config value.
210 |    *
211 |    * #### Notes
212 |    * Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/config).
213 |    *
214 |    * The promise is fulfilled on a valid response and rejected otherwise.
215 |    *
216 |    * Sends the update to the server, and changes our local copy of the data
217 |    * immediately.
218 |    */
219 |   set(key: string, value: JSONValue): Promise {
220 |      let d: JSONObject = {};
221 |      d[key] = value;
222 |      if (this._className) {
223 |       let d2: JSONObject = {};
224 |       d2[this._className] = d;
225 |       return this._section.update(d2);
226 |     } else {
227 |       return this._section.update(d);
228 |     }
229 |   }
230 | 
231 |   /**
232 |    * Get data from the Section with our classname, if available.
233 |    *
234 |    * #### Notes
235 |    * If we have no classname, get all of the data in the Section
236 |    */
237 |   private _classData(): JSONObject {
238 |     let data = this._section.data;
239 |     if (this._className && this._className in data) {
240 |       return data[this._className] as JSONObject;
241 |     }
242 |     return data;
243 |   }
244 | 
245 |   private _section: IConfigSection = null;
246 |   private _defaults: JSONObject = null;
247 |   private _className = '';
248 | }
249 | 
250 | 
251 | /**
252 |  * A namespace for ConfigWithDefaults statics.
253 |  */
254 | export
255 | namespace ConfigWithDefaults {
256 |   /**
257 |    * The options used to initialize a ConfigWithDefaults object.
258 |    */
259 |   export
260 |   interface IOptions {
261 |     /**
262 |      * The configuration section.
263 |      */
264 |     section: IConfigSection;
265 | 
266 |     /**
267 |      * The default values.
268 |      */
269 |     defaults?: JSONObject;
270 | 
271 |     /**
272 |      * The optional classname namespace.
273 |      */
274 |     className?: string;
275 |   }
276 | }
277 | 


--------------------------------------------------------------------------------
/src/contents/validate.ts:
--------------------------------------------------------------------------------
 1 | // Copyright (c) Jupyter Development Team.
 2 | // Distributed under the terms of the Modified BSD License.
 3 | 
 4 | import {
 5 |   Contents
 6 | } from './index';
 7 | 
 8 | 
 9 | /**
10 |  * Validate a property as being on an object, and optionally
11 |  * of a given type.
12 |  */
13 | function validateProperty(object: any, name: string, typeName?: string): void {
14 |   if (!object.hasOwnProperty(name)) {
15 |     throw Error(`Missing property '${name}'`);
16 |   }
17 |   if (typeName !== void 0) {
18 |     let valid = true;
19 |     let value = object[name];
20 |     switch (typeName) {
21 |     case 'array':
22 |       valid = Array.isArray(value);
23 |       break;
24 |     case 'object':
25 |       valid = typeof value !== 'undefined';
26 |       break;
27 |     default:
28 |       valid = typeof value === typeName;
29 |     }
30 |     if (!valid) {
31 |       throw new Error(`Property '${name}' is not of type '${typeName}`);
32 |     }
33 |   }
34 | }
35 | 
36 | /**
37 |  * Validate an `Contents.IModel` object.
38 |  */
39 | export
40 | function validateContentsModel(model: Contents.IModel): void {
41 |   validateProperty(model, 'name', 'string');
42 |   validateProperty(model, 'path', 'string');
43 |   validateProperty(model, 'type', 'string');
44 |   validateProperty(model, 'created', 'string');
45 |   validateProperty(model, 'last_modified', 'string');
46 |   validateProperty(model, 'mimetype', 'object');
47 |   validateProperty(model, 'content', 'object');
48 |   validateProperty(model, 'format', 'object');
49 | }
50 | 
51 | 
52 | /**
53 |  * Validate an `Contents.ICheckpointModel` object.
54 |  */
55 | export
56 | function validateCheckpointModel(model: Contents.ICheckpointModel): void  {
57 |   validateProperty(model, 'id', 'string');
58 |   validateProperty(model, 'last_modified', 'string');
59 | }
60 | 


--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
 1 | // Copyright (c) Jupyter Development Team.
 2 | // Distributed under the terms of the Modified BSD License.
 3 | 
 4 | import {
 5 |   Token
 6 | } from '@phosphor/coreutils';
 7 | 
 8 | import {
 9 |   ServiceManager
10 | } from './manager';
11 | 
12 | export * from './config';
13 | export * from './contents';
14 | export * from './kernel';
15 | export * from './manager';
16 | export * from './nbformat';
17 | export * from './session';
18 | export * from './terminal';
19 | 
20 | export {
21 |   IAjaxSettings
22 | } from './utils';
23 | 
24 | import * as utils
25 |   from './utils';
26 | 
27 | export { utils };
28 | 
29 | 
30 | /* tslint:disable */
31 | /**
32 |  * The default services provider token.
33 |  */
34 | export
35 | const IServiceManager = new Token('jupyter.services.services');
36 | /* tslint:enable */
37 | 
38 | 
39 | /**
40 |  * The service manager interface.
41 |  */
42 | export
43 | interface IServiceManager extends ServiceManager.IManager { };
44 | 


--------------------------------------------------------------------------------
/src/kernel/comm.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import {
  5 |   JSONObject, JSONValue
  6 | } from '@phosphor/coreutils';
  7 | 
  8 | import {
  9 |   DisposableDelegate
 10 | } from '@phosphor/disposable';
 11 | 
 12 | import {
 13 |   Kernel
 14 | } from './kernel';
 15 | 
 16 | import {
 17 |   KernelMessage
 18 | } from './messages';
 19 | 
 20 | 
 21 | /**
 22 |  * Comm channel handler.
 23 |  */
 24 | export
 25 | class CommHandler extends DisposableDelegate implements Kernel.IComm {
 26 |   /**
 27 |    * Construct a new comm channel.
 28 |    */
 29 |   constructor(target: string, id: string, kernel: Kernel.IKernel, disposeCb: () => void) {
 30 |     super(disposeCb);
 31 |     this._id = id;
 32 |     this._target = target;
 33 |     this._kernel = kernel;
 34 |   }
 35 | 
 36 |   /**
 37 |    * The unique id for the comm channel.
 38 |    */
 39 |   get commId(): string {
 40 |     return this._id;
 41 |   }
 42 | 
 43 |   /**
 44 |    * The target name for the comm channel.
 45 |    */
 46 |   get targetName(): string {
 47 |     return this._target;
 48 |   }
 49 | 
 50 |   /**
 51 |    * Get the callback for a comm close event.
 52 |    *
 53 |    * #### Notes
 54 |    * This is called when the comm is closed from either the server or
 55 |    * client.
 56 |    *
 57 |    * **See also:** [[ICommClose]], [[close]]
 58 |    */
 59 |   get onClose(): (msg: KernelMessage.ICommCloseMsg) => void {
 60 |     return this._onClose;
 61 |   }
 62 | 
 63 |   /**
 64 |    * Set the callback for a comm close event.
 65 |    *
 66 |    * #### Notes
 67 |    * This is called when the comm is closed from either the server or
 68 |    * client.
 69 |    *
 70 |    * **See also:** [[close]]
 71 |    */
 72 |   set onClose(cb: (msg: KernelMessage.ICommCloseMsg) => void) {
 73 |     this._onClose = cb;
 74 |   }
 75 | 
 76 |   /**
 77 |    * Get the callback for a comm message received event.
 78 |    */
 79 |   get onMsg(): (msg: KernelMessage.ICommMsgMsg) => void {
 80 |     return this._onMsg;
 81 |   }
 82 | 
 83 |   /**
 84 |    * Set the callback for a comm message received event.
 85 |    */
 86 |   set onMsg(cb: (msg: KernelMessage.ICommMsgMsg) => void) {
 87 |     this._onMsg = cb;
 88 |   }
 89 | 
 90 |   /**
 91 |    * Test whether the comm has been disposed.
 92 |    */
 93 |   get isDisposed(): boolean {
 94 |     return (this._kernel === null);
 95 |   }
 96 | 
 97 |   /**
 98 |    * Open a comm with optional data and metadata.
 99 |    *
100 |    * #### Notes
101 |    * This sends a `comm_open` message to the server.
102 |    *
103 |    * **See also:** [[ICommOpen]]
104 |    */
105 |   open(data?: JSONValue, metadata?: JSONObject): Kernel.IFuture {
106 |     if (this.isDisposed || this._kernel.isDisposed) {
107 |       return;
108 |     }
109 |     let options: KernelMessage.IOptions = {
110 |       msgType: 'comm_open',
111 |       channel: 'shell',
112 |       username: this._kernel.username,
113 |       session: this._kernel.clientId
114 |     };
115 |     let content: KernelMessage.ICommOpen = {
116 |       comm_id: this._id,
117 |       target_name: this._target,
118 |       data: data || {}
119 |     };
120 |     let msg = KernelMessage.createShellMessage(options, content, metadata);
121 |     return this._kernel.sendShellMessage(msg, false, true);
122 |   }
123 | 
124 |   /**
125 |    * Send a `comm_msg` message to the kernel.
126 |    *
127 |    * #### Notes
128 |    * This is a no-op if the comm has been closed.
129 |    *
130 |    * **See also:** [[ICommMsg]]
131 |    */
132 |   send(data: JSONValue, metadata?: JSONObject, buffers: (ArrayBuffer | ArrayBufferView)[] = [], disposeOnDone: boolean = true): Kernel.IFuture {
133 |     if (this.isDisposed || this._kernel.isDisposed) {
134 |       return;
135 |     }
136 |     let options: KernelMessage.IOptions = {
137 |       msgType: 'comm_msg',
138 |       channel: 'shell',
139 |       username: this._kernel.username,
140 |       session: this._kernel.clientId
141 |     };
142 |     let content: KernelMessage.ICommMsg = {
143 |       comm_id: this._id,
144 |       data: data
145 |     };
146 |     let msg = KernelMessage.createShellMessage(options, content, metadata, buffers);
147 |     return this._kernel.sendShellMessage(msg, false, true);
148 |   }
149 | 
150 |   /**
151 |    * Close the comm.
152 |    *
153 |    * #### Notes
154 |    * This will send a `comm_close` message to the kernel, and call the
155 |    * `onClose` callback if set.
156 |    *
157 |    * This is a no-op if the comm is already closed.
158 |    *
159 |    * **See also:** [[ICommClose]], [[onClose]]
160 |    */
161 |   close(data?: JSONValue, metadata?: JSONObject): Kernel.IFuture {
162 |     if (this.isDisposed || this._kernel.isDisposed) {
163 |       return;
164 |     }
165 |     let options: KernelMessage.IOptions = {
166 |       msgType: 'comm_msg',
167 |       channel: 'shell',
168 |       username: this._kernel.username,
169 |       session: this._kernel.clientId
170 |     };
171 |     let content: KernelMessage.ICommClose = {
172 |       comm_id: this._id,
173 |       data: data || {}
174 |     };
175 |     let msg = KernelMessage.createShellMessage(options, content, metadata);
176 |     let future = this._kernel.sendShellMessage(msg, false, true);
177 |     options.channel = 'iopub';
178 |     let ioMsg = KernelMessage.createMessage(options, content, metadata);
179 |     let onClose = this._onClose;
180 |     if (onClose) {
181 |       onClose(ioMsg as KernelMessage.ICommCloseMsg);
182 |     }
183 |     this.dispose();
184 |     return future;
185 |   }
186 | 
187 |   /**
188 |    * Dispose of the resources held by the comm.
189 |    */
190 |   dispose(): void {
191 |     if (this.isDisposed) {
192 |       return;
193 |     }
194 |     this._onClose = null;
195 |     this._onMsg = null;
196 |     this._kernel = null;
197 |     super.dispose();
198 |   }
199 | 
200 |   private _target = '';
201 |   private _id = '';
202 |   private _kernel: Kernel.IKernel = null;
203 |   private _onClose: (msg: KernelMessage.ICommCloseMsg) => void = null;
204 |   private _onMsg: (msg: KernelMessage.ICommMsgMsg) => void = null;
205 | }
206 | 


--------------------------------------------------------------------------------
/src/kernel/future.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import {
  5 |   DisposableDelegate
  6 | } from '@phosphor/disposable';
  7 | 
  8 | import {
  9 |   Kernel
 10 | } from './kernel';
 11 | 
 12 | import {
 13 |   KernelMessage
 14 | } from './messages';
 15 | 
 16 | 
 17 | /**
 18 |  * Implementation of a kernel future.
 19 |  */
 20 | export
 21 | class KernelFutureHandler extends DisposableDelegate implements Kernel.IFuture {
 22 |   /**
 23 |    * Construct a new KernelFutureHandler.
 24 |    */
 25 |   constructor(cb: () => void, msg: KernelMessage.IShellMessage, expectShell: boolean, disposeOnDone: boolean) {
 26 |     super(cb);
 27 |     this._msg = msg;
 28 |     if (!expectShell) {
 29 |       this._setFlag(Private.KernelFutureFlag.GotReply);
 30 |     }
 31 |     this._disposeOnDone = disposeOnDone;
 32 |   }
 33 | 
 34 |   /**
 35 |    * Get the original outgoing message.
 36 |    */
 37 |   get msg(): KernelMessage.IShellMessage {
 38 |     return this._msg;
 39 |   }
 40 | 
 41 |   /**
 42 |    * Check for message done state.
 43 |    */
 44 |   get isDone(): boolean {
 45 |     return this._testFlag(Private.KernelFutureFlag.IsDone);
 46 |   }
 47 | 
 48 |   /**
 49 |    * Get the reply handler.
 50 |    */
 51 |   get onReply(): (msg: KernelMessage.IShellMessage) => void {
 52 |     return this._reply;
 53 |   }
 54 | 
 55 |   /**
 56 |    * Set the reply handler.
 57 |    */
 58 |   set onReply(cb: (msg: KernelMessage.IShellMessage) => void) {
 59 |     this._reply = cb;
 60 |   }
 61 | 
 62 |   /**
 63 |    * Get the iopub handler.
 64 |    */
 65 |   get onIOPub(): (msg: KernelMessage.IIOPubMessage) => void {
 66 |     return this._iopub;
 67 |   }
 68 | 
 69 |   /**
 70 |    * Set the iopub handler.
 71 |    */
 72 |   set onIOPub(cb: (msg: KernelMessage.IIOPubMessage) => void) {
 73 |     this._iopub = cb;
 74 |   }
 75 | 
 76 |   /**
 77 |    * Get the done handler.
 78 |    */
 79 |   get onDone(): () => void  {
 80 |     return this._done;
 81 |   }
 82 | 
 83 |   /**
 84 |    * Set the done handler.
 85 |    */
 86 |   set onDone(cb: () => void) {
 87 |     this._done = cb;
 88 |   }
 89 | 
 90 |   /**
 91 |    * Get the stdin handler.
 92 |    */
 93 |   get onStdin(): (msg: KernelMessage.IStdinMessage) => void {
 94 |     return this._stdin;
 95 |   }
 96 | 
 97 |   /**
 98 |    * Set the stdin handler.
 99 |    */
100 |   set onStdin(cb: (msg: KernelMessage.IStdinMessage) => void) {
101 |     this._stdin = cb;
102 |   }
103 | 
104 |   /**
105 |    * Register hook for IOPub messages.
106 |    *
107 |    * @param hook - The callback invoked for an IOPub message.
108 |    *
109 |    * #### Notes
110 |    * The IOPub hook system allows you to preempt the handlers for IOPub messages handled
111 |    * by the future. The most recently registered hook is run first.
112 |    * If the hook returns false, any later hooks and the future's onIOPub handler will not run.
113 |    * If a hook throws an error, the error is logged to the console and the next hook is run.
114 |    * If a hook is registered during the hook processing, it won't run until the next message.
115 |    * If a hook is removed during the hook processing, it will be deactivated immediately.
116 |    */
117 |   registerMessageHook(hook: (msg: KernelMessage.IIOPubMessage) => boolean): void {
118 |     this._hooks.add(hook);
119 |   }
120 | 
121 |   /**
122 |    * Remove a hook for IOPub messages.
123 |    *
124 |    * @param hook - The hook to remove.
125 |    *
126 |    * #### Notes
127 |    * If a hook is removed during the hook processing, it will be deactivated immediately.
128 |    */
129 |   removeMessageHook(hook: (msg: KernelMessage.IIOPubMessage) => boolean): void {
130 |     if (this.isDisposed) {
131 |       return;
132 |     }
133 |     this._hooks.remove(hook);
134 |   }
135 | 
136 |   /**
137 |    * Dispose and unregister the future.
138 |    */
139 |   dispose(): void {
140 |     this._stdin = null;
141 |     this._iopub = null;
142 |     this._reply = null;
143 |     this._done = null;
144 |     this._msg = null;
145 |     if (this._hooks) { this._hooks.dispose(); }
146 |     this._hooks = null;
147 |     super.dispose();
148 |   }
149 | 
150 |   /**
151 |    * Handle an incoming kernel message.
152 |    */
153 |   handleMsg(msg: KernelMessage.IMessage): void {
154 |     switch (msg.channel) {
155 |     case 'shell':
156 |       this._handleReply(msg as KernelMessage.IShellMessage);
157 |       break;
158 |     case 'stdin':
159 |       this._handleStdin(msg as KernelMessage.IStdinMessage);
160 |       break;
161 |     case 'iopub':
162 |       this._handleIOPub(msg as KernelMessage.IIOPubMessage);
163 |       break;
164 |     }
165 |   }
166 | 
167 |   private _handleReply(msg: KernelMessage.IShellMessage): void {
168 |     let reply = this._reply;
169 |     if (reply) { reply(msg); }
170 |     this._setFlag(Private.KernelFutureFlag.GotReply);
171 |     if (this._testFlag(Private.KernelFutureFlag.GotIdle)) {
172 |       this._handleDone();
173 |     }
174 |   }
175 | 
176 |   private _handleStdin(msg: KernelMessage.IStdinMessage): void {
177 |     let stdin = this._stdin;
178 |     if (stdin) { stdin(msg); }
179 |   }
180 | 
181 |   private _handleIOPub(msg: KernelMessage.IIOPubMessage): void {
182 |     let process = this._hooks.process(msg);
183 |     let iopub = this._iopub;
184 |     if (process && iopub) { iopub(msg); }
185 |     if (KernelMessage.isStatusMsg(msg) &&
186 |         msg.content.execution_state === 'idle') {
187 |       this._setFlag(Private.KernelFutureFlag.GotIdle);
188 |       if (this._testFlag(Private.KernelFutureFlag.GotReply)) {
189 |         this._handleDone();
190 |       }
191 |     }
192 |   }
193 | 
194 |   private _handleDone(): void {
195 |     if (this.isDone) {
196 |       return;
197 |     }
198 |     this._setFlag(Private.KernelFutureFlag.IsDone);
199 |     let done = this._done;
200 |     if (done) done();
201 |     this._done = null;
202 |     if (this._disposeOnDone) {
203 |       this.dispose();
204 |     }
205 |   }
206 | 
207 |   /**
208 |    * Test whether the given future flag is set.
209 |    */
210 |   private _testFlag(flag: Private.KernelFutureFlag): boolean {
211 |     return (this._status & flag) !== 0;
212 |   }
213 | 
214 |   /**
215 |    * Set the given future flag.
216 |    */
217 |   private _setFlag(flag: Private.KernelFutureFlag): void {
218 |     this._status |= flag;
219 |   }
220 | 
221 |   private _msg: KernelMessage.IShellMessage = null;
222 |   private _status = 0;
223 |   private _stdin: (msg: KernelMessage.IStdinMessage) => void = null;
224 |   private _iopub: (msg: KernelMessage.IIOPubMessage) => void = null;
225 |   private _reply: (msg: KernelMessage.IShellMessage) => void = null;
226 |   private _done: () => void = null;
227 |   private _hooks = new Private.HookList();
228 |   private _disposeOnDone = true;
229 | }
230 | 
231 | namespace Private {
232 |   /**
233 |    * A polyfill for a function to run code outside of the current execution context.
234 |    */
235 |   let defer = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setImmediate;
236 | 
237 |   export
238 |   class HookList {
239 |     /**
240 |      * Register a hook.
241 |      *
242 |      * @param hook - The callback to register.
243 |      */
244 |     add(hook: (msg: T) => boolean): void {
245 |       this.remove(hook);
246 |       this._hooks.push(hook);
247 |     }
248 | 
249 |     /**
250 |      * Remove a hook.
251 |      *
252 |      * @param hook - The callback to remove.
253 |      */
254 |     remove(hook: (msg: T) => boolean): void {
255 |       if (this.isDisposed) {
256 |         return;
257 |       }
258 |       let index = this._hooks.indexOf(hook);
259 |       if (index >= 0) {
260 |         this._hooks[index] = null;
261 |         this._scheduleCompact();
262 |       }
263 |     }
264 | 
265 |     /**
266 |      * Process a message through the hooks.
267 |      *
268 |      * #### Notes
269 |      * The most recently registered hook is run first.
270 |      * If the hook returns false, any later hooks will not run.
271 |      * If a hook throws an error, the error is logged to the console and the next hook is run.
272 |      * If a hook is registered during the hook processing, it won't run until the next message.
273 |      * If a hook is removed during the hook processing, it will be deactivated immediately.
274 |      */
275 |     process(msg: T): boolean {
276 |       let continueHandling: boolean;
277 |       // most recently-added hook is called first
278 |       for (let i = this._hooks.length-1; i>=0; i--) {
279 |         let hook = this._hooks[i];
280 |         if (hook === null) { continue; }
281 |         try {
282 |           continueHandling = hook(msg);
283 |         } catch(err) {
284 |           continueHandling = true;
285 |           console.error(err);
286 |         }
287 |         if (continueHandling === false) {
288 |           return false;
289 |         }
290 |       }
291 |       return true;
292 |     }
293 | 
294 |     /**
295 |      * Test whether the HookList has been disposed.
296 |      */
297 |     get isDisposed(): boolean {
298 |       return (this._hooks === null);
299 |     }
300 | 
301 |     /**
302 |      * Dispose the hook list.
303 |      */
304 |     dispose(): void {
305 |       this._hooks = null;
306 |     }
307 | 
308 |     /**
309 |      * Schedule a cleanup of the list, removing any hooks that have been nulled out.
310 |      */
311 |     private _scheduleCompact(): void {
312 |       if (!this._cleanupScheduled) {
313 |         this._cleanupScheduled = true;
314 |         defer(() => {
315 |           this._cleanupScheduled = false;
316 |           this._compact();
317 |         })
318 |       }
319 |     }
320 | 
321 |     /**
322 |      * Compact the list, removing any nulls.
323 |      */
324 |     private _compact(): void {
325 |       if (this.isDisposed) {
326 |         return;
327 |       }
328 |       let numNulls = 0;
329 |       for (let i = 0, len = this._hooks.length; i < len; i++) {
330 |         let hook = this._hooks[i];
331 |         if (this._hooks[i] === null) {
332 |           numNulls++;
333 |         } else {
334 |           this._hooks[i-numNulls] = hook;
335 |         }
336 |       }
337 |       this._hooks.length -= numNulls;
338 |     }
339 | 
340 |     private _hooks: ((msg: T) => boolean)[] = [];
341 |     private _cleanupScheduled: boolean;
342 |   }
343 | 
344 |   /**
345 |    * Bit flags for the kernel future state.
346 |    */
347 |   export
348 |   enum KernelFutureFlag {
349 |     GotReply = 0x1,
350 |     GotIdle = 0x2,
351 |     IsDone = 0x4,
352 |     DisposeOnDone = 0x8,
353 |   }
354 | }
355 | 


--------------------------------------------------------------------------------
/src/kernel/index.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) Jupyter Development Team.
2 | // Distributed under the terms of the Modified BSD License.
3 | 'use strict';
4 | 
5 | export * from './kernel';
6 | export * from './manager';
7 | export * from './messages';
8 | 


--------------------------------------------------------------------------------
/src/kernel/manager.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import {
  5 |   ArrayExt, IIterator, iter
  6 | } from '@phosphor/algorithm';
  7 | 
  8 | import {
  9 |   JSONExt
 10 | } from '@phosphor/coreutils';
 11 | 
 12 | import {
 13 |   ISignal, Signal
 14 | } from '@phosphor/signaling';
 15 | 
 16 | import * as utils
 17 |   from '../utils';
 18 | 
 19 | import {
 20 |   IAjaxSettings
 21 | } from '../utils';
 22 | 
 23 | import {
 24 |   Kernel
 25 | } from './kernel';
 26 | 
 27 | 
 28 | /**
 29 |  * An implementation of a kernel manager.
 30 |  */
 31 | export
 32 | class KernelManager implements Kernel.IManager {
 33 |   /**
 34 |    * Construct a new kernel manager.
 35 |    *
 36 |    * @param options - The default options for kernel.
 37 |    */
 38 |   constructor(options: Kernel.IOptions = {}) {
 39 |     this._baseUrl = options.baseUrl || utils.getBaseUrl();
 40 |     this._wsUrl = options.wsUrl || utils.getWsUrl(this._baseUrl);
 41 |     this._token = options.token || utils.getConfigOption('token');
 42 |     this._ajaxSettings = JSON.stringify(utils.ajaxSettingsWithToken(options.ajaxSettings, options.token));
 43 | 
 44 |     // Initialize internal data.
 45 |     this._readyPromise = this._refreshSpecs().then(() => {
 46 |       return this._refreshRunning();
 47 |     });
 48 | 
 49 |     // Set up polling.
 50 |     this._runningTimer = (setInterval as any)(() => {
 51 |       this._refreshRunning();
 52 |     }, 10000);
 53 |     this._specsTimer = (setInterval as any)(() => {
 54 |       this._refreshSpecs();
 55 |     }, 61000);
 56 |   }
 57 | 
 58 |   /**
 59 |    * A signal emitted when the specs change.
 60 |    */
 61 |   get specsChanged(): ISignal {
 62 |     return this._specsChanged;
 63 |   }
 64 | 
 65 |   /**
 66 |    * A signal emitted when the running kernels change.
 67 |    */
 68 |   get runningChanged(): ISignal {
 69 |     return this._runningChanged;
 70 |   }
 71 | 
 72 |   /**
 73 |    * Test whether the terminal manager is disposed.
 74 |    */
 75 |   get isDisposed(): boolean {
 76 |     return this._isDisposed;
 77 |   }
 78 | 
 79 |   /**
 80 |    * Dispose of the resources used by the manager.
 81 |    */
 82 |   dispose(): void {
 83 |     if (this.isDisposed) {
 84 |       return;
 85 |     }
 86 |     this._isDisposed = true;
 87 |     clearInterval(this._runningTimer);
 88 |     clearInterval(this._specsTimer);
 89 |     Signal.clearData(this);
 90 |     this._specs = null;
 91 |     this._running = [];
 92 |   }
 93 | 
 94 |   /**
 95 |    * Get the base url of the manager.
 96 |    */
 97 |   get baseUrl(): string {
 98 |     return this._baseUrl;
 99 |   }
100 | 
101 |   /**
102 |    * Get the ws url of the manager.
103 |    */
104 |   get wsUrl(): string {
105 |     return this._wsUrl;
106 |   }
107 | 
108 |   /**
109 |    * The default ajax settings for the manager.
110 |    */
111 |   get ajaxSettings(): IAjaxSettings {
112 |     return JSON.parse(this._ajaxSettings);
113 |   }
114 | 
115 |   /**
116 |    * Set the default ajax settings for the manager.
117 |    */
118 |   set ajaxSettings(value: IAjaxSettings) {
119 |     this._ajaxSettings = JSON.stringify(value);
120 |   }
121 | 
122 |   /**
123 |    * Get the most recently fetched kernel specs.
124 |    */
125 |   get specs(): Kernel.ISpecModels | null {
126 |     return this._specs;
127 |   }
128 | 
129 |   /**
130 |    * Test whether the manager is ready.
131 |    */
132 |   get isReady(): boolean {
133 |     return this._isReady;
134 |   }
135 | 
136 |   /**
137 |    * A promise that fulfills when the manager is ready.
138 |    */
139 |   get ready(): Promise {
140 |     return this._readyPromise;
141 |   }
142 | 
143 |   /**
144 |    * Create an iterator over the most recent running kernels.
145 |    *
146 |    * @returns A new iterator over the running kernels.
147 |    */
148 |   running(): IIterator {
149 |     return iter(this._running);
150 |   }
151 | 
152 |   /**
153 |    * Force a refresh of the specs from the server.
154 |    *
155 |    * @returns A promise that resolves when the specs are fetched.
156 |    *
157 |    * #### Notes
158 |    * This is intended to be called only in response to a user action,
159 |    * since the manager maintains its internal state.
160 |    */
161 |   refreshSpecs(): Promise {
162 |     return this._refreshSpecs();
163 |   }
164 | 
165 |   /**
166 |    * Force a refresh of the running kernels.
167 |    *
168 |    * @returns A promise that with the list of running sessions.
169 |    *
170 |    * #### Notes
171 |    * This is not typically meant to be called by the user, since the
172 |    * manager maintains its own internal state.
173 |    */
174 |   refreshRunning(): Promise {
175 |     return this._refreshRunning();
176 |   }
177 | 
178 |   /**
179 |    * Start a new kernel.  See also [[startNewKernel]].
180 |    *
181 |    * @param options - Overrides for the default options.
182 |    */
183 |   startNew(options?: Kernel.IOptions): Promise {
184 |     return Kernel.startNew(this._getOptions(options)).then(kernel => {
185 |       this._onStarted(kernel);
186 |       return kernel;
187 |     });
188 |   }
189 | 
190 |   /**
191 |    * Find a kernel by id.
192 |    *
193 |    * @param options - Overrides for the default options.
194 |    */
195 |   findById(id: string, options?: Kernel.IOptions): Promise {
196 |     return Kernel.findById(id, this._getOptions(options));
197 |   }
198 | 
199 |   /**
200 |    * Connect to a running kernel.  See also [[connectToKernel]].
201 |    *
202 |    * @param options - Overrides for the default options.
203 |    */
204 |   connectTo(id: string, options?: Kernel.IOptions): Promise {
205 |     return Kernel.connectTo(id, this._getOptions(options)).then(kernel => {
206 |       this._onStarted(kernel);
207 |       return kernel;
208 |     });
209 |   }
210 | 
211 |   /**
212 |    * Shut down a kernel by id.
213 |    *
214 |    * @param options - Overrides for the default options.
215 |    *
216 |    * #### Notes
217 |    * This will emit [[runningChanged]] if the running kernels list
218 |    * changes.
219 |    */
220 |   shutdown(id: string, options?: Kernel.IOptions): Promise {
221 |     return Kernel.shutdown(id, this._getOptions(options)).then(() => {
222 |       this._onTerminated(id);
223 |     });
224 |   }
225 | 
226 |   /**
227 |    * Handle a kernel terminating.
228 |    */
229 |   private _onTerminated(id: string): void {
230 |     let index = ArrayExt.findFirstIndex(this._running, value => value.id === id);
231 |     if (index !== -1) {
232 |       this._running.splice(index, 1);
233 |       this._runningChanged.emit(this._running.slice());
234 |     }
235 |   }
236 | 
237 |   /**
238 |    * Handle a kernel starting.
239 |    */
240 |   private _onStarted(kernel: Kernel.IKernel): void {
241 |     let id = kernel.id;
242 |     let index = ArrayExt.findFirstIndex(this._running, value => value.id === id);
243 |     if (index === -1) {
244 |       this._running.push(kernel.model);
245 |       this._runningChanged.emit(this._running.slice());
246 |     }
247 |     kernel.terminated.connect(() => {
248 |       this._onTerminated(id);
249 |     });
250 |   }
251 | 
252 |   /**
253 |    * Refresh the specs.
254 |    */
255 |   private _refreshSpecs(): Promise {
256 |     let options = {
257 |       baseUrl: this._baseUrl,
258 |       token: this._token,
259 |       ajaxSettings: this.ajaxSettings
260 |     };
261 |     return Kernel.getSpecs(options).then(specs => {
262 |       if (!JSONExt.deepEqual(specs, this._specs)) {
263 |         this._specs = specs;
264 |         this._specsChanged.emit(specs);
265 |       }
266 |     });
267 |   }
268 | 
269 |   /**
270 |    * Refresh the running sessions.
271 |    */
272 |   private _refreshRunning(): Promise {
273 |     return Kernel.listRunning(this._getOptions({})).then(running => {
274 |       this._isReady = true;
275 |       if (!JSONExt.deepEqual(running, this._running)) {
276 |         this._running = running.slice();
277 |         this._runningChanged.emit(running);
278 |       }
279 |     });
280 |   }
281 | 
282 |   /**
283 |    * Get optionally overidden options.
284 |    */
285 |   private _getOptions(options: Kernel.IOptions = {}): Kernel.IOptions {
286 |     options.baseUrl = this._baseUrl;
287 |     options.wsUrl = this._wsUrl;
288 |     options.token = this._token;
289 |     options.ajaxSettings = options.ajaxSettings || this.ajaxSettings;
290 |     return options;
291 |   }
292 | 
293 |   private _baseUrl = '';
294 |   private _wsUrl = '';
295 |   private _token = '';
296 |   private _ajaxSettings = '';
297 |   private _running: Kernel.IModel[] = [];
298 |   private _specs: Kernel.ISpecModels = null;
299 |   private _isDisposed = false;
300 |   private _runningTimer = -1;
301 |   private _specsTimer = -1;
302 |   private _readyPromise: Promise;
303 |   private _isReady = false;
304 |   private _specsChanged = new Signal(this);
305 |   private _runningChanged = new Signal(this);
306 | }
307 | 


--------------------------------------------------------------------------------
/src/kernel/serialize.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import {
  5 |   KernelMessage
  6 | } from './messages';
  7 | 
  8 | 
  9 | /**
 10 |  * Deserialize and return the unpacked message.
 11 |  *
 12 |  * #### Notes
 13 |  * Handles JSON blob strings and binary messages.
 14 |  */
 15 | export
 16 | function deserialize(data: ArrayBuffer | string): KernelMessage.IMessage {
 17 |   let value: KernelMessage.IMessage;
 18 |   if (typeof data === 'string') {
 19 |     value = JSON.parse(data);
 20 |   } else {
 21 |     value = deserializeBinary(data);
 22 |   }
 23 |   return value;
 24 | }
 25 | 
 26 | 
 27 | /**
 28 |  * Serialize a kernel message for transport.
 29 |  *
 30 |  * #### Notes
 31 |  * If there is binary content, an `ArrayBuffer` is returned,
 32 |  * otherwise the message is converted to a JSON string.
 33 |  */
 34 | export
 35 | function serialize(msg: KernelMessage.IMessage): string | ArrayBuffer {
 36 |   let value: string | ArrayBuffer;
 37 |   if (msg.buffers && msg.buffers.length) {
 38 |     value = serializeBinary(msg);
 39 |   } else {
 40 |     value = JSON.stringify(msg);
 41 |   }
 42 |   return value;
 43 | }
 44 | 
 45 | 
 46 | /**
 47 |  * Deserialize a binary message to a Kernel Message.
 48 |  */
 49 | function deserializeBinary(buf: ArrayBuffer): KernelMessage.IMessage {
 50 |   let data = new DataView(buf);
 51 |   // read the header: 1 + nbufs 32b integers
 52 |   let nbufs = data.getUint32(0);
 53 |   let offsets: number[] = [];
 54 |   if (nbufs < 2) {
 55 |     throw new Error('Invalid incoming Kernel Message');
 56 |   }
 57 |   for (let i = 1; i <= nbufs; i++) {
 58 |     offsets.push(data.getUint32(i * 4));
 59 |   }
 60 |   let jsonBytes = new Uint8Array(buf.slice(offsets[0], offsets[1]));
 61 |   let msg = JSON.parse((new TextDecoder('utf8')).decode(jsonBytes));
 62 |   // the remaining chunks are stored as DataViews in msg.buffers
 63 |   msg.buffers = [];
 64 |   for (let i = 1; i < nbufs; i++) {
 65 |     let start = offsets[i];
 66 |     let stop = offsets[i + 1] || buf.byteLength;
 67 |     msg.buffers.push(new DataView(buf.slice(start, stop)));
 68 |   }
 69 |   return msg;
 70 | }
 71 | 
 72 | 
 73 | /**
 74 |  * Implement the binary serialization protocol.
 75 |  *
 76 |  * Serialize Kernel message to ArrayBuffer.
 77 |  */
 78 | function serializeBinary(msg: KernelMessage.IMessage): ArrayBuffer {
 79 |   let offsets: number[] = [];
 80 |   let buffers: ArrayBuffer[] = [];
 81 |   let encoder = new TextEncoder('utf8');
 82 |   let jsonUtf8 = encoder.encode(JSON.stringify(msg, replaceBuffers));
 83 |   buffers.push(jsonUtf8.buffer);
 84 |   for (let i = 0; i < msg.buffers.length; i++) {
 85 |     // msg.buffers elements could be either views or ArrayBuffers
 86 |     // buffers elements are ArrayBuffers
 87 |     let b: any = msg.buffers[i];
 88 |     buffers.push(b instanceof ArrayBuffer ? b : b.buffer);
 89 |   }
 90 |   let nbufs = buffers.length;
 91 |   offsets.push(4 * (nbufs + 1));
 92 |   for (let i = 0; i + 1 < buffers.length; i++) {
 93 |     offsets.push(offsets[offsets.length - 1] + buffers[i].byteLength);
 94 |   }
 95 |   let msgBuf = new Uint8Array(
 96 |     offsets[offsets.length - 1] + buffers[buffers.length - 1].byteLength
 97 |   );
 98 |   // use DataView.setUint32 for network byte-order
 99 |   let view = new DataView(msgBuf.buffer);
100 |   // write nbufs to first 4 bytes
101 |   view.setUint32(0, nbufs);
102 |   // write offsets to next 4 * nbufs bytes
103 |   for (let i = 0; i < offsets.length; i++) {
104 |     view.setUint32(4 * (i + 1), offsets[i]);
105 |   }
106 |   // write all the buffers at their respective offsets
107 |   for (let i = 0; i < buffers.length; i++) {
108 |     msgBuf.set(new Uint8Array(buffers[i]), offsets[i]);
109 |   }
110 |   return msgBuf.buffer;
111 | }
112 | 
113 | 
114 | /**
115 |  * Filter `"buffers"` key for `JSON.stringify`.
116 |  */
117 | function replaceBuffers(key: string, value: any) {
118 |   if (key === 'buffers') {
119 |     return undefined;
120 |   }
121 |   return value;
122 | }
123 | 


--------------------------------------------------------------------------------
/src/kernel/validate.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import {
  5 |   Kernel
  6 | } from './kernel';
  7 | 
  8 | import {
  9 |   KernelMessage
 10 | } from './messages';
 11 | 
 12 | 
 13 | /**
 14 |  * Required fields for `IKernelHeader`.
 15 |  */
 16 | const HEADER_FIELDS = ['username', 'version', 'session', 'msg_id', 'msg_type'];
 17 | 
 18 | /**
 19 |  * Requred fields and types for contents of various types of `kernel.IMessage`
 20 |  * messages on the iopub channel.
 21 |  */
 22 | const IOPUB_CONTENT_FIELDS: {[key: string]: any} = {
 23 |   stream: { name: 'string', text: 'string' },
 24 |   display_data: { data: 'object', metadata: 'object' },
 25 |   execute_input: { code: 'string', execution_count: 'number' },
 26 |   execute_result: { execution_count: 'number', data: 'object',
 27 |                     metadata: 'object' },
 28 |   error: { ename: 'string', evalue: 'string', traceback: 'object' },
 29 |   status: { execution_state: 'string' },
 30 |   clear_output: { wait: 'boolean' },
 31 |   comm_open: { comm_id: 'string', target_name: 'string', data: 'object' },
 32 |   comm_msg: { comm_id: 'string', data: 'object' },
 33 |   comm_close: { comm_id: 'string' },
 34 |   shutdown_reply: { restart : 'boolean' }  // Emitted by the IPython kernel.
 35 | };
 36 | 
 37 | 
 38 | /**
 39 |  * Validate a property as being on an object, and optionally
 40 |  * of a given type.
 41 |  */
 42 | function validateProperty(object: any, name: string, typeName?: string): void {
 43 |   if (!object.hasOwnProperty(name)) {
 44 |     throw Error(`Missing property '${name}'`);
 45 |   }
 46 |   if (typeName !== void 0) {
 47 |     let valid = true;
 48 |     let value = object[name];
 49 |     switch (typeName) {
 50 |     case 'array':
 51 |       valid = Array.isArray(value);
 52 |       break;
 53 |     case 'object':
 54 |       valid = typeof value !== 'undefined';
 55 |       break;
 56 |     default:
 57 |       valid = typeof value === typeName;
 58 |     }
 59 |     if (!valid) {
 60 |       throw new Error(`Property '${name}' is not of type '${typeName}`);
 61 |     }
 62 |   }
 63 | }
 64 | 
 65 | 
 66 | /**
 67 |  * Validate the header of a kernel message.
 68 |  */
 69 | function validateHeader(header: KernelMessage.IHeader): void {
 70 |   for (let i = 0; i < HEADER_FIELDS.length; i++) {
 71 |     validateProperty(header, HEADER_FIELDS[i], 'string');
 72 |   }
 73 | }
 74 | 
 75 | 
 76 | /**
 77 |  * Validate a kernel message object.
 78 |  */
 79 | export
 80 | function validateMessage(msg: KernelMessage.IMessage) : void {
 81 |   validateProperty(msg, 'metadata', 'object');
 82 |   validateProperty(msg, 'content', 'object');
 83 |   validateProperty(msg, 'channel', 'string');
 84 |   validateHeader(msg.header);
 85 |   if (msg.channel === 'iopub') {
 86 |     validateIOPubContent(msg as KernelMessage.IIOPubMessage);
 87 |   }
 88 | }
 89 | 
 90 | 
 91 | /**
 92 |  * Validate content an kernel message on the iopub channel.
 93 |  */
 94 | function validateIOPubContent(msg: KernelMessage.IIOPubMessage) : void {
 95 |   if (msg.channel === 'iopub') {
 96 |     let fields = IOPUB_CONTENT_FIELDS[msg.header.msg_type];
 97 |     // Check for unknown message type.
 98 |     if (fields === void 0) {
 99 |       return;
100 |     }
101 |     let names = Object.keys(fields);
102 |     let content = msg.content;
103 |     for (let i = 0; i < names.length; i++) {
104 |       validateProperty(content, names[i], fields[names[i]]);
105 |     }
106 |   }
107 | }
108 | 
109 | 
110 | /**
111 |  * Validate a `Kernel.IModel` object.
112 |  */
113 | export
114 | function validateModel(model: Kernel.IModel) : void {
115 |   validateProperty(model, 'name', 'string');
116 |   validateProperty(model, 'id', 'string');
117 | }
118 | 
119 | 
120 | /**
121 |  * Validate a server kernelspec model to a client side model.
122 |  */
123 | export
124 | function validateSpecModel(data: any): Kernel.ISpecModel {
125 |   let spec = data.spec;
126 |   if (!spec) {
127 |     throw new Error('Invalid kernel spec');
128 |   }
129 |   validateProperty(data, 'name', 'string');
130 |   validateProperty(data, 'resources', 'object');
131 |   validateProperty(spec, 'language', 'string');
132 |   validateProperty(spec, 'display_name', 'string');
133 |   validateProperty(spec, 'argv', 'array');
134 |   return {
135 |     name: data.name,
136 |     resources: data.resources,
137 |     language: spec.language,
138 |     display_name: spec.display_name,
139 |     argv: spec.argv
140 |   };
141 | }
142 | 
143 | /**
144 |  * Validate a `Kernel.ISpecModels` object.
145 |  */
146 | export
147 | function validateSpecModels(data: any): Kernel.ISpecModels {
148 |   if (!data.hasOwnProperty('kernelspecs')) {
149 |     throw new Error('No kernelspecs found');
150 |   }
151 |   let keys = Object.keys(data.kernelspecs);
152 |   let kernelspecs: { [key: string]: Kernel.ISpecModel } = Object.create(null);
153 |   let defaultSpec = data.default;
154 | 
155 |   for (let i = 0; i < keys.length; i++) {
156 |     let ks = data.kernelspecs[keys[i]];
157 |     try {
158 |       kernelspecs[keys[i]] = validateSpecModel(ks);
159 |     } catch (err) {
160 |       // Remove the errant kernel spec.
161 |       console.warn(`Removing errant kernel spec: ${keys[i]}`);
162 |     }
163 |   }
164 |   keys = Object.keys(kernelspecs);
165 |   if (!keys.length) {
166 |     throw new Error('No valid kernelspecs found');
167 |   }
168 |   if (!defaultSpec || typeof defaultSpec !== 'string' ||
169 |       !(defaultSpec in kernelspecs)) {
170 |     defaultSpec = keys[0];
171 |     console.warn(`Default kernel not found, using '${keys[0]}'`);
172 |   }
173 |   return {
174 |     default: defaultSpec,
175 |     kernelspecs,
176 |   };
177 | }
178 | 


--------------------------------------------------------------------------------
/src/manager.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import {
  5 |   JSONObject
  6 | } from '@phosphor/coreutils';
  7 | 
  8 | import {
  9 |   IDisposable
 10 | } from '@phosphor/disposable';
 11 | 
 12 | import {
 13 |   ISignal, Signal
 14 | } from '@phosphor/signaling';
 15 | 
 16 | import {
 17 |   Contents, ContentsManager
 18 | } from './contents';
 19 | 
 20 | import {
 21 |   Kernel
 22 | } from './kernel';
 23 | 
 24 | import {
 25 |   Session, SessionManager
 26 | } from './session';
 27 | 
 28 | import {
 29 |   TerminalSession, TerminalManager
 30 | } from './terminal';
 31 | 
 32 | import {
 33 |   IAjaxSettings, getBaseUrl, getWsUrl, ajaxSettingsWithToken
 34 | } from './utils';
 35 | 
 36 | 
 37 | /**
 38 |  * A Jupyter services manager.
 39 |  */
 40 | export
 41 | class ServiceManager implements ServiceManager.IManager {
 42 |   /**
 43 |    * Construct a new services provider.
 44 |    */
 45 |   constructor(options?: ServiceManager.IOptions) {
 46 |     options = options || {};
 47 |     options.wsUrl = options.wsUrl || getWsUrl();
 48 |     options.baseUrl = options.baseUrl || getBaseUrl();
 49 |     options.ajaxSettings = ajaxSettingsWithToken(options.ajaxSettings, options.token);
 50 |     this._sessionManager = new SessionManager(options);
 51 |     this._contentsManager = new ContentsManager(options);
 52 |     this._terminalManager = new TerminalManager(options);
 53 |     this._sessionManager.specsChanged.connect((sender, specs) => {
 54 |       this._specsChanged.emit(specs);
 55 |     });
 56 |     this._readyPromise = this._sessionManager.ready.then(() => {
 57 |       if (this._terminalManager.isAvailable()) {
 58 |         return this._terminalManager.ready;
 59 |       }
 60 |     });
 61 |   }
 62 | 
 63 |   /**
 64 |    * A signal emitted when the kernel specs change.
 65 |    */
 66 |   get specsChanged(): ISignal {
 67 |     return this._specsChanged;
 68 |   }
 69 | 
 70 |   /**
 71 |    * Test whether the terminal manager is disposed.
 72 |    */
 73 |   get isDisposed(): boolean {
 74 |     return this._isDisposed;
 75 |   }
 76 | 
 77 |   /**
 78 |    * Dispose of the resources used by the manager.
 79 |    */
 80 |   dispose(): void {
 81 |     if (this.isDisposed) {
 82 |       return;
 83 |     }
 84 |     this._isDisposed = true;
 85 |     Signal.clearData(this);
 86 |     this._sessionManager.dispose();
 87 |     this._contentsManager.dispose();
 88 |     this._sessionManager.dispose();
 89 |   }
 90 | 
 91 |   /**
 92 |    * The kernel spec models.
 93 |    */
 94 |   get specs(): Kernel.ISpecModels | null {
 95 |     return this._sessionManager.specs;
 96 |   }
 97 | 
 98 |   /**
 99 |    * Get the base url of the server.
100 |    */
101 |   get baseUrl(): string {
102 |     return this._sessionManager.baseUrl;
103 |   }
104 | 
105 |   /**
106 |    * Get the session manager instance.
107 |    */
108 |   get sessions(): SessionManager {
109 |     return this._sessionManager;
110 |   }
111 | 
112 |   /**
113 |    * Get the contents manager instance.
114 |    */
115 |   get contents(): ContentsManager {
116 |     return this._contentsManager;
117 |   }
118 | 
119 |   /**
120 |    * Get the terminal manager instance.
121 |    */
122 |   get terminals(): TerminalManager {
123 |     return this._terminalManager;
124 |   }
125 | 
126 |   /**
127 |    * Test whether the manager is ready.
128 |    */
129 |   get isReady(): boolean {
130 |     return this._sessionManager.isReady || this._terminalManager.isReady;
131 |   }
132 | 
133 |   /**
134 |    * A promise that fulfills when the manager is ready.
135 |    */
136 |   get ready(): Promise {
137 |     return this._readyPromise;
138 |   }
139 | 
140 |   private _sessionManager: SessionManager = null;
141 |   private _contentsManager: ContentsManager = null;
142 |   private _terminalManager: TerminalManager = null;
143 |   private _isDisposed = false;
144 |   private _readyPromise: Promise;
145 |   private _specsChanged = new Signal(this);
146 | }
147 | 
148 | 
149 | /**
150 |  * The namespace for `ServiceManager` statics.
151 |  */
152 | export
153 | namespace ServiceManager {
154 |   /**
155 |    * A service manager interface.
156 |    */
157 |   export
158 |   interface IManager extends IDisposable {
159 |     /**
160 |      * A signal emitted when the kernel specs change.
161 |      */
162 |     specsChanged: ISignal;
163 | 
164 |     /**
165 |      * The kernel spec models.
166 |      */
167 |     readonly specs: Kernel.ISpecModels | null;
168 | 
169 |     /**
170 |      * The base url of the manager.
171 |      */
172 |     readonly baseUrl: string;
173 | 
174 |     /**
175 |      * The session manager for the manager.
176 |      */
177 |     readonly sessions: Session.IManager;
178 | 
179 |     /**
180 |      * The contents manager for the manager.
181 |      */
182 |     readonly contents: Contents.IManager;
183 | 
184 |     /**
185 |      * The terminals manager for the manager.
186 |      */
187 |     readonly terminals: TerminalSession.IManager;
188 | 
189 |     /**
190 |      * Test whether the manager is ready.
191 |      */
192 |     readonly isReady: boolean;
193 | 
194 |     /**
195 |      * A promise that fulfills when the manager is initially ready.
196 |      */
197 |     readonly ready: Promise;
198 |   }
199 | 
200 |   /**
201 |    * The options used to create a service manager.
202 |    */
203 |   export
204 |   interface IOptions extends JSONObject {
205 |     /**
206 |      * The base url of the server.
207 |      */
208 |     baseUrl?: string;
209 | 
210 |     /**
211 |      * The base ws url of the server.
212 |      */
213 |     wsUrl?: string;
214 | 
215 |     /**
216 |      * The authentication token for the API.
217 |      */
218 |     token?: string;
219 | 
220 |     /**
221 |      * The ajax settings for the manager.
222 |      */
223 |     ajaxSettings?: IAjaxSettings;
224 |   }
225 | }
226 | 


--------------------------------------------------------------------------------
/src/nbformat.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | // Notebook format interfaces
  5 | // https://nbformat.readthedocs.io/en/latest/format_description.html
  6 | // https://github.com/jupyter/nbformat/blob/master/nbformat/v4/nbformat.v4.schema.json
  7 | 
  8 | import {
  9 |   JSONObject, JSONExt
 10 | } from '@phosphor/coreutils';
 11 | 
 12 | 
 13 | /**
 14 |  * A namespace for nbformat interfaces.
 15 |  */
 16 | export
 17 | namespace nbformat {
 18 |   /**
 19 |    * The major version of the notebook format.
 20 |    */
 21 |   export
 22 |   const MAJOR_VERSION: number = 4;
 23 | 
 24 |   /**
 25 |    * The minor version of the notebook format.
 26 |    */
 27 |   export
 28 |   const MINOR_VERSION: number = 1;
 29 | 
 30 |   /**
 31 |    * The kernelspec metadata.
 32 |    */
 33 |   export
 34 |   interface IKernelspecMetadata extends JSONObject {
 35 |     name: string;
 36 |     display_name: string;
 37 |   }
 38 | 
 39 |   /**
 40 |    * The language info metatda
 41 |    */
 42 |   export
 43 |   interface ILanguageInfoMetadata extends JSONObject {
 44 |     name: string;
 45 |     codemirror_mode?: string | JSONObject;
 46 |     file_extension?: string;
 47 |     mimetype?: string;
 48 |     pygments_lexer?: string;
 49 |   }
 50 | 
 51 |   /**
 52 |    * The default metadata for the notebook.
 53 |    */
 54 |   export
 55 |   interface INotebookMetadata extends JSONObject {
 56 |     kernelspec?: IKernelspecMetadata;
 57 |     language_info?: ILanguageInfoMetadata;
 58 |     orig_nbformat?: number;
 59 |   }
 60 | 
 61 |   /**
 62 |    * The notebook content.
 63 |    */
 64 |   export
 65 |   interface INotebookContent extends JSONObject {
 66 |     metadata: INotebookMetadata;
 67 |     nbformat_minor: number;
 68 |     nbformat: number;
 69 |     cells: ICell[];
 70 |   }
 71 | 
 72 |   /**
 73 |    * A multiline string.
 74 |    */
 75 |   export
 76 |   type MultilineString = string | string[];
 77 | 
 78 |   /**
 79 |    * A mime-type keyed dictionary of data.
 80 |    */
 81 |   export
 82 |   interface IMimeBundle extends JSONObject {
 83 |     [key: string]: MultilineString | JSONObject;
 84 |   }
 85 | 
 86 |   /**
 87 |    * Media attachments (e.g. inline images).
 88 |    */
 89 |   export
 90 |   interface IAttachments {
 91 |     [key: string]: IMimeBundle;
 92 |   }
 93 | 
 94 |   /**
 95 |    * The code cell's prompt number. Will be null if the cell has not been run.
 96 |    */
 97 |   export
 98 |   type ExecutionCount = number | null;
 99 | 
100 |   /**
101 |    * Cell output metadata.
102 |    */
103 |   export
104 |   type OutputMetadata = JSONObject;
105 | 
106 |   /**
107 |    * Validate a mime type/value pair.
108 |    *
109 |    * @param type - The mimetype name.
110 |    *
111 |    * @param value - The value associated with the type.
112 |    *
113 |    * @returns Whether the type/value pair are valid.
114 |    */
115 |   export
116 |   function validateMimeValue(type: string, value: MultilineString | JSONObject): boolean {
117 |     // Check if "application/json" or "application/foo+json"
118 |     const jsonTest = /^application\/(.*?)+\+json$/;
119 |     const isJSONType = type === 'application/json' || jsonTest.test(type);
120 | 
121 |     let isString = (x: any) => {
122 |       return Object.prototype.toString.call(x) === '[object String]';
123 |     };
124 | 
125 |     // If it is an array, make sure if is not a JSON type and it is an
126 |     // array of strings.
127 |     if (Array.isArray(value)) {
128 |       if (isJSONType) {
129 |         return false;
130 |       }
131 |       let valid = true;
132 |       (value as string[]).forEach(v => {
133 |         if (!isString(v)) {
134 |           valid = false;
135 |         }
136 |       });
137 |       return valid;
138 |     }
139 | 
140 |     // If it is a string, make sure we are not a JSON type.
141 |     if (isString(value)) {
142 |       return !isJSONType;
143 |     }
144 | 
145 |     // It is not a string, make sure it is a JSON type.
146 |     if (!isJSONType) {
147 |       return false;
148 |     }
149 | 
150 |     // It is a JSON type, make sure it is a valid JSON object.
151 |     return JSONExt.isObject(value);
152 |   }
153 | 
154 |   /**
155 |    * A type which describes the type of cell.
156 |    */
157 |   export
158 |   type CellType = 'code' | 'markdown' | 'raw';
159 | 
160 |   /**
161 |    * Cell-level metadata.
162 |    */
163 |   export
164 |   interface IBaseCellMetadata extends JSONObject {
165 |     /**
166 |      * Whether the cell is trusted.
167 |      *
168 |      * #### Notes
169 |      * This is not strictly part of the nbformat spec, but it is added by
170 |      * the contents manager.
171 |      *
172 |      * See https://jupyter-notebook.readthedocs.io/en/latest/security.html.
173 |      */
174 |     trusted: boolean;
175 | 
176 |     /**
177 |      * The cell's name. If present, must be a non-empty string.
178 |      */
179 |     name?: string;
180 | 
181 |     /**
182 |      * The cell's tags. Tags must be unique, and must not contain commas.
183 |      */
184 |     tags?: string[];
185 |   }
186 | 
187 |   /**
188 |    * The base cell interface.
189 |    */
190 |   export
191 |   interface IBaseCell extends JSONObject {
192 |     /**
193 |      * String identifying the type of cell.
194 |      */
195 |     cell_type: CellType;
196 | 
197 |     /**
198 |      * Contents of the cell, represented as an array of lines.
199 |      */
200 |     source: MultilineString;
201 | 
202 |     /**
203 |      * Cell-level metadata.
204 |      */
205 |     metadata: ICellMetadata;
206 |   }
207 | 
208 |   /**
209 |    * Metadata for the raw cell.
210 |    */
211 |   export
212 |   interface IRawCellMetadata extends IBaseCellMetadata {
213 |     /**
214 |      * Raw cell metadata format for nbconvert.
215 |      */
216 |     format?: string;
217 |   }
218 | 
219 |   /**
220 |    * A raw cell.
221 |    */
222 |   export
223 |   interface IRawCell extends IBaseCell {
224 |     /**
225 |      * String identifying the type of cell.
226 |      */
227 |     cell_type: 'raw';
228 | 
229 |     /**
230 |      * Cell-level metadata.
231 |      */
232 |     metadata: IRawCellMetadata;
233 | 
234 |     /**
235 |      * Cell attachments.
236 |      */
237 |     attachments?: IAttachments;
238 |   }
239 | 
240 |   /**
241 |    * A markdown cell.
242 |    */
243 |   export
244 |   interface IMarkdownCell extends IBaseCell {
245 |     /**
246 |      * String identifying the type of cell.
247 |      */
248 |     cell_type: 'markdown';
249 | 
250 |     /**
251 |      * Cell attachments.
252 |      */
253 |     attachments?: IAttachments;
254 |   }
255 | 
256 |   /**
257 |    * Metadata for a code cell.
258 |    */
259 |   export
260 |   interface ICodeCellMetadata extends IBaseCellMetadata {
261 |     /**
262 |      * Whether the cell is collapsed/expanded.
263 |      */
264 |     collapsed?: boolean;
265 | 
266 |     /**
267 |      * Whether the cell's output is scrolled, unscrolled, or autoscrolled.
268 |      */
269 |     scrolled?: boolean | 'auto';
270 |   }
271 | 
272 |   /**
273 |    * A code cell.
274 |    */
275 |   export
276 |   interface ICodeCell extends IBaseCell {
277 |     /**
278 |      * String identifying the type of cell.
279 |      */
280 |     cell_type: 'code';
281 | 
282 |     /**
283 |      * Cell-level metadata.
284 |      */
285 |     metadata: ICodeCellMetadata;
286 | 
287 |     /**
288 |      * Execution, display, or stream outputs.
289 |      */
290 |     outputs: IOutput[];
291 | 
292 |     /**
293 |      * The code cell's prompt number. Will be null if the cell has not been run.
294 |      */
295 |     execution_count: ExecutionCount;
296 |   }
297 | 
298 |   /**
299 |    * A cell union type.
300 |    */
301 |   export
302 |   type ICell = IRawCell | IMarkdownCell | ICodeCell;
303 | 
304 | 
305 |   /**
306 |    * A union metadata type.
307 |    */
308 |   export
309 |   type ICellMetadata = IBaseCellMetadata | IRawCellMetadata | ICodeCellMetadata;
310 | 
311 |   /**
312 |    * The valid output types.
313 |    */
314 |   export
315 |   type OutputType = 'execute_result' | 'display_data' | 'stream' | 'error';
316 | 
317 |   /**
318 |    * The base output type.
319 |    */
320 |   export
321 |   interface IBaseOutput extends JSONObject {
322 |     /**
323 |      * Type of cell output.
324 |      */
325 |     output_type: OutputType;
326 |   }
327 | 
328 |   /**
329 |    * Result of executing a code cell.
330 |    */
331 |   export
332 |   interface IExecuteResult extends IBaseOutput {
333 |     /**
334 |      * Type of cell output.
335 |      */
336 |     output_type: 'execute_result';
337 | 
338 |     /**
339 |      * A result's prompt number.
340 |      */
341 |     execution_count: ExecutionCount;
342 | 
343 |     /**
344 |      * A mime-type keyed dictionary of data.
345 |      */
346 |     data: IMimeBundle;
347 | 
348 |     /**
349 |      * Cell output metadata.
350 |      */
351 |     metadata: OutputMetadata;
352 |   }
353 | 
354 |   /**
355 |    * Data displayed as a result of code cell execution.
356 |    */
357 |   export
358 |   interface IDisplayData extends IBaseOutput {
359 |     /**
360 |      * Type of cell output.
361 |      */
362 |     output_type: 'display_data';
363 | 
364 |     /**
365 |      * A mime-type keyed dictionary of data.
366 |      */
367 |     data: IMimeBundle;
368 | 
369 |     /**
370 |      * Cell output metadata.
371 |      */
372 |     metadata: OutputMetadata;
373 |   }
374 | 
375 |   /**
376 |    * Stream output from a code cell.
377 |    */
378 |   export
379 |   interface IStream extends IBaseOutput {
380 |     /**
381 |      * Type of cell output.
382 |      */
383 |     output_type: 'stream';
384 | 
385 |     /**
386 |      * The name of the stream.
387 |      */
388 |     name: 'stdout' | 'stderr';
389 | 
390 |     /**
391 |      * The stream's text output.
392 |      */
393 |     text: MultilineString;
394 |   }
395 | 
396 |   /**
397 |    * Output of an error that occurred during code cell execution.
398 |    */
399 |   export
400 |   interface IError extends IBaseOutput {
401 |     /**
402 |      * Type of cell output.
403 |      */
404 |     output_type: 'error';
405 | 
406 |     /**
407 |      * The name of the error.
408 |      */
409 |     ename: string;
410 | 
411 |     /**
412 |      * The value, or message, of the error.
413 |      */
414 |     evalue: string;
415 | 
416 |     /**
417 |      * The error's traceback.
418 |      */
419 |     traceback: string[];
420 |   }
421 | 
422 |   /**
423 |    * An output union type.
424 |    */
425 |   export
426 |   type IOutput = IExecuteResult | IDisplayData | IStream | IError;
427 | }
428 | 


--------------------------------------------------------------------------------
/src/session/index.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) Jupyter Development Team.
2 | // Distributed under the terms of the Modified BSD License.
3 | 
4 | export * from './manager';
5 | export * from './session';
6 | 


--------------------------------------------------------------------------------
/src/session/manager.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import {
  5 |   ArrayExt, IIterator, iter
  6 | } from '@phosphor/algorithm';
  7 | 
  8 | import {
  9 |   JSONExt
 10 | } from '@phosphor/coreutils';
 11 | 
 12 | import {
 13 |   ISignal, Signal
 14 | } from '@phosphor/signaling';
 15 | 
 16 | import {
 17 |   Kernel
 18 | } from '../kernel';
 19 | 
 20 | import * as utils
 21 |   from '../utils';
 22 | 
 23 | import {
 24 |   IAjaxSettings
 25 | } from '../utils';
 26 | 
 27 | import {
 28 |   Session
 29 | } from './session';
 30 | 
 31 | 
 32 | /**
 33 |  * An implementation of a session manager.
 34 |  */
 35 | export
 36 | class SessionManager implements Session.IManager {
 37 |   /**
 38 |    * Construct a new session manager.
 39 |    *
 40 |    * @param options - The default options for each session.
 41 |    */
 42 |   constructor(options: Session.IOptions = {}) {
 43 |     this._baseUrl = options.baseUrl || utils.getBaseUrl();
 44 |     this._wsUrl = options.wsUrl || utils.getWsUrl(this._baseUrl);
 45 |     this._ajaxSettings = JSON.stringify(options.ajaxSettings || {});
 46 | 
 47 |     // Initialize internal data.
 48 |     this._readyPromise = this._refreshSpecs().then(() => {
 49 |       return this._refreshRunning();
 50 |     });
 51 | 
 52 |     // Set up polling.
 53 |     this._runningTimer = (setInterval as any)(() => {
 54 |       this._refreshRunning();
 55 |     }, 10000);
 56 |     this._specsTimer = (setInterval as any)(() => {
 57 |       this._refreshSpecs();
 58 |     }, 61000);
 59 |   }
 60 | 
 61 |   /**
 62 |    * A signal emitted when the kernel specs change.
 63 |    */
 64 |   get specsChanged(): ISignal {
 65 |     return this._specsChanged;
 66 |   }
 67 | 
 68 |   /**
 69 |    * A signal emitted when the running sessions change.
 70 |    */
 71 |   get runningChanged(): ISignal {
 72 |     return this._runningChanged;
 73 |   }
 74 | 
 75 |   /**
 76 |    * Test whether the terminal manager is disposed.
 77 |    */
 78 |   get isDisposed(): boolean {
 79 |     return this._isDisposed;
 80 |   }
 81 | 
 82 |   /**
 83 |    * Dispose of the resources used by the manager.
 84 |    */
 85 |   dispose(): void {
 86 |     if (this.isDisposed) {
 87 |       return;
 88 |     }
 89 |     this._isDisposed = true;
 90 |     clearInterval(this._runningTimer);
 91 |     clearInterval(this._specsTimer);
 92 |     Signal.clearData(this);
 93 |     this._running = [];
 94 |   }
 95 | 
 96 |   /**
 97 |    * The base url of the manager.
 98 |    */
 99 |   get baseUrl(): string {
100 |     return this._baseUrl;
101 |   }
102 | 
103 |   /**
104 |    * The base ws url of the manager.
105 |    */
106 |   get wsUrl(): string {
107 |     return this._wsUrl;
108 |   }
109 | 
110 |   /**
111 |    * The default ajax settings for the manager.
112 |    */
113 |   get ajaxSettings(): IAjaxSettings {
114 |     return JSON.parse(this._ajaxSettings);
115 |   }
116 | 
117 |   /**
118 |    * Set the default ajax settings for the manager.
119 |    */
120 |   set ajaxSettings(value: IAjaxSettings) {
121 |     this._ajaxSettings = JSON.stringify(value);
122 |   }
123 | 
124 |   /**
125 |    * Get the most recently fetched kernel specs.
126 |    */
127 |   get specs(): Kernel.ISpecModels | null {
128 |     return this._specs;
129 |   }
130 | 
131 |   /**
132 |    * Test whether the manager is ready.
133 |    */
134 |   get isReady(): boolean {
135 |     return this._specs !== null;
136 |   }
137 | 
138 |   /**
139 |    * A promise that fulfills when the manager is ready.
140 |    */
141 |   get ready(): Promise {
142 |     return this._readyPromise;
143 |   }
144 | 
145 |   /**
146 |    * Create an iterator over the most recent running sessions.
147 |    *
148 |    * @returns A new iterator over the running sessions.
149 |    */
150 |   running(): IIterator {
151 |     return iter(this._running);
152 |   }
153 | 
154 |   /**
155 |    * Force a refresh of the specs from the server.
156 |    *
157 |    * @returns A promise that resolves when the specs are fetched.
158 |    *
159 |    * #### Notes
160 |    * This is intended to be called only in response to a user action,
161 |    * since the manager maintains its internal state.
162 |    */
163 |   refreshSpecs(): Promise {
164 |     return this._refreshSpecs();
165 |   }
166 | 
167 |   /**
168 |    * Force a refresh of the running sessions.
169 |    *
170 |    * @returns A promise that with the list of running sessions.
171 |    *
172 |    * #### Notes
173 |    * This is not typically meant to be called by the user, since the
174 |    * manager maintains its own internal state.
175 |    */
176 |   refreshRunning(): Promise {
177 |     return this._refreshRunning();
178 |   }
179 | 
180 |   /**
181 |    * Start a new session.  See also [[startNewSession]].
182 |    *
183 |    * @param options - Overrides for the default options, must include a
184 |    *   `'path'`.
185 |    */
186 |   startNew(options: Session.IOptions): Promise {
187 |     return Session.startNew(this._getOptions(options)).then(session => {
188 |       this._onStarted(session);
189 |       return session;
190 |     });
191 |   }
192 | 
193 |   /**
194 |    * Find a session by id.
195 |    */
196 |   findById(id: string, options?: Session.IOptions): Promise {
197 |     return Session.findById(id, this._getOptions(options));
198 |   }
199 | 
200 |   /**
201 |    * Find a session by path.
202 |    */
203 |   findByPath(path: string, options?: Session.IOptions): Promise {
204 |     return Session.findByPath(path, this._getOptions(options));
205 |   }
206 | 
207 |   /*
208 |    * Connect to a running session.  See also [[connectToSession]].
209 |    */
210 |   connectTo(id: string, options?: Session.IOptions): Promise {
211 |     return Session.connectTo(id, this._getOptions(options)).then(session => {
212 |       this._onStarted(session);
213 |       return session;
214 |     });
215 |   }
216 | 
217 |   /**
218 |    * Shut down a session by id.
219 |    */
220 |   shutdown(id: string, options?: Session.IOptions): Promise {
221 |     return Session.shutdown(id, this._getOptions(options)).then(() => {
222 |       this._onTerminated(id);
223 |     });
224 |   }
225 | 
226 |   /**
227 |    * Get optionally overidden options.
228 |    */
229 |   private _getOptions(options: Session.IOptions = {}): Session.IOptions {
230 |     options.baseUrl = this._baseUrl;
231 |     options.wsUrl = this._wsUrl;
232 |     options.ajaxSettings = options.ajaxSettings || this.ajaxSettings;
233 |     return options;
234 |   }
235 | 
236 |   /**
237 |    * Handle a session terminating.
238 |    */
239 |   private _onTerminated(id: string): void {
240 |     let index = ArrayExt.findFirstIndex(this._running, value => value.id === id);
241 |     if (index !== -1) {
242 |       this._running.splice(index, 1);
243 |       this._runningChanged.emit(this._running.slice());
244 |     }
245 |   }
246 | 
247 |   /**
248 |    * Handle a session starting.
249 |    */
250 |   private _onStarted(session: Session.ISession): void {
251 |     let id = session.id;
252 |     let index = ArrayExt.findFirstIndex(this._running, value => value.id === id);
253 |     if (index === -1) {
254 |       this._running.push(session.model);
255 |       this._runningChanged.emit(this._running.slice());
256 |     }
257 |     session.terminated.connect(() => {
258 |       this._onTerminated(id);
259 |     });
260 |     session.pathChanged.connect(() => {
261 |       this._onChanged(session.model);
262 |     });
263 |     session.kernelChanged.connect(() => {
264 |       this._onChanged(session.model);
265 |     });
266 |   }
267 | 
268 |   /**
269 |    * Handle a change to a session.
270 |    */
271 |   private _onChanged(model: Session.IModel): void {
272 |     let index = ArrayExt.findFirstIndex(this._running, value => value.id === model.id);
273 |     if (index !== -1) {
274 |       this._running[index] = model;
275 |       this._runningChanged.emit(this._running.slice());
276 |     }
277 |   }
278 | 
279 |   /**
280 |    * Refresh the specs.
281 |    */
282 |   private _refreshSpecs(): Promise {
283 |     let options = {
284 |       baseUrl: this._baseUrl,
285 |       ajaxSettings: this.ajaxSettings
286 |     };
287 |     return Kernel.getSpecs(options).then(specs => {
288 |       if (!JSONExt.deepEqual(specs, this._specs)) {
289 |         this._specs = specs;
290 |         this._specsChanged.emit(specs);
291 |       }
292 |     });
293 |   }
294 | 
295 |   /**
296 |    * Refresh the running sessions.
297 |    */
298 |   private _refreshRunning(): Promise {
299 |     return Session.listRunning(this._getOptions({})).then(running => {
300 |       if (!JSONExt.deepEqual(running, this._running)) {
301 |         this._running = running.slice();
302 |         this._runningChanged.emit(running);
303 |       }
304 |     });
305 |   }
306 | 
307 |   private _baseUrl = '';
308 |   private _wsUrl = '';
309 |   private _ajaxSettings = '';
310 |   private _isDisposed = false;
311 |   private _running: Session.IModel[] = [];
312 |   private _specs: Kernel.ISpecModels = null;
313 |   private _runningTimer = -1;
314 |   private _specsTimer = -1;
315 |   private _readyPromise: Promise;
316 |   private _specsChanged = new Signal(this);
317 |   private _runningChanged = new Signal(this);
318 | }
319 | 


--------------------------------------------------------------------------------
/src/session/validate.ts:
--------------------------------------------------------------------------------
 1 | // Copyright (c) Jupyter Development Team.
 2 | // Distributed under the terms of the Modified BSD License.
 3 | 
 4 | import {
 5 |   validateModel as validateKernelModel
 6 | } from '../kernel/validate';
 7 | 
 8 | import {
 9 |   Session
10 | } from './session';
11 | 
12 | 
13 | /**
14 |  * Validate a property as being on an object, and optionally
15 |  * of a given type.
16 |  */
17 | function validateProperty(object: any, name: string, typeName?: string): void {
18 |   if (!object.hasOwnProperty(name)) {
19 |     throw Error(`Missing property '${name}'`);
20 |   }
21 |   if (typeName !== void 0) {
22 |     let valid = true;
23 |     let value = object[name];
24 |     switch (typeName) {
25 |     case 'array':
26 |       valid = Array.isArray(value);
27 |       break;
28 |     case 'object':
29 |       valid = typeof value !== 'undefined';
30 |       break;
31 |     default:
32 |       valid = typeof value === typeName;
33 |     }
34 |     if (!valid) {
35 |       throw new Error(`Property '${name}' is not of type '${typeName}`);
36 |     }
37 |   }
38 | }
39 | 
40 | 
41 | /**
42 |  * Validate an `Session.IModel` object.
43 |  */
44 | export
45 | function validateModel(model: Session.IModel): void {
46 |   validateProperty(model, 'id', 'string');
47 |   validateProperty(model, 'notebook', 'object');
48 |   validateProperty(model, 'kernel', 'object');
49 |   validateKernelModel(model.kernel);
50 |   validateProperty(model.notebook, 'path', 'string');
51 | }
52 | 


--------------------------------------------------------------------------------
/src/terminal/default.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import {
  5 |   ArrayExt, each, map, toArray
  6 | } from '@phosphor/algorithm';
  7 | 
  8 | import {
  9 |   JSONPrimitive
 10 | } from '@phosphor/coreutils';
 11 | 
 12 | import {
 13 |   ISignal, Signal
 14 | } from '@phosphor/signaling';
 15 | 
 16 | import {
 17 |   IAjaxSettings
 18 | } from '../utils';
 19 | 
 20 | import * as utils
 21 |   from '../utils';
 22 | 
 23 | import {
 24 |   TerminalSession
 25 | } from './terminal';
 26 | 
 27 | 
 28 | /**
 29 |  * The url for the terminal service.
 30 |  */
 31 | const TERMINAL_SERVICE_URL = 'api/terminals';
 32 | 
 33 | 
 34 | /**
 35 |  * An implementation of a terminal interface.
 36 |  */
 37 | export
 38 | class DefaultTerminalSession implements TerminalSession.ISession {
 39 |   /**
 40 |    * Construct a new terminal session.
 41 |    */
 42 |   constructor(name: string, options: TerminalSession.IOptions = {}) {
 43 |     this._name = name;
 44 |     this._baseUrl = options.baseUrl || utils.getBaseUrl();
 45 |     this._token = options.token || utils.getConfigOption('token');
 46 |     this._ajaxSettings = JSON.stringify(
 47 |       utils.ajaxSettingsWithToken(options.ajaxSettings, this._token)
 48 |     );
 49 |     this._wsUrl = options.wsUrl || utils.getWsUrl(this._baseUrl);
 50 |     this._readyPromise = this._initializeSocket();
 51 |     this.terminated = new Signal(this);
 52 |   }
 53 | 
 54 |   /**
 55 |    * A signal emitted when the session is shut down.
 56 |    */
 57 |   readonly terminated: Signal;
 58 | 
 59 |   /**
 60 |    * A signal emitted when a message is received from the server.
 61 |    */
 62 |   get messageReceived(): ISignal {
 63 |     return this._messageReceived;
 64 |   }
 65 | 
 66 |   /**
 67 |    * Get the name of the terminal session.
 68 |    */
 69 |   get name(): string {
 70 |     return this._name;
 71 |   }
 72 | 
 73 |   /**
 74 |    * Get the model for the terminal session.
 75 |    */
 76 |   get model(): TerminalSession.IModel {
 77 |     return { name: this._name };
 78 |   }
 79 | 
 80 |   /**
 81 |    * The base url of the terminal.
 82 |    */
 83 |   get baseUrl(): string {
 84 |     return this._baseUrl;
 85 |   }
 86 | 
 87 |   /**
 88 |    * Get a copy of the default ajax settings for the terminal.
 89 |    */
 90 |   get ajaxSettings(): IAjaxSettings {
 91 |     return JSON.parse(this._ajaxSettings);
 92 |   }
 93 | 
 94 |   /**
 95 |    * Set the default ajax settings for the terminal.
 96 |    */
 97 |   set ajaxSettings(value: IAjaxSettings) {
 98 |     this._ajaxSettings = JSON.stringify(value);
 99 |   }
100 | 
101 |   /**
102 |    * Test whether the session is ready.
103 |    */
104 |   get isReady(): boolean {
105 |     return this._isReady;
106 |   }
107 | 
108 |   /**
109 |    * A promise that fulfills when the session is ready.
110 |    */
111 |   get ready(): Promise {
112 |     return this._readyPromise;
113 |   }
114 | 
115 |   /**
116 |    * Test whether the session is disposed.
117 |    */
118 |   get isDisposed(): boolean {
119 |     return this._isDisposed;
120 |   }
121 | 
122 |   /**
123 |    * Dispose of the resources held by the session.
124 |    */
125 |   dispose(): void {
126 |     if (this._isDisposed) {
127 |       return;
128 |     }
129 |     this._isDisposed = true;
130 |     if (this._ws) {
131 |       this._ws.close();
132 |       this._ws = null;
133 |     }
134 |     delete Private.running[this._url];
135 |     this._readyPromise = null;
136 |     Signal.clearData(this);
137 |   }
138 | 
139 |   /**
140 |    * Send a message to the terminal session.
141 |    */
142 |   send(message: TerminalSession.IMessage): void {
143 |     if (this._isDisposed) {
144 |       return;
145 |     }
146 | 
147 |     let msg: JSONPrimitive[] = [message.type];
148 |     msg.push(...message.content);
149 |     let value = JSON.stringify(msg);
150 |     if (this._isReady) {
151 |       this._ws.send(value);
152 |       return;
153 |     }
154 |     this.ready.then(() => {
155 |       this._ws.send(value);
156 |     });
157 |   }
158 | 
159 |   /**
160 |    * Reconnect to the terminal.
161 |    *
162 |    * @returns A promise that resolves when the terminal has reconnected.
163 |    */
164 |   reconnect(): Promise {
165 |     this._readyPromise = this._initializeSocket();
166 |     return this._readyPromise;
167 |   }
168 | 
169 |   /**
170 |    * Shut down the terminal session.
171 |    */
172 |   shutdown(): Promise {
173 |     let options = {
174 |       baseUrl: this._baseUrl,
175 |       ajaxSettings: this.ajaxSettings
176 |     };
177 |     return DefaultTerminalSession.shutdown(this.name, options);
178 |   }
179 | 
180 |   /**
181 |    * Connect to the websocket.
182 |    */
183 |   private _initializeSocket(): Promise {
184 |     let name = this._name;
185 |     if (this._ws) {
186 |       this._ws.close();
187 |     }
188 |     this._isReady = false;
189 |     this._url = Private.getTermUrl(this._baseUrl, this._name);
190 |     Private.running[this._url] = this;
191 |     let wsUrl = utils.urlPathJoin(this._wsUrl, `terminals/websocket/${name}`);
192 |     if (this._token) {
193 |       wsUrl = wsUrl + `?token=${this._token}`;
194 |     }
195 |     this._ws = new WebSocket(wsUrl);
196 | 
197 |     this._ws.onmessage = (event: MessageEvent) => {
198 |       if (this._isDisposed) {
199 |         return;
200 |       }
201 | 
202 |       let data = JSON.parse(event.data) as JSONPrimitive[];
203 |       this._messageReceived.emit({
204 |         type: data[0] as TerminalSession.MessageType,
205 |         content: data.slice(1)
206 |       });
207 |     };
208 | 
209 |     return new Promise((resolve, reject) => {
210 |       this._ws.onopen = (event: MessageEvent) => {
211 |         if (this._isDisposed) {
212 |           return;
213 |         }
214 |         this._isReady = true;
215 |         resolve(void 0);
216 |       };
217 |       this._ws.onerror = (event: Event) => {
218 |         if (this._isDisposed) {
219 |           return;
220 |         }
221 |         reject(event);
222 |       };
223 |     });
224 |   }
225 | 
226 |   private _name: string;
227 |   private _baseUrl: string;
228 |   private _wsUrl: string;
229 |   private _url: string;
230 |   private _token = '';
231 |   private _ajaxSettings = '';
232 |   private _ws: WebSocket = null;
233 |   private _isDisposed = false;
234 |   private _readyPromise: Promise;
235 |   private _isReady = false;
236 |   private _messageReceived = new Signal(this);
237 | }
238 | 
239 | 
240 | /**
241 |  * The static namespace for `DefaultTerminalSession`.
242 |  */
243 | export
244 | namespace DefaultTerminalSession {
245 |   /**
246 |    * Whether the terminal service is available.
247 |    */
248 |   export
249 |   function isAvailable(): boolean {
250 |     let available = String(utils.getConfigOption('terminalsAvailable'));
251 |     return available.toLowerCase() === 'true';
252 |   }
253 | 
254 |   /**
255 |    * Start a new terminal session.
256 |    *
257 |    * @options - The session options to use.
258 |    *
259 |    * @returns A promise that resolves with the session instance.
260 |    */
261 |   export
262 |   function startNew(options: TerminalSession.IOptions = {}): Promise {
263 |     if (!TerminalSession.isAvailable()) {
264 |       throw Private.unavailableMsg;
265 |     }
266 |     let baseUrl = options.baseUrl || utils.getBaseUrl();
267 |     let url = Private.getBaseUrl(baseUrl);
268 |     let ajaxSettings = utils.ajaxSettingsWithToken(options.ajaxSettings, options.token);
269 |     ajaxSettings.method = 'POST';
270 |     ajaxSettings.dataType = 'json';
271 | 
272 |     return utils.ajaxRequest(url, ajaxSettings).then(success => {
273 |       if (success.xhr.status !== 200) {
274 |         throw utils.makeAjaxError(success);
275 |       }
276 |       let name = (success.data as TerminalSession.IModel).name;
277 |       return new DefaultTerminalSession(name, options);
278 |     });
279 |   }
280 | 
281 |   /*
282 |    * Connect to a running session.
283 |    *
284 |    * @param name - The name of the target session.
285 |    *
286 |    * @param options - The session options to use.
287 |    *
288 |    * @returns A promise that resolves with the new session instance.
289 |    *
290 |    * #### Notes
291 |    * If the session was already started via `startNew`, the existing
292 |    * session object is used as the fulfillment value.
293 |    *
294 |    * Otherwise, if `options` are given, we resolve the promise after
295 |    * confirming that the session exists on the server.
296 |    *
297 |    * If the session does not exist on the server, the promise is rejected.
298 |    */
299 |   export
300 |   function connectTo(name: string, options: TerminalSession.IOptions = {}): Promise {
301 |     if (!TerminalSession.isAvailable()) {
302 |       return Promise.reject(Private.unavailableMsg);
303 |     }
304 |     let baseUrl = options.baseUrl || utils.getBaseUrl();
305 |     let url = Private.getTermUrl(baseUrl, name);
306 |     if (url in Private.running) {
307 |       return Promise.resolve(Private.running[url]);
308 |     }
309 |     return listRunning(options).then(models => {
310 |       let index = ArrayExt.findFirstIndex(models, model => {
311 |         return model.name === name;
312 |       });
313 |       if (index !== -1) {
314 |         let session = new DefaultTerminalSession(name, options);
315 |         return Promise.resolve(session);
316 |       }
317 |       return Promise.reject('Could not find session');
318 |     });
319 |   }
320 | 
321 |   /**
322 |    * List the running terminal sessions.
323 |    *
324 |    * @param options - The session options to use.
325 |    *
326 |    * @returns A promise that resolves with the list of running session models.
327 |    */
328 |   export
329 |   function listRunning(options: TerminalSession.IOptions = {}): Promise {
330 |     if (!TerminalSession.isAvailable()) {
331 |       return Promise.reject(Private.unavailableMsg);
332 |     }
333 |     let url = Private.getBaseUrl(options.baseUrl);
334 |     let ajaxSettings = utils.ajaxSettingsWithToken(options.ajaxSettings, options.token);
335 |     ajaxSettings.method = 'GET';
336 |     ajaxSettings.dataType = 'json';
337 | 
338 |     return utils.ajaxRequest(url, ajaxSettings).then(success => {
339 |       if (success.xhr.status !== 200) {
340 |         throw utils.makeAjaxError(success);
341 |       }
342 |       let data = success.data as TerminalSession.IModel[];
343 |       if (!Array.isArray(data)) {
344 |         throw utils.makeAjaxError(success, 'Invalid terminal data');
345 |       }
346 |       // Update the local data store.
347 |       let urls = toArray(map(data, item => {
348 |           return utils.urlPathJoin(url, item.name);
349 |       }));
350 |       each(Object.keys(Private.running), runningUrl => {
351 |         if (urls.indexOf(runningUrl) === -1) {
352 |           let session = Private.running[runningUrl];
353 |           session.terminated.emit(void 0);
354 |           session.dispose();
355 |         }
356 |       });
357 |       return data;
358 |     });
359 |   }
360 | 
361 |   /**
362 |    * Shut down a terminal session by name.
363 |    *
364 |    * @param name - The name of the target session.
365 |    *
366 |    * @param options - The session options to use.
367 |    *
368 |    * @returns A promise that resolves when the session is shut down.
369 |    */
370 |   export
371 |   function shutdown(name: string, options: TerminalSession.IOptions = {}): Promise {
372 |     if (!TerminalSession.isAvailable()) {
373 |       return Promise.reject(Private.unavailableMsg);
374 |     }
375 |     let url = Private.getTermUrl(options.baseUrl, name);
376 |     let ajaxSettings = utils.ajaxSettingsWithToken(options.ajaxSettings, options.token);
377 |     ajaxSettings.method = 'DELETE';
378 |     return utils.ajaxRequest(url, ajaxSettings).then(success => {
379 |       if (success.xhr.status !== 204) {
380 |         throw utils.makeAjaxError(success);
381 |       }
382 |       Private.killTerminal(url);
383 |     }, err => {
384 |       if (err.xhr.status === 404) {
385 |         let response = JSON.parse(err.xhr.responseText) as any;
386 |         console.warn(response['message']);
387 |         Private.killTerminal(url);
388 |         return;
389 |       }
390 |       return Promise.reject(err);
391 |     });
392 |   }
393 | 
394 | }
395 | 
396 | 
397 | /**
398 |  * A namespace for private data.
399 |  */
400 | namespace Private {
401 |   /**
402 |    * A mapping of running terminals by url.
403 |    */
404 |   export
405 |   const running: { [key: string]: DefaultTerminalSession } = Object.create(null);
406 | 
407 |   /**
408 |    * A promise returned for when terminals are unavailable.
409 |    */
410 |   export
411 |   const unavailableMsg = 'Terminals Unavailable';
412 | 
413 |   /**
414 |    * Get the url for a terminal.
415 |    */
416 |   export
417 |   function getTermUrl(baseUrl: string, name: string): string {
418 |     return utils.urlPathJoin(baseUrl, TERMINAL_SERVICE_URL, name);
419 |   }
420 | 
421 |   /**
422 |    * Get the base url.
423 |    */
424 |   export
425 |   function getBaseUrl(baseUrl: string): string {
426 |     return utils.urlPathJoin(baseUrl, TERMINAL_SERVICE_URL);
427 |   }
428 | 
429 |   /**
430 |    * Kill a terminal by url.
431 |    */
432 |   export
433 |   function killTerminal(url: string): void {
434 |     // Update the local data store.
435 |     if (Private.running[url]) {
436 |       let session = Private.running[url];
437 |       session.terminated.emit(void 0);
438 |       session.dispose();
439 |     }
440 |   }
441 | }
442 | 


--------------------------------------------------------------------------------
/src/terminal/index.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) Jupyter Development Team.
2 | // Distributed under the terms of the Modified BSD License.
3 | 
4 | export * from './manager';
5 | export * from './terminal';
6 | 


--------------------------------------------------------------------------------
/src/terminal/manager.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import {
  5 |   ArrayExt, IIterator, iter
  6 | } from '@phosphor/algorithm';
  7 | 
  8 | import {
  9 |   JSONExt
 10 | } from '@phosphor/coreutils';
 11 | 
 12 | import {
 13 |   ISignal, Signal
 14 | } from '@phosphor/signaling';
 15 | 
 16 | import {
 17 |   IAjaxSettings
 18 | } from '../utils';
 19 | 
 20 | import * as utils
 21 |   from '../utils';
 22 | 
 23 | import {
 24 |   TerminalSession
 25 | } from './terminal';
 26 | 
 27 | 
 28 | /**
 29 |  * A terminal session manager.
 30 |  */
 31 | export
 32 | class TerminalManager implements TerminalSession.IManager {
 33 |   /**
 34 |    * Construct a new terminal manager.
 35 |    */
 36 |   constructor(options: TerminalManager.IOptions = {}) {
 37 |     this._baseUrl = options.baseUrl || utils.getBaseUrl();
 38 |     this._wsUrl = options.wsUrl || utils.getWsUrl(this._baseUrl);
 39 |     this._ajaxSettings = JSON.stringify(options.ajaxSettings || {});
 40 | 
 41 |     // Set up state handling if terminals are available.
 42 |     if (TerminalSession.isAvailable()) {
 43 |       // Initialize internal data.
 44 |       this._readyPromise = this._refreshRunning();
 45 | 
 46 |       // Set up polling.
 47 |       this._refreshTimer = (setInterval as any)(() => {
 48 |         this._refreshRunning();
 49 |       }, 10000);
 50 |     }
 51 |   }
 52 | 
 53 |   /**
 54 |    * A signal emitted when the running terminals change.
 55 |    */
 56 |   get runningChanged(): ISignal {
 57 |     return this._runningChanged;
 58 |   }
 59 | 
 60 |   /**
 61 |    * Test whether the terminal manager is disposed.
 62 |    */
 63 |   get isDisposed(): boolean {
 64 |     return this._isDisposed;
 65 |   }
 66 | 
 67 |   /**
 68 |    * The base url of the manager.
 69 |    */
 70 |   get baseUrl(): string {
 71 |     return this._baseUrl;
 72 |   }
 73 | 
 74 |   /**
 75 |    * The base ws url of the manager.
 76 |    */
 77 |   get wsUrl(): string {
 78 |     return this._wsUrl;
 79 |   }
 80 | 
 81 |   /**
 82 |    * The default ajax settings for the manager.
 83 |    */
 84 |   get ajaxSettings(): IAjaxSettings {
 85 |     return JSON.parse(this._ajaxSettings);
 86 |   }
 87 | 
 88 |   /**
 89 |    * Set the default ajax settings for the manager.
 90 |    */
 91 |   set ajaxSettings(value: IAjaxSettings) {
 92 |     this._ajaxSettings = JSON.stringify(value);
 93 |   }
 94 | 
 95 |   /**
 96 |    * Test whether the manger is ready.
 97 |    */
 98 |   get isReady(): boolean {
 99 |     return this._isReady;
100 |   }
101 | 
102 |   /**
103 |    * Dispose of the resources used by the manager.
104 |    */
105 |   dispose(): void {
106 |     if (this.isDisposed) {
107 |       return;
108 |     }
109 |     this._isDisposed = true;
110 |     clearInterval(this._refreshTimer);
111 |     Signal.clearData(this);
112 |     this._running = [];
113 |   }
114 | 
115 |   /**
116 |    * A promise that fulfills when the manager is ready.
117 |    */
118 |   get ready(): Promise {
119 |     return this._readyPromise || Promise.reject('Terminals unavailable');
120 |   }
121 | 
122 |   /**
123 |    * Whether the terminal service is available.
124 |    */
125 |   isAvailable(): boolean {
126 |     return TerminalSession.isAvailable();
127 |   }
128 | 
129 |   /**
130 |    * Create an iterator over the most recent running terminals.
131 |    *
132 |    * @returns A new iterator over the running terminals.
133 |    */
134 |   running(): IIterator {
135 |     return iter(this._running);
136 |   }
137 | 
138 |   /**
139 |    * Create a new terminal session.
140 |    *
141 |    * @param ajaxSettings - The ajaxSettings to use, overrides manager
142 |    *   settings.
143 |    *
144 |    * @returns A promise that resolves with the terminal instance.
145 |    *
146 |    * #### Notes
147 |    * The baseUrl and wsUrl of the options will be forced
148 |    * to the ones used by the manager. The ajaxSettings of the manager
149 |    * will be used unless overridden.
150 |    */
151 |   startNew(options?: TerminalSession.IOptions): Promise {
152 |     return TerminalSession.startNew(this._getOptions(options)).then(session => {
153 |       this._onStarted(session);
154 |       return session;
155 |     });
156 |   }
157 | 
158 |   /*
159 |    * Connect to a running session.
160 |    *
161 |    * @param name - The name of the target session.
162 |    *
163 |    * @param ajaxSettings - The ajaxSettings to use, overrides manager
164 |    *   settings.
165 |    *
166 |    * @returns A promise that resolves with the new session instance.
167 |    *
168 |    * #### Notes
169 |    * The baseUrl and wsUrl of the options will be forced
170 |    * to the ones used by the manager. The ajaxSettings of the manager
171 |    * will be used unless overridden.
172 |    */
173 |   connectTo(name: string, options?: IAjaxSettings): Promise {
174 |     return TerminalSession.connectTo(name, this._getOptions(options)).then(session => {
175 |       this._onStarted(session);
176 |       return session;
177 |     });
178 |   }
179 | 
180 |   /**
181 |    * Shut down a terminal session by name.
182 |    */
183 |   shutdown(name: string): Promise {
184 |     return TerminalSession.shutdown(name, this._getOptions()).then(() => {
185 |       this._onTerminated(name);
186 |     });
187 |   }
188 | 
189 |   /**
190 |    * Force a refresh of the running sessions.
191 |    *
192 |    * @returns A promise that with the list of running sessions.
193 |    *
194 |    * #### Notes
195 |    * This is not typically meant to be called by the user, since the
196 |    * manager maintains its own internal state.
197 |    */
198 |   refreshRunning(): Promise {
199 |     return this._refreshRunning();
200 |   }
201 | 
202 | 
203 |   /**
204 |    * Handle a session terminating.
205 |    */
206 |   private _onTerminated(name: string): void {
207 |     let index = ArrayExt.findFirstIndex(this._running, value => value.name === name);
208 |     if (index !== -1) {
209 |       this._running.splice(index, 1);
210 |       this._runningChanged.emit(this._running.slice());
211 |     }
212 |   }
213 | 
214 |   /**
215 |    * Handle a session starting.
216 |    */
217 |   private _onStarted(session: TerminalSession.ISession): void {
218 |     let name = session.name;
219 |     let index = ArrayExt.findFirstIndex(this._running, value => value.name === name);
220 |     if (index === -1) {
221 |       this._running.push(session.model);
222 |       this._runningChanged.emit(this._running.slice());
223 |     }
224 |     session.terminated.connect(() => {
225 |       this._onTerminated(name);
226 |     });
227 |   }
228 | 
229 |   /**
230 |    * Refresh the running sessions.
231 |    */
232 |   private _refreshRunning(): Promise {
233 |     return TerminalSession.listRunning(this._getOptions({})).then(running => {
234 |       this._isReady = true;
235 |       if (!JSONExt.deepEqual(running, this._running)) {
236 |         this._running = running.slice();
237 |         this._runningChanged.emit(running);
238 |       }
239 |     });
240 |   }
241 | 
242 |   /**
243 |    * Get a set of options to pass.
244 |    */
245 |   private _getOptions(options: TerminalSession.IOptions = {}): TerminalSession.IOptions {
246 |     options.baseUrl = this.baseUrl;
247 |     options.wsUrl = this.wsUrl;
248 |     options.ajaxSettings = options.ajaxSettings || this.ajaxSettings;
249 |     return options;
250 |   }
251 | 
252 |   private _baseUrl = '';
253 |   private _wsUrl = '';
254 |   private _ajaxSettings = '';
255 |   private _running: TerminalSession.IModel[] = [];
256 |   private _isDisposed = false;
257 |   private _isReady = false;
258 |   private _refreshTimer = -1;
259 |   private _readyPromise: Promise;
260 |   private _runningChanged = new Signal(this);
261 | }
262 | 
263 | 
264 | 
265 | /**
266 |  * The namespace for TerminalManager statics.
267 |  */
268 | export
269 | namespace TerminalManager {
270 |   /**
271 |    * The options used to initialize a terminal manager.
272 |    */
273 |   export
274 |   interface IOptions {
275 |     /**
276 |      * The base url.
277 |      */
278 |     baseUrl?: string;
279 | 
280 |     /**
281 |      * The base websocket url.
282 |      */
283 |     wsUrl?: string;
284 | 
285 |     /**
286 |      * The authentication token for the API.
287 |      */
288 |     token?: string;
289 | 
290 |     /**
291 |      * The Ajax settings used for server requests.
292 |      */
293 |     ajaxSettings?: utils.IAjaxSettings;
294 |   }
295 | }
296 | 


--------------------------------------------------------------------------------
/src/terminal/terminal.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import {
  5 |   IIterator
  6 | } from '@phosphor/algorithm';
  7 | 
  8 | import {
  9 |   JSONPrimitive, JSONObject
 10 | } from '@phosphor/coreutils';
 11 | 
 12 | import {
 13 |   IDisposable
 14 | } from '@phosphor/disposable';
 15 | 
 16 | import {
 17 |   ISignal
 18 | } from '@phosphor/signaling';
 19 | 
 20 | import {
 21 |   IAjaxSettings
 22 | } from '../utils';
 23 | 
 24 | import * as utils
 25 |   from '../utils';
 26 | 
 27 | import {
 28 |   DefaultTerminalSession
 29 | } from './default';
 30 | 
 31 | 
 32 | /**
 33 |  * The namespace for ISession statics.
 34 |  */
 35 | export
 36 | namespace TerminalSession {
 37 |   /**
 38 |    * An interface for a terminal session.
 39 |    */
 40 |   export
 41 |   interface ISession extends IDisposable {
 42 |     /**
 43 |      * A signal emitted when the session is shut down.
 44 |      */
 45 |     terminated: ISignal;
 46 | 
 47 |     /**
 48 |      * A signal emitted when a message is received from the server.
 49 |      */
 50 |     messageReceived: ISignal;
 51 | 
 52 |     /**
 53 |      * Get the name of the terminal session.
 54 |      */
 55 |     readonly name: string;
 56 | 
 57 |     /**
 58 |      * The model associated with the session.
 59 |      */
 60 |     readonly model: IModel;
 61 | 
 62 |     /**
 63 |      * The base url of the session.
 64 |      */
 65 |     readonly baseUrl: string;
 66 | 
 67 |     /**
 68 |      * The Ajax settings used for server requests.
 69 |      */
 70 |     ajaxSettings: utils.IAjaxSettings;
 71 | 
 72 |     /**
 73 |      * Test whether the session is ready.
 74 |      */
 75 |     readonly isReady: boolean;
 76 | 
 77 |     /**
 78 |      * A promise that fulfills when the session is initially ready.
 79 |      */
 80 |     readonly ready: Promise;
 81 | 
 82 |     /**
 83 |      * Send a message to the terminal session.
 84 |      */
 85 |     send(message: IMessage): void;
 86 | 
 87 |     /**
 88 |      * Reconnect to the terminal.
 89 |      *
 90 |      * @returns A promise that resolves when the terminal has reconnected.
 91 |      */
 92 |     reconnect(): Promise;
 93 | 
 94 |     /**
 95 |      * Shut down the terminal session.
 96 |      */
 97 |     shutdown(): Promise;
 98 |   }
 99 | 
100 |   /**
101 |    * Test whether the terminal service is available.
102 |    */
103 |   export
104 |   function isAvailable(): boolean {
105 |     return DefaultTerminalSession.isAvailable();
106 |   }
107 | 
108 |   /**
109 |    * Start a new terminal session.
110 |    *
111 |    * @options - The session options to use.
112 |    *
113 |    * @returns A promise that resolves with the session instance.
114 |    */
115 |   export
116 |   function startNew(options?: IOptions): Promise {
117 |     return DefaultTerminalSession.startNew(options);
118 |   }
119 | 
120 |   /*
121 |    * Connect to a running session.
122 |    *
123 |    * @param name - The name of the target session.
124 |    *
125 |    * @param options - The session options to use.
126 |    *
127 |    * @returns A promise that resolves with the new session instance.
128 |    *
129 |    * #### Notes
130 |    * If the session was already started via `startNew`, the existing
131 |    * session object is used as the fulfillment value.
132 |    *
133 |    * Otherwise, if `options` are given, we resolve the promise after
134 |    * confirming that the session exists on the server.
135 |    *
136 |    * If the session does not exist on the server, the promise is rejected.
137 |    */
138 |   export
139 |   function connectTo(name: string, options?: IOptions): Promise {
140 |     return DefaultTerminalSession.connectTo(name, options);
141 |   }
142 | 
143 |   /**
144 |    * List the running terminal sessions.
145 |    *
146 |    * @param options - The session options to use.
147 |    *
148 |    * @returns A promise that resolves with the list of running session models.
149 |    */
150 |   export
151 |   function listRunning(options?: IOptions): Promise {
152 |     return DefaultTerminalSession.listRunning(options);
153 |   }
154 | 
155 |   /**
156 |    * Shut down a terminal session by name.
157 |    *
158 |    * @param name - The name of the target session.
159 |    *
160 |    * @param options - The session options to use.
161 |    *
162 |    * @returns A promise that resolves when the session is shut down.
163 |    */
164 |   export
165 |   function shutdown(name: string, options?: IOptions): Promise {
166 |     return DefaultTerminalSession.shutdown(name, options);
167 |   }
168 | 
169 |   /**
170 |    * The options for intializing a terminal session object.
171 |    */
172 |   export
173 |   interface IOptions extends JSONObject {
174 |     /**
175 |      * The base url.
176 |      */
177 |     baseUrl?: string;
178 | 
179 |     /**
180 |      * The base websocket url.
181 |      */
182 |     wsUrl?: string;
183 | 
184 |     /**
185 |      * The authentication token for the API.
186 |      */
187 |     token?: string;
188 | 
189 |     /**
190 |      * The Ajax settings used for server requests.
191 |      */
192 |     ajaxSettings?: utils.IAjaxSettings;
193 |   }
194 | 
195 |   /**
196 |    * The server model for a terminal session.
197 |    */
198 |   export
199 |   interface IModel extends JSONObject {
200 |     /**
201 |      * The name of the terminal session.
202 |      */
203 |     readonly name: string;
204 |   }
205 | 
206 |   /**
207 |    * A message from the terminal session.
208 |    */
209 |   export
210 |   interface IMessage {
211 |     /**
212 |      * The type of the message.
213 |      */
214 |     readonly type: MessageType;
215 | 
216 |     /**
217 |      * The content of the message.
218 |      */
219 |     readonly content?: JSONPrimitive[];
220 |   }
221 | 
222 |   /**
223 |    * Valid message types for the terminal.
224 |    */
225 |   export
226 |   type MessageType = 'stdout' | 'disconnect' | 'set_size' | 'stdin';
227 | 
228 |   /**
229 |    * The interface for a terminal manager.
230 |    *
231 |    * #### Notes
232 |    * The manager is responsible for maintaining the state of running
233 |    * terminal sessions.
234 |    */
235 |   export
236 |   interface IManager extends IDisposable {
237 |     /**
238 |      * A signal emitted when the running terminals change.
239 |      */
240 |     runningChanged: ISignal;
241 | 
242 |     /**
243 |      * The base url of the manager.
244 |      */
245 |     readonly baseUrl: string;
246 | 
247 |     /**
248 |      * The base ws url of the manager.
249 |      */
250 |     readonly wsUrl: string;
251 | 
252 |     /**
253 |      * The default ajax settings for the manager.
254 |      */
255 |     ajaxSettings?: IAjaxSettings;
256 | 
257 |     /**
258 |      * Test whether the manager is ready.
259 |      */
260 |     readonly isReady: boolean;
261 | 
262 |     /**
263 |      * A promise that fulfills when the manager is ready.
264 |      */
265 |     readonly ready: Promise;
266 | 
267 |     /**
268 |      * Whether the terminal service is available.
269 |      */
270 |     isAvailable(): boolean;
271 | 
272 |     /**
273 |      * Create an iterator over the known running terminals.
274 |      *
275 |      * @returns A new iterator over the running terminals.
276 |      */
277 |     running(): IIterator;
278 | 
279 |     /**
280 |      * Create a new terminal session.
281 |      *
282 |      * @param options - The options used to create the session.
283 |      *
284 |      * @returns A promise that resolves with the terminal instance.
285 |      *
286 |      * #### Notes
287 |      * The baseUrl and wsUrl of the options will be forced
288 |      * to the ones used by the manager. The ajaxSettings of the manager
289 |      * will be used unless overridden.
290 |      */
291 |     startNew(options?: IOptions): Promise;
292 | 
293 |     /*
294 |      * Connect to a running session.
295 |      *
296 |      * @param name - The name of the target session.
297 |      *
298 |      * @param ajaxSettings - The ajaxSettings to use, overrides manager
299 |      *   settings.
300 |      *
301 |      * @returns A promise that resolves with the new session instance.
302 |      *
303 |      * #### Notes
304 |      * The baseUrl and wsUrl of the options will be forced
305 |      * to the ones used by the manager. The ajaxSettings of the manager
306 |      * will be used unless overridden.
307 |      */
308 |     connectTo(name: string, options?: IOptions): Promise;
309 | 
310 |     /**
311 |      * Shut down a terminal session by name.
312 |      *
313 |      * @param name - The name of the terminal session.
314 |      *
315 |      * @returns A promise that resolves when the session is shut down.
316 |      */
317 |     shutdown(name: string): Promise;
318 | 
319 |     /**
320 |      * Force a refresh of the running terminal sessions.
321 |      *
322 |      * @returns A promise that with the list of running sessions.
323 |      *
324 |      * #### Notes
325 |      * This is not typically meant to be called by the user, since the
326 |      * manager maintains its own internal state.
327 |      */
328 |     refreshRunning(): Promise;
329 |   }
330 | }
331 | 


--------------------------------------------------------------------------------
/src/tsconfig.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "compilerOptions": {
 3 |     "declaration": true,
 4 |     "noImplicitAny": true,
 5 |     "noEmitOnError": true,
 6 |     "lib": ["dom", "es5", "es2015.collection", "es2015.promise"],
 7 |     "types": ["text-encoding"],
 8 |     "module": "commonjs",
 9 |     "moduleResolution": "node",
10 |     "target": "ES5",
11 |     "outDir": "../lib"
12 |   },
13 |   "exclude": ["typedoc.d.ts"]
14 | }
15 | 


--------------------------------------------------------------------------------
/src/typedoc.d.ts:
--------------------------------------------------------------------------------
 1 | // Copyright (c) Jupyter Development Team.
 2 | // Distributed under the terms of the Modified BSD License.
 3 | 
 4 | /*
 5 |  * TODO: remove this file and the excludes entry in tsconfig.json
 6 |  * after typedoc understands the lib compiler option and @types packages.
 7 |  */
 8 | /// 
 9 | /// 
10 | /// 
11 | /// 
12 | /// 
13 | 


--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) Jupyter Development Team.
2 | // Distributed under the terms of the Modified BSD License.
3 | 
4 | /*
5 |  * TODO: switch the below typings to @types packages when they are available.
6 |  */
7 | /// 
8 | /// 
9 | 


--------------------------------------------------------------------------------
/test/integration_test.py:
--------------------------------------------------------------------------------
  1 | # Copyright (c) Jupyter Development Team.
  2 | # Distributed under the terms of the Modified BSD License.
  3 | 
  4 | from __future__ import print_function, absolute_import
  5 | 
  6 | from __future__ import print_function, absolute_import
  7 | 
  8 | import atexit
  9 | import json
 10 | import os
 11 | from subprocess import Popen
 12 | import sys
 13 | import shutil
 14 | import tempfile
 15 | 
 16 | from tornado import gen
 17 | from tornado.ioloop import IOLoop
 18 | from notebook.notebookapp import NotebookApp
 19 | from traitlets import Bool, Unicode
 20 | 
 21 | 
 22 | HERE = os.path.dirname(__file__)
 23 | 
 24 | 
 25 | def create_notebook_dir():
 26 |     """Create a temporary directory with some file structure."""
 27 |     root_dir = tempfile.mkdtemp(prefix='mock_contents')
 28 |     os.mkdir(os.path.join(root_dir, 'src'))
 29 |     with open(os.path.join(root_dir, 'src', 'temp.txt'), 'w') as fid:
 30 |         fid.write('hello')
 31 |     atexit.register(lambda: shutil.rmtree(root_dir, True))
 32 |     return root_dir
 33 | 
 34 | 
 35 | def get_command(nbapp):
 36 |     """Get the command to run"""
 37 |     terminalsAvailable = nbapp.web_app.settings['terminals_available']
 38 |     # Compatibility with Notebook 4.2.
 39 |     token = getattr(nbapp, 'token', '')
 40 |     cmd = ['mocha', '--timeout', '20000',
 41 |            '--retries', '2',
 42 |            'build/integration.js',
 43 |            '--baseUrl=%s' % nbapp.connection_url,
 44 |            '--terminalsAvailable=%s' % terminalsAvailable]
 45 |     if nbapp.token:
 46 |         cmd.append('--token=%s' % token)
 47 |     return cmd
 48 | 
 49 | 
 50 | @gen.coroutine
 51 | def run(cmd):
 52 |     """Run the cmd and exit with the return code"""
 53 |     yield gen.moment  # sync up with the ioloop
 54 | 
 55 |     shell = os.name == 'nt'
 56 |     proc = Popen(cmd, shell=shell)
 57 |     print('\n\nRunning command: "%s"\n\n' % ' '.join(cmd))
 58 | 
 59 |     # Poll the process once per second until finished.
 60 |     while 1:
 61 |         yield gen.sleep(1)
 62 |         if proc.poll() is not None:
 63 |             break
 64 | 
 65 |     exit(proc.returncode)
 66 | 
 67 | 
 68 | @gen.coroutine
 69 | def exit(code):
 70 |     """Safely stop the app and then exit with the given code."""
 71 |     yield gen.moment   # sync up with the ioloop
 72 |     IOLoop.current().stop()
 73 |     sys.exit(code)
 74 | 
 75 | 
 76 | class TestApp(NotebookApp):
 77 |     """A notebook app that runs a mocha test."""
 78 | 
 79 |     open_browser = Bool(False)
 80 |     notebook_dir = Unicode(create_notebook_dir())
 81 | 
 82 | 
 83 | def main():
 84 |     """Run the unit test."""
 85 |     app = TestApp()
 86 | 
 87 |     if app.version == '4.3.0':
 88 |         msg = ('Cannot run unit tests against Notebook 4.3.0.  '
 89 |                'Please upgrade to Notebook 4.3.1+')
 90 |         print(msg)
 91 |         sys.exit(1)
 92 | 
 93 |     app.initialize([])  # reserve sys.argv for the command
 94 |     cmd = get_command(app)
 95 |     run(cmd)
 96 | 
 97 |     try:
 98 |         app.start()
 99 |     except KeyboardInterrupt:
100 |         exit(1)
101 | 
102 | 
103 | if __name__ == '__main__':
104 |     main()
105 | 
106 | 
107 | 


--------------------------------------------------------------------------------
/test/src/config/config.spec.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import expect = require('expect.js');
  5 | 
  6 | import {
  7 |   JSONObject
  8 | } from '@phosphor/coreutils';
  9 | 
 10 | import {
 11 |   ConfigSection, ConfigWithDefaults
 12 | } from '../../../lib/config';
 13 | 
 14 | import {
 15 |   RequestHandler, ajaxSettings, expectAjaxError
 16 | } from '../utils';
 17 | 
 18 | 
 19 | describe('config', () => {
 20 | 
 21 |   describe('ConfigSection.create()', () => {
 22 | 
 23 |     it('should complete properly', (done) => {
 24 |       let handler = new RequestHandler(() => {
 25 |         handler.respond(200, {});
 26 |       });
 27 |       ConfigSection.create({ name: 'test' }).then(config => {
 28 |         done();
 29 |       });
 30 |     });
 31 | 
 32 |     it('should accept ajaxOptions', (done) => {
 33 |       let handler = new RequestHandler(() => {
 34 |         handler.respond(200, {});
 35 |       });
 36 |       ConfigSection.create({ name: 'test', ajaxSettings }).then(config => {
 37 |         done();
 38 |       });
 39 |     });
 40 | 
 41 |     it('should load a config', (done) => {
 42 |       let handler = new RequestHandler(() => {
 43 |         handler.respond(200, { foo: 'bar' });
 44 |       });
 45 |       ConfigSection.create({ name: 'test' }).then(config => {
 46 |         expect(config.data['foo']).to.be('bar');
 47 |         done();
 48 |       });
 49 |     });
 50 | 
 51 |     it('should fail for an incorrect response', (done) => {
 52 |       let handler = new RequestHandler(() => {
 53 |         handler.respond(201, { });
 54 |       });
 55 |       let configPromise = ConfigSection.create({ name: 'test' });
 56 |       expectAjaxError(configPromise, done, 'Invalid Status: 201');
 57 |     });
 58 | 
 59 |   });
 60 | 
 61 |   describe('#update()', () => {
 62 | 
 63 |     it('should update a config', (done) => {
 64 |       let handler = new RequestHandler(() => {
 65 |         handler.respond(200, {});
 66 |       });
 67 |       ConfigSection.create({ name: 'test' }).then(config => {
 68 |         handler.onRequest = () => {
 69 |           handler.respond(200, config.data );
 70 |         };
 71 |         let update = config.update( { foo: 'baz', spam: 'eggs' });
 72 |         update.then((data: any) => {
 73 |           expect(data.foo).to.be('baz');
 74 |           expect(config.data['foo']).to.be('baz');
 75 |           expect(data['spam']).to.be('eggs');
 76 |           expect(config.data['spam']).to.be('eggs');
 77 |           done();
 78 |         });
 79 |       });
 80 |     });
 81 | 
 82 |     it('should accept ajaxOptions', (done) => {
 83 |       let handler = new RequestHandler(() => {
 84 |         handler.respond(200, {});
 85 |       });
 86 |       ConfigSection.create({ name: 'test', ajaxSettings }).then(config => {
 87 |         handler.onRequest = () => {
 88 |           handler.respond(200, config.data );
 89 |         };
 90 |         let update = config.update({ foo: 'baz', spam: 'eggs' });
 91 |         update.then((data: any) => {
 92 |           expect(data.foo).to.be('baz');
 93 |           done();
 94 |         });
 95 |       });
 96 |     });
 97 | 
 98 |     it('should fail for an incorrect response', (done) => {
 99 |       let handler = new RequestHandler(() => {
100 |         handler.respond(200, {});
101 |       });
102 |       ConfigSection.create({ name: 'test' }).then(config => {
103 |         handler.onRequest = () => {
104 |           handler.respond(201, { });
105 |         };
106 |         let update = config.update({ foo: 'baz' });
107 |         expectAjaxError(update, done, 'Invalid Status: 201');
108 |       });
109 |     });
110 | 
111 |   });
112 | 
113 | });
114 | 
115 | 
116 | describe('jupyter.services - ConfigWithDefaults', () => {
117 | 
118 |   describe('#constructor()', () => {
119 | 
120 |     it('should complete properly', (done) => {
121 |       let handler = new RequestHandler(() => {
122 |         handler.respond(200, { testclass: { foo: 'bar' } });
123 |       });
124 |       let defaults: JSONObject = { spam: 'eggs' };
125 |       let className = 'testclass';
126 |       ConfigSection.create({ name: 'test' }).then(section => {
127 |         let config = new ConfigWithDefaults({ section, defaults, className });
128 |         expect(config).to.be.a(ConfigWithDefaults);
129 |         done();
130 |       });
131 |     });
132 | 
133 |   });
134 | 
135 |   describe('#get()', () => {
136 | 
137 |     it('should get a new config value', (done) => {
138 |       let handler = new RequestHandler(() => {
139 |         handler.respond(200, { testclass: { foo: 'bar' } });
140 |       });
141 |       let defaults: JSONObject = { foo: 'bar' };
142 |       let className = 'testclass';
143 |       ConfigSection.create({ name: 'test' }).then(section => {
144 |         let config = new ConfigWithDefaults({ section, defaults, className });
145 |         let data = config.get('foo');
146 |         expect(data).to.be('bar');
147 |         done();
148 |       });
149 |     });
150 | 
151 |     it('should get a default config value', (done) => {
152 |       let handler = new RequestHandler(() => {
153 |         handler.respond(200, { testclass: { foo: 'bar' } });
154 |       });
155 |       let defaults: JSONObject = { spam: 'eggs' };
156 |       let className = 'testclass';
157 |       ConfigSection.create({ name: 'test' }).then(section => {
158 |         let config = new ConfigWithDefaults({ section, defaults, className });
159 |         let data = config.get('spam');
160 |         expect(data).to.be('eggs');
161 |         done();
162 |       });
163 |     });
164 | 
165 |     it('should get a default config value with no class', (done) => {
166 |       let handler = new RequestHandler(() => {
167 |         handler.respond(200, { foo: 'bar' });
168 |       });
169 |       let defaults: JSONObject = { spam: 'eggs' };
170 |       let className = 'testclass';
171 |       ConfigSection.create({ name: 'test' }).then(section => {
172 |         let config = new ConfigWithDefaults({ section, defaults, className });
173 |         let data = config.get('spam');
174 |         expect(data).to.be('eggs');
175 |         done();
176 |       });
177 |     });
178 | 
179 |     it('should get a falsey value', (done) => {
180 |       let handler = new RequestHandler(() => {
181 |         handler.respond(200, { testclass: { foo: 0 } });
182 |       });
183 |       let defaults: JSONObject = { foo: true };
184 |       let className = 'testclass';
185 |       ConfigSection.create({ name: 'test' }).then(section => {
186 |         let config = new ConfigWithDefaults({ section, defaults, className });
187 |         let data = config.get('foo');
188 |         expect(data).to.not.be.ok();
189 |         done();
190 |       });
191 |     });
192 |   });
193 | 
194 |   describe('#set()', () => {
195 | 
196 |     it('should set a value in a class immediately', (done) => {
197 |       let handler = new RequestHandler(() => {
198 |         handler.respond(200, {});
199 |       });
200 |       let className = 'testclass';
201 |       ConfigSection.create({ name: 'test' }).then(section => {
202 |         let config = new ConfigWithDefaults({ section, className });
203 |         handler.onRequest = () => {
204 |           handler.respond(200, {});
205 |           done();
206 |         };
207 |         let set = config.set('foo', 'bar');
208 |         expect(set).to.be.a(Promise);
209 |         let data = section.data['testclass'] as JSONObject;
210 |         expect(data['foo']).to.be('bar');
211 |       });
212 |     });
213 | 
214 |     it('should set a top level value', (done) => {
215 |       let handler = new RequestHandler(() => {
216 |         handler.respond(200, {});
217 |       });
218 |       ConfigSection.create({ name: 'test' }).then(section => {
219 |         handler.onRequest = () => {
220 |           handler.respond(200, {foo: 'bar'});
221 |         };
222 |         let config = new ConfigWithDefaults({ section });
223 |         let set = config.set('foo', 'bar');
224 |         expect(section.data['foo']).to.be('bar');
225 |         set.then((data) => {
226 |           expect(section.data['foo']).to.be('bar');
227 |           done();
228 |         });
229 |       });
230 | 
231 |     });
232 | 
233 |     it('should fail for an invalid response', (done) => {
234 |       let handler = new RequestHandler(() => {
235 |         handler.respond(200, {});
236 |       });
237 |       ConfigSection.create({ name: 'test' }).then(section => {
238 |         handler.onRequest = () => {
239 |           handler.respond(201, {foo: 'bar'});
240 |         };
241 |         let config = new ConfigWithDefaults({ section });
242 |         let set = config.set('foo', 'bar');
243 |         expect(section.data['foo']).to.be('bar');
244 |         expectAjaxError(set, done, 'Invalid Status: 201');
245 |       });
246 | 
247 |     });
248 | 
249 |   });
250 | 
251 | });
252 | 


--------------------------------------------------------------------------------
/test/src/contents/validate.spec.ts:
--------------------------------------------------------------------------------
 1 | // Copyright (c) Jupyter Development Team.
 2 | // Distributed under the terms of the Modified BSD License.
 3 | 
 4 | import expect = require('expect.js');
 5 | 
 6 | import {
 7 |   validateContentsModel, validateCheckpointModel
 8 | } from '../../../lib/contents/validate';
 9 | 
10 | import {
11 |   DEFAULT_FILE
12 | } from '../utils';
13 | 
14 | 
15 | describe('validate', () => {
16 | 
17 |   describe('validateContentsModel()', () => {
18 | 
19 |     it('should pass with valid data', () => {
20 |       validateContentsModel(DEFAULT_FILE);
21 |     });
22 | 
23 |     it('should fail on missing data', () => {
24 |       let model = JSON.parse(JSON.stringify(DEFAULT_FILE));
25 |       delete model['path'];
26 |       expect(() => validateContentsModel(model)).to.throwError();
27 |     });
28 | 
29 |     it('should fail on incorrect data', () => {
30 |       let model = JSON.parse(JSON.stringify(DEFAULT_FILE));
31 |       model.type = 1;
32 |       expect(() => validateContentsModel(model)).to.throwError();
33 |     });
34 | 
35 |   });
36 | 
37 |   describe('validateCheckpointModel()', () => {
38 | 
39 |     it('should pass with valid data', () => {
40 |       validateCheckpointModel({ id: 'foo', last_modified: 'yesterday '});
41 |     });
42 | 
43 |     it('should fail on missing data', () => {
44 |       let model = { id: 'foo' };
45 |       expect(() => validateCheckpointModel(model as any)).to.throwError();
46 |     });
47 | 
48 |     it('should fail on incorrect data', () => {
49 |       let model = { id: 1, last_modified: '1'};
50 |       expect(() => validateCheckpointModel(model as any)).to.throwError();
51 |     });
52 | 
53 |   });
54 | 
55 | });
56 | 


--------------------------------------------------------------------------------
/test/src/kernel/manager.spec.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import expect = require('expect.js');
  5 | 
  6 | import {
  7 |   toArray
  8 | } from '@phosphor/algorithm';
  9 | 
 10 | import {
 11 |   JSONExt
 12 | } from '@phosphor/coreutils';
 13 | 
 14 | import {
 15 |   uuid, copy
 16 | } from '../../../lib/utils';
 17 | 
 18 | import {
 19 |   KernelManager, Kernel
 20 | } from '../../../lib/kernel';
 21 | 
 22 | import {
 23 |   KernelTester, KERNEL_OPTIONS, PYTHON_SPEC, KERNELSPECS
 24 | } from '../utils';
 25 | 
 26 | 
 27 | 
 28 | let PYTHON3_SPEC = JSON.parse(JSON.stringify(PYTHON_SPEC));
 29 | PYTHON3_SPEC.name = 'Python3';
 30 | PYTHON3_SPEC.display_name = 'python3';
 31 | 
 32 | 
 33 | describe('kernel/manager', () => {
 34 | 
 35 |   let tester: KernelTester;
 36 |   let manager: KernelManager;
 37 |   let data: Kernel.IModel[];
 38 | 
 39 |   beforeEach((done) => {
 40 |     tester = new KernelTester();
 41 |     data = [{ id: uuid(), name: 'test' },
 42 |             { id: uuid(), name: 'test2' }];
 43 |     tester.runningKernels = data;
 44 |     manager = new KernelManager();
 45 |     expect(manager.specs).to.be(null);
 46 |     expect(manager.running().next()).to.be(void 0);
 47 |     manager.ready.then(done, done);
 48 |   });
 49 | 
 50 |   afterEach((done) => {
 51 |     manager.ready.then(() => {
 52 |       manager.dispose();
 53 |       tester.dispose();
 54 |       done();
 55 |     }).catch(done);
 56 |   });
 57 | 
 58 |   describe('KernelManager', () => {
 59 | 
 60 |     describe('#constructor()', () => {
 61 | 
 62 |       it('should take the options as an argument', () => {
 63 |         manager.dispose();
 64 |         manager = new KernelManager(KERNEL_OPTIONS);
 65 |         expect(manager instanceof KernelManager).to.be(true);
 66 |       });
 67 | 
 68 |     });
 69 | 
 70 |     describe('#baseUrl', () => {
 71 | 
 72 |       it('should get the base url of the server', () => {
 73 |         manager.dispose();
 74 |         manager = new KernelManager({ baseUrl: 'foo' });
 75 |         expect(manager.baseUrl).to.be('foo');
 76 |       });
 77 | 
 78 |     });
 79 | 
 80 |     describe('#wsUrl', () => {
 81 | 
 82 |       it('should get the ws url of the server', () => {
 83 |         manager.dispose();
 84 |         manager = new KernelManager({ wsUrl: 'bar' });
 85 |         expect(manager.wsUrl).to.be('bar');
 86 |       });
 87 | 
 88 |     });
 89 | 
 90 |     describe('#ajaxSettings', () => {
 91 | 
 92 |       it('should get the ajax sessions of the server', () => {
 93 |         manager.dispose();
 94 |         let ajaxSettings = { withCredentials: true };
 95 |         manager = new KernelManager({ ajaxSettings });
 96 |         expect(manager.ajaxSettings).to.eql(ajaxSettings);
 97 |       });
 98 | 
 99 |     });
100 | 
101 |     describe('#specs', () => {
102 | 
103 |       it('should get the kernel specs', () => {
104 |         expect(manager.specs.default).to.be(KERNELSPECS.default);
105 |       });
106 | 
107 |     });
108 | 
109 |     describe('#running()', () => {
110 | 
111 |       it('should get the running sessions', () => {
112 |         let test = JSONExt.deepEqual(toArray(data), toArray(manager.running()));
113 |         expect(test).to.be(true);
114 |       });
115 | 
116 |     });
117 | 
118 |     describe('#specsChanged', () => {
119 | 
120 |       it('should be emitted when the specs change', (done) => {
121 |         let specs = copy(KERNELSPECS) as Kernel.ISpecModels;
122 |         specs.default = 'shell';
123 |         tester.specs = specs;
124 |         manager.specsChanged.connect((sender, args) => {
125 |           expect(sender).to.be(manager);
126 |           expect(args.default).to.be(specs.default);
127 |           done();
128 |         });
129 |         manager.refreshSpecs();
130 |       });
131 | 
132 |     });
133 | 
134 |     describe('#runningChanged', () => {
135 | 
136 |       it('should be emitted in refreshRunning when the running kernels changed', (done) => {
137 |         data = [{ id: uuid(), name: 'test' }];
138 |         tester.runningKernels = data;
139 |         manager.runningChanged.connect((sender, args) => {
140 |           expect(sender).to.be(manager);
141 |           expect(JSONExt.deepEqual(toArray(args), data)).to.be(true);
142 |           done();
143 |         });
144 |         manager.refreshRunning();
145 |       });
146 | 
147 |       it('should be emitted when a kernel is shut down', (done) => {
148 |         manager.startNew().then(kernel => {
149 |           manager.runningChanged.connect(() => {
150 |             manager.dispose();
151 |             done();
152 |           });
153 |           return kernel.shutdown();
154 |         }).catch(done);
155 |       });
156 | 
157 |     });
158 | 
159 |     describe('#isReady', () => {
160 | 
161 |       it('should test whether the manager is ready', (done) => {
162 |         manager.dispose();
163 |         manager = new KernelManager();
164 |         expect(manager.isReady).to.be(false);
165 |         manager.ready.then(() => {
166 |           expect(manager.isReady).to.be(true);
167 |           done();
168 |         }).catch(done);
169 |       });
170 | 
171 |     });
172 | 
173 |     describe('#ready', () => {
174 | 
175 |       it('should resolve when the manager is ready', (done) => {
176 |         manager.ready.then(done, done);
177 |       });
178 | 
179 |     });
180 | 
181 |     describe('#refreshSpecs()', () => {
182 | 
183 |       it('should update list of kernel specs', (done) => {
184 |         let specs = copy(KERNELSPECS) as Kernel.ISpecModels;
185 |         specs.default = 'shell';
186 |         tester.specs = specs;
187 |         manager.refreshSpecs().then(() => {
188 |           expect(manager.specs.default).to.be(specs.default);
189 |           done();
190 |         }).catch(done);
191 |       });
192 | 
193 |     });
194 | 
195 |     describe('#refreshRunning()', () => {
196 | 
197 |       it('should update the running kernels', (done) => {
198 |         data = [{ id: uuid(), name: 'test' }];
199 |         tester.runningKernels = data;
200 |         manager.refreshRunning().then(() => {
201 |           let running = toArray(manager.running());
202 |           expect(running[0]).to.eql(data[0]);
203 |           expect(running[1]).to.be(void 0);
204 |           done();
205 |         });
206 |       });
207 | 
208 |     });
209 | 
210 |     describe('#startNew()', () => {
211 | 
212 |       it('should start a new kernel', (done) => {
213 |         manager.startNew().then(kernel => {
214 |           expect(kernel.status).to.be('unknown');
215 |           kernel.statusChanged.connect(() => {
216 |             if (kernel.status === 'starting') {
217 |               kernel.shutdown().then(done, done);
218 |             }
219 |           });
220 |         });
221 | 
222 |       });
223 | 
224 |       it('should emit a runningChanged signal', (done) => {
225 |         manager.runningChanged.connect(() => {
226 |           done();
227 |         });
228 |         manager.startNew();
229 |       });
230 | 
231 |     });
232 | 
233 |     describe('#findById()', () => {
234 | 
235 |       it('should find an existing kernel by id', (done) => {
236 |         let id = uuid();
237 |         tester.runningKernels = [{ name: 'foo', id }];
238 |         manager.findById(id).then(model => {
239 |           expect(model.name).to.be('foo');
240 |           expect(model.id).to.be(id);
241 |         }).then(done, done);
242 |       });
243 | 
244 |     });
245 | 
246 |     describe('#connectTo()', () => {
247 | 
248 |       it('should connect to an existing kernel', (done) => {
249 |         let id = uuid();
250 |         tester.runningKernels = [{ name: 'foo', id }];
251 |         return manager.connectTo(id).then(kernel => {
252 |           expect(kernel.name).to.be('foo');
253 |           expect(kernel.id).to.be(id);
254 |           return kernel.shutdown();
255 |         }).then(done, done);
256 |       });
257 | 
258 |       it('should emit a runningChanged signal', (done) => {
259 |         manager.runningChanged.connect(() => {
260 |           done();
261 |         });
262 |         let id = uuid();
263 |         tester.runningKernels = [{ name: 'foo', id }];
264 |         manager.connectTo(id);
265 |       });
266 | 
267 |     });
268 | 
269 |     describe('shutdown()', () => {
270 | 
271 |       it('should shut down a kernel by id', (done) => {
272 |         manager.shutdown('foo').then(done, done);
273 |       });
274 | 
275 |       it('should emit a runningChanged signal', (done) => {
276 |         manager.runningChanged.connect((sender, args) => {
277 |           done();
278 |         });
279 |         manager.shutdown(data[0].id);
280 |       });
281 | 
282 |     });
283 | 
284 |   });
285 | 
286 | });
287 | 
288 | 


--------------------------------------------------------------------------------
/test/src/kernel/messages.spec.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import expect = require('expect.js');
  5 | 
  6 | import {
  7 |   KernelMessage
  8 | } from '../../../lib/kernel';
  9 | 
 10 | 
 11 | describe('kernel/messages', () => {
 12 | 
 13 |   describe('KernelMessage.isStreamMsg()', () => {
 14 | 
 15 |     it('should check for a stream message type', () => {
 16 |       let msg = KernelMessage.createMessage({
 17 |         msgType: 'stream', channel: 'iopub', session: 'baz'
 18 |       });
 19 |       expect(KernelMessage.isStreamMsg(msg)).to.be(true);
 20 |       msg = KernelMessage.createMessage({
 21 |         msgType: 'foo', channel: 'iopub', session: 'baz'
 22 |       });
 23 |       expect(KernelMessage.isStreamMsg(msg)).to.be(false);
 24 |     });
 25 | 
 26 |   });
 27 | 
 28 |   describe('KernelMessage.isDisplayDataMsg()', () => {
 29 | 
 30 |     it('should check for a display data message type', () => {
 31 |       let msg = KernelMessage.createMessage({
 32 |         msgType: 'display_data', channel: 'iopub', session: 'baz'
 33 |       });
 34 |       expect(KernelMessage.isDisplayDataMsg(msg)).to.be(true);
 35 |       msg = KernelMessage.createMessage({
 36 |         msgType: 'foo', channel: 'iopub', session: 'baz'
 37 |       });
 38 |       expect(KernelMessage.isDisplayDataMsg(msg)).to.be(false);
 39 |     });
 40 | 
 41 |   });
 42 | 
 43 |   describe('KernelMessage.isExecuteInputMsg()', () => {
 44 | 
 45 |     it('should check for a execute input message type', () => {
 46 |       let msg = KernelMessage.createMessage({
 47 |         msgType: 'execute_input', channel: 'iopub', session: 'baz'
 48 |       });
 49 |       expect(KernelMessage.isExecuteInputMsg(msg)).to.be(true);
 50 |       msg = KernelMessage.createMessage({
 51 |         msgType: 'foo', channel: 'iopub', session: 'baz'
 52 |       });
 53 |       expect(KernelMessage.isExecuteInputMsg(msg)).to.be(false);
 54 |     });
 55 | 
 56 |   });
 57 | 
 58 |   describe('KernelMessage.isExecuteResultMsg()', () => {
 59 | 
 60 |     it('should check for an execute result message type', () => {
 61 |       let msg = KernelMessage.createMessage({
 62 |         msgType: 'execute_result', channel: 'iopub', session: 'baz'
 63 |       });
 64 |       expect(KernelMessage.isExecuteResultMsg(msg)).to.be(true);
 65 |       msg = KernelMessage.createMessage({
 66 |         msgType: 'foo', channel: 'iopub', session: 'baz'
 67 |       });
 68 |       expect(KernelMessage.isExecuteResultMsg(msg)).to.be(false);
 69 |     });
 70 | 
 71 |   });
 72 | 
 73 |   describe('KernelMessage.isStatusMsg()', () => {
 74 | 
 75 |     it('should check for a status message type', () => {
 76 |       let msg = KernelMessage.createMessage({
 77 |         msgType: 'status', channel: 'iopub', session: 'baz'
 78 |       });
 79 |       expect(KernelMessage.isStatusMsg(msg)).to.be(true);
 80 |       msg = KernelMessage.createMessage({
 81 |         msgType: 'foo', channel: 'iopub', session: 'baz'
 82 |       });
 83 |       expect(KernelMessage.isStatusMsg(msg)).to.be(false);
 84 |     });
 85 | 
 86 |   });
 87 | 
 88 |   describe('KernelMessage.isClearOutputMsg()', () => {
 89 | 
 90 |     it('should check for a clear output message type', () => {
 91 |       let msg = KernelMessage.createMessage({
 92 |         msgType: 'clear_output', channel: 'iopub', session: 'baz'
 93 |       });
 94 |       expect(KernelMessage.isClearOutputMsg(msg)).to.be(true);
 95 |       msg = KernelMessage.createMessage({
 96 |         msgType: 'foo', channel: 'iopub', session: 'baz'
 97 |       });
 98 |       expect(KernelMessage.isClearOutputMsg(msg)).to.be(false);
 99 |     });
100 | 
101 |   });
102 | 
103 |   describe('KernelMessage.isCommOpenMsg()', () => {
104 | 
105 |     it('should check for a comm open message type', () => {
106 |       let msg = KernelMessage.createMessage({
107 |         msgType: 'comm_open', channel: 'iopub', session: 'baz'
108 |       });
109 |       expect(KernelMessage.isCommOpenMsg(msg)).to.be(true);
110 |       msg = KernelMessage.createMessage({
111 |         msgType: 'foo', channel: 'iopub', session: 'baz'
112 |       });
113 |       expect(KernelMessage.isCommOpenMsg(msg)).to.be(false);
114 |     });
115 | 
116 |   });
117 | 
118 |   describe('KernelMessage.isErrorMsg()', () => {
119 | 
120 |     it('should check for an message type', () => {
121 |       let msg = KernelMessage.createMessage({
122 |         msgType: 'error', channel: 'iopub', session: 'baz'
123 |       });
124 |       expect(KernelMessage.isErrorMsg(msg)).to.be(true);
125 |       msg = KernelMessage.createMessage({
126 |         msgType: 'foo', channel: 'iopub', session: 'baz'
127 |       });
128 |       expect(KernelMessage.isErrorMsg(msg)).to.be(false);
129 |     });
130 | 
131 |   });
132 | 
133 |   describe('KernelMessage.isInputRequestMsg()', () => {
134 | 
135 |     it('should check for an input_request message type', () => {
136 |       let msg = KernelMessage.createMessage({
137 |         msgType: 'input_request', channel: 'stdin', session: 'baz'
138 |       });
139 |       expect(KernelMessage.isInputRequestMsg(msg)).to.be(true);
140 |       msg = KernelMessage.createMessage({
141 |         msgType: 'foo', channel: 'stdin', session: 'baz'
142 |       });
143 |       expect(KernelMessage.isStatusMsg(msg)).to.be(false);
144 |     });
145 | 
146 |   });
147 | 
148 | });
149 | 
150 | 


--------------------------------------------------------------------------------
/test/src/kernel/validate.spec.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import expect = require('expect.js');
  5 | 
  6 | import {
  7 |   Kernel, KernelMessage
  8 | } from '../../../lib/kernel';
  9 | 
 10 | import {
 11 |   validateMessage, validateModel, validateSpecModel, validateSpecModels
 12 | } from '../../../lib/kernel/validate';
 13 | 
 14 | import {
 15 |   PYTHON_SPEC
 16 | } from '../utils';
 17 | 
 18 | 
 19 | describe('kernel/validate', () => {
 20 | 
 21 |   describe('validateMessage', () => {
 22 | 
 23 |     it('should pass a valid message', () => {
 24 |       let msg = KernelMessage.createMessage({
 25 |         msgType: 'comm_msg', channel: 'iopub', session: 'foo'
 26 |       }, { comm_id: 'foo', data: {} });
 27 |       validateMessage(msg);
 28 |     });
 29 | 
 30 |     it('should throw if missing a field', () => {
 31 |       let msg = KernelMessage.createMessage({
 32 |         msgType: 'comm_msg', channel: 'iopub', session: 'baz'
 33 |       });
 34 |       delete msg.channel;
 35 |       expect(() => validateMessage(msg)).to.throwError();
 36 |     });
 37 | 
 38 |     it('should throw if a field is invalid', () => {
 39 |       let msg = KernelMessage.createMessage({
 40 |         msgType: 'comm_msg', channel: 'iopub', session: 'baz'
 41 |       });
 42 |       (msg as any).header.username = 1;
 43 |       expect(() => validateMessage(msg)).to.throwError();
 44 |     });
 45 | 
 46 |     it('should throw if the parent header is given an invalid', () => {
 47 |       let msg = KernelMessage.createMessage({
 48 |         msgType: 'comm_msg', channel: 'iopub', session: 'baz'
 49 |       });
 50 |       msg.parent_header = msg.header;
 51 |       (msg as any).parent_header.username = 1;
 52 |       expect(() => validateMessage(msg)).to.throwError();
 53 |     });
 54 | 
 55 |     it('should throw if the channel is not a string', () => {
 56 |       let msg = KernelMessage.createMessage({
 57 |         msgType: 'comm_msg', channel: 'iopub', session: 'baz'
 58 |       });
 59 |       (msg as any).channel = 1;
 60 |       expect(() => validateMessage(msg)).to.throwError();
 61 |     });
 62 | 
 63 |     it('should validate an iopub message', () => {
 64 |       let msg = KernelMessage.createMessage({
 65 |         msgType: 'comm_close', channel: 'iopub', session: 'baz'
 66 |       }, { comm_id: 'foo' });
 67 |       validateMessage(msg);
 68 |     });
 69 | 
 70 |     it('should ignore on an unknown iopub message type', () => {
 71 |       let msg = KernelMessage.createMessage({
 72 |         msgType: 'foo', channel: 'iopub', session: 'baz'
 73 |       }, { });
 74 |       validateMessage(msg);
 75 |     });
 76 | 
 77 |     it('should throw on missing iopub message content', () => {
 78 |       let msg = KernelMessage.createMessage({
 79 |         msgType: 'error', channel: 'iopub', session: 'baz'
 80 |       }, { });
 81 |       expect(() => validateMessage(msg)).to.throwError();
 82 |     });
 83 | 
 84 |     it('should throw on invalid iopub message content', () => {
 85 |       let msg = KernelMessage.createMessage({
 86 |         msgType: 'clear_output', channel: 'iopub', session: 'baz'
 87 |       }, { wait: 1 });
 88 |       expect(() => validateMessage(msg)).to.throwError();
 89 |     });
 90 | 
 91 |   });
 92 | 
 93 |   describe('#validateModel()', () => {
 94 | 
 95 |     it('should pass a valid id', () => {
 96 |       let id: Kernel.IModel = { name: 'foo', id: 'baz' };
 97 |       validateModel(id);
 98 |     });
 99 | 
100 |     it('should fail on missing data', () => {
101 |       expect(() => validateModel({ name: 'foo' })).to.throwError();
102 |       expect(() => validateModel({ id: 'foo' })).to.throwError();
103 |     });
104 | 
105 |   });
106 | 
107 |   describe('#validateSpecModel', () => {
108 | 
109 |     it('should pass with valid data', () => {
110 |       validateSpecModel(PYTHON_SPEC);
111 |     });
112 | 
113 |     it('should fail on missing data', () => {
114 |       let spec = JSON.parse(JSON.stringify(PYTHON_SPEC));
115 |       delete spec['name'];
116 |       expect(() => validateSpecModel(spec)).to.throwError();
117 |     });
118 | 
119 |     it('should fail on incorrect data', () => {
120 |       let spec = JSON.parse(JSON.stringify(PYTHON_SPEC));
121 |       spec.spec.language = 1;
122 |       expect(() => validateSpecModel(spec)).to.throwError();
123 |     });
124 | 
125 |   });
126 | 
127 |   describe('#validateSpecModel', () => {
128 | 
129 |     it('should pass with valid data', () => {
130 |       const model: Kernel.ISpecModels = {
131 |         default: 'python',
132 |         kernelspecs: {
133 |           python: PYTHON_SPEC
134 |         }
135 |       };
136 |       validateSpecModels(model);
137 |     });
138 | 
139 |     it('should fail on missing data', () => {
140 |       const model: any = {
141 |         default: 'python',
142 |       };
143 |       expect(() => validateSpecModels(model)).to.throwError();
144 |     });
145 | 
146 |   });
147 | 
148 | });
149 | 


--------------------------------------------------------------------------------
/test/src/manager.spec.ts:
--------------------------------------------------------------------------------
 1 | // Copyright (c) Jupyter Development Team.
 2 | // Distributed under the terms of the Modified BSD License.
 3 | 'use strict';
 4 | 
 5 | import expect = require('expect.js');
 6 | 
 7 | import {
 8 |   ContentsManager
 9 | } from '../../lib/contents';
10 | 
11 | import {
12 |   ServiceManager
13 | } from '../../lib/manager';
14 | 
15 | import {
16 |   SessionManager
17 | } from '../../lib/session';
18 | 
19 | import {
20 |   TerminalManager
21 | } from '../../lib/terminal';
22 | 
23 | import {
24 |   KernelTester
25 | } from './utils';
26 | 
27 | 
28 | describe('manager', () => {
29 | 
30 |   describe('SessionManager', () => {
31 | 
32 |     let manager: ServiceManager.IManager;
33 |     let tester: KernelTester;
34 | 
35 |     beforeEach((done) => {
36 |       tester = new KernelTester();
37 |       manager = new ServiceManager();
38 |       manager.ready.then(done, done);
39 |     });
40 | 
41 |     afterEach(() => {
42 |       manager.dispose();
43 |       tester.dispose();
44 |     });
45 | 
46 |     describe('#constructor()', () => {
47 | 
48 |       it('should create a new session manager', () => {
49 |         expect(manager).to.be.a(ServiceManager);
50 |       });
51 | 
52 |     });
53 | 
54 |     describe('#sessions', () => {
55 | 
56 |       it('should be the sessions manager instance', () => {
57 |         expect(manager.sessions).to.be.a(SessionManager);
58 |       });
59 | 
60 |     });
61 | 
62 |     describe('#contents', () => {
63 | 
64 |       it('should be the contents manager instance', () => {
65 |         expect(manager.contents).to.be.a(ContentsManager);
66 |       });
67 | 
68 |     });
69 | 
70 |     describe('#terminals', () => {
71 | 
72 |       it('should be the terminal manager instance', () => {
73 |         expect(manager.terminals).to.be.a(TerminalManager);
74 |       });
75 | 
76 |     });
77 | 
78 |     describe('#isReady', () => {
79 | 
80 |       it('should test whether the manager is ready', (done) => {
81 |         manager.dispose();
82 |         manager = new ServiceManager();
83 |         expect(manager.isReady).to.be(false);
84 |         manager.ready.then(() => {
85 |           expect(manager.isReady).to.be(true);
86 |           done();
87 |         }).catch(done);
88 |       });
89 | 
90 |     });
91 | 
92 |   });
93 | 
94 | });
95 | 


--------------------------------------------------------------------------------
/test/src/mockxhr.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 'use strict';
  4 | 
  5 | // Mock implementation of XMLHttpRequest following
  6 | // https://developer.mozilla.org/en-US/docs/Web/API/xmlhttprequest
  7 | 
  8 | 
  9 | /**
 10 |  * Mock XMLHttpRequest object.
 11 |  * Handles a global list of request, and adds the ability to respond()
 12 |  * to them.
 13 |  */
 14 | export
 15 | class MockXMLHttpRequest {
 16 | 
 17 |   static UNSENT = 0;            // open() has not been called yet.
 18 |   static OPENED = 1;            // send() has been called.
 19 |   static HEADERS_RECEIVED = 2;  // send() has been called, and headers and status are available.
 20 |   static LOADING = 3;           // Downloading; responseText holds partial data.
 21 |   static DONE = 4;              // The operation is complete.
 22 | 
 23 |   /**
 24 |    * Global list of XHRs.
 25 |    */
 26 |   static requests: MockXMLHttpRequest[] = [];
 27 | 
 28 |   /**
 29 |    * Register a callback for the next request.
 30 |    *
 31 |    * It is automatically cleared after the request.
 32 |    */
 33 |   static onRequest: (request: MockXMLHttpRequest) => void = null;
 34 | 
 35 |   /**
 36 |    * Ready state of the request.
 37 |    */
 38 |   get readyState(): number {
 39 |     return this._readyState;
 40 |   }
 41 | 
 42 |   /**
 43 |    * Password of the request.
 44 |    */
 45 |   get password(): string {
 46 |     return this._password;
 47 |   }
 48 | 
 49 |   /**
 50 |    * Async status of the request.
 51 |    */
 52 |   get async(): boolean {
 53 |     return this._async;
 54 |   }
 55 | 
 56 |   /**
 57 |    * Response data for the request.
 58 |    */
 59 |   get response(): any {
 60 |     return this._response;
 61 |   }
 62 | 
 63 |   /**
 64 |    * Response data for the request as string.
 65 |    */
 66 |   get responseText(): string {
 67 |     return '' + this._response;
 68 |   }
 69 | 
 70 |   /**
 71 |    * Enumerated value that represents the response type.
 72 |    */
 73 |   get responseType(): string {
 74 |     return this._responseType;
 75 |   }
 76 | 
 77 |   /**
 78 |    * Status code of the response of the request.
 79 |    */
 80 |   get status(): number {
 81 |     return this._status;
 82 |   }
 83 | 
 84 |   /**
 85 |    * The status string returned by the server.
 86 |    */
 87 |   get statusText() {
 88 |     return this._statusText;
 89 |   }
 90 | 
 91 |   /**
 92 |    * Get the number of milliseconds to wait before a request is
 93 |    * automatically canceled.
 94 |    */
 95 |   get timeout(): number {
 96 |     return this._timeout;
 97 |   }
 98 | 
 99 |   /**
100 |    * Set the number of milliseconds to wait before a request is
101 |    * automatically canceled.
102 |    */
103 |   set timeout(timeout: number) {
104 |     this._timeout = timeout;
105 |   }
106 | 
107 |   /**
108 |    * Set a callback for with the request is loaded.
109 |    */
110 |   set onload(cb: () => void) {
111 |     this._onLoad = cb;
112 |   }
113 | 
114 |   /**
115 |    * Set a callback for when the request has an error.
116 |    */
117 |   set onerror(cb: (evt?: any) => void) {
118 |     this._onError = cb;
119 |   }
120 | 
121 |   /**
122 |    * Set a callback for when the request is in progress.
123 |    */
124 |   set onprogress(cb: () => void) {
125 |     throw new Error('Not implemented');
126 |     //this._onProgress = cb;
127 |   }
128 | 
129 |   /**
130 |    * Set a callback for when the ready state changes.
131 |    */
132 |   set onreadystatechange(cb: () => void) {
133 |     this._onReadyState = cb;
134 |   }
135 | 
136 |   /**
137 |    * Set a callback for when the ready state changes.
138 |    */
139 |   set ontimeout(cb: () => void) {
140 |     this._onTimeout = cb;
141 |   }
142 | 
143 |   /**
144 |    * Get the method of the request.
145 |    */
146 |   get method(): string {
147 |     return this._method;
148 |   }
149 | 
150 |   /**
151 |    * Get the url of the request.
152 |    */
153 |   get url(): string {
154 |     return this._url;
155 |   }
156 | 
157 |   /**
158 |    * Get the body request data.
159 |    */
160 |   get requestBody(): any {
161 |     return this._data;
162 |   }
163 | 
164 |   /**
165 |    * Initialize a request.
166 |    */
167 |   open(method: string, url: string, async?: boolean, user?: string, password?:string): void {
168 |     this._method = method;
169 |     this._url = url;
170 |     if (async !== void 0) {
171 |       this._async = async;
172 |     }
173 |     if (user !== void 0) {
174 |       this._user = user;
175 |     }
176 |     this._password = password || '';
177 |     this._readyState = MockXMLHttpRequest.OPENED;
178 |     doLater(() => {
179 |       var onReadyState = this._onReadyState;
180 |       if (onReadyState) onReadyState();
181 |     });
182 |   }
183 | 
184 |   /**
185 |    * Override the MIME type returned by the server.
186 |    */
187 |   overrideMimeType(mime: string): void {
188 |     this._mimetype = mime;
189 |   }
190 | 
191 |   /**
192 |    * Sends the request.
193 |    */
194 |   send(data?: any) {
195 |     if (data !== void 0) {
196 |       this._data = data;
197 |     }
198 |     MockXMLHttpRequest.requests.push(this);
199 |     setTimeout(() => {
200 |       if (MockXMLHttpRequest.requests.indexOf(this) === -1) {
201 |         console.warn('Unhandled request:', JSON.stringify(this));
202 |         throw new Error('Unhandled request: ' + JSON.stringify(this));
203 |       }
204 |       var onRequest = MockXMLHttpRequest.onRequest;
205 |       if (onRequest) onRequest(this);
206 |       if (this._timeout > 0) {
207 |         setTimeout(() => {
208 |           if (this._readyState != MockXMLHttpRequest.DONE) {
209 |             var cb = this._onTimeout;
210 |             if (cb) cb();
211 |           }
212 |         }, this._timeout);
213 |       }
214 |     }, 0);
215 |   }
216 | 
217 |   /**
218 |    * Get a copy of the HTTP request header.
219 |    */
220 |   get requestHeaders(): { [key: string]: any } {
221 |     return JSON.parse(JSON.stringify(this._requestHeader));
222 |   }
223 | 
224 |   /**
225 |    * Set the value of an HTTP request header.
226 |    */
227 |   setRequestHeader(header: string, value: string) {
228 |     this._requestHeader[header] = value;
229 |   }
230 | 
231 |   /**
232 |    * Returns the string containing the text of the specified header,
233 |    * or null if either the response has not yet been received
234 |    * or the header doesn't exist in the response.
235 |    */
236 |   getResponseHeader(header: string): string {
237 |     if (this._responseHeader.hasOwnProperty(header)) {
238 |       return this._responseHeader[header];
239 |     }
240 |   }
241 | 
242 |   /**
243 |    * Respond to a Mock XHR.
244 |    */
245 |   respond(statusCode: number, response: any, header?: any) {
246 |     if (header === void 0) {
247 |       header = {'Content-Type': 'text/json'};
248 |     }
249 |     if (typeof response !== 'string') {
250 |       response = JSON.stringify(response);
251 |     }
252 |     this._status = statusCode;
253 |     this._response = response;
254 |     this._responseHeader = header;
255 |     this._readyState = MockXMLHttpRequest.DONE;
256 | 
257 |     doLater(() => {
258 |       this._statusText = `${statusCode} ${statusReasons[statusCode]}`;
259 |       var onReadyState = this._onReadyState;
260 |       if (onReadyState) onReadyState();
261 |       var onLoad = this._onLoad;
262 |       if (onLoad) onLoad();
263 |     });
264 |   }
265 | 
266 |   /**
267 |    * Simulate a request error.
268 |    */
269 |   error(error: Error): void {
270 |     this._response = '';
271 |     this._readyState = MockXMLHttpRequest.DONE;
272 | 
273 |     doLater(() => {
274 |       var onError = this._onError;
275 |       if (onError) onError(error);
276 |     });
277 |   }
278 | 
279 |   private _readyState = MockXMLHttpRequest.UNSENT;
280 |   private _response: any = '';
281 |   private _responseType = '';
282 |   private _status = -1;
283 |   private _statusText = '';
284 |   private _timeout = -1;
285 |   private _mimetype = '';
286 |   private _data: any;
287 |   private _method = '';
288 |   private _url = '';
289 |   private _async = true;
290 |   private _user = '';
291 |   private _password = '';
292 |   private _onLoad: () => void = null;
293 |   private _onError: (evt: Error) => void = null;
294 |   private _onProgress: () => void = null;
295 |   private _requestHeader: { [key: string]: any } = Object.create(null);
296 |   private _responseHeader: { [key: string]: any } = Object.create(null);
297 |   private _onReadyState: () => void = null;
298 |   private _onTimeout: () => void = null;
299 | }
300 | 
301 | 
302 | 
303 | /**
304 |  * Do something in the future ensuring total ordering wrt to Promises.
305 |  */
306 | function doLater(cb: () => void): void {
307 |   Promise.resolve().then(cb);
308 | }
309 | 
310 | 
311 | /**
312 |  * Status code reasons.
313 |  */
314 | var statusReasons: { [ key: number]: string } = {
315 |   100: 'Continue',
316 |   101: 'Switching Protocols',
317 |   102: 'Processing',
318 |   200: 'OK',
319 |   201: 'Created',
320 |   202: 'Accepted',
321 |   203: 'Non-Authoritative Information',
322 |   204: 'No Content',
323 |   205: 'Reset Content',
324 |   206: 'Partial Content',
325 |   207: 'Multi-Status',
326 |   300: 'Multiple Choices',
327 |   301: 'Moved Permanently',
328 |   302: 'Moved Temporarily',
329 |   303: 'See Other',
330 |   304: 'Not Modified',
331 |   305: 'Use Proxy',
332 |   307: 'Temporary Redirect',
333 |   400: 'Bad Request',
334 |   401: 'Unauthorized',
335 |   402: 'Payment Required',
336 |   403: 'Forbidden',
337 |   404: 'Not Found',
338 |   405: 'Method Not Allowed',
339 |   406: 'Not Acceptable',
340 |   407: 'Proxy Authentication Required',
341 |   408: 'Request Time-out',
342 |   409: 'Conflict',
343 |   410: 'Gone',
344 |   411: 'Length Required',
345 |   412: 'Precondition Failed',
346 |   413: 'Request Entity Too Large',
347 |   414: 'Request-URI Too Large',
348 |   415: 'Unsupported Media Type',
349 |   416: 'Requested range not satisfiable',
350 |   417: 'Expectation Failed',
351 |   422: 'Unprocessable Entity',
352 |   423: 'Locked',
353 |   424: 'Failed Dependency',
354 |   500: 'Internal Server Error',
355 |   501: 'Not Implemented',
356 |   502: 'Bad Gateway',
357 |   503: 'Service Unavailable',
358 |   504: 'Gateway Time-out',
359 |   505: 'HTTP Version not supported',
360 |   507: 'Insufficient Storage'
361 | }
362 | 


--------------------------------------------------------------------------------
/test/src/nbformat.spec.ts:
--------------------------------------------------------------------------------
 1 | // Copyright (c) Jupyter Development Team.
 2 | // Distributed under the terms of the Modified BSD License.
 3 | 
 4 | import expect = require('expect.js');
 5 | 
 6 | import {
 7 |   nbformat
 8 | } from '../../lib/nbformat';
 9 | 
10 | 
11 | const VALIDATE = nbformat.validateMimeValue;
12 | 
13 | 
14 | describe('notebook/nbformat', () => {
15 | 
16 |   describe('validateMimeValue', () => {
17 | 
18 |     it('should return true for a valid json object', () => {
19 |       expect(VALIDATE('application/json', { 'foo': 1 })).to.be(true);
20 |     });
21 | 
22 |     it('should return true for a valid json-like object', () => {
23 |       expect(VALIDATE('application/foo+json', { 'foo': 1 })).to.be(true);
24 |     });
25 | 
26 |     it('should return true for a valid string object', () => {
27 |       expect(VALIDATE('text/plain', 'foo')).to.be(true);
28 |     });
29 | 
30 |     it('should return true for a valid array of strings object', () => {
31 |       expect(VALIDATE('text/plain', ['foo', 'bar'])).to.be(true);
32 |     });
33 | 
34 |     it('should return false for a json type with string data', () => {
35 |       expect(VALIDATE('application/foo+json', 'bar')).to.be(false);
36 |     });
37 | 
38 |     it('should return false for a string type with json data', () => {
39 |       expect(VALIDATE('foo/bar', { 'foo': 1 })).to.be(false);
40 |     });
41 | 
42 |   });
43 | 
44 | });
45 | 


--------------------------------------------------------------------------------
/test/src/session/manager.spec.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import expect = require('expect.js');
  5 | 
  6 | import {
  7 |   toArray
  8 | } from '@phosphor/algorithm';
  9 | 
 10 | import {
 11 |   JSONExt
 12 | } from '@phosphor/coreutils';
 13 | 
 14 | import {
 15 |   Kernel
 16 | } from '../../../lib/kernel';
 17 | 
 18 | import {
 19 |   SessionManager, Session
 20 | } from '../../../lib/session';
 21 | 
 22 | import {
 23 |   uuid, copy
 24 | } from '../../../lib/utils';
 25 | 
 26 | import {
 27 |   KernelTester, KERNELSPECS
 28 | } from '../utils';
 29 | 
 30 | 
 31 | /**
 32 |  * Create a unique session id.
 33 |  */
 34 | function createSessionModel(id = ''): Session.IModel {
 35 |   return {
 36 |     id: id || uuid(),
 37 |     notebook: { path: uuid() },
 38 |     kernel: { id: uuid(), name: uuid() }
 39 |   };
 40 | }
 41 | 
 42 | 
 43 | describe('session/manager', () => {
 44 | 
 45 |   let tester: KernelTester;
 46 |   let session: Session.ISession;
 47 |   let manager: SessionManager;
 48 |   let data: Session.IModel[];
 49 | 
 50 |   beforeEach((done) => {
 51 |     data = [createSessionModel(), createSessionModel()];
 52 |     tester = new KernelTester();
 53 |     tester.runningSessions = data;
 54 |     manager = new SessionManager();
 55 |     expect(manager.specs).to.be(null);
 56 |     expect(manager.running().next()).to.be(void 0);
 57 |     manager.ready.then(done, done);
 58 |   });
 59 | 
 60 |   afterEach((done) => {
 61 |     manager.ready.then(() => {
 62 |       manager.dispose();
 63 |       if (session) {
 64 |         session.dispose();
 65 |       }
 66 |       tester.dispose();
 67 |       done();
 68 |     }).catch(done);
 69 |   });
 70 | 
 71 |   describe('SessionManager', () => {
 72 | 
 73 |     describe('#constructor()', () => {
 74 | 
 75 |       it('should create a new session manager', () => {
 76 |         expect(manager instanceof SessionManager).to.be(true);
 77 |       });
 78 | 
 79 |     });
 80 | 
 81 |     describe('#baseUrl', () => {
 82 | 
 83 |       it('should get the base url of the server', () => {
 84 |         manager.dispose();
 85 |         manager = new SessionManager({ baseUrl: 'foo' });
 86 |         expect(manager.baseUrl).to.be('foo');
 87 |       });
 88 | 
 89 |     });
 90 | 
 91 |     describe('#wsUrl', () => {
 92 | 
 93 |       it('should get the ws url of the server', () => {
 94 |         manager.dispose();
 95 |         manager = new SessionManager({ wsUrl: 'bar' });
 96 |         expect(manager.wsUrl).to.be('bar');
 97 |       });
 98 | 
 99 |     });
100 | 
101 |     describe('#ajaxSettings', () => {
102 | 
103 |       it('should get the ajax sessions of the server', () => {
104 |         manager.dispose();
105 |         let ajaxSettings = { withCredentials: true };
106 |         manager = new SessionManager({ ajaxSettings });
107 |         expect(manager.ajaxSettings).to.eql(ajaxSettings);
108 |       });
109 | 
110 |     });
111 | 
112 |     describe('#specs', () => {
113 | 
114 |       it('should be the kernel specs', () => {
115 |         expect(manager.specs.default).to.be(KERNELSPECS.default);
116 |       });
117 | 
118 |     });
119 | 
120 |     describe('#isReady', () => {
121 | 
122 |       it('should test whether the manager is ready', (done) => {
123 |         manager.dispose();
124 |         manager = new SessionManager();
125 |         expect(manager.isReady).to.be(false);
126 |         manager.ready.then(() => {
127 |           expect(manager.isReady).to.be(true);
128 |           done();
129 |         }).catch(done);
130 |       });
131 | 
132 |     });
133 | 
134 |     describe('#ready', () => {
135 | 
136 |       it('should resolve when the manager is ready', (done) => {
137 |         manager.ready.then(done, done);
138 |       });
139 | 
140 |     });
141 | 
142 |     describe('#running()', () => {
143 | 
144 |       it('should get the running sessions', () => {
145 |         let test = JSONExt.deepEqual(toArray(manager.running()), data);
146 |         expect(test).to.be(true);
147 |       });
148 | 
149 |     });
150 | 
151 |     describe('#specsChanged', () => {
152 | 
153 |       it('should be emitted when the specs change', (done) => {
154 |         let specs = copy(KERNELSPECS) as Kernel.ISpecModels;
155 |         specs.default = 'shell';
156 |         tester.specs = specs;
157 |         manager.specsChanged.connect((sender, args) => {
158 |           expect(sender).to.be(manager);
159 |           expect(args.default).to.be(specs.default);
160 |           done();
161 |         });
162 |         manager.refreshSpecs();
163 |       });
164 | 
165 |     });
166 | 
167 |     describe('#runningChanged', () => {
168 | 
169 |       it('should be emitted in refreshRunning when the running sessions changed', (done) => {
170 |         let sessionModels = [createSessionModel(), createSessionModel()];
171 |         tester.runningSessions = sessionModels;
172 |         manager.runningChanged.connect((sender, args) => {
173 |           expect(sender).to.be(manager);
174 |           expect(JSONExt.deepEqual(toArray(args), sessionModels)).to.be(true);
175 |           done();
176 |         });
177 |         manager.refreshRunning();
178 |       });
179 | 
180 |       it('should be emitted when a session is shut down', (done) => {
181 |         manager.startNew({ path: 'foo' }).then(s => {
182 |           manager.runningChanged.connect(() => {
183 |             manager.dispose();
184 |             done();
185 |           });
186 |           return s.shutdown();
187 |         }).catch(done);
188 |       });
189 | 
190 |       it('should be emitted when a session is renamed', (done) => {
191 |         manager.startNew({ path: 'foo' }).then(s => {
192 |           let model = {
193 |             id: s.id,
194 |             kernel: s.kernel.model,
195 |             notebook: { path: 'bar' }
196 |           };
197 |           tester.onRequest = () => {
198 |             tester.respond(200, model);
199 |           };
200 |           manager.runningChanged.connect(() => {
201 |             manager.dispose();
202 |             done();
203 |           });
204 |           return s.rename(model.notebook.path);
205 |         }).catch(done);
206 |       });
207 | 
208 |       it('should be emitted when a session changes kernels', (done) => {
209 |         manager.startNew({ path: 'foo' }).then(s => {
210 |           let model = {
211 |             id: s.id,
212 |             kernel: {
213 |               name: 'foo',
214 |               id: uuid()
215 |             },
216 |             notebook: { path: 'bar' }
217 |           };
218 |           let name = model.kernel.name;
219 |           tester.onRequest = request => {
220 |             if (request.method === 'PATCH') {
221 |               tester.respond(200, model);
222 |             } else {
223 |               tester.respond(200, { name, id: model.kernel.id });
224 |             }
225 |           };
226 |           manager.runningChanged.connect(() => {
227 |             manager.dispose();
228 |             done();
229 |           });
230 |           return s.changeKernel({ name });
231 |         }).catch(done);
232 |       });
233 | 
234 |     });
235 | 
236 |     describe('#refreshRunning()', () => {
237 | 
238 |       it('should refresh the list of session ids', (done) => {
239 |         let sessionModels = [createSessionModel(), createSessionModel()];
240 |         tester.runningSessions = sessionModels;
241 |         manager.refreshRunning().then(() => {
242 |           let running = toArray(manager.running());
243 |           expect(running[0]).to.eql(sessionModels[0]);
244 |           expect(running[1]).to.eql(sessionModels[1]);
245 |           done();
246 |         });
247 | 
248 |       });
249 | 
250 |     });
251 | 
252 |     describe('#refreshSpecs()', () => {
253 | 
254 |       it('should refresh the specs', (done) => {
255 |         let specs = copy(KERNELSPECS) as Kernel.ISpecModels;
256 |         specs.default = 'shell';
257 |         tester.specs = specs;
258 |         manager.refreshSpecs().then(() => {
259 |           expect(manager.specs.default).to.be(specs.default);
260 |           done();
261 |         }).catch(done);
262 |       });
263 | 
264 |     });
265 | 
266 |     describe('#startNew()', () => {
267 | 
268 |       it('should start a session', (done) => {
269 |         manager.startNew({ path: 'test.ipynb'}).then(s => {
270 |           session = s;
271 |           expect(session.id).to.be.ok();
272 |           done();
273 |         }).catch(done);
274 |       });
275 | 
276 |       it('should emit a runningChanged signal', (done) => {
277 |         manager.runningChanged.connect(() => {
278 |           done();
279 |         });
280 |         manager.startNew({ path: 'foo.ipynb' });
281 |       });
282 | 
283 |     });
284 | 
285 |     describe('#findByPath()', () => {
286 | 
287 |       it('should find an existing session by path', (done) => {
288 |         manager.startNew({ path: 'test.ipynb' }).then(s => {
289 |           session = s;
290 |           tester.runningSessions = [s.model];
291 |           return manager.findByPath(session.path);
292 |         }).then(newModel => {
293 |           expect(newModel.id).to.be(session.id);
294 |           done();
295 |         }).catch(done);
296 |       });
297 | 
298 |     });
299 | 
300 | 
301 |     describe('#findById()', () => {
302 | 
303 |       it('should find an existing session by id', (done) => {
304 |         manager.startNew({ path: 'test.ipynb' }).then(s => {
305 |           session = s;
306 |           tester.runningSessions = [s.model];
307 |           return manager.findById(session.id);
308 |         }).then(newModel => {
309 |           expect(newModel.id).to.be(session.id);
310 |           done();
311 |         }).catch(done);
312 |       });
313 | 
314 |     });
315 | 
316 |     describe('#connectTo()', () => {
317 | 
318 |       it('should connect to a running session', (done) => {
319 |         manager.startNew({ path: 'test.ipynb' }).then(s => {
320 |           session = s;
321 |           manager.connectTo(session.id).then(newSession => {
322 |             expect(newSession.id).to.be(session.id);
323 |             expect(newSession.kernel.id).to.be(session.kernel.id);
324 |             expect(newSession).to.not.be(session);
325 |             expect(newSession.kernel).to.not.be(session.kernel);
326 |             newSession.dispose();
327 |             done();
328 |           });
329 |         }).catch(done);
330 |       });
331 | 
332 |       it('should emit a runningChanged signal', (done) => {
333 |         manager.runningChanged.connect(() => {
334 |           done();
335 |         });
336 |         let model = createSessionModel();
337 |         tester.runningSessions = [model];
338 |         manager.connectTo(model.id);
339 |       });
340 | 
341 |     });
342 | 
343 |     describe('shutdown()', () => {
344 | 
345 |       it('should shut down a session by id', (done) => {
346 |         manager.shutdown('foo').then(done, done);
347 |       });
348 | 
349 |       it('should emit a runningChanged signal', (done) => {
350 |         manager.runningChanged.connect((sender, args) => {
351 |           done();
352 |         });
353 |         manager.shutdown(data[0].id);
354 |       });
355 | 
356 |     });
357 | 
358 |   });
359 | 
360 | });
361 | 


--------------------------------------------------------------------------------
/test/src/session/validate.spec.ts:
--------------------------------------------------------------------------------
 1 | // Copyright (c) Jupyter Development Team.
 2 | // Distributed under the terms of the Modified BSD License.
 3 | 
 4 | import expect = require('expect.js');
 5 | 
 6 | import {
 7 |   Session
 8 | } from '../../../lib/session';
 9 | 
10 | import {
11 |   validateModel
12 | } from '../../../lib/session/validate';
13 | 
14 | 
15 | describe('session/validate', () => {
16 | 
17 |   describe('#validateModel()', () => {
18 | 
19 |     it('should pass a valid id', () => {
20 |       let id: Session.IModel = {
21 |         id: 'foo',
22 |         kernel: { name: 'foo', id: '123'},
23 |         notebook: { path: 'bar' }
24 |       };
25 |       validateModel(id);
26 |     });
27 | 
28 |     it('should fail on missing data', () => {
29 |       let id: Session.IModel = {
30 |         id: 'foo',
31 |         kernel: { name: 'foo', id: '123'},
32 |       };
33 |       expect(() => validateModel(id)).to.throwError();
34 |     });
35 | 
36 |   });
37 | 
38 | });
39 | 


--------------------------------------------------------------------------------
/test/src/target.ts:
--------------------------------------------------------------------------------
 1 | // Copyright (c) Jupyter Development Team.
 2 | // Distributed under the terms of the Modified BSD License.
 3 | 
 4 | // Stub for requirejs define
 5 | declare var define: any;
 6 | 
 7 | 
 8 | define(() => {
 9 |   let myModule = {
10 |     test: () => { return 1; },
11 |     test2: () => { throw Error('Nope'); }
12 |   };
13 |   return myModule;
14 | });
15 | 


--------------------------------------------------------------------------------
/test/src/terminal/manager.spec.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import expect = require('expect.js');
  5 | 
  6 | import {
  7 |   toArray
  8 | } from '@phosphor/algorithm';
  9 | 
 10 | import {
 11 |   JSONExt
 12 | } from '@phosphor/coreutils';
 13 | 
 14 | import {
 15 |   TerminalSession, TerminalManager
 16 | } from '../../../lib/terminal';
 17 | 
 18 | import {
 19 |   TerminalTester
 20 | } from '../utils';
 21 | 
 22 | 
 23 | describe('terminals', () => {
 24 | 
 25 |   let tester: TerminalTester;
 26 |   let manager: TerminalSession.IManager;
 27 |   let data: TerminalSession.IModel[] =  [{ name: 'foo'}, { name: 'bar' }];
 28 | 
 29 |   beforeEach((done) => {
 30 |     tester = new TerminalTester();
 31 |     tester.runningTerminals = data;
 32 |     manager = new TerminalManager();
 33 |     return manager.ready.then(done, done);
 34 |   });
 35 | 
 36 |   afterEach((done) => {
 37 |     manager.ready.then(() => {
 38 |       manager.dispose();
 39 |       tester.dispose();
 40 |       done();
 41 |     }).catch(done);
 42 |   });
 43 | 
 44 |   describe('TerminalManager', () => {
 45 | 
 46 |     describe('#constructor()', () => {
 47 | 
 48 |       it('should accept no options', () => {
 49 |         manager.dispose();
 50 |         manager = new TerminalManager();
 51 |         expect(manager).to.be.a(TerminalManager);
 52 |       });
 53 | 
 54 |       it('should accept options', () => {
 55 |         manager.dispose();
 56 |         manager = new TerminalManager({
 57 |           baseUrl: 'foo',
 58 |           wsUrl: 'bar',
 59 |           ajaxSettings: {}
 60 |         });
 61 |         expect(manager).to.be.a(TerminalManager);
 62 |       });
 63 | 
 64 |     });
 65 | 
 66 |     describe('#baseUrl', () => {
 67 | 
 68 |       it('should get the base url of the server', () => {
 69 |         manager.dispose();
 70 |         manager = new TerminalManager({ baseUrl: 'foo' });
 71 |         expect(manager.baseUrl).to.be('foo');
 72 |       });
 73 | 
 74 |     });
 75 | 
 76 |     describe('#wsUrl', () => {
 77 | 
 78 |       it('should get the ws url of the server', () => {
 79 |         manager.dispose();
 80 |         manager = new TerminalManager({ wsUrl: 'bar' });
 81 |         expect(manager.wsUrl).to.be('bar');
 82 |       });
 83 | 
 84 |     });
 85 | 
 86 |     describe('#ajaxSettings', () => {
 87 | 
 88 |       it('should get the ajax sessions of the server', () => {
 89 |         let ajaxSettings = { withCredentials: true };
 90 |         manager.dispose();
 91 |         manager = new TerminalManager({ ajaxSettings });
 92 |         expect(manager.ajaxSettings).to.eql(ajaxSettings);
 93 |       });
 94 | 
 95 |     });
 96 | 
 97 |     describe('#isReady', () => {
 98 | 
 99 |       it('should test whether the manager is ready', (done) => {
100 |         manager.dispose();
101 |         manager = new TerminalManager();
102 |         expect(manager.isReady).to.be(false);
103 |         manager.ready.then(() => {
104 |           expect(manager.isReady).to.be(true);
105 |           done();
106 |         }).catch(done);
107 |       });
108 | 
109 |     });
110 | 
111 |     describe('#ready', () => {
112 | 
113 |       it('should resolve when the manager is ready', (done) => {
114 |         manager.ready.then(done, done);
115 |       });
116 | 
117 |     });
118 | 
119 |     describe('#isAvailable()', () => {
120 | 
121 |       it('should test whether terminal sessions are available', () => {
122 |         expect(TerminalSession.isAvailable()).to.be(true);
123 |       });
124 | 
125 |     });
126 | 
127 |     describe('#running()', () => {
128 | 
129 |       it('should give an iterator over the list of running models', () => {
130 |         expect(toArray(manager.running())).to.eql(data);
131 |       });
132 | 
133 |     });
134 | 
135 |     describe('#startNew()', () => {
136 | 
137 |       it('should startNew a new terminal session', (done) => {
138 |         manager.startNew().then(session => {
139 |           expect(session.name).to.be.ok();
140 |           done();
141 |         }).catch(done);
142 |       });
143 | 
144 |       it('should emit a runningChanged signal', (done) => {
145 |         manager.runningChanged.connect(() => {
146 |           done();
147 |         });
148 |         manager.startNew();
149 |       });
150 | 
151 |     });
152 | 
153 |     describe('#connectTo()', () => {
154 | 
155 |       it('should connect to an existing kernel', (done) => {
156 |         return manager.connectTo(data[0].name).then(session => {
157 |           expect(session.name).to.be(data[0].name);
158 |         }).then(done, done);
159 |       });
160 | 
161 |       it('should emit a runningChanged signal', (done) => {
162 |         tester.runningTerminals = [{ name: 'baz' }];
163 |         manager.runningChanged.connect(() => {
164 |           done();
165 |         });
166 |         manager.connectTo('baz');
167 |       });
168 | 
169 |     });
170 | 
171 |     describe('#shutdown()', () => {
172 | 
173 |       it('should shut down a terminal session by name', (done) => {
174 |         manager.startNew().then(session => {
175 |           return manager.shutdown(session.name);
176 |         }).then(() => {
177 |           done();
178 |         }).catch(done);
179 |       });
180 | 
181 |       it('should emit a runningChanged signal', (done) => {
182 |         manager.runningChanged.connect((sender, args) => {
183 |           done();
184 |         });
185 |         manager.shutdown(data[0].name);
186 |       });
187 | 
188 |     });
189 | 
190 |     describe('#runningChanged', () => {
191 | 
192 |       it('should be emitted when the running terminals changed', (done) => {
193 |         let newData: TerminalSession.IModel[] = [{ name: 'foo'}];
194 |         tester.runningTerminals = newData;
195 |         manager.runningChanged.connect((sender, args) => {
196 |           expect(sender).to.be(manager);
197 |           expect(JSONExt.deepEqual(toArray(args), newData)).to.be(true);
198 |           done();
199 |         });
200 |         manager.refreshRunning();
201 |       });
202 | 
203 |       it('should be emitted when a session is shut down', (done) => {
204 |         manager.startNew().then(session => {
205 |           manager.runningChanged.connect(() => {
206 |             manager.dispose();
207 |             done();
208 |           });
209 |           return session.shutdown();
210 |         }).catch(done);
211 |       });
212 | 
213 |     });
214 | 
215 |     describe('#refreshRunning()', () => {
216 | 
217 |       it('should update the running session models', (done) => {
218 |         let newData: TerminalSession.IModel[] = [{ name: 'foo'}];
219 |         tester.runningTerminals = newData;
220 |         manager.refreshRunning().then(() => {
221 |           let running = toArray(manager.running());
222 |           expect(JSONExt.deepEqual(newData, running)).to.be(true);
223 |           done();
224 |         }).catch(done);
225 |       });
226 | 
227 |     });
228 | 
229 |   });
230 | 
231 | });
232 | 


--------------------------------------------------------------------------------
/test/src/terminal/terminal.spec.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 
  4 | import expect = require('expect.js');
  5 | 
  6 | import {
  7 |   toArray
  8 | } from '@phosphor/algorithm';
  9 | 
 10 | import {
 11 |   JSONExt
 12 | } from '@phosphor/coreutils';
 13 | 
 14 | import {
 15 |   TerminalSession
 16 | } from '../../../lib/terminal';
 17 | 
 18 | import {
 19 |   getBaseUrl
 20 | } from '../../../lib/utils';
 21 | 
 22 | import {
 23 |   TerminalTester
 24 | } from '../utils';
 25 | 
 26 | 
 27 | describe('terminals', () => {
 28 | 
 29 |   let tester: TerminalTester;
 30 |   let session: TerminalSession.ISession;
 31 | 
 32 |   beforeEach(() => {
 33 |     tester = new TerminalTester();
 34 |   });
 35 | 
 36 |   afterEach(() => {
 37 |     if (session) {
 38 |       session.dispose();
 39 |     }
 40 |     tester.dispose();
 41 |   });
 42 | 
 43 |   describe('TerminalSession', () => {
 44 | 
 45 |     describe('.isAvailable()', () => {
 46 | 
 47 |       it('should test whether terminal sessions are available', () => {
 48 |         expect(TerminalSession.isAvailable()).to.be(true);
 49 |       });
 50 | 
 51 |     });
 52 | 
 53 |     describe('.startNew()', () => {
 54 | 
 55 |       it('should startNew a terminal session', (done) => {
 56 |         TerminalSession.startNew().then(s => {
 57 |           session = s;
 58 |           expect(session.name).to.be.ok();
 59 |           done();
 60 |         }).catch(done);
 61 |       });
 62 | 
 63 |     });
 64 | 
 65 |     describe('.connectTo', () => {
 66 | 
 67 |       it('should give back an existing session', () => {
 68 |         return TerminalSession.startNew().then(s => {
 69 |           session = s;
 70 |           return TerminalSession.connectTo(s.name);
 71 |         }).then(newSession => {
 72 |             expect(newSession).to.be(session);
 73 |          });
 74 |       });
 75 | 
 76 |       it('should give back a session that exists on the server', () => {
 77 |         tester.onRequest = () => {
 78 |           tester.respond(200, [{ name: 'foo' }]);
 79 |         };
 80 |         return TerminalSession.connectTo('foo').then(s => {
 81 |           expect(s.name).to.be('foo');
 82 |           s.dispose();
 83 |         });
 84 |       });
 85 | 
 86 |       it('should reject if the session does not exist on the server', () => {
 87 |         tester.onRequest = () => {
 88 |           tester.respond(200, [{ name: 'foo' }]);
 89 |         };
 90 |         return TerminalSession.connectTo('bar').then(
 91 |           () => { throw Error('should not get here'); },
 92 |           () => undefined
 93 |         );
 94 |       });
 95 | 
 96 |     });
 97 | 
 98 | 
 99 |     describe('.shutdown()', () => {
100 | 
101 |       it('should shut down a terminal session by name', (done) => {
102 |         TerminalSession.startNew().then(s => {
103 |           session = s;
104 |           return TerminalSession.shutdown(s.name);
105 |         }).then(() => {
106 |           done();
107 |         }).catch(done);
108 |       });
109 | 
110 |       it('should handle a 404 status', (done) => {
111 |         tester.onRequest = () => {
112 |           tester.respond(404, { });
113 |         };
114 |         TerminalSession.shutdown('foo').then(done, done);
115 |       });
116 | 
117 |     });
118 | 
119 |     describe('.listRunning()', () => {
120 | 
121 |       it('should list the running session models', (done) => {
122 |         let data: TerminalSession.IModel[] = [{ name: 'foo'}, { name: 'bar' }];
123 |         tester.runningTerminals = data;
124 |         TerminalSession.listRunning().then(models => {
125 |           expect(JSONExt.deepEqual(data, toArray(models))).to.be(true);
126 |           done();
127 |         }).catch(done);
128 |       });
129 | 
130 |     });
131 | 
132 |   });
133 | 
134 |   describe('.ISession', () => {
135 | 
136 |     beforeEach((done) => {
137 |       TerminalSession.startNew().then(s => {
138 |         session = s;
139 |         done();
140 |       });
141 |     });
142 | 
143 |     afterEach(() => {
144 |       session.dispose();
145 |     });
146 | 
147 |     describe('#terminated', () => {
148 | 
149 |       it('should be emitted when the session is shut down', (done) => {
150 |         session.terminated.connect((sender, args) => {
151 |           expect(sender).to.be(session);
152 |           expect(args).to.be(void 0);
153 |           done();
154 |         });
155 |         session.shutdown();
156 |       });
157 | 
158 |     });
159 | 
160 |     describe('#messageReceived', () => {
161 | 
162 |       it('should be emitted when a message is received', (done) => {
163 |         session.messageReceived.connect((sender, msg) => {
164 |           expect(sender).to.be(session);
165 |           expect(msg.type).to.be('stdout');
166 |           expect(toArray(msg.content)).to.eql(['foo bar']);
167 |           done();
168 |         });
169 |         tester.sendRaw(JSON.stringify(['stdout', 'foo bar']));
170 |       });
171 | 
172 |     });
173 | 
174 |     describe('#name', () => {
175 | 
176 |       it('should be the name of the session', (done) => {
177 |         session.dispose();
178 |         TerminalSession.startNew().then(s => {
179 |           session = s;
180 |           expect(session.name).to.be.ok();
181 |           done();
182 |         }).catch(done);
183 |       });
184 | 
185 |     });
186 | 
187 |     context('#baseUrl', () => {
188 | 
189 |       it('should be the base url of the server', () => {
190 |         expect(session.baseUrl).to.be(getBaseUrl());
191 |       });
192 | 
193 |     });
194 | 
195 |     describe('#isDisposed', () => {
196 | 
197 |       it('should test whether the object is disposed', () => {
198 |         expect(session.isDisposed).to.be(false);
199 |         session.dispose();
200 |         expect(session.isDisposed).to.be(true);
201 |       });
202 | 
203 |     });
204 | 
205 |     describe('#dispose()', () => {
206 | 
207 |       it('should dispose of the resources used by the session', () => {
208 |         session.dispose();
209 |         expect(session.isDisposed).to.be(true);
210 |       });
211 | 
212 |       it('should be safe to call more than once', () => {
213 |         session.dispose();
214 |         session.dispose();
215 |         expect(session.isDisposed).to.be(true);
216 |       });
217 | 
218 |     });
219 | 
220 |     context('#isReady', () => {
221 | 
222 |       it('should test whether the terminal is ready', (done) => {
223 |         session.shutdown();
224 |         TerminalSession.startNew().then(s => {
225 |           session = s;
226 |           expect(session.isReady).to.be(false);
227 |           return session.ready;
228 |         }).then(() => {
229 |           expect(session.isReady).to.be(true);
230 |           done();
231 |         }).catch(done);
232 |       });
233 | 
234 |     });
235 | 
236 |     describe('#ready', () => {
237 | 
238 |       it('should resolve when the terminal is ready', (done) => {
239 |         session.ready.then(done, done);
240 |       });
241 | 
242 |     });
243 | 
244 |     describe('#send()', () => {
245 | 
246 |       it('should send a message to the socket', (done) => {
247 |         tester.onMessage(msg => {
248 |           expect(msg.type).to.be('stdin');
249 |           done();
250 |         });
251 |         session.ready.then(() => {
252 |           session.send({ type: 'stdin', content: [1, 2] });
253 |         }).catch(done);
254 |       });
255 | 
256 |     });
257 | 
258 |     describe('#reconnect()', () => {
259 | 
260 |       it('should reconnect to the socket', (done) => {
261 |         session.ready.then(() => {
262 |           let promise = session.reconnect();
263 |           expect(session.isReady).to.be(false);
264 |           return promise;
265 |         }).then(() => {
266 |           expect(session.isReady).to.be(true);
267 |         }).then(done, done);
268 |       });
269 | 
270 |     });
271 | 
272 |     describe('#shutdown()', () => {
273 | 
274 |       it('should shut down the terminal session', (done) => {
275 |         session.shutdown().then(done, done);
276 |       });
277 | 
278 |       it('should handle a 404 status', (done) => {
279 |         tester.onRequest = () => {
280 |           tester.respond(404, { });
281 |         };
282 |         session.shutdown().then(done, done);
283 |       });
284 | 
285 |     });
286 | 
287 |   });
288 | 
289 | });
290 | 


--------------------------------------------------------------------------------
/test/src/tsconfig.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "compilerOptions": {
 3 |     "experimentalDecorators": true,
 4 |     "noImplicitAny": true,
 5 |     "noEmitOnError": true,
 6 |     "lib": ["DOM", "ES5", "ES2015.Promise", "ES2015.Collection", 
 7 |             "ES2015.Iterable", "ES2015.Symbol"],
 8 |     "types": ["mocha"],
 9 |     "module": "commonjs",
10 |     "moduleResolution": "node",
11 |     "target": "ES5",
12 |     "outDir": "../build"
13 |   }
14 | }
15 | 


--------------------------------------------------------------------------------
/test/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | 
2 | /// 
3 | 


--------------------------------------------------------------------------------
/test/src/utils.spec.ts:
--------------------------------------------------------------------------------
  1 | // Copyright (c) Jupyter Development Team.
  2 | // Distributed under the terms of the Modified BSD License.
  3 | 'use strict';
  4 | 
  5 | import expect = require('expect.js');
  6 | 
  7 | import {
  8 |   JSONObject
  9 | } from '@phosphor/coreutils';
 10 | 
 11 | import {
 12 |   extend, copy, uuid, urlPathJoin, urlEncodeParts,
 13 |   jsonToQueryString, getConfigOption,
 14 |   getBaseUrl, getWsUrl, ajaxRequest, loadObject
 15 | } from '../../lib/utils';
 16 | 
 17 | import {
 18 |   MockXMLHttpRequest
 19 | } from './mockxhr';
 20 | 
 21 | 
 22 | declare var global: any;
 23 | global.requirejs = require('requirejs');
 24 | 
 25 | 
 26 | describe('jupyter-js-utils', () => {
 27 | 
 28 |   describe('extend()', () => {
 29 | 
 30 |     it('should copy the contents of one object to another', () => {
 31 |       let target = {
 32 |         foo: 'bar',
 33 |         baz: { fizz: 0, buzz: 1}
 34 |       };
 35 |       let source = {
 36 |         baz: { buzz: 2}
 37 |       };
 38 |       let newObj = extend(target, source);
 39 |       expect(newObj.foo).to.be('bar');
 40 |       expect(newObj.baz.buzz).to.be(2);
 41 |       expect(newObj.baz.fizz).to.be(0);
 42 |     });
 43 | 
 44 |   });
 45 | 
 46 |   describe('copy()', () => {
 47 | 
 48 |     it('should get a copy of an object', () => {
 49 |       let source: JSONObject = {
 50 |         foo: 'bar',
 51 |         baz: { fizz: 0, buzz: [1, 2]}
 52 |       };
 53 |       let newObj: any = copy(source);
 54 |       expect(newObj.baz.buzz).to.eql([1, 2]);
 55 |       newObj.baz.fizz = 4;
 56 |       expect((source as any).baz.fizz).to.be(0);
 57 |     });
 58 | 
 59 |   });
 60 | 
 61 |   describe('uuid()', () => {
 62 | 
 63 |     it('should generate a random 32 character hex string', () => {
 64 |       let id0 = uuid();
 65 |       let id1 = uuid();
 66 |       expect(id0.length).to.be(32);
 67 |       expect(id1.length).to.be(32);
 68 |       expect(id0).to.not.eql(id1);
 69 |     });
 70 | 
 71 |   });
 72 | 
 73 |   describe('#urlPathJoin()', () => {
 74 | 
 75 |     it('should join a sequence of url components', () => {
 76 |       expect(urlPathJoin('/foo/', 'bar/')).to.be('/foo/bar/');
 77 |     });
 78 | 
 79 |   });
 80 | 
 81 |   describe('urlEncodeParts()', () => {
 82 | 
 83 |     it('should encode and join a sequence of url components', () => {
 84 |       expect(urlEncodeParts('>/>')).to.be('%3E/%3E');
 85 |     });
 86 | 
 87 |   });
 88 | 
 89 |   describe('jsonToQueryString()', () => {
 90 | 
 91 |     it('should return a serialized object string suitable for a query', () => {
 92 |       let obj: JSONObject = {
 93 |         name: 'foo',
 94 |         id: 'baz'
 95 |       };
 96 |       expect(jsonToQueryString(obj)).to.be('?name=foo&id=baz');
 97 |     });
 98 | 
 99 |   });
100 | 
101 |   describe('getConfigOption()', () => {
102 | 
103 |     it('should get a config option passed on the command line', () => {
104 |       expect(getConfigOption('foo')).to.be('bar');
105 |     });
106 | 
107 |     it('should return `undefined` for a option that was not given', () => {
108 |       expect(getConfigOption('baz')).to.be(void 0);
109 |     });
110 | 
111 |   });
112 | 
113 |   describe('getBaseUrl()', () => {
114 | 
115 |     it('should get the default base url', () => {
116 |       expect(getBaseUrl()).to.be('http://localhost:8888/');
117 |     });
118 | 
119 |   });
120 | 
121 |   describe('getWsUrl()', () => {
122 | 
123 |     it('should get the default ws url', () => {
124 |       expect(getWsUrl()).to.be('ws://localhost:8888/');
125 |     });
126 | 
127 |   });
128 | 
129 |   describe('ajaxRequest()', () => {
130 | 
131 |     it('should handle default values', (done) => {
132 |       let called = false;
133 |       MockXMLHttpRequest.onRequest = request => {
134 |         expect(request.method).to.be('GET');
135 |         expect(request.password).to.be('');
136 |         expect(request.async).to.be(true);
137 |         expect(Object.keys(request.requestHeaders)).to.eql([]);
138 |         let url = request.url;
139 |         expect(url.indexOf('hello?')).to.be(0);
140 |         called = true;
141 |         request.respond(200, 'hello!');
142 |       };
143 | 
144 |       ajaxRequest('hello', {}).then(response => {
145 |         expect(called).to.be(true);
146 |         expect(response.data).to.be('hello!');
147 |         expect(response.xhr.statusText).to.be('200 OK');
148 |       }).then(done, done);
149 |     });
150 | 
151 |     it('should allow overrides', (done) => {
152 |       MockXMLHttpRequest.onRequest = request => {
153 |         expect(request.method).to.be('POST');
154 |         expect(request.password).to.be('password');
155 |         expect(Object.keys(request.requestHeaders)).to.eql(['Content-Type', 'foo']);
156 |         let url = request.url;
157 |         expect(url.indexOf('hello?')).to.be(-1);
158 |         expect(url.indexOf('hello')).to.be(0);
159 |         request.respond(200, 'hello!');
160 |       };
161 |       ajaxRequest('hello', {
162 |         method: 'POST',
163 |         password: 'password',
164 |         cache: true,
165 |         contentType: 'bar',
166 |         requestHeaders: {
167 |           foo: 'bar'
168 |         },
169 |         timeout: 5
170 |       }).then(response => {
171 |         expect(response.data).to.be('hello!');
172 |         expect(response.xhr.statusText).to.be('200 OK');
173 |         expect(response.ajaxSettings.method).to.be('POST');
174 |       }).then(done, done);
175 |     });
176 | 
177 |     it('should reject the promise for a bad status response', (done) => {
178 |       MockXMLHttpRequest.onRequest = request => {
179 |         request.respond(400, 'denied!');
180 |       };
181 |       ajaxRequest('hello', {}).catch(response => {
182 |         expect(response.xhr.statusText).to.be('400 Bad Request');
183 |         expect(response.throwError).to.be('400 Bad Request');
184 |       }).then(done, done);
185 |     });
186 | 
187 |     it('should reject the promise on an error', (done) => {
188 |       MockXMLHttpRequest.onRequest = request => {
189 |         request.error(new Error('Denied!'));
190 |       };
191 |       ajaxRequest('hello', {}).catch(response => {
192 |         expect(response.event.message).to.be('Denied!');
193 |       }).then(done, done);
194 |     });
195 | 
196 |   });
197 | 
198 |   describe('#loadObject()', () => {
199 | 
200 |     it('should accept a name and a module name to load', done => {
201 |       // The path is relative to mocha.
202 |       loadObject('test', '../../../test/build/target').then(func => {
203 |         expect(func()).to.be(1);
204 |       }).then(done, done);
205 |     });
206 | 
207 |     it('should reject if the module name is not found', done => {
208 |       loadObject('test', 'foo').catch(error => {
209 |         expect(error.message.indexOf("Cannot find module 'foo'")).to.not.be(-1);
210 |       }).then(done, done);
211 |     });
212 | 
213 |     it('should reject if the object is not found', done => {
214 |       loadObject('foo', '../../../test/build/target').catch(error => {
215 |         expect(error.message).to.be("Object 'foo' not found in module '../../../test/build/target'");
216 |       }).then(done, done);
217 |     });
218 | 
219 |     it('should accept a registry', done => {
220 |       let registry = { test: () => { return 1; }};
221 |       loadObject('test', void 0, registry).then(func => {
222 |         expect(func()).to.be(1);
223 |       }).then(done, done);
224 |     });
225 | 
226 |     it('should reject if the object is not in the registry', done => {
227 |       let registry = { test: () => { return 1; }};
228 |       loadObject('foo', void 0, registry).catch(error => {
229 |         expect(error.message).to.be("Object 'foo' not found in registry");
230 |       }).then(done, done);
231 |     });
232 | 
233 |   });
234 | 
235 | });
236 | 


--------------------------------------------------------------------------------
/typedoc.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "mode": "file",
 3 |   "module": "commonjs",
 4 |   "excludeNotExported": true,
 5 |   "target": "es5",
 6 |   "moduleResolution": "node",
 7 |   "out": "docs/",
 8 |   "excludeExternals": "true",
 9 |   "exclude": "**/node_modules/@types/**/*.ts"
10 | }
11 | 
12 | 
13 | 


--------------------------------------------------------------------------------
/typings/path-posix/path-posix.d.ts:
--------------------------------------------------------------------------------
 1 | // Type definitions for path-posix v1.0.0
 2 | // Project: https://github.com/jden/node-path-posix
 3 | // Definitions by: Steven Silvester 
 4 | 
 5 | 
 6 | declare module 'path-posix' {
 7 |   export
 8 |   function basename(path: string, ext?: string): string;
 9 | 
10 |   export
11 |   function dirname(path: string): string;
12 | 
13 |   export
14 |   function extname(path: string): string;
15 | 
16 |   export
17 |   function format(obj: IPathObject): string;
18 | 
19 |   export
20 |   function isAbsolute(path: string): boolean;
21 | 
22 |   export
23 |   function join(...parts: string[]): string;
24 | 
25 |   export
26 |   function normalize(path: string): string;
27 | 
28 |   export
29 |   function parse(path: string): IPathObject;
30 | 
31 |   export
32 |   function relative(from: string, to: string): string;
33 | 
34 |   export
35 |   function resolve(path: string, ...others: string[]): string;
36 | 
37 |   export
38 |   var sep: string;
39 | 
40 |   export
41 |   var delimiter: string;
42 | 
43 |   export
44 |   interface IPathObject {
45 |     dir?: string;
46 |     root?: string;
47 |     base?: string;
48 |     name?: string;
49 |     ext?: string;
50 |   }
51 | }
52 | 
53 | 


--------------------------------------------------------------------------------
/typings/url-parse/url-parse.d.ts:
--------------------------------------------------------------------------------
 1 | // Type definitions for url-parse v1.1.8
 2 | // Project: https://github.com/unshiftio/url-parse
 3 | // Definitions by: Steven Silvester 
 4 | 
 5 | // We use the hack mentioned in https://github.com/Microsoft/TypeScript/issues/5073
 6 | // to enable `import * as urlparse from 'url-parse';`
 7 | 
 8 | declare module 'url-parse' {
 9 |   interface IURL {
10 |     protocol: string;
11 |     slashes: boolean;
12 |     auth: string;
13 |     username: string;
14 |     password: string;
15 |     host: string;
16 |     hostname: string;
17 |     port: string;
18 |     pathname: string;
19 |     query: any;
20 |     hash: string;
21 |     href: string;
22 |     origin: string;
23 |   }
24 |   function parse(url: string, parseQuery?: boolean): IURL;
25 |   namespace parse { }
26 |   export = parse;
27 | }
28 | 


--------------------------------------------------------------------------------
/typings/xmlhttprequest/xmlhttprequest.d.ts:
--------------------------------------------------------------------------------
1 | 
2 | declare module 'xmlhttprequest' {
3 |     export var XMLHttpRequest: XMLHttpRequest;
4 | }
5 | 


--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
 1 | var version = require('./package.json').version;
 2 | 
 3 | module.exports = {
 4 |     entry: './lib',
 5 |     output: {
 6 |         filename: './dist/index.js',
 7 |         library: '@jupyterlab/services',
 8 |         libraryTarget: 'umd',
 9 |         umdNamedDefine: true,
10 |         publicPath: 'https://unpkg.com/@jupyterlab/services@' + version + '/dist/'
11 |     },
12 |     bail: true,
13 |     devtool: 'source-map'
14 | };
15 | 


--------------------------------------------------------------------------------