{"_id":"@alexmsmithca/fusion-core","_rev":"1-e729fa0d0aaa5166e04916fed0e94f7b","name":"@alexmsmithca/fusion-core","dist-tags":{"latest":"2.2.1-withExtPattern.0"},"versions":{"2.2.1-withExtPattern.0":{"name":"@alexmsmithca/fusion-core","description":"A generic entry point class for FusionJS applications that is used by the FusionJS runtime.","version":"2.2.1-withExtPattern.0","license":"MIT","repository":{"directory":"fusion-core","type":"git","url":"git+https://github.com/fusionjs/fusionjs.git"},"main":"./dist-node-cjs/index.js","module":"./dist-node-esm/index.js","browser":{"./dist-node-cjs/index.js":"./dist-browser-cjs/index.js","./dist-node-esm/index.js":"./dist-browser-esm/index.js"},"scripts":{"clean":"cup-clean","lint":"eslint . --ignore-path .gitignore","test":"jest","prepublish":"npm run build","build":"npm run clean && cup-build","flow":"flow"},"dependencies":{"koa":"^2.7.0","koa-compose":"^4.1.0","node-mocks-http":"^1.7.5","toposort":"^2.0.2","ua-parser-js":"^0.7.21","uuid":"^3.3.2"},"devDependencies":{"@babel/preset-env":"^7.8.4","@babel/core":"^7.8.4","@babel/plugin-proposal-class-properties":"^7.8.3","@babel/plugin-transform-flow-strip-types":"^7.8.3","babel-eslint":"^10.0.3","create-universal-package":"^4.1.0","eslint":"^6.8.0","eslint-config-fusion":"0.0.0-monorepo","eslint-plugin-cup":"^2.0.2","eslint-plugin-flowtype":"^4.6.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jest":"^23.6.0","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.18.3","eslint-plugin-react-hooks":"^2.3.0","flow-bin":"^0.109.0","node-fetch":"^2.6.0","prettier":"^1.19.1","jest":"^25.1.0"},"engines":{"node":">=8.9.4","npm":">=5.0.0","yarn":">=1.0.0"},"homepage":"https://fusionjs.com/api/fusion-core","sideEffects":false,"bugs":{"url":"https://github.com/fusionjs/fusionjs/issues"},"_id":"@alexmsmithca/fusion-core@2.2.1-withExtPattern.0","_nodeVersion":"12.16.3","_npmVersion":"6.14.4","dist":{"integrity":"sha512-lzJX+AVXmC7qSEesw1FX9yKRMOSYICLBKsehwg2XeFuJbkWcBPRZ/BvEx6YXp27RANraEiXQUIZj4tXmX9kTvA==","shasum":"50653d7e98e6dddeb1576f3599e67ea6985c0e50","tarball":"https://registry.npmjs.org/@alexmsmithca/fusion-core/-/fusion-core-2.2.1-withExtPattern.0.tgz","fileCount":107,"unpackedSize":632725,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe3F3YCRA9TVsSAnZWagAA83EP/R4WUxegC0kQPj9eYsVG\ndDfJNIf2A0VfAk5aMfm/U/iD85WsVK7ziEdBMCEtyHGe521d/QGPCY00VTTt\nvS2MJk8uLUFIgPsJHOxsMC3SI7s9cCa5jGVcQjVr+7F7+iuDtnVKlaBzz++/\nq4aaVqPnInQqMWZwi2NgNgz+NmbOiGWyu/KkdxL94vKihwauixuvpRrmvEgJ\nOe0Zvh1p6U2RzUDvscLkhF8W0e9dNoMp8GRyJpv10NdeT/cQrlBdmPD2Vlkb\nb38ui0u67uzFdQg2XHujQIzafZ/XgWANL5uvNXYh03kzcN9ZfL77zg5jcDG/\npHZxdVMPK5j3riqj3kaix78NHA3jLmv27613z1zhwrJxE+VHizWdGhWtrqak\nDrJC6j2Y/mouAdophn59Zk+GJMplKd9mFcC2dt2Tn3r0Qkd9o2uLMYUfsOHh\nEXY/fEBn7pmcDKEWDu+/kL4M4/J1JT6Ue6xH17x9oFvQp+8o3wtLs23Geqco\nRNvjcjsY/zVSyTknVI3Wnn5pGd8CizfvFLnSCiDWCop5ApnerrsH9iEU0H3v\nude/s8mlywQDeT0Kfd63Wopd337L7mttAUv6u+RfCh71i3QTrDfzyakragM0\nT9aGQj+e434zOGasc7SCBj6UCni4nTFavQ5x2fgeWv91ERzH5fztwbIQaTew\naPcx\r\n=8qMX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC07mPwWAFrBC1+Tf9e0B8pfOKp9PcDGb5RYWkXkb4+PAIgKEXcBlsOLSRfF+xGNpnFZTQVoyb8K6zvKJpoY/1zArs="}]},"maintainers":[{"name":"alexmsmithca","email":"AlexMSmithCA@gmail.com"}],"_npmUser":{"name":"alexmsmithca","email":"AlexMSmithCA@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fusion-core_2.2.1-withExtPattern.0_1591500247994_0.01854084877514084"},"_hasShrinkwrap":false}},"time":{"created":"2020-06-07T03:24:07.960Z","2.2.1-withExtPattern.0":"2020-06-07T03:24:08.307Z","modified":"2022-04-04T12:41:20.655Z"},"maintainers":[{"name":"alexmsmithca","email":"AlexMSmithCA@gmail.com"}],"description":"A generic entry point class for FusionJS applications that is used by the FusionJS runtime.","homepage":"https://fusionjs.com/api/fusion-core","repository":{"directory":"fusion-core","type":"git","url":"git+https://github.com/fusionjs/fusionjs.git"},"bugs":{"url":"https://github.com/fusionjs/fusionjs/issues"},"license":"MIT","readme":"# fusion-core\n\n[![Build status](https://badge.buildkite.com/7a82192275779f6a8ba81f7d4a1b0d294256838faa1dfdf080.svg?branch=master)](https://buildkite.com/uberopensource/fusionjs)\n\nThe `fusion-core` package provides a generic entry point class for Fusion.js applications that is used by the Fusion.js runtime. It also provides primitives for implementing server-side code, and utilities for registering plugins into an application to augment its functionality.\n\nIf you're using React, you should use the [`fusion-react`](https://github.com/fusionjs/fusionjs/tree/master/fusion-react) package instead of `fusion-core`.\n\n---\n\n### Table of contents\n\n* [Usage](#usage)\n* [API](#api)\n  * [App](#app)\n  * [Dependency registration](#dependency-registration)\n  * [Plugin](#plugin)\n  * [Token](#token)\n  * [Memoization](#memoization)\n  * [Middleware](#middleware)\n  * [Sanitization](#sanitization)\n  * [Virtual modules](#virtual-modules)\n* [Examples](#examples)\n\n---\n\n### Usage\n\n```js\n// main.js\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport {renderToString} from 'react-dom/server';\nimport App from 'fusion-core';\n\nconst el = <div>Hello</div>;\n\nconst render = el =>\n  __NODE__\n    ? `<div id=\"root\">${renderToString(el)}</div>`\n    : ReactDOM.render(el, document.getElementById('root'));\n\nexport default function() {\n  return new App(el, render);\n}\n```\n\n---\n\n### API\n\n#### App\n\n```js\nimport App from 'fusion-core';\n```\n\nA class that represents an application. An application is responsible for rendering (both virtual DOM and server-side rendering). The functionality of an application is extended via [plugins](#plugin).\n\n**Constructor**\n\n```flow\nconst app: App = new App(el: any, render: Plugin<Render>|Render);\n```\n\n* `el: any` - a template root. In a React application, this would be a React element created via `React.createElement` or a JSX expression.\n* `render: Plugin<Render>|Render` - defines how rendering should occur. A Plugin should provide a value of type `Render`\n  * `type Render = (el:any) => any`\n\n**app.register**\n\n```flow\napp.register(plugin: Plugin);\napp.register(token: Token, plugin: Plugin);\napp.register(token: Token, value: any);\n```\n\nCall this method to register a plugin or configuration value into a Fusion.js application.\n\nYou can optionally pass a token as the first argument to associate the plugin/value to the token, so that they can be referenced by other plugins within Fusion.js' dependency injection system.\n\n* `plugin: Plugin` - a [Plugin](#plugin) created via [`createPlugin`](#createplugin)\n* `token: Token` - a [Token](#token) created via [`createToken`](#createtoken)\n* `value: any` - a configuration value\n* returns `undefined`\n\n**app.middleware**\n\n```js\napp.middleware((deps: Object<string, Token>), (deps: Object) => Middleware);\napp.middleware((middleware: Middleware));\n```\n\n* `deps: Object<string,Token>` - A map of local dependency names to [DI tokens](#token)\n* `middleware: Middleware` - a [middleware](#middleware)\n* returns `undefined`\n\nThis method is a shortcut for registering middleware plugins. Typically, you should write middlewares as plugins so you can organize different middlewares into different files.\n\n**app.enhance**\n\n```flow\napp.enhance(token: Token, value: any => Plugin | Value);\n```\n\nThis method is useful for composing / enhancing functionality of existing tokens in the DI system.\n\n**app.cleanup**\n\n```js\nawait app.cleanup();\n```\n\nCalls all plugin cleanup methods. Useful for testing.\n\n* returns `Promise`\n\n---\n\n#### Dependency registration\n\n##### ElementToken\n\n```js\nimport App, {ElementToken} from 'fusion-core';\napp.register(ElementToken, element);\n```\n\nThe element token is used to register the root element with the Fusion.js app. This is typically a React element.\n\n##### RenderToken\n\n```js\nimport ReactDOM from 'react-dom';\nimport {renderToString} from 'react-dom/server';\nconst render = el =>\n  __NODE__\n    ? renderToString(el)\n    : ReactDOM.render(el, document.getElementById('root'));\nimport App, {RenderToken} from 'fusion-core';\nconst app = new App();\napp.register(RenderToken, render);\n```\n\nThe render token is used to register the render function with the Fusion.js app. This is a function that knows how to\nrender your application on the server/browser, and allows `fusion-core` to remain agnostic of the virtual DOM library.\n\n##### SSRDeciderToken\n\n```js\nimport App, {SSRDeciderToken} from 'fusion-core';\napp.enhance(SSRDeciderToken, SSRDeciderEnhancer);\n```\n\nThs `SSRDeciderToken` can be enhanced to control server rendering logic.\n\n##### HttpServerToken\n\n```js\nimport App, {HttpServerToken} from 'fusion-core';\napp.register(HttpServerToken, server);\n```\n\nThe `HttpServerToken` is used to register the current server as a dependency that can be utilized from plugins that require\naccess to it. This is normally not required but is available for specific usage cases.\n\n##### RouteTagsToken\n\n```js\nimport {RouteTagsToken, createPlugin} from 'fusion-core';\n\ncreatePlugin({\n  deps: {\n    RouteTags: RouteTagsToken,\n  },\n  middleware({RouteTags}) {\n    return (ctx, next) => {\n      const routeTags = RouteTags.from(ctx);\n      if (ctx.path === '/graphql') {\n        routeTags.name = 'graphql';\n        routeTags.customTag = 'custom-value';\n      }\n    }\n  }\n});\n```\n\nThe RouteTagsToken exposes an Object for holding tags related to a given request. There is a default tag called 'name' which refers\nto a stable name for a given route. This is useful for situations where you need low cardinality values for metrics and tracing. By\ndefault, the route name is set to 'unknown_route' by fusion-core. If you are using `fusion-plugin-react-router` it will automatically\nset the route name to the matched react route.\n\n---\n\n#### Plugin\n\nA plugin encapsulates some functionality into a single coherent package that exposes a programmatic API and/or installs middlewares into an application.\n\nPlugins can be created via `createPlugin`\n\n```flow\ntype Plugin {\n  deps: Object<string, Token>,\n  provides: (deps: Object) => any,\n  middleware: (deps: Object, service: any) => Middleware,\n  cleanup: ?(service: any) => void\n}\n```\n\n##### createPlugin\n\n```js\nimport {createPlugin} from 'fusion-core';\n```\n\nCreates a plugin that can be registered via `app.register()`\n\n```flow\nconst plugin: Plugin = createPlugin({\n  deps: Object,\n  provides: (deps: Object) => any,\n  middleware: (deps: Object, service: any) => Middleware,\n  cleanup: ?(service: any) => void\n});\n```\n\n* `deps: Object<string, Token>` - A map of local dependency names to [DI tokens](#token)\n* `provides: (deps: Object) => any` - A function that provides a service\n* `middleware: (deps: Object, service: any) => Middleware` - A function that provides a middleware\n* `cleanup: ?(service: any)` => Runs when `app.cleanup` is called. Useful for tests\n* returns `plugin: Plugin` - A Fusion.js plugin\n\n---\n\n#### Token\n\nA token is a label that can be associated to a plugin or configuration when they are registered to an application. Other plugins can then import them via dependency injection, by mapping a object key in `deps` to a token\n\n```flow\ntype Token {\n  name: string,\n  ref: mixed,\n  type: number,\n  optional: ?Token,\n}\n```\n\n##### createToken\n\n```flow\nconst token:Token = createToken(name: string);\n```\n\n* `name: string` - a human-readable name for the token. Used for generating useful error messages.\n* returns `token: Token`\n\n---\n\n#### Memoization\n\n```flow\nimport {memoize} from 'fusion-core';\n```\n\nIt may be desirable to share the same instance of a particular request-scoped value across different plugins. For example, session state, which is associated with specific requests but might be used in several plugins.\n\nFusion.js provides a `memoize` utility function for this purpose:\n\n* `fn: (ctx: Context) => any` - A function to be memoized\n* returns `memoized: (ctx: Context) => any`\n\nFor example, using session state as an example:\n\n```\nconst getSession = memoize(ctx => createSession(ctx));\n```\n\nThe first time `getSession` is invoked with a given `ctx` object, `createSession(ctx)` will be invoked and a session state instance will be created. Then, any subsequent calls of `getSession` with the exact same `ctx` will yield the existing session state instance for that request.\n\nUnder the hood, these lookups work similar to a `WeakMap` so these memoized values are garbage collected along with each `ctx` object.\n\nNote that by convention, Fusion.js plugins provide these memoized getters via a `from` method.\n\n```js\nconst memoized = {from: memoize((fn: (ctx: Context) => any))};\n```\n\nThis method is meant to be called from a [middleware](#middleware), for example:\n\n```js\ncreatePlugin({\n  deps: {Session: SessionToken},\n  middleware({Session}) {\n    return (ctx, next) => {\n      const state = Session.from(ctx);\n    }\n  }\n}\n```\n\n---\n\n#### Middleware\n\n```flow\ntype Middleware = (ctx: Context, next: () => Promise) => Promise\n```\n\n* `ctx: Context` - a [Context](#context)\n* `next: () => Promise` - An asynchronous function call that represents rendering\n\nA middleware function is essentially a [Koa](http://koajs.com/) middleware, a function that takes two argument: a `ctx` object that has some Fusion.js-specific properties, and a `next` callback function.\nHowever, it has some additional properties on `ctx` and can run both on the `server` and the `browser`.\n\nIn Fusion.js, the `next()` call represents the time when virtual DOM rendering happens. Typically, you'll want to run all your logic before that, and simply have a `return next()` statement at the end of the function. Even in cases where virtual DOM rendering is not applicable, this pattern is still the simplest way to write a middleware.\n\nIn a few more advanced cases, however, you might want to do things _after_ virtual dom rendering. In that case, you can call `await next()` instead:\n\n```js\nconst middleware = () => async (ctx, next) => {\n  // this happens before virtual dom rendering\n  const start = new Date();\n\n  await next();\n\n  // this happens after virtual rendeing, but before the response is sent to the browser\n  console.log('timing: ', new Date() - start);\n};\n```\n\nPlugins can add dependency injected middlewares.\n\n```js\n// fusion-plugin-some-api\nconst APIPlugin = createPlugin({\n  deps: {\n    logger: LoggerToken,\n  },\n  provides: ({logger}) => {\n    return new APIClient(logger);\n  },\n  middleware: ({logger}, apiClient) => {\n    return async (ctx, next) => {\n      // do middleware things...\n      await next();\n      // do middleware things...\n    };\n  },\n});\n```\n\n##### Context\n\nMiddlewares receive a `ctx` object as their first argument. This object has a property called `element` in both server and client.\n\n* `ctx: Object`\n  * `element: Object`\n\nAdditionally, when server-side rendering a page, Fusion.js sets `ctx.template` to an object with the following properties:\n\n* `ctx: Object`\n  * `template: Object`\n    * `htmlAttrs: Object` - attributes for the `<html>` tag. For example `{lang: 'en-US'}` turns into `<html lang=\"en-US\">`. Default: empty object\n    * `bodyAttrs: Object` - attributes for the `<body>` tag. For example `{test: 'test'}` turns into `<body test=\"test\">`. Default: empty object\n    * `title: string` - The content for the `<title>` tag. Default: empty string\n    * `head: Array<SanitizedHTML>` - A list of [sanitized HTML strings](#html-sanitization). Default: empty array\n    * `body: Array<SanitizedHTML>` - A list of [sanitized HTML strings](#html-sanitization). Default: empty array\n\nWhen a request does not require a server-side render, `ctx.body` follows regular Koa semantics.\n\nIn the server, `ctx` also exposes the same properties as a [Koa context](http://koajs.com/#context)\n\n* `ctx: Object`\n  * `req: http.IncomingMessage` - [Node's `request` object](https://nodejs.org/api/http.html#http_class_http_incomingmessage)\n  * `res: Response` - [Node's `response` object](https://nodejs.org/api/http.html#http_class_http_serverresponse)\n  * `request: Request` - [Koa's `request` object](https://koajs.com/#request): View Koa request details\n    * `header: Object` - alias of `request.headers`\n    * `headers: Object` - map of parsed HTTP headers\n    * `method: string` - HTTP method\n    * `url: string` - request URL\n    * `originalUrl: string` - same as `url`, except that `url` may be modified (e.g. for URL rewriting)\n    * `path: string` - request pathname\n    * `query: Object` - parsed querystring as an object\n    * `querystring: string` - querystring without `?`\n    * `host: string` - host and port\n    * `hostname: string` - get hostname when present. Supports X-Forwarded-Host when app.proxy is true, otherwise Host is used\n    * `length:number` - return request Content-Length as a number when present, or undefined.\n    * `origin: string` - request origin, including protocol and host\n    * `href: string` - full URL including protocol, host, and URL\n    * `fresh: boolean` - check for cache negotiation\n    * `stale: boolean` - inverse of `fresh`\n    * `socket: Socket` - request socket\n    * `protocol: string` - return request protocol, \"https\" or \"http\". Supports X-Forwarded-Proto when app.proxy is true\n    * `secure: boolean` - shorthand for ctx.protocol == \"https\" to check if a request was issued via TLS.\n    * `ip: string` - remote IP address\n    * `ips: Array<string>` - proxy IPs\n    * `subdomains: Array<string>` - return subdomains as an array.For example, if the domain is \"tobi.ferrets.example.com\": If app.subdomainOffset is not set, ctx.subdomains is \\[\"ferrets\", \"tobi\"\\]\n    * `is: (...types: ...string) => boolean` - request type check `is('json', 'urlencoded')`\n    * `accepts: (...types: ...string) => boolean` - request MIME type check\n    * `acceptsEncodings: (...encodings: ...string) => boolean` - check if encodings are acceptable\n    * `acceptsCharset: (...charsets: ...string) => boolean` - check if charsets are acceptable\n    * `acceptsLanguages: (...languages: ...string) => boolean` - check if langs are acceptable\n    * `get: (name: String) => string` - returns a header field\n\n  * `response: Response` - [Koa's `response` object](https://koajs.com/#response): View Koa response details\n    * `header: Object` - alias of `request.headers`\n    * `headers: Object` - map of parsed HTTP headers\n    * `socket: Socket` - response socket\n    * `status: String` - response status. By default, `response.status` is set to `404` unlike node's `res.statusCode` which defaults to `200`.\n    * `message: String` - response status message. By default, `response.message` is associated with `response.status`.\n    * `length: Number` - response Content-Length as a number when present, or deduce from `ctx.body` when possible, or `undefined`.\n    * `body: String, Buffer, Stream, Object(JSON), null` - get response body\n    * `get: (name: String) => string` - returns a header field\n    * `set: (field: String, value: String) => undefined` - set response header `field` to `value`\n    * `set: (fields: Object) => undefined` - set response `fields`\n    * `append: (field: String, value: String) => undefined` - append response header `field` with `value`\n    * `remove: (field: String) => undefined` - remove header `field`\n    * `type: String` - response `Content-Type`\n    * `is: (...types: ...string) => boolean` - response type check `is('json', 'urlencoded')`\n    * `redirect: (url: String, alt: ?String) => undefined`- perform a 302 redirect to `url`\n    * `attachment (filename: ?String) => undefined` - set `Content-Disposition` to \"attachment\" to signal the client to prompt for download. Optionally specify the `filename` of the download.\n    * `headerSent: boolean` - check if a response header has already been sent\n    * `lastModified: Date` - `Last-Modified` header as a `Date`\n    * `etag: String` - set the ETag of a response including the wrapped `\"`s.\n    * `vary: (field: String) => String` - vary on `field`\n    * `flushHeaders () => undefined` - flush any set headers, and begin the body\n\n  * `cookies: {get, set}` - cookies based on [Cookie Module](https://github.com/pillarjs/cookies): View Koa cookies details\n    * `get: (name: string, options: ?Object) => string` - get a cookie\n      * `name: string`\n      * `options: {signed: boolean}`\n    * `set: (name: string, value: string, options: ?Object)`\n      * `name: string`\n      * `value: string`\n      * `options: Object` - Optional\n        * `maxAge: number` - a number representing the milliseconds from Date.now() for expiry\n        * `signed: boolean` - sign the cookie value\n        * `expires: Date` - a Date for cookie expiration\n        * `path: string` - cookie path, /' by default\n        * `domain: string` - cookie domain\n        * `secure: boolean` - secure cookie\n        * `httpOnly: boolean` - server-accessible cookie, true by default\n        * `overwrite: boolean` - a boolean indicating whether to overwrite previously set cookies of the same name (false by default). If this is true, all cookies set during the same request with the same name (regardless of path or domain) are filtered out of the Set-Cookie header when setting this cookie.\n\n  * `state: Object` - recommended namespace for passing information through middleware and to your frontend views `ctx.state.user = await User.find(id)`\n  * `throw: (status: ?number, message: ?string, properties: ?Object) => void` - throws an error\n    * `status: number` - HTTP status code\n    * `message: string` - error message\n    * `properties: Object` - is merged to the error object\n  * `assert: (value: any, status: ?number, message: ?string, properties: ?Object)` - throws if `value` is falsy. Uses [Assert](https://github.com/jshttp/http-assert)\n    * `value: any`\n    * `status: number` - HTTP status code\n    * `message: string` - error message\n    * `properties: Object` - is merged to the error object\n  * `respond: boolean` - set to true to bypass Koa's built-in response handling. You should not use this flag.\n  * `app: Object` - a reference to the Koa instance\n\n#### Sanitization\n\n* **html**\n\n  ```js\n  import {html} from 'fusion-core';\n  ```\n\n  A template tag that creates safe HTML objects that are compatible with `ctx.template.head` and `ctx.template.body`. Template string interpolations are escaped. Use this function to prevent XSS attacks.\n\n  ```flow\n  const sanitized: SanitizedHTML = html`<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">`\n  ```\n\n* **escape**\n\n  ```js\n  import {escape} from 'fusion-core';\n  ```\n\n  Escapes HTML\n\n  ```flow\n  const escaped:string = escape(value: string)\n  ```\n\n  * `value: string` - the string to be escaped\n\n* **unescape**\n\n  ```js\n  import {unescape} from 'fusion-core';\n  ```\n\n  Unescapes HTML\n\n  ```flow\n  const unescaped:string = unescape(value: string)\n  ```\n\n  * `value: string` - the string to be unescaped\n\n* **dangerouslySetHTML**\n\n  ```js\n  import {dangerouslySetHTML} from 'fusion-core';\n  ```\n\n  A function that blindly creates a trusted SanitizedHTML object without sanitizing against XSS. Do not use this function unless you have manually sanitized your input and written tests against XSS attacks.\n\n  ```flow\n  const trusted:string = dangerouslySetHTML(value: string)\n  ```\n\n  * `value: string` - the string to be trusted\n\n#### Virtual modules\n\nVirtual modules are the means for userland consumption of Fusion.js-owned static analysis and build artifacts in a way that:\n\n1. Does not expose any underlying implementation details, such as module bundlers or transpilers\n2. Provides a strong API contract with type safety with editor support\n3. Provides a high degree of robustness with build-time errors in cases where static analysis will fail, including user misuse like not providing statically analyzable arguments.\n\nIn practice, a virtual module is implemented via a coupled agglomeration of babel plugin(s), webpack loader(s), and webpack plugin(s).\n\nFusion.js currently provides the following virtual modules:\n\n* **assetUrl**\n\n  Converts asset relative paths (e.g. `./src/asset.js`) to the fully qualified URL (e.g. `/_static/asset.js`).\n\n  ```js\n  import {assetUrl} from 'fusion-core';\n\n  assetUrl('path/to/some/file');\n  // => Path to the asset\n  ```\n\n* **chunkId**\n\n  This is a useful building block for implementing things such as translations and module async/lazy loading.\n\n  ```js\n  import {chunkId} from 'fusion-core';\n\n  chunkId('path/to/some/module');\n  // => Array of client-side chunk ids for the module\n  ```\n\n* **syncChunkIds**\n\n* **syncChunkPaths**\n\n* **workerUrl**\n\n  The `workerUrl` virtual module allows transpiling and loading a web worker. The result of the virtual call should be passed into the `Worker` constructor.\n\n  ```js\n  import {workerUrl} from 'fusion-core';\n\n  // Path to the asset\n  const url = workerUrl('path/to/some/worker.js');\n  const myWorker = new Worker(url);\n  ```\n\n---\n\n### Examples\n\n#### Dependency injection\n\nTo use plugins, you need to register them with your Fusion.js application. You do this by calling\n`app.register` with the plugin and a token for that plugin. The token is a value used to keep track of\nwhat plugins are registered, and to allow plugins to depend on one another.\n\nYou can think of Tokens as names of interfaces. There's a list of common tokens in the `fusion-tokens` package.\n\nHere's how you create a plugin:\n\n```js\nimport {createPlugin} from 'fusion-core';\n// fusion-plugin-console-logger\nconst ConsoleLoggerPlugin = createPlugin({\n  provides: () => {\n    return console;\n  },\n});\n```\n\nAnd here's how you register it:\n\n```js\n// src/main.js\nimport ConsoleLoggerPlugin from 'fusion-plugin-console-logger';\nimport {LoggerToken} from 'fusion-tokens';\nimport App from 'fusion-core';\n\nexport default function main() {\n  const app = new App(...);\n  app.register(LoggerToken, ConsoleLoggerPlugin);\n  return app;\n}\n```\n\nNow let's say we have a plugin that requires a `logger`. We can map `logger` to `LoggerToken` to inject the logger provided by `ConsoleLoggerPlugin` to the `logger` variable.\n\n```js\n// fusion-plugin-some-api\nimport {createPlugin} from 'fusion-core';\nimport {LoggerToken} from 'fusion-tokens';\n\nconst APIPlugin = createPlugin({\n  deps: {\n    logger: LoggerToken,\n  },\n  provides: ({logger}) => {\n    logger.log('Hello world');\n    return new APIClient(logger);\n  },\n});\n```\n\nThe API plugin is declaring that it needs a logger that matches the API documented by the `LoggerToken`. The user then provides an implementation of that logger by registering the `fusion-plugin-console-logger` plugin with the `LoggerToken`.\n\n#### Implementing HTTP endpoints\n\nYou can use a plugin to implement a RESTful HTTP endpoint. To achieve this, run code conditionally based on the URL of the request\n\n```js\napp.middleware(async (ctx, next) => {\n  if (ctx.method === 'GET' && ctx.path === '/api/v1/users') {\n    ctx.body = await getUsers();\n  }\n  return next();\n});\n```\n\n#### Serialization and hydration\n\nA plugin can be atomically responsible for serialization/deserialization of data from the server to the client.\n\nThe example below shows a plugin that grabs the project version from package.json and logs it in the browser:\n\n```js\n// plugins/version-plugin.js\nimport fs from 'fs';\nimport {html, unescape, createPlugin} from 'fusion-core'; // html sanitization\n\nexport default createPlugin({\n  middleware: () => {\n    const data = __NODE__ && JSON.parse(fs.readFileSync('package.json').toString());\n    return async (ctx, next) => {\n      if (__NODE__) {\n        ctx.template.head.push(html`<meta id=\"app-version\" content=\"${data.version}\">`);\n        return next();\n      } else {\n        const version = unescape(document.getElementById('app-version').content);\n        console.log(`Version: ${version}`);\n        return next();\n      }\n    });\n  }\n});\n```\n\nWe can then consume the plugin like this:\n\n```js\n// main.js\nimport React from 'react';\nimport App from 'fusion-core';\nimport VersionPlugin from './plugins/version-plugin';\n\nconst root = <div>Hello world</div>;\n\nconst render = el =>\n  __NODE__ ? renderToString(el) : render(el, document.getElementById('root'));\n\nexport default function() {\n  const app = new App(root, render);\n  app.register(VersionPlugin);\n  return app;\n}\n```\n\n#### HTML sanitization\n\nDefault-on HTML sanitization is important for preventing security threats such as XSS attacks.\n\nFusion automatically sanitizes `htmlAttrs` and `title`. When pushing HTML strings to `head` or `body`, you must use the `html` template tag to mark your HTML as sanitized:\n\n```js\nimport {html} from 'fusion-core';\n\nconst middleware = (ctx, next) => {\n  if (ctx.element) {\n    const userData = await getUserData();\n    // userData can't be trusted, and is automatically escaped\n    ctx.template.body.push(html`<div>${userData}</div>`)\n  }\n  return next();\n}\n```\n\nIf `userData` above was `<script>alert(1)</script>`, ththe string would be automatically turned into `<div>\\u003Cscript\\u003Ealert(1)\\u003C/script\\u003E</div>`. Note that only `userData` is escaped, but the HTML in your code stays intact.\n\nIf your HTML is complex and needs to be broken into smaller strings, you can also nest sanitized HTML strings like this:\n\n```js\nconst notUserData = html`<h1>Hello</h1>`;\nconst body = html`<div>${notUserData}</div>`;\n```\n\nNote that you cannot mix sanitized HTML with unsanitized strings:\n\n```js\nctx.template.body.push(html`<h1>Safe</h1>` + 'not safe'); // will throw an error when rendered\n```\n\nAlso note that only template strings can have template tags (i.e. <code>html&#x60;&lt;div&gt;&lt;/div&gt;&#x60;</code>). The following are NOT valid Javascript: `html\"<div></div>\"` and `html'<div></div>'`.\n\nIf you get an <code>Unsanitized html. You must use html&#x60;[your html here]&#x60;</code> error, remember to prepend the `html` template tag to your template string.\n\nIf you have already taken steps to sanitize your input against XSS and don't wish to re-sanitize it, you can use `dangerouslySetHTML(string)` to let Fusion.js render the unescaped dynamic string.\n\n#### Enhancing a dependency\n\nIf you wanted to add a header to every request sent using the registered `fetch`.\n\n```js\napp.register(FetchToken, window.fetch);\napp.enhance(FetchToken, fetch => {\n  return (url, params = {}) => {\n    return fetch(url, {\n      ...params,\n      headers: {\n        ...params.headers,\n        'x-test': 'test',\n      },\n    });\n  };\n});\n```\n\nYou can also return a `Plugin` from the enhancer function, which `provides` the enhanced value, allowing\nthe enhancer to have dependencies and even middleware.\n\n```js\napp.register(FetchToken, window.fetch);\napp.enhance(FetchToken, fetch => {\n  return createPlugin({\n    provides: () => (url, params = {}) => {\n      return fetch(url, {\n        ...params,\n        headers: {\n          ...params.headers,\n          'x-test': 'test',\n        },\n      });\n    },\n  });\n});\n```\n\n#### Controlling SSR behavior\n\nBy default we do not perfrom SSR for any paths that match the following extensions: js, gif, jpg, png, pdf and json. You can control SSR behavior by enhancing the SSRDeciderToken. This will give you the ability to apply custom logic around which routes go through the renderer. You may enhance the SSRDeciderToken with either a function, or a plugin if you need dependencies.\n\n```js\nimport {SSRDeciderToken} from 'fusion-core';\napp.enhance(SSRDeciderToken, decide => ctx =>\n  decide(ctx) && !ctx.path.match(/ignore-ssr-route/)\n);\n```\n\n#### Troubleshooting\n\n##### Registered without depending\n\n```\nError: Registered token without depending on it: \"TOKEN_NAME\"\n```\n\nThis exception is thrown when a value is registered to a token which neither\nappears in a plugin's `deps` nor is enhanced with `app.enhance`. Note that the\nvalue we refer to here means any value that is not a plugin (created by calling\n`createPlugin`).\n\nCommonly this happens when you register a value like server-side config in both\nthe browser and server environments, when really it should only be registered\nin the server because the only plugins that use it are server plugins. Wrap the\n`app.register` call in a code fence to fix the error.\n\n```js\nif (__NODE__) {\n  app.register(ConfigToken, mySecretConfig);\n}\n```\n\nYou can see that this error prevents accidentally leaking configuration to the\nclient.\n\nThis error could also be thrown if no plugins in either environment depend on\nthe token, but you need to use Fusion.js' DI system to store a value for use in\nyour application. In most cases, you should not need to do this, however if you\nare sure you want to, you can get around this by wrapping the value in a plugin\nto prevent the exception from being thrown.\n\n```js\napp.register(ValueToken, createPlugin({\n  provides: () => myValue,\n}));\n```\n\nIf you do not need to access the value by associating it with a token, there\nshould be no reason to use the Fusion.js DI system for it. It is recommended to\nimport and use the value directly in your application.\n","readmeFilename":"README.md"}