{"_id":"@acdlite/router","_rev":"7-b5327bcdf01c54d9c5e3d22a3394134e","name":"@acdlite/router","description":"An experiment in functional routing.","dist-tags":{"latest":"0.1.4"},"versions":{"0.1.0":{"name":"@acdlite/router","version":"0.1.0","description":"An experiment in functional routing.","main":"lib/index.js","scripts":{"build":"babel src --out-dir lib","test":"karma start --single-run","test:watch":"karma start"},"keywords":["routing","router","middleware","react-router","history","functional"],"author":{"name":"Andrew Clark","email":"acdlite@me.com"},"license":"MIT","devDependencies":{"babel-cli":"^6.2.0","babel-core":"^6.1.21","babel-eslint":"^4.1.5","babel-loader":"^6.2.0","babel-polyfill":"^6.3.14","babel-preset-es2015":"^6.1.18","babel-preset-react":"^6.1.18","babel-preset-stage-0":"^6.1.18","chai":"^3.4.1","eslint":"^1.9.0","eslint-config-airbnb":"^1.0.0","eslint-plugin-react":"^3.9.0","history":"^1.14.0","karma":"^0.13.15","karma-chrome-launcher":"^0.2.1","karma-mocha":"^0.2.1","karma-mocha-reporter":"^1.1.1","karma-phantomjs-launcher":"^0.2.1","karma-phantomjs-shim":"^1.1.2","karma-sinon":"^1.0.4","karma-sourcemap-loader":"^0.3.6","karma-webpack":"^1.7.0","mocha":"^2.3.4","phantomjs":"^1.9.18","react":"^0.14.3","react-addons-test-utils":"^0.14.2","react-dom":"^0.14.2","react-router":"^1.0.2","sinon":"^1.17.2","webpack":"^1.12.6"},"dependencies":{"lodash":"^3.10.1","query-string":"^3.0.0","react-router":"^1.0.0","recompose":"^0.11.2"},"gitHead":"904791154608290ac4a67c2f557eb2b1aff5a672","_id":"@acdlite/router@0.1.0","_shasum":"aa6dd8c392ec1570426c09785f870ac57154a444","_from":".","_npmVersion":"3.3.6","_nodeVersion":"5.0.0","_npmUser":{"name":"acdlite","email":"acdlite@me.com"},"dist":{"shasum":"aa6dd8c392ec1570426c09785f870ac57154a444","tarball":"https://registry.npmjs.org/@acdlite/router/-/router-0.1.0.tgz","integrity":"sha512-GPDl9mCHjhOFzrXLSKWHWH4rW+UX+JW7fogdm0PmqXY65exsH20BHQsUrVn8mcw5dstiYypukb2w5KEf16u5lg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH925fpyDTxFbsMa2dQ6XXUjYgRkbOF+EP0cvVL+wc2BAiEAqVv73FdS6fALOWPLlSX2MU8zAirvRz3hHeC0Ib1qc4I="}]},"maintainers":[{"name":"acdlite","email":"acdlite@me.com"}]},"0.1.1":{"name":"@acdlite/router","version":"0.1.1","description":"An experiment in functional routing.","main":"lib/index.js","scripts":{"prepublish":"npm run test && npm run build","build":"babel src --out-dir lib","test":"karma start --single-run","test:watch":"karma start"},"keywords":["routing","router","middleware","react-router","history","functional"],"author":{"name":"Andrew Clark","email":"acdlite@me.com"},"license":"MIT","devDependencies":{"babel-cli":"^6.2.0","babel-core":"^6.1.21","babel-eslint":"^4.1.5","babel-loader":"^6.2.0","babel-polyfill":"^6.3.14","babel-preset-es2015":"^6.1.18","babel-preset-react":"^6.1.18","babel-preset-stage-0":"^6.1.18","chai":"^3.4.1","eslint":"^1.9.0","eslint-config-airbnb":"^1.0.0","eslint-plugin-react":"^3.9.0","history":"^1.14.0","karma":"^0.13.15","karma-chrome-launcher":"^0.2.1","karma-mocha":"^0.2.1","karma-mocha-reporter":"^1.1.1","karma-phantomjs-launcher":"^0.2.1","karma-phantomjs-shim":"^1.1.2","karma-sinon":"^1.0.4","karma-sourcemap-loader":"^0.3.6","karma-webpack":"^1.7.0","mocha":"^2.3.4","phantomjs":"^1.9.18","react":"^0.14.3","react-addons-test-utils":"^0.14.2","react-dom":"^0.14.2","react-router":"^1.0.2","sinon":"^1.17.2","webpack":"^1.12.6"},"dependencies":{"lodash":"^3.10.1","query-string":"^3.0.0","react-router":"^1.0.0","recompose":"^0.11.2"},"gitHead":"8dd409e7cd14373f6d0b7a1b29ecbc60ff550e03","_id":"@acdlite/router@0.1.1","_shasum":"b4fdbe840600ee2d998983c24110a2127d9d08bf","_from":".","_npmVersion":"3.3.6","_nodeVersion":"5.0.0","_npmUser":{"name":"acdlite","email":"acdlite@me.com"},"dist":{"shasum":"b4fdbe840600ee2d998983c24110a2127d9d08bf","tarball":"https://registry.npmjs.org/@acdlite/router/-/router-0.1.1.tgz","integrity":"sha512-FAkv5m6DmKy6r/PATrvo3o+oRX3t8e1hrSoiMyy7nCoPPrWFhsQJse8rSQiMzGTPY78cKiNbetP6RkGhHfzujA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF8Kxo/rOChX4nFSCZVhuJBR4BkDwqPt7DQoHKGc5eMqAiBKK3WOEChzhojHMWnv9yL/7wRIv//d1Bi8/JcZmHxSiw=="}]},"maintainers":[{"name":"acdlite","email":"acdlite@me.com"}]},"0.1.2":{"name":"@acdlite/router","version":"0.1.2","description":"An experiment in functional routing.","main":"lib/index.js","scripts":{"prepublish":"npm run test && npm run build","build":"babel src --out-dir lib","test":"karma start --single-run","lint":"eslint src","test:watch":"karma start"},"keywords":["routing","router","middleware","react-router","history","functional"],"author":{"name":"Andrew Clark","email":"acdlite@me.com"},"license":"MIT","devDependencies":{"babel-cli":"^6.2.0","babel-core":"^6.1.21","babel-eslint":"^4.1.5","babel-loader":"^6.2.0","babel-polyfill":"^6.3.14","babel-preset-es2015":"^6.1.18","babel-preset-react":"^6.1.18","babel-preset-stage-0":"^6.1.18","chai":"^3.4.1","eslint":"^1.9.0","eslint-config-airbnb":"^1.0.0","eslint-plugin-react":"^3.9.0","history":"^1.14.0","karma":"^0.13.15","karma-chrome-launcher":"^0.2.1","karma-mocha":"^0.2.1","karma-mocha-reporter":"^1.1.1","karma-phantomjs-launcher":"^0.2.1","karma-phantomjs-shim":"^1.1.2","karma-sinon":"^1.0.4","karma-sourcemap-loader":"^0.3.6","karma-webpack":"^1.7.0","mocha":"^2.3.4","phantomjs":"^1.9.18","react":"^0.14.3","react-addons-test-utils":"^0.14.2","react-dom":"^0.14.2","react-router":"^1.0.2","sinon":"^1.17.2","webpack":"^1.12.6"},"dependencies":{"lodash":"^3.10.1","query-string":"^3.0.0","react-router":"^1.0.0","recompose":"^0.11.2"},"gitHead":"9fc403fc0a53fb550dfef0cc7cc9ccc6bc4f9577","_id":"@acdlite/router@0.1.2","_shasum":"7dec29c265f7ed9b482bdbf1d51a862afa5267c6","_from":".","_npmVersion":"3.3.6","_nodeVersion":"5.0.0","_npmUser":{"name":"acdlite","email":"acdlite@me.com"},"dist":{"shasum":"7dec29c265f7ed9b482bdbf1d51a862afa5267c6","tarball":"https://registry.npmjs.org/@acdlite/router/-/router-0.1.2.tgz","integrity":"sha512-AhWSVd8rLtyT4NQNGCWFO+sUB759hlbHVGNfdoBQV2LM/k07ZqRNrg7VotoGriSW9ZpYZ4X8uuBq9QC+8laDuA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH0sXzkLxgswvkbsoO/fS4Khdws1C2h9eSbuumbhKUsIAiEApMEwIg1NJ3oQBx8KbF4DSg+vxlE2BD4Lbyxko1ZlqnM="}]},"maintainers":[{"name":"acdlite","email":"acdlite@me.com"}]},"0.1.3":{"name":"@acdlite/router","version":"0.1.3","description":"An experiment in functional routing.","main":"lib/index.js","scripts":{"prepublish":"npm run test && npm run build","build":"babel src --out-dir lib","test":"karma start --single-run","lint":"eslint src","test:watch":"karma start"},"keywords":["routing","router","middleware","react-router","history","functional"],"author":{"name":"Andrew Clark","email":"acdlite@me.com"},"license":"MIT","devDependencies":{"babel-cli":"^6.2.0","babel-core":"^6.1.21","babel-eslint":"^4.1.5","babel-loader":"^6.2.0","babel-polyfill":"^6.3.14","babel-preset-es2015":"^6.1.18","babel-preset-react":"^6.1.18","babel-preset-stage-0":"^6.1.18","chai":"^3.4.1","eslint":"^1.9.0","eslint-config-airbnb":"^1.0.0","eslint-plugin-react":"^3.9.0","history":"^1.14.0","karma":"^0.13.15","karma-chrome-launcher":"^0.2.1","karma-mocha":"^0.2.1","karma-mocha-reporter":"^1.1.1","karma-phantomjs-launcher":"^0.2.1","karma-phantomjs-shim":"^1.1.2","karma-sinon":"^1.0.4","karma-sourcemap-loader":"^0.3.6","karma-webpack":"^1.7.0","mocha":"^2.3.4","phantomjs":"^1.9.18","react":"^0.14.3","react-addons-test-utils":"^0.14.2","react-dom":"^0.14.2","react-router":"^1.0.2","sinon":"^1.17.2","webpack":"^1.12.6"},"dependencies":{"lodash":"^3.10.1","query-string":"^3.0.0","react-router":"^1.0.0","recompose":"^0.11.2"},"gitHead":"5352d623b43f2e89184728ffccb5a092e1dab770","_id":"@acdlite/router@0.1.3","_shasum":"cd4e199f70d9d5d96e915383904b3d0ebe585bfc","_from":".","_npmVersion":"3.3.6","_nodeVersion":"5.0.0","_npmUser":{"name":"acdlite","email":"acdlite@me.com"},"dist":{"shasum":"cd4e199f70d9d5d96e915383904b3d0ebe585bfc","tarball":"https://registry.npmjs.org/@acdlite/router/-/router-0.1.3.tgz","integrity":"sha512-mXFsm2qvnjyhHnBdCGL6FlX3C2rss3DOm5QO7zQBxvyryYLZLTbk3P73IQs9+bi78+FrJeC5ROvV6FQkEHiKiQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCxqnKKiaNVSfrFTh2mlVV8aLysqUb6DylOPbfAyeXMIQIgd75PLNNy+1rHHIDW0x17GJ6kgQLLr05E4yjTrFjnDqA="}]},"maintainers":[{"name":"acdlite","email":"acdlite@me.com"}]},"0.1.4":{"name":"@acdlite/router","version":"0.1.4","description":"An experiment in functional routing.","main":"lib/index.js","scripts":{"prepublish":"npm run test && npm run build","build":"babel src --out-dir lib","test":"karma start --single-run","lint":"eslint src","test:watch":"karma start"},"keywords":["routing","router","middleware","react-router","history","functional"],"author":{"name":"Andrew Clark","email":"acdlite@me.com"},"license":"MIT","devDependencies":{"babel-cli":"^6.2.0","babel-core":"^6.1.21","babel-eslint":"^4.1.5","babel-loader":"^6.2.0","babel-polyfill":"^6.3.14","babel-preset-es2015":"^6.1.18","babel-preset-react":"^6.1.18","babel-preset-stage-0":"^6.1.18","chai":"^3.4.1","eslint":"^1.9.0","eslint-config-airbnb":"^1.0.0","eslint-plugin-react":"^3.9.0","history":"^1.14.0","karma":"^0.13.15","karma-chrome-launcher":"^0.2.1","karma-mocha":"^0.2.1","karma-mocha-reporter":"^1.1.1","karma-phantomjs-launcher":"^0.2.1","karma-phantomjs-shim":"^1.1.2","karma-sinon":"^1.0.4","karma-sourcemap-loader":"^0.3.6","karma-webpack":"^1.7.0","mocha":"^2.3.4","phantomjs":"^1.9.18","react":"^0.14.3","react-addons-test-utils":"^0.14.2","react-dom":"^0.14.2","react-router":"^1.0.2","sinon":"^1.17.2","webpack":"^1.12.6"},"dependencies":{"lodash":"^3.10.1","query-string":"^3.0.0","react-router":"^1.0.0","recompose":"^0.11.2"},"gitHead":"f082f92070a095e3e6c0acc18f40d775401895e8","_id":"@acdlite/router@0.1.4","_shasum":"d98cd41b23ee9f46542076ccfa4651a38f776948","_from":".","_npmVersion":"3.3.6","_nodeVersion":"5.0.0","_npmUser":{"name":"acdlite","email":"acdlite@me.com"},"dist":{"shasum":"d98cd41b23ee9f46542076ccfa4651a38f776948","tarball":"https://registry.npmjs.org/@acdlite/router/-/router-0.1.4.tgz","integrity":"sha512-2IIvdPpUekFQX576+ytpQL1hvGPPa117iVRywcN+q70aEgL7z9z94qcBCN70kbF3ZUq8OIdN0kgHO6FLHh6IBg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCC/w77ra9s38yt+ZESAiujJm4pVgLZQ7DirUv8dUskoAIgNEqjR9329KyTFUf3UOsZGz659uXBMzqQegN8lVfP8tk="}]},"maintainers":[{"name":"acdlite","email":"acdlite@me.com"}]}},"readme":"# Router\n\n[![build status](https://img.shields.io/travis/acdlite/router/master.svg?style=flat-square)](https://travis-ci.org/acdlite/router)\n[![npm version](https://img.shields.io/npm/v/@acdlite/router.svg?style=flat-square)](https://www.npmjs.com/package/@acdlite/router)\n\nAn experiment in functional routing for JavaScript applications.\n\n```\nnpm install --save @acdlite/router\n```\n\nThe name is intentionally generic because it's still in the experimental phase (perhaps forever).\n\nKey features:\n\n- A router is defined using composable middleware functions.\n- A router is a function that turns a path into a state object. That's it. This allows for total separation of history management from route matching.\n- Because history management is separate, the server-side API is identical to the client-side API.\n\n**[See below for a proof-of-concept that mimics the React Router API.](https://github.com/acdlite/router#proof-of-concept-react-router-like-api)**\n\n## Should I use this?\n\nNo.\n\nWell, maybe. I'm currently using this in a side project, but I wouldn't recommend it for any production apps.\n\n## How it works\n\nA \"router\" in the context of this project is a function that accepts a path and a callback. The router turns the path into a state object by passing it through a series of middleware. Once the middleware completes, the callback is called (either synchronously or asynchronously) with the final state object, which can be used to render an app.\n\nHistory management is considered a separate concern — just pass the router a string. On the client, use a project like [history](https://github.com/rackt/history). On the server, use your favorite web framework like [Express](http://expressjs.com/en/index.html) or [Koa](https://github.com/koajs/koa).\n\n```js\nconst router = createRouter(...middlewares)\n\nrouter('/some/path', (error, state) => {\n  // Render app using state\n})\n```\n\n### Middleware\n\nA middleware is a function that accepts Node-style callback (we'll call it a listener) and returns a new Node-style callback with augmented behavior.\n\n```js\ntype Listener = (error: Error, state: Object) => void\ntype Middleware = (next: Listener) => Listener\n```\n\nAn important feature of middleware is that they are composable:\n\n```js\n// Middlewares 1, 2, and 3 will run in sequence from left to right\nconst combinedMiddleware = compose(middleware1, middleware2, middlware3)\n```\n\nRouter middleware is much like middleware in Redux. It is used to augment a state object as it passes through a router. Here's an example of a middleware that adds a `query` field:\n\n```js\nimport queryString from 'query-string'\n\nconst parseQuery = next => (error, state) => {\n  if (error) return next(error)\n\n  next(null, {\n    ...state,\n    query: queryString.parse(state.search)\n  })\n}\n```\n\nAs with React props and Redux state, we treat router state as immutable.\n\n### State object conventions\n\nAll state objects should have the fields `path`, `pathname`, `search`, and `hash`. When you pass a path string to a router function, the remaining fields are extracted from the path. The reverse also works: if instead of a path string you pass an initial state object to a router function with `pathname`, `search`, and `hash`, a `path` field is added. This allows middleware to depend on those fields without having to do their own parsing.\n\nThere are two additional fields which have special meanings: `redirect` and `done`. `redirect` is self-explanatory: a middleware should skip any state object with a `redirect` field by passing it to the next middleware. Similarly, a state object with `done: true` indicates that a previous middleware has already handled it, and it needs no further processing by remaining middleware. (There are some circumstances where it may be appropriate for a middleware to process a `done` state object.)\n\nHandling all these special cases can get tedious. The `handle()` allows you to create a middleware that handles specific cases. It's a bit like a switch statement, or pattern matching. Example\n\n```js\nimport { handle } from '@acdlite/router'\n\nconst middleware = handle({\n  // Handle error\n  error: next => (error, state) => {...}\n\n  // Handle redirect\n  redirect: next => (error, state) => {...}\n\n  // Handle done\n  done: next => (error, state) => {...}\n\n  // Handle all other cases\n  next: next => (error, state) => {...}\n})\n```\n\n`next()` is the most common handler.\n\nIf a handler is omitted, the default behavior is to pass the state object through to the next middleware, unchanged.\n\n## Proof-of-concept: React Router-like API\n\nAs a proof-of-concept, the `react-router/` directory includes utilities for implementing a React Router-like API using middleware. It supports:\n\n- Nested route matching, with params\n- Plain object routes or JSX routes\n- Asynchronous route fetching, using `config.getChildRoutes()`\n- Asynchronous component fetching, using `config.getComponent()`\n- Index routes\n\nNot yet completed:\n\n- `<Redirect>` routes\n\nInternally, it uses several of React Router's methods, so the route matching behavior should be identical.\n\nExample:\n\n```js\nimport { createRouter } from '@acdlite/router'\nimport { nestedRoute, getComponents, Route, IndexRoute } from '@acdlite/router/react-router'\nimport createHistory from 'history/lib/createBrowserHistory'\n\nconst reactRouter = createRouter(\n  nestedRoute(\n    <Route path=\"/\" component={App}>\n      <Route path=\"post\">\n        <IndexRoute component={PostIndex} />\n        <Route path=\":id\" component={Post} />\n      </Route>\n    </Route>\n  ),\n  getComponents,\n  // ... add additional middleware, if desired\n)\n\nconst history = createHistory()\n\n// Listen for location updates\nhistory.listen(location => {\n  // E.g. after navigating to '/post/123'\n  // Routers can accept either a path string or an object with `pathname`,\n  // `query`, and `search`, so we can pass the location object directly.\n  reactRouter(location, {\n    // Route was successful\n    done: (error, state) => {\n      // Returns a state object with info about the matched routes\n      expect(state).to.eql({\n        params: { id: '123' },\n        routes: [...] // Array of matching route config objects\n        components: [App, Post], // Array of matching components\n        // ...plus other fields from the location object\n      })\n\n      // Render your app using state...\n    },\n\n    // Handle redirects\n    redirect: (error, state) => {\n      history.replace(state.redirect)\n    },\n\n    // Handle errors\n    error: error => {\n      throw error\n    }\n  }\n})\n```\n\nA key thing to note is that the server-side API is exactly the same: instead of using history, just pass a path string directly to the router, and implement `done()`, `redirect()` and `error()` as appropriate.\n\nAlso note that there's no interdependency between history and your routing logic.\n\nThe router returns the matched components, but it's up to you to render them how you like. An easy way to start is using Recompose's [`nest()`](https://github.com/acdlite/recompose/blob/master/docs/API.md#nest) function:\n\n```js\nconst Component = nest(...state.components)\nReactDOM.render(<Component {...state.params} {...state.query} />)\n```\n\nThat gets you 90% of the way to parity with React Router. Conveniences like the `<Link>` component and transition hooks would need to be re-implemented, but are fairly straightforward.\n","maintainers":[{"name":"acdlite","email":"acdlite@me.com"}],"time":{"modified":"2022-06-12T14:15:58.511Z","created":"2015-12-12T21:41:41.239Z","0.1.0":"2015-12-12T21:41:41.239Z","0.1.1":"2015-12-12T21:42:54.212Z","0.1.2":"2015-12-12T23:47:54.929Z","0.1.3":"2015-12-12T23:53:05.412Z","0.1.4":"2015-12-13T05:59:24.389Z"},"keywords":["routing","router","middleware","react-router","history","functional"],"author":{"name":"Andrew Clark","email":"acdlite@me.com"},"license":"MIT","readmeFilename":"README.md"}