{"id":26502229,"url":"https://github.com/d-oliveros/isomorphine","last_synced_at":"2025-03-20T17:39:27.665Z","repository":{"id":57278138,"uuid":"39425039","full_name":"d-oliveros/isomorphine","owner":"d-oliveros","description":"Require server-side modules from the browser, remotely.","archived":false,"fork":false,"pushed_at":"2017-05-17T10:30:45.000Z","size":166,"stargazers_count":63,"open_issues_count":2,"forks_count":6,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-17T02:17:03.174Z","etag":null,"topics":["isomorphic","rpc","webpack-loader"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/d-oliveros.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-21T05:02:22.000Z","updated_at":"2024-09-29T10:56:52.000Z","dependencies_parsed_at":"2022-09-18T17:13:34.725Z","dependency_job_id":null,"html_url":"https://github.com/d-oliveros/isomorphine","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d-oliveros%2Fisomorphine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d-oliveros%2Fisomorphine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d-oliveros%2Fisomorphine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/d-oliveros%2Fisomorphine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/d-oliveros","download_url":"https://codeload.github.com/d-oliveros/isomorphine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244664735,"owners_count":20490202,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["isomorphic","rpc","webpack-loader"],"created_at":"2025-03-20T17:39:26.937Z","updated_at":"2025-03-20T17:39:27.650Z","avatar_url":"https://github.com/d-oliveros.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Isomorphine\n\nIsomorphine is a webpack loader that lets you access server-side modules in the browser. It works by injecting an rpc routing layer behind the scenes. This lets you require and run server-side code from the browser, without doing AJAX requests or express routes.\n\nWhen requiring a server-side entity from the browser, the module is provided as a mirror of the server-side entity. This mirror will automatically transport any method call to the server, and resolve the results in the browser seamlessly. When requiring a module from the server's execution context, the module is resolved as-is, without any mirroring or routing whatsover.\n\nYou can securely share and use server-side code in the browser (for example your database models) and eliminate data fetching boilerplate. The server-side modules will not be required directly in the browser, so you can require modules containing browser-incompatible libraries.\n\nIt does _not_ expose server-side code. It also provides a [security mechanism](#rpc-context) for remote procedure calls (RPCs), and supports promises \u0026 async/await.\n\nYou don't need to do HTTP requests and endpoints anymore. You can skip your application's routing layer, and focus on your application's purpose.\n\n\n### Summary\n\n* [Usage](#installation)\n* [How It Works](#how-it-works)\n* [Examples](#examples)\n* [Promise \u0026 Async/Await support](#promise--es7-support)\n* [Security \u0026 RPC context](#rpc-context)\n* [Caveats](#caveats)\n* [Comparison](#comparison)\n* [Philosophy](#philosophy)\n\n\n### Requirements\n- Node\n- Webpack\n\n### Installation\n\n```bash\nnpm install isomorphine\n```\n\nThen you must [add isomorphine as a webpack loader](#webpack-configuration).\n\n### Usage\n\nIsomorphine has only one method: `isomorphine.proxy()`\n\n`isomorphine.proxy()` - Creates an object exposing the modules in the directory where it was called. This is similar to [require-all](https://github.com/felixge/node-require-all), but also enables each module to be used in the browser.\n\n```js\nvar isomorphine = require('isomorphine');\n\n// This will provide the entities in this folder,\n// similar to 'require-all' but in a browser-compatible way.\nmodule.exports = isomorphine.proxy();\n```\n\nEach file in the current directory represents a property in the resulting object. Each file must export a function either in `module.exports` or via `export default`. Directories are scanned recursively (See [require-all](https://github.com/felixge/node-require-all)). When using these modules from the browser through isomorphine, function calls will be proxied to the server.\n\nThis will let you use any server-side entity remotely, through the object created with `isomorphine.proxy()`. Just require this `morphine` object from the browser or the server, and take anything you need out of it. For example:\n\n```js\n/**\n * Suppose the file we defined above is in ./models/index.js,\n * and the User model is located in ./models/User.\n */\nvar User = require('./models').User;\n\n$('#button').on('click', function() {\n\n  // When called from the browser, isomorphine will transport this function call\n  // to the server, process the results, and resolve this callback function.\n  // When called from the server, the function is called directly.\n  User.create({ name: 'someone' }, function(err, user) {\n    console.log('Im the browser, and I created a user in the db!');\n  });\n});\n```\n\nYou also need to mount isomorphine's RPC interface on your current express-based app, by doing:\n\n```js\nvar express = require('express');\nvar morphine = require('./models'); // Suppose this is the file we created above\n\nvar app = express();\n\n// Mounts the rpc layer middleware. This will enable remote function calls\napp.use(morphine.router);\n\napp.listen(3000, function() {\n  console.log('App listening at port 3000');\n});\n```\n\nAlternatively, you can start isomorphine's router as a stand-alone http server by doing:\n\n```js\nvar morphine = require('./models'); // Suppose this is the file we created above\n\n// This will enable remote function calls, and must be run in the server\nmorphine.router.listen(3000, function() {\n  console.log('RPC interface listening at port 3000');\n});\n```\n\nBy default, isomorphine will make the remote procedure calls to the same host and port of the client's current `window.location`. To manually specify the host and port of your API server, you can do:\n\n```js\nvar morphine = isomorphine.proxy();\n\nmorphine.config({\n  host: 'api.mysite.com', // default is the current browser location hostname\n  port: '3000'            // default is the current browser location port\n});\n```\n\nRemember to read the [caveats](#caveats) for common gotchas, and the section below for a more in-depth explanation.\n\n\n### How It Works\n\n* Check the [barebone example](https://github.com/d-oliveros/isomorphine/tree/master/examples/barebone), and the [isomorphic todoMVC](https://github.com/d-oliveros/isomorphic-todomvc) for [full working examples](#examples).\n\nIsomorphine detects server-side entities by scanning the file structure recursively, and building objects whose methods represent files in the server. Each file has to export a function (via `module.exports` or `export default`). Read the [caveats](#caveats) for common gotchas.\n\nThe internal behavior of `isomorphine.proxy()` differs depending on whether its being ran in the browser or the server:\n\n* When called from the server: `isomorphine.proxy()` requires all files in the current directory (similar to [require-all](https://github.com/felixge/node-require-all)) and also creates an express-based router that will handle remote procedure calls (RPCs) to the methods in these entities.\n\n* When called from the browser: `isomorphine.proxy()` creates a mirror to the server-side entities. The mirror is preprocessed and injected by webpack, so you must [add isomorphine as a webpack loader](#webpack-configuration). No router is created in the browser, and no server-side modules are actually `require()`'d in the browser.\n\nIn this example, we will be using a fictitious server-side model called `User`, written in vanilla ES5. We will be splitting each model method in its own file. Please note that only files that export a function can be used in the browser. Promises and ES7 async/await are [also supported](#promise--es7-support).\n\n```js\n// in /models/User/create.js\n\nmodule.exports = function createUser(username, callback) {\n  var user = {\n    _id: 123,\n    type: 'user',\n    name: username,\n    created: new Date()\n  };\n\n  // It doesn't matter how you handle your data layer.\n  // Isomorphine is framework-agnostic, so you can use anything you want, like\n  // mongoose, sequelize, direct db drivers, or whatever really.\n  db.create(user, callback);\n}\n```\n\n```js\n// in /models/User/delete.js\n\nmodule.exports = function deleteUser(userId, callback) {\n  db.remove({ _id: userId }, callback);\n}\n```\n\nTo make the these functions available in the browser, you have to create an isomorphic proxy to this folder by using `isomorphine.proxy()`.\n\n`isomorphine.proxy()` will make these modules available in the browser, without breaking your bundle due to incompatible server-only libraries, bloating your bundle's size, or exposing your server's code. In this example, we will create an isomorphic proxy with `isomorphine.proxy()`, and export it in the index file of the `/models` directory, thus exposing all the models in the browser.\n\n```js\n// in /models/index.js\nvar isomorphine = require('isomorphine');\n\n// This will be our main isomorphic gateway to this folder\nmodule.exports = isomorphine.proxy();\n```\n\nWe are calling `isomorphine.proxy()` in the `index.js` file of the `./models` folder, thus providing all the models in the `./models` folder to the browser, as mirror entity maps.\n\nBased on this example structure, the object created by `isomorphine.proxy()` is:\n\n```js\n// The browser gets this -\u003e\n{\n  User: {\n    create: [func ProxiedMethod],\n    delete: [func ProxiedMethod]\n  },\n  config: [func] // This method sets the host and port of your API server\n}\n\n// Whereas the server gets this -\u003e\n{\n  User: {\n    create: [func createUser],\n    delete: [func deleteUser]\n  },\n  config: [func emptyFunction] // This method does nothing in the server\n  router: [func] // this is the RPC API router that must be mounted in your app\n}\n\n// /models/User/create.js and /models/User/delete.js are not actually being\n// required in the browser, so don't worry about browser-incompatible modules\n```\n\nYou can use this fictitious `User` model in the browser by doing this, for example:\n\n```js\n// in /client.js, running in the browser\n\n// We can interact with this server-side entity (User)\n// without manually having to do any HTTP requests.\n//\n// If you were to require this model directly from the browser, you'd be requiring\n// your whole data layer, probably including your database initialization files\n// and other modules that are *not* browser-compatible.\n//\n// By requiring 'User' through '../models/index.js', which is the file where\n// we put 'isomorphine.proxy()', we are actually requiring an object\n// which mirrors the methods in the real server-side model.\n//\n// No server-side modules are actually required. Webpack generates an entity\n// map before running this code, so it already knows the API surface area of\n// your server-side model.\n//\n// Remember not to require 'User' directly. You need to require User through\n// the gateway we created above. Otherwise, you'd be requiring all the model\n// dependencies in the browser.\n//\nvar User = require('../models').User;\n\n// eg. using jquery\n$('#button').on('click', function() {\n\n  // When called from the browser, the browser serialized each parameter sent\n  // to the function call, and sends the serialized payload via a HTTP request\n  // to Isomorphine's endpoint in the server.\n  //\n  // Isomorphine serializes the result of the function call in the server,\n  // returns the resulting values, deserializes the values, and calls\n  // this callback function with the resulting values.\n  User.create('someUser99', function(err, user) {\n\n    window.alert('User created');\n\n    // uhh, lets delete the user better!\n    User.delete(user._id, function(err) {\n      window.alert('User deleted');\n    });\n  });\n});\n```\n\nTo make this work, you must mount the express-based router created by `isomorphine.proxy()` in your app:\n\n```js\n// in /server.js\nvar express = require('express');\n\n// suppose the file we defined above is in ./models/index.js\nvar morphine = require('./models');\n\nvar app = express();\n\n// Mounts the rpc layer middleware. This will enable remote function calls\napp.use(morphine.router);\n\n// you can mount other middleware and routes in the same app\n// app.get('/login', mySecureLoginCtrl);\n\napp.listen(3000, function() {\n  console.log('Server listening at port 3000');\n});\n\n// Alternatively, if you don't wish to mount isomorphine's middleware in your app,\n// you can just start listening for RPCs by doing\nvar morphine = require('./models');\nmorphine.router.listen(3000, function() {\n  console.log('Server listening at port 3000');\n});\n```\n\nMultiple arguments in exported functions are supported. You can define and use functions as you would normally do:\n\n```js\n// in /client.js, running in the browser\nvar User = require('./models').User;\n\nUser.edit(user._id, { name: 'newName' }, 'moreargs', (err, editedUser, stats) =\u003e {\n  console.log('User edited. Server said:');\n  console.log(editedUser);\n  console.log(stats);\n});\n```\n\n**Your server's files will _not_ be exposed in the browser, nor they will get added to the bundled file.**\n\nIt also supports promises and ES7 async/await\n\n```js\n// in /client.js, running in the browser\nimport { User } from '../models';\n\n$('#button').on('click', async () =\u003e {\n\n  // using promise-based server-side entities, and async/await in the browser\n  const user = await User.create('someUser99');\n  window.alert('User created');\n\n  // lets delete this user\n  await User.delete(user._id);\n  window.alert('User deleted');\n});\n```\n\nOther than reducing boilerplate code, it really shines in isomorphic applications, where you need the same piece of code running in the browser and the server, for example when doing server-side rendering of a react application.\n\n```js\nimport { User } from '../models';\n\n// In the browser, calling 'User.get()' will actually make a HTTP request\n// to the server, which will make the actual function call,\n// serialize the results back to the browser, and resolve the promise with the value(s).\n//\n// In the server, 'User.get()' will be called directly\n// without doing any HTTP requests or any routing whatsoever.\n//\n// This same piece of code can be run seamlessly in the browser and the server\nexport default class MyComponent extends React.Component {\n  async componentDidMount() {\n    const user = await User.get(123);\n    this.setState({ user });\n  }\n}\n```\n\nPlease read the [caveats](#caveats) for common gotchas, and the section below for working examples.\n\n\n### Examples\n\n* [Barebone](https://github.com/d-oliveros/isomorphine/tree/master/examples/barebone) - Barebone example using express, jquery, webpack.\n* [Isomorphic React](https://github.com/d-oliveros/isomorphine/tree/master/examples/isomorphic-react) - Server-side rendered React example using React, [Baobab](https://github.com/Yomguithereal/baobab), [Babel](https://github.com/babel/babel).\n* [isomorphic TodoMVC](https://github.com/d-oliveros/isomorphic-todomvc) for a full isomorphic TodoMVC react example.\n\nAlso go to [Wiselike](https://wiselike.com) to see it running in a production environment, and [ask me anything here!](https://wiselike.com/david)\n\n\n### Webpack Configuration\n\nIn order for Isomorphine to work, you need to specify Isomorphine as a webpack loader in your `webpack.config.js` file. The main `isomorphine` package contains the webpack loader so you don't need to install anything else.\n\n```js\nmodule.exports = {\n  entry: {...},\n\n  module: {\n    preLoaders: [{ loaders: ['isomorphine'] }]\n  },\n  ...\n};\n```\n\nFor instructions and a usage guide, please read [Usage](#usage)\n\n\n### Promise / ES7 Support\n\nIsomorphine supports promises, async/await, and callback-based functions.\n\n```js\n// Promise support\nmodule.exports = function getUser() {\n  return db.findAsync({ _id: 123 }); // supposing this returns a promise\n}\n```\n\n```js\n// Another promise support example\nmodule.exports = function readSomeFile() {\n  return new Promise((resolve, reject) =\u003e {\n    fs.readFile('somefile.txt', function(err, file) {\n      if (err) return reject(err);\n      resolve(file);\n    });\n  });\n}\n```\n\n```js\n// ES7 async/await support\nexport default async function getUser() {\n  const user = await MyUserModel.find({ _id: 123 });\n\n  // await doMoreStuff()...\n\n  return user;\n}\n```\n\n```js\n// callback-based support\nmodule.exports = function getUser(uid, callback) {\n  MyUserModel.find({ _id: uid }, (err, user) =\u003e {\n    if (err) return callback(err);\n\n    console.log('Got a user!');\n\n    callback(null, user);\n  });\n}\n```\n\n\n### RPC Context\n\nAllowing any API endpoint to be called from the browser, needs a proper validation mechanism to avoid getting exploited easily.\n\nWhen a call to a server-side function is done from the browser, a special context is passed to the function call. A special `xhr` flag and the request object `req` are passed as the function's context, in `this.xhr` and `this.req`:\n\n```js\n/**\n * When an endpoint is called from the browser, 'this.xhr' will be true,\n * and you'll be able to access the request object in 'this.req'.\n */\nmodule.exports = function createUser(username, callback) {\n  if (this.xhr) {\n    console.log('This function is being called remotely!');\n    console.log('Request is', this.req);\n  }\n\n  myUserModel.create(username, callback);\n}\n```\n\nYou can use this context to validate incoming requests. Please note, Isomorphine is unobtrusive and comes with no security middleware by default (other than this mechanism).\n\nYou *must* implement your own security mechanism yourself in an earlier middleware stage (using cookies or redis sessions or JWT or w/e):\n\n```js\nmodule.exports = function deleteUser(userId, callback) {\n\n  // Suppose I have a previous middleware step that adds `isAdmin`\n  // to the request object, based on the user's session or else\n  if (this.xhr \u0026\u0026 !this.req.isAdmin) {\n    return callback(401);\n  }\n\n  myUserModel.delete(userId, callback)\n}\n```\n\nIf the function is not being called remotely, `this.req` will be null, so make sure to validate `this.xhr` before trying to do something with the request object `this.req`.\n\n\n### Caveats\n\n* Your files must export a function in `module.exports` (or `export default` if using ES6 syntax) if you want to be able to call them from the browser. There is currently no support for exporting objects yet. If anyone figures out a way to determine the exports of a module without requiring its dependencies I'll be happy to merge the PR :D\n\n* Your modules have to be required through the isomorphine proxy. You can not require a server-side entity directly. If you do, you will be importing all the server-side code to the browser's bundle, and possibly breaking your app due to browser-incompatible modules, like `fs`, `express`, `mongo`, database drivers, etc.\n\n* When a function is called directly from the server (eg when called by a cron job), there's no `this.req` object being passed to the function calls, so you must validate sensitive paths in an earlier stage.\n\n* Also, please note that the file where you create the isomorphic proxy, has to be browser-compatible. Isomorphine works by proxying the methods contained in the specified directory, but the file itself will be required as-is in the browser.\n\n\n### Comparison\n\nThe two examples below achieve the same result. They both create a web server, register a route that returns a user by user ID, starts listening at port 3000, and call this endpoint from the browser.\n\nOne example uses isomorphine, while the other one uses the common approach of building an API route, calling a model from a controller, and building a client-side wrapper for data-fetching logic.\n\n##### With isomorphine:\n\n```js\n// in ./api/User/get.js\n\n// Dummy User.get() method\nmodule.exports = function(id, callback) {\n  var user = {\n    id: id,\n    name: 'Some User'\n  };\n\n  callback(null, user);\n};\n```\n\n```js\n// in ./api/index.js\nvar isomoprhine = require('isomorphine');\n\n// make the API entities callable from the browser\nvar morphine = isomorphine.proxy();\n\n// start listening for RPC calls\nmorphine.router.listen(3000, function() {\n  console.log('Interface listening at port 3000');\n});\n\nmodule.exports = morphine;\n```\n\n```js\n// in ./client.js\nvar User = require('./api').User;\nvar userId = 123;\n\n// just use the model and be happy\nUser.get(userId, function(err, user) {\n  console.log('User is', user);\n});\n```\n\n\n##### Without isomorphine:\n\n```js\n// in ./api/User/get.js\n\n// Dummy User.get() method\nmodule.exports = function(id, callback) {\n  var user = {\n    id: id,\n    name: 'Some User'\n  };\n\n  callback(null, user);\n};\n```\n\n```js\n// in ./api/index.js\nvar User = require('./User');\n\nvar express = require('express');\nvar bodyParser = require('body-parser');\nvar http = require('http');\n\nvar app = express();\n\napp.use(bodyParser.urlencoded());\napp.use(bodyParser.json());\n\napp.get('/api/user/:id', function(req, res, next) {\n  var userId = req.params.id || 123;\n\n  User.get(userId, function(err, user) {\n    if (err) {\n      return next(err);\n    }\n    res.set('Content-Type', 'application/json');\n    res.send(user);\n  });\n});\n\nvar server = http.createServer(app);\nserver.listen(3000, function() {\n  console.log('Server listening at port 3000');\n});\n```\n\n```js\n// in ./client.js\nvar request = require('request');\nvar userId = 1;\n\n// unnecesary boilerplate code\nfunction getUser(id, callback) {\n  request.get(`/api/user/${id}`, function(err, res) {\n    if (err) {\n      return callback(err);\n    }\n    var user = res.data;\n    callback(null, user);\n  });\n}\n\n// gets the user\ngetUser(userId, function(err, user) {\n  console.log('User is', user);\n});\n```\n\n\n##### With Isomorphine (condensed):\n\n```js\nvar isomoprhine = require('isomorphine');\nvar morphine = isomorphine.proxy();\nmorphine.router.listen(3000, function() {\n  console.log('Interface listening at port 3000');\n});\nmodule.exports = morphine;\nvar User = require('./api').User;\nvar userId = 123;\nUser.get(userId, function(err, user) {\n  console.log('User is', user);\n});\n```\n\n\n##### Without Isomorphine (condensed):\n\n```js\nvar User = require('./User');\nvar express = require('express');\nvar bodyParser = require('body-parser');\nvar http = require('http');\nvar app = express();\napp.use(bodyParser.urlencoded());\napp.use(bodyParser.json());\napp.get('/api/user/:id', function(req, res, next) {\n  var userId = req.params.id || 123;\n  User.get(userId, function(err, user) {\n    if (err) {\n      return next(err);\n    }\n    res.set('Content-Type', 'application/json');\n    res.send(user);\n  });\n});\nvar server = http.createServer(app);\nserver.listen(3000, function() {\n  console.log('Server listening at port 3000');\n});\nvar request = require('request');\nvar userId = 1;\nfunction getUser(id, callback) {\n  request.get(`/api/user/${id}`, function(err, res) {\n    if (err) {\n      return callback(err);\n    }\n    var user = res.data;\n    callback(null, user);\n  });\n}\ngetUser(userId, function(err, user) {\n  console.log('User is', user);\n});\n```\n\nThis example took 11 lines of code using Isomorphine. Doing this the traditional way took 35 lines. _And its only one route_\n\nIf we were to add more CRUD routes to our model, each route would require:\n\n1. A controller\n2. Some request validation\n3. A method in your model (maybe)\n4. Some wrapper in your client-side application\n\nPlus having to mantain these new components. Multiply this for each route you are currently mantaining, and you'll realize there has to be a better way to streamline your application's development.\n\nWith isomorphine, you can just call the server-side model directly. The model is already being supplied to the browser, so you would only require:\n\n1. A new method in your model\n\nNo need to re-write the data-fetching layer in the client application, or get parameters out of request object. heck, you don't even need to define and mantain routes. If you need to access the request object for validation purposes or else, you can access it through `this.req` as specified [here](#rpc-context).\n\n_Disclaimer: I'm not saying Isomorphine is the best fit for every case. You should have your routes and middleware in place to serve the client and views, and to handle special routes like auth actions, and should be providing everything you need to correctly authenticate remote calls to your methods. Starting isomorphine directly from `morphine.router.listen()` is not recommended, as Isomorphine is only intended to handle RPCs to server methods. It is not meant to server as a full-blown HTTP server or application stack._\n\n\n### Philosophy\n\nIsomorphine proposes an endpoint-less API approach in an attempt to further abstract the barriers between the server and the browser. It is meant to increase code reusability between the server and the browser, specially in an isomorphic full-stack javascript environment.\n\nThe idea is to encapsulate the routing layer within javascript's native syntax for importing and exporting modules, while providing a middleware interface to let you mount its RPC handler the way you want. This massively reduces development times, as you don't have to worry about connecting the browser and server together, so you can focus solely in your application's purpose.\n\nThe original idea was to use ES6's `Proxy` in the browser, to proxy any function call from any property of any object existing in any file in the server. Unfortunately, I quickly found out that there was no out-of-the-box support for `Proxy` in most of the major browsers. This led to the idea of using Webpack to pre-generate a map of server-side entities based on filenames. While this work, full support for any type of export will probably come after a wider adoption of ES6's `Proxy` in the browser, or a more advanced webpack loader.\n\n\n### Tests\n\n```bash\nmocha test\n```\n\nCheers.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fd-oliveros%2Fisomorphine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fd-oliveros%2Fisomorphine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fd-oliveros%2Fisomorphine/lists"}