├── .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 | --------------------------------------------------------------------------------