├── .gitignore
├── index.js
├── public
├── index.html
├── app.html
├── index.js
├── angularFalcor.js
└── config.js
├── api
├── index.js
└── falcorRouter.js
├── LICENSE
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | jspm_packages
2 | node_modules
3 | .idea
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // Starts API server
2 |
3 | // Hooks into babel to transpile all ES6 code to ES5 on require()
4 | require('babel/register');
5 |
6 | // Start the Web API module
7 | require('./api/index.js').start();
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Angular1 + Falcor test
10 |
11 | Loading App...
12 |
13 |
14 |
15 |
21 |
22 |
--------------------------------------------------------------------------------
/api/index.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import bodyParser from 'body-parser';
3 | import FalcorServer from 'falcor-express';
4 | import {todoRouterFactory} from './falcorRouter.js';
5 |
6 | const app = express();
7 |
8 | app.use(bodyParser.urlencoded({ extended: false }));
9 |
10 | // Simple middleware to handle get/post
11 | app.use('/model.json', FalcorServer.dataSourceRoute(function(req, res) {
12 | console.log(`${req.method}: ${req.originalUrl}`);
13 | return todoRouterFactory();
14 | }));
15 |
16 | app.use('/', express.static('./public/'));
17 |
18 | export const start = () => {
19 | return app.listen(9090, function(err) {
20 | if (err) {
21 | console.error(err);
22 | return;
23 | }
24 | console.log("navigate to http://localhost:9090");
25 | });
26 | };
--------------------------------------------------------------------------------
/public/app.html:
--------------------------------------------------------------------------------
1 |
2 |
Todos: {{ctrl.model.getViewValue('todos.length')}}
3 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Loading Data...
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Jason Stone
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-falcor",
3 | "version": "1.0.0",
4 | "description": "Example of using FalcorJS with AngularJS v1",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js",
8 | "test": "echo \"Error: no test specified\" && exit 1",
9 | "postinstall": "node ./node_modules/.bin/jspm install"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/rolaveric/angular-falcor.git"
14 | },
15 | "author": "Jason Stone (https://github.com/rolaveric)",
16 | "license": "MIT",
17 | "bugs": {
18 | "url": "https://github.com/rolaveric/angular-falcor/issues"
19 | },
20 | "homepage": "https://github.com/rolaveric/angular-falcor",
21 | "private": true,
22 | "jspm": {
23 | "directories": {
24 | "baseURL": "public"
25 | },
26 | "dependencies": {
27 | "angular": "github:angular/bower-angular@^1.4.7",
28 | "falcor": "npm:falcor@^0.1.13",
29 | "falcor-http-datasource": "npm:falcor-http-datasource@^0.1.2",
30 | "text": "github:systemjs/plugin-text@^0.0.2"
31 | },
32 | "devDependencies": {
33 | "babel": "npm:babel-core@^5.6.4",
34 | "babel-runtime": "npm:babel-runtime@^5.8.20",
35 | "core-js": "npm:core-js@^0.9.17"
36 | }
37 | },
38 | "dependencies": {
39 | "babel": "^5.8.21",
40 | "body-parser": "^1.13.3",
41 | "express": "^4.13.3",
42 | "falcor-express": "^0.1.2",
43 | "falcor-json-graph": "^1.1.5",
44 | "falcor-router": "^0.2.9",
45 | "jspm": "^0.16.1"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/public/index.js:
--------------------------------------------------------------------------------
1 | import 'babel/polyfill';
2 | import angular from 'angular';
3 | import falcor from './angularFalcor.js';
4 | import template from './app.html!text';
5 |
6 | class MyController {
7 | constructor($scope, falcor) {
8 | this.$scope = $scope;
9 | this.falcor = falcor;
10 | this.activated = false;
11 |
12 | this.page = 1;
13 | this.pageSize = 10;
14 | this.newName = '';
15 |
16 | // On load
17 | this.activate();
18 | }
19 |
20 | /**
21 | * Handles on load processing, like creating the FalcorJS model and loading initial data
22 | */
23 | activate() {
24 | // Define module, using cached data
25 | this.model = new this.falcor.Model({
26 | source: new this.falcor.HttpDataSource('/model.json')
27 | }).batch();
28 | this.activated = true;
29 | }
30 |
31 | /**
32 | * Creates a new todos item, setting it in the Falcor model and then retrieving the updated data.
33 | */
34 | addTodo() {
35 | if (this.newName) {
36 | // todos.add(name, done)
37 | this.model.call(['todos', 'add'], [{name: this.newName, done: false}])
38 | .subscribe(_ => this.$scope.$evalAsync());
39 | this.newName = '';
40 | }
41 | }
42 |
43 | /**
44 | * Updates the current page being viewed.
45 | * @param newPage {number}
46 | */
47 | changePage(newPage) {
48 | this.page = newPage;
49 | }
50 | }
51 | MyController.$inject = ['$scope', 'falcor'];
52 |
53 | angular.module('falcorExample', [falcor])
54 | .directive('falcorSample', () => {
55 | return {
56 | template: template,
57 | controller: MyController,
58 | controllerAs: 'ctrl',
59 | bindToController: true,
60 | scope: {}
61 | };
62 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FalcorJS + Angular v1 example
2 |
3 | A quick example of using [FalcorJS](https://netflix.github.io/falcor/) with [AngularJS v1](https://angularjs.org/).
4 |
5 | # Setup
6 |
7 | Install dependencies from both npm and jspm
8 |
9 | `npm install`
10 |
11 | Start server
12 |
13 | `npm start`
14 |
15 | Open [http://localhost:9090](http://localhost:9090)
16 |
17 | # How it works
18 |
19 | The key to making FalcorJS work with AngularJS is getting it slotted nicely into the digest loop so that changes are detected and shown on the view.
20 | Since FalcorJS is making it's own async http calls without `$http`, we need to let AngularJS know when a change may have occurred.
21 |
22 | The usual tool for this job is `$scope.$apply()`, but we have to be 100% sure that another `$scope.$apply()` call isn't already in progress or it will thrown an error.
23 | We could use `$timeout()` to achieve the same thing, but it will always cause a new digest loop to occur, even if there's already one in progress.
24 | A [better alternative](http://www.bennadel.com/blog/2605-scope-evalasync-vs-timeout-in-angularjs.htm) is to use `$scope.$evalAsync()` which gives us the best of both worlds.
25 | Since FalcorJS returns Observables, we simply call `$scope.$evalAsync()` when subscribing to an Observable.
26 |
27 | # References
28 |
29 | * [FalcorJS Website](http://netflix.github.io/falcor/)
30 | * [Calling Functions](http://netflix.github.io/falcor/documentation/model.html#calling-functions): particularly important for operations like 'add()' and 'remove()'
31 | * [FalcorJS Router Demo](https://github.com/Netflix/falcor-router-demo)
32 | * [FalcorJS Express Demo](https://github.com/Netflix/falcor-express-demo): Uses `falcor-router-demo` as the router.
33 | * [BabelJS](https://babeljs.io/): Awesome ES2015 (ES6) transpiler
34 | * [ES2015 Reference](https://babeljs.io/docs/learn-es2015/)
35 | * [Reactive Extensions (Rx)](http://reactivex.io/)
36 | * [RxJS](https://github.com/Reactive-Extensions/RxJS)
37 | * Observable Specifications
38 | * [Jafar Husain's Observable Specification](https://github.com/jhusain/observable-spec)
39 | * [zenparsing's Observable Type Proposal](https://github.com/zenparsing/es-observable)
--------------------------------------------------------------------------------
/public/angularFalcor.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import falcor from 'falcor';
3 | import HttpDataSource from 'falcor-http-datasource';
4 |
5 | export function falcorFactory($parse, $rootScope) {
6 | // Add the HttpDatasource to the falcor object
7 | falcor.HttpDataSource = HttpDataSource;
8 |
9 | // Add a 'getViewValue()' method to the Model prototype
10 |
11 | var inProgress = []; // async calls which are currently in progress
12 | /**
13 | * Attempts to return a value in a way that can be used directly from a template.
14 | *
15 | * Works by attempting to retrieve the value from the Model cache.
16 | * If it can't be found, it initiates an Observable to retrieve the value and returns undefined.
17 | * The intention being that the real value will eventually be returned on subsequent $digest loops.
18 | *
19 | * @param path {string|array} Path to the value on the Model that's required. Must resolve to only a single value.
20 | */
21 | falcor.Model.prototype.getViewValue = function(path) {
22 | if (path) {
23 | // If the path is an array, join them together
24 | if (angular.isArray(path)) {
25 | path = path.reduce((memo, token, index) => {
26 | token = String(token);
27 | if (!isNaN(token)) {
28 | token = '[' + token + ']';
29 | } else if (/(^[0-9])|([^0-9a-zA-Z_\$])/.test(token)) {
30 | // If it starts with a number or contains anything other than letters, numbers, _ and $, wrap in brackets
31 | token = "['" + token + "']";
32 | } else if (index > 0) {
33 | token = '.' + token;
34 | }
35 | return memo + token;
36 | }, '');
37 | }
38 |
39 | // Try to get the value from the cache
40 | const value = $parse(path)(this.getCache(path));
41 | if (typeof value !== 'undefined') {
42 | return typeof value === 'object' ? value.value : value;
43 | }
44 |
45 | // If a call for the same path isn't already in progress...
46 | if (!inProgress.find((i) => i.model === this && i.path === path)) {
47 | // Make an async call for the value, and we'll get the result on the next digest loop
48 | const cache = {model: this, path: path};
49 | inProgress.push(cache);
50 | this.getValue(path)
51 | .subscribe(() => {
52 | // Invalidate the observable cache
53 | inProgress.splice(inProgress.findIndex((i) => i === cache), 1);
54 |
55 | // Make sure the $digest loop picks up on the change
56 | $rootScope.$evalAsync();
57 | });
58 | }
59 | }
60 | };
61 |
62 | /**
63 | * Returns a function which can be used with ngModelOptions `getterSetter: true` set.
64 | * @param path {string|Array} Path to the value on the Model that's being get and set. Must resolve to only a single value.
65 | * @returns {Function}
66 | */
67 | falcor.Model.prototype.viewGetterSetter = function(path) {
68 | var model = this;
69 | return function (newValue) {
70 | return arguments.length
71 | ? model.setValue(path, newValue).subscribe(_ => $rootScope.$evalAsync())
72 | : model.getViewValue(path);
73 | }
74 | };
75 |
76 | return falcor;
77 | }
78 | falcorFactory.$inject = ['$parse', '$rootScope'];
79 |
80 | /**
81 | * Generates a filter which produces a range of numbers for pagination.
82 | * Useful when all you have is an item count to iterate over.
83 | */
84 | function pageRangeFilterProvider() {
85 | /**
86 | * Returns an array of indexes that can be used in pagination.
87 | * @param itemCount {number} Number of items being paged through, meaning max index must be itemCount - 1
88 | * @param [currentPage=1] {number} Current page number being viewed
89 | * @param [pageSize=5] {number} Max size of each page
90 | * @returns {number[]}
91 | */
92 | return function pageRangeFilter(itemCount, currentPage, pageSize) {
93 | itemCount = isNaN(itemCount) ? 0 : itemCount;
94 | currentPage = currentPage || 1;
95 | pageSize = pageSize || 5;
96 | const range = [];
97 | for (var x = (currentPage - 1) * pageSize; x < currentPage * pageSize && x < itemCount; x++) {
98 | range.push(x);
99 | }
100 | return range;
101 | };
102 | }
103 |
104 | angular.module('falcor', [])
105 | .factory('falcor', falcorFactory)
106 | .filter('pageRange', pageRangeFilterProvider);
107 |
108 | export default 'falcor';
--------------------------------------------------------------------------------
/api/falcorRouter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Creates a Falcor Router which delivers a virtual JSON Graph of a "Todo list" model.
3 | *
4 | * {
5 | * todos: {
6 | * [index]: {
7 | * name: string,
8 | * done: boolean
9 | * },
10 | * add: (name: string, done: boolean) => {},
11 | * remove: (index: integer)
12 | * }
13 | * }
14 | */
15 |
16 | import Router from 'falcor-router';
17 | import jsonGraph from 'falcor-json-graph';
18 |
19 | // Instead of using a real database, just going to cheat and keep it in memory - for demo purposes
20 | const data = {
21 | todos: [
22 | {
23 | name: 'get milk from corner store',
24 | done: false
25 | },
26 | {
27 | name: 'withdraw money from ATM',
28 | done: true
29 | }
30 | ]
31 | };
32 |
33 | export const TodoRouter = Router.createClass([
34 | // Todo items
35 | {
36 | route: "todos[{integers:indexes}]['name', 'done']",
37 | // Gets items
38 | get: function (pathSet) {
39 | // Build a set of PathValues
40 | const pathValues = pathSet.indexes.reduce((memo, index) => {
41 | memo.push(
42 | {path: ['todos', index, 'name'], value: data.todos[index].name},
43 | {path: ['todos', index, 'done'], value: data.todos[index].done}
44 | );
45 | return memo;
46 | }, []);
47 |
48 | // Using a promise which resolves after 2 seconds to mimic latency
49 | return new Promise((resolve) => {
50 | setTimeout(() => {
51 | console.log('resolving');
52 | resolve(pathValues);
53 | }, 2000);
54 | });
55 | },
56 |
57 | // Updates items
58 | set: function (jsonGraphArg) {
59 | return Object.keys(jsonGraphArg.todos)
60 | .reduce((memo, index) => {
61 | const args = jsonGraphArg.todos[index];
62 | const todo = data.todos[index];
63 |
64 | // Check if existing record is defined
65 | if (!todo) {
66 | ['name', 'done'].forEach((key) => {
67 | if (args.hasOwnProperty(key)) {
68 | memo.push(
69 | {path: ['todos', index, key], value: jsonGraph.error(`Todo ${index} not found`)}
70 | );
71 | }
72 | });
73 | } else {
74 | // Apply changes
75 | ['name', 'done'].forEach((key) => {
76 | if (args.hasOwnProperty(key)) {
77 | todo[key] = args[key];
78 | memo.push(
79 | {path: ['todos', index, key], value: args[key]}
80 | );
81 | }
82 | });
83 | }
84 | return memo;
85 | }, []);
86 | }
87 | },
88 |
89 | // Simulates access to todo items count
90 | {
91 | route: "todos.length",
92 | get: function () {
93 | return Promise.resolve({
94 | jsonGraph: {
95 | todos: {
96 | length: data.todos.length
97 | }
98 | }
99 | });
100 | }
101 | },
102 |
103 | // Handles 'todos.add(name: string, done: boolean)' function calls
104 | {
105 | route: 'todos.add',
106 | call: function (callPath, args) {
107 | const newItem = args[0];
108 |
109 | // Validate arguments
110 | // TODO: Use JSON Schema to simplify this
111 | if (!newItem.name || typeof newItem.name !== 'string') {
112 | throw new Error("invalid name value");
113 | }
114 | if (typeof newItem.done !== 'boolean') {
115 | throw new Error("invalid done value")
116 | }
117 |
118 | // Add new record to the data
119 | const newLength = data.todos.push({name: newItem.name, done: newItem.done});
120 |
121 | // Returns list of changed/invalidated paths
122 | return [
123 | {
124 | path: ['todos', newLength - 1, 'name'],
125 | value: newItem.name
126 | },
127 | {
128 | path: ['todos', newLength - 1, 'done'],
129 | value: newItem.done
130 | },
131 | {
132 | path: ['todos', 'length'],
133 | value: newLength
134 | }
135 | ];
136 | }
137 | },
138 |
139 | // Handles 'todos.remove(index)' function calls
140 | {
141 | route: 'todos.remove',
142 | call: function (callPath, args) {
143 | const index = args[0];
144 |
145 | // Validate arguments
146 | if (isNan(index) || index < 0 || index >= data.todos.length) {
147 | throw new Error("invalid index");
148 | }
149 |
150 | // Remove record from data
151 | data.todos.splice(index, 1);
152 |
153 | // Returns list of changed/invalidated paths
154 | return [
155 | {
156 | path: ['todos', {from: index, to: data.todos.length}, 'name'],
157 | invalidated: true
158 | },
159 | {
160 | path: ['todos', 'length'],
161 | value: data.todos.length
162 | }
163 | ];
164 | }
165 | }
166 | ]);
167 |
168 | export const todoRouterFactory = () => new TodoRouter();
--------------------------------------------------------------------------------
/public/config.js:
--------------------------------------------------------------------------------
1 | System.config({
2 | baseURL: "/",
3 | defaultJSExtensions: true,
4 | transpiler: "babel",
5 | babelOptions: {
6 | "optional": [
7 | "runtime"
8 | ]
9 | },
10 | paths: {
11 | "github:*": "jspm_packages/github/*",
12 | "npm:*": "jspm_packages/npm/*"
13 | },
14 |
15 | map: {
16 | "angular": "github:angular/bower-angular@1.4.4",
17 | "babel": "npm:babel-core@5.8.22",
18 | "babel-runtime": "npm:babel-runtime@5.8.20",
19 | "core-js": "npm:core-js@0.9.18",
20 | "falcor": "npm:falcor@0.1.12",
21 | "falcor-http-datasource": "npm:falcor-http-datasource@0.1.1",
22 | "text": "github:systemjs/plugin-text@0.0.2",
23 | "github:jspm/nodelibs-assert@0.1.0": {
24 | "assert": "npm:assert@1.3.0"
25 | },
26 | "github:jspm/nodelibs-buffer@0.1.0": {
27 | "buffer": "npm:buffer@3.4.3"
28 | },
29 | "github:jspm/nodelibs-domain@0.1.0": {
30 | "domain-browser": "npm:domain-browser@1.1.4"
31 | },
32 | "github:jspm/nodelibs-events@0.1.1": {
33 | "events": "npm:events@1.0.2"
34 | },
35 | "github:jspm/nodelibs-http@1.7.1": {
36 | "Base64": "npm:Base64@0.2.1",
37 | "events": "github:jspm/nodelibs-events@0.1.1",
38 | "inherits": "npm:inherits@2.0.1",
39 | "stream": "github:jspm/nodelibs-stream@0.1.0",
40 | "url": "github:jspm/nodelibs-url@0.1.0",
41 | "util": "github:jspm/nodelibs-util@0.1.0"
42 | },
43 | "github:jspm/nodelibs-https@0.1.0": {
44 | "https-browserify": "npm:https-browserify@0.0.0"
45 | },
46 | "github:jspm/nodelibs-path@0.1.0": {
47 | "path-browserify": "npm:path-browserify@0.0.0"
48 | },
49 | "github:jspm/nodelibs-process@0.1.1": {
50 | "process": "npm:process@0.10.1"
51 | },
52 | "github:jspm/nodelibs-stream@0.1.0": {
53 | "stream-browserify": "npm:stream-browserify@1.0.0"
54 | },
55 | "github:jspm/nodelibs-url@0.1.0": {
56 | "url": "npm:url@0.10.3"
57 | },
58 | "github:jspm/nodelibs-util@0.1.0": {
59 | "util": "npm:util@0.10.3"
60 | },
61 | "npm:asap@2.0.3": {
62 | "domain": "github:jspm/nodelibs-domain@0.1.0",
63 | "process": "github:jspm/nodelibs-process@0.1.1"
64 | },
65 | "npm:assert@1.3.0": {
66 | "util": "npm:util@0.10.3"
67 | },
68 | "npm:babel-runtime@5.8.20": {
69 | "process": "github:jspm/nodelibs-process@0.1.1"
70 | },
71 | "npm:buffer@3.4.3": {
72 | "base64-js": "npm:base64-js@0.0.8",
73 | "ieee754": "npm:ieee754@1.1.6",
74 | "is-array": "npm:is-array@1.0.1"
75 | },
76 | "npm:core-js@0.9.18": {
77 | "fs": "github:jspm/nodelibs-fs@0.1.2",
78 | "process": "github:jspm/nodelibs-process@0.1.1",
79 | "systemjs-json": "github:systemjs/plugin-json@0.1.0"
80 | },
81 | "npm:core-util-is@1.0.1": {
82 | "buffer": "github:jspm/nodelibs-buffer@0.1.0"
83 | },
84 | "npm:domain-browser@1.1.4": {
85 | "events": "github:jspm/nodelibs-events@0.1.1"
86 | },
87 | "npm:falcor-http-datasource@0.1.1": {
88 | "xmlhttprequest": "npm:xmlhttprequest@1.7.0"
89 | },
90 | "npm:falcor-json-graph@1.1.5": {
91 | "falcor-path-syntax": "npm:falcor-path-syntax@0.2.1"
92 | },
93 | "npm:falcor@0.1.12": {
94 | "asap": "npm:asap@2.0.3",
95 | "buffer": "github:jspm/nodelibs-buffer@0.1.0",
96 | "falcor-json-graph": "npm:falcor-json-graph@1.1.5",
97 | "falcor-path-syntax": "npm:falcor-path-syntax@0.2.1",
98 | "falcor-path-utils": "npm:falcor-path-utils@0.3.0",
99 | "path": "github:jspm/nodelibs-path@0.1.0",
100 | "process": "github:jspm/nodelibs-process@0.1.1",
101 | "promise": "npm:promise@7.0.0",
102 | "rx": "npm:rx@2.5.3",
103 | "util": "github:jspm/nodelibs-util@0.1.0"
104 | },
105 | "npm:https-browserify@0.0.0": {
106 | "http": "github:jspm/nodelibs-http@1.7.1"
107 | },
108 | "npm:inherits@2.0.1": {
109 | "util": "github:jspm/nodelibs-util@0.1.0"
110 | },
111 | "npm:path-browserify@0.0.0": {
112 | "process": "github:jspm/nodelibs-process@0.1.1"
113 | },
114 | "npm:promise@7.0.0": {
115 | "asap": "npm:asap@2.0.3",
116 | "fs": "github:jspm/nodelibs-fs@0.1.2"
117 | },
118 | "npm:punycode@1.3.2": {
119 | "process": "github:jspm/nodelibs-process@0.1.1"
120 | },
121 | "npm:readable-stream@1.1.13": {
122 | "buffer": "github:jspm/nodelibs-buffer@0.1.0",
123 | "core-util-is": "npm:core-util-is@1.0.1",
124 | "events": "github:jspm/nodelibs-events@0.1.1",
125 | "inherits": "npm:inherits@2.0.1",
126 | "isarray": "npm:isarray@0.0.1",
127 | "process": "github:jspm/nodelibs-process@0.1.1",
128 | "stream-browserify": "npm:stream-browserify@1.0.0",
129 | "string_decoder": "npm:string_decoder@0.10.31"
130 | },
131 | "npm:rx@2.5.3": {
132 | "process": "github:jspm/nodelibs-process@0.1.1"
133 | },
134 | "npm:stream-browserify@1.0.0": {
135 | "events": "github:jspm/nodelibs-events@0.1.1",
136 | "inherits": "npm:inherits@2.0.1",
137 | "readable-stream": "npm:readable-stream@1.1.13"
138 | },
139 | "npm:string_decoder@0.10.31": {
140 | "buffer": "github:jspm/nodelibs-buffer@0.1.0"
141 | },
142 | "npm:url@0.10.3": {
143 | "assert": "github:jspm/nodelibs-assert@0.1.0",
144 | "punycode": "npm:punycode@1.3.2",
145 | "querystring": "npm:querystring@0.2.0",
146 | "util": "github:jspm/nodelibs-util@0.1.0"
147 | },
148 | "npm:util@0.10.3": {
149 | "inherits": "npm:inherits@2.0.1",
150 | "process": "github:jspm/nodelibs-process@0.1.1"
151 | },
152 | "npm:xmlhttprequest@1.7.0": {
153 | "buffer": "github:jspm/nodelibs-buffer@0.1.0",
154 | "child_process": "github:jspm/nodelibs-child_process@0.1.0",
155 | "fs": "github:jspm/nodelibs-fs@0.1.2",
156 | "http": "github:jspm/nodelibs-http@1.7.1",
157 | "https": "github:jspm/nodelibs-https@0.1.0",
158 | "process": "github:jspm/nodelibs-process@0.1.1",
159 | "url": "github:jspm/nodelibs-url@0.1.0"
160 | }
161 | }
162 | });
163 |
--------------------------------------------------------------------------------