[
  {
    "path": ".eslintignore",
    "content": "dist\ncoverage\nnode_modules\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"parser\": \"@typescript-eslint/parser\",\n  \"parserOptions\": {\n    \"ecmaVersion\": 2019,\n    \"sourceType\": \"module\",\n    \"ecmaFeatures\": {\n      \"jsx\": true\n    }\n  },\n  \"extends\": [\n    \"prettier/@typescript-eslint\",\n    \"plugin:prettier/recommended\",\n    \"plugin:react/recommended\",\n    \"plugin:@typescript-eslint/recommended\"\n  ],\n  \"plugins\": [\"@typescript-eslint\"],\n  \"globals\": {\n    \"__DEV__\": true\n  },\n  \"env\": {\n    \"browser\": true\n  },\n  \"settings\": {\n    \"react\": {\n      \"pragma\": \"React\",\n      \"version\": \"detect\"\n    }\n  },\n  \"rules\": {\n    \"@typescript-eslint/no-use-before-define\": 0,\n    \"@typescript-eslint/no-var-requires\": 0,\n    \"@typescript-eslint/no-unused-vars\": 0,\n    \"@typescript-eslint/ban-ts-comment\": 1,\n    \"@typescript-eslint/camelcase\": 0,\n    \"@typescript-eslint/interface-name-prefix\": 0,\n    \"@typescript-eslint/no-explicit-any\": 0,\n    \"@typescript-eslint/no-empty-function\": 0,\n    \"@typescript-eslint/explicit-function-return-type\": 2,\n    \"@typescript-eslint/explicit-module-boundary-types\": 0,\n    \"@typescript-eslint/no-non-null-assertion\": 0,\n    \"no-shadow\": 1,\n    \"prefer-const\": 0,\n    \"jsx-quotes\": 0,\n    \"prefer-rest-params\":0,\n    \"react/prop-types\": 0,\n    \"react/no-deprecated\": 1,\n    \"react/display-name\": 1\n  }\n}\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Use Unix line endings in all text files.\n* text=auto eol=lf\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: ci\n\non: [push, pull_request]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v2\n\n      - run: npm i\n      - run: npm run check\n"
  },
  {
    "path": ".gitignore",
    "content": "# dependencies\nnode_modules/\nnpm-debug.log*\nyarn-error.log\nyarn.lock\nlerna-debug.log\n**/**/node_modules/\n**/**/**/node_modules/\n\n# production\n/dist\n\n# misc\n.DS_Store\n.cache\ncoverage\n\n# Editor directories and files\n.idea\n.vscode\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw*\n\n# Lock files\npackage-lock.json\nyarn.lock\n"
  },
  {
    "path": ".prettierignore",
    "content": "dist/\n"
  },
  {
    "path": ".prettierrc",
    "content": "tabWidth: 2\nuseTabs: false\nsemi: false\narrowParens: always\nsingleQuote: true\nprintWidth: 80\ntrailingComma: none\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 yisar h-a-n-a\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\"><img src=\"https://avatars0.githubusercontent.com/u/68577605?s=200&v=4\" alt=\"berial logo\" width=\"150\"></p>\n<h1 align=\"center\">Berial</h1>\n<p align=\"center\">:imp: Simple micro-front-end framework.</p>\n<p align=\"center\">\n<a href=\"https://github.com/berialjs/berial/actions\"><img src=\"https://img.shields.io/github/workflow/status/berialjs/berial/ci.svg\" alt=\"Build Status\"></a>\n<a href=\"https://npmjs.com/package/berial\"><img src=\"https://img.shields.io/npm/v/berial.svg\" alt=\"npm-v\"></a>\n<a href=\"https://npmjs.com/package/berial\"><img src=\"https://img.shields.io/npm/dt/berial.svg\" alt=\"npm-d\"></a>\n</p>\n\n### Why Berial\n\nBerial is a new approach to a popular idea: build a javascript framework for front-end microservices.\n\nThere are any wonderful features of it, such as Asynchronous rendering pipeline, Web components (shadow DOM + scoped css), JavaScript sandbox (Proxy).\n\nNote: diffence form fre, Berial will pay attention to business value.\n\n### Use\n\n```html\n<one-app></one-app>\n<two-app></two-app>\n\n<script type=\"module\">\n  import { register } from 'berial'\n  register([{\n    name: 'one-app',\n    url: '1.html',\n    allowList: ['fre'] // 沙箱白名单\n  },{\n    name: 'two-app',\n    scripts: ['2.js'], // 可选\n    styles: ['2.css']\n  }])\n</script>\n```\n\n### License\n\nMIT ©yisar ©h-a-n-a \n"
  },
  {
    "path": "example/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\n# next.js build output\n.next\n\n.idea/\nserver/config.js\nserver/hcy.js\nserver/san.js\n\ndist/\n"
  },
  {
    "path": "example/App.js",
    "content": "import { h, useEffect } from 'fre'\nimport { register } from '../dist/berial.esm'\n\n\nfunction App() {\n\n  const changeRoute = (pathname) => {\n    history.pushState({}, '', pathname)\n  }\n\n  return <div>\n    <header className='header'>\n      <button onClick={() => changeRoute('/')} >Fre</button>\n      <button onClick={() => changeRoute('/react')} >React</button>\n      <button onClick={() => changeRoute('/vue')} >Vue</button>\n    </header>\n    <child-fre></child-fre>\n    <child-react></child-react>\n    <child-vue></child-vue>\n  </div>\n}\n\nexport default App\n"
  },
  {
    "path": "example/README.md",
    "content": "# Example\n\n```\nyarn install\nyarn start\n```\n"
  },
  {
    "path": "example/index.css",
    "content": ".header {\n  height: 60px;\n  display: flex;\n  align-items: center;\n  padding: 0 24px;\n}\n\n.header button {\n  padding:10px 30px;\n  cursor: pointer;\n  background: #000;\n  color: #fff;\n  border: none;\n  margin: 10px;\n}\n"
  },
  {
    "path": "example/index.html",
    "content": "<html>\n  <head> </head>\n  <body>\n    <div id='app'></div>\n  </body>\n</html>\n"
  },
  {
    "path": "example/index.js",
    "content": "import { h, render } from 'fre'\nimport './index.css'\nimport { register } from '../dist/berial.esm'\n\nimport App from './App'\n\nif (!window.IS_BERIAL_SANDBOX) {\n  render(<App />, document.getElementById('app'))\n}\n\nregister([\n  {\n    name: 'child-fre',\n    url: 'https://berial-child-fre.vercel.app',\n    path: ({ pathname }) => pathname !== '/react' && pathname !== '/vue',\n    allowList:['document']\n  },\n  {\n    name: 'child-react',\n    url: 'https://berial-child-react.vercel.app',\n    path: ({ pathname }) => pathname === '/react'\n  },\n  {\n    name: 'child-vue',\n    url: 'https://berial-child-vue.vercel.app',\n    path: ({ pathname }) => pathname === '/vue'\n  }\n])\n"
  },
  {
    "path": "example/package.json",
    "content": "{\n  \"scripts\": {\n    \"start\": \"cross-env NODE_ENV=development webpack-dev-server --mode production\"\n  },\n  \"dependencies\": {\n    \"fre\": \"^2.0.0-alpha.1\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.6.4\",\n    \"@babel/plugin-transform-react-jsx\": \"^7.3.0\",\n    \"@babel/preset-env\": \"^7.6.3\",\n    \"babel-loader\": \"^8.0.6\",\n    \"concurrently\": \"^5.3.0\",\n    \"cross-env\": \"^6.0.3\",\n    \"css-loader\": \"^3.2.0\",\n    \"html-webpack-plugin\": \"^3.2.0\",\n    \"mini-css-extract-plugin\": \"^0.8.0\",\n    \"style-loader\": \"^1.0.1\",\n    \"webpack\": \"^4.41.1\",\n    \"webpack-cli\": \"^3.3.9\",\n    \"webpack-dev-server\": \"^3.8.2\"\n  }\n}\n"
  },
  {
    "path": "example/webpack.config.js",
    "content": "const path = require('path')\nconst HtmlWebpackPlugin = require('html-webpack-plugin')\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin')\n\nmodule.exports = {\n  entry: './index.js',\n  output: {\n    path: path.resolve(__dirname, 'dist'),\n    filename: '[name].js',\n    library: 'one-app',\n    libraryTarget: 'umd',\n    umdNamedDefine: true,\n    publicPath:\n      process.env.NODE_ENV === 'production'\n        ? 'https://berial.vercel.app'\n        : 'http://localhost:3000'\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.js$/,\n        use: {\n          loader: 'babel-loader',\n          options: {\n            plugins: [\n              [\n                '@babel/plugin-transform-react-jsx',\n                {\n                  pragma: 'h'\n                }\n              ]\n            ]\n          }\n        }\n      },\n      {\n        test: /\\.css$/,\n        use: [MiniCssExtractPlugin.loader, 'css-loader']\n      }\n    ]\n  },\n  optimization: {\n    splitChunks: false\n  },\n  plugins: [\n    new HtmlWebpackPlugin({\n      template: './index.html'\n    }),\n\n    new MiniCssExtractPlugin({\n      filename: '[name].css'\n    })\n  ],\n  devServer: {\n    headers: { 'Access-Control-Allow-Origin': '*' },\n    contentBase: path.join(__dirname, 'dist'),\n    compress: true,\n    port: 3000,\n    historyApiFallback: true,\n    hot: true,\n    open: true\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"berial\",\n  \"version\": \"0.1.6\",\n  \"description\": \"micro frontend\",\n  \"main\": \"dist/berial.js\",\n  \"module\": \"dist/berial.esm.js\",\n  \"types\": \"dist/types/index.d.ts\",\n  \"scripts\": {\n    \"build\": \"rollup -c\",\n    \"dev\": \"rollup -c --watch\",\n    \"check\": \"run-p fmt-check lint\",\n    \"fix\": \"run-s \\\"lint -- --fix\\\"\",\n    \"fmt\": \"run-s \\\"fmt-check -- --write\\\"\",\n    \"fmt-check\": \"prettier --check **/*.{json,ts}\",\n    \"lint\": \"eslint **/*.ts\",\n    \"type\": \"tsc --project tsconfig.json --skipLibCheck --noEmit\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/berialjs/berial.git\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/berialjs/berial/issues\"\n  },\n  \"homepage\": \"https://github.com/berialjs/berial#readme\",\n  \"devDependencies\": {\n    \"cross-env\": \"^7.0.2\",\n    \"eslint\": \"^7.5.0\",\n    \"eslint-config-prettier\": \"^6.11.0\",\n    \"eslint-plugin-prettier\": \"^3.1.4\",\n    \"eslint-plugin-react\": \"^7.20.5\",\n    \"http-server\": \"^0.12.3\",\n    \"husky\": \"^4.2.5\",\n    \"npm-run-all\": \"^4.1.5\",\n    \"prettier\": \"^2.0.5\",\n    \"rollup\": \"^2.23.0\",\n    \"rollup-plugin-dts\": \"^1.4.11\",\n    \"rollup-plugin-typescript2\": \"^0.27.1\",\n    \"serve\": \"^11.3.2\",\n    \"tslib\": \"^2.0.0\",\n    \"typescript\": \"^3.9.7\",\n    \"@typescript-eslint/eslint-plugin\": \"^3.7.0\",\n    \"@typescript-eslint/parser\": \"^3.7.0\"\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"npm run fmt && npm run fix;\"\n    }\n  }\n}\n"
  },
  {
    "path": "plugin/bridge-event.ts",
    "content": "import { mixin } from 'berial'\n\nexport function bridgeEvent(): void {\n  mixin({ boostrap })\n}\n\nasync function boostrap(app): Promise<void> {\n  const shadowRoot = app.host\n  const define = Object.defineProperty\n  const fromNode = shadowRoot,\n    toNode = shadowRoot.host\n  BRIDGE_EVENT_NAMES.map((eventName) => {\n    fromNode.addEventListener(eventName, (fromEvent) => {\n      fromEvent.stopPropagation()\n      const Event = fromEvent.constructor\n      const toEvent = new Event(eventName, {\n        ...fromEvent,\n        bubbles: true,\n        cancelable: true,\n        composed: true\n      })\n      const {\n        path = [],\n        target = path[0],\n        srcElement = path[0],\n        toElement = path[0],\n        preventDefault\n      } = fromEvent as any\n      define(toEvent, 'path', { get: () => path })\n      define(toEvent, 'target', { get: () => target })\n      define(toEvent, 'srcElement', { get: () => srcElement })\n      define(toEvent, 'toElement', { get: () => toElement })\n      define(toEvent, 'preventDefault', {\n        value: () => {\n          preventDefault.call(fromEvent)\n          return preventDefault.call(toEvent)\n        }\n      })\n      toNode.dispatchEvent(toEvent)\n    })\n  })\n}\n\nconst BRIDGE_EVENT_NAMES = [\n  'abort',\n  'animationcancel',\n  'animationend',\n  'animationiteration',\n  'auxclick',\n  'blur',\n  'change',\n  'click',\n  'close',\n  'contextmenu',\n  'doubleclick',\n  'error',\n  'focus',\n  'gotpointercapture',\n  'input',\n  'keydown',\n  'keypress',\n  'keyup',\n  'load',\n  'loadend',\n  'loadstart',\n  'lostpointercapture',\n  'mousedown',\n  'mousemove',\n  'mouseout',\n  'mouseover',\n  'mouseup',\n  'pointercancel',\n  'pointerdown',\n  'pointerenter',\n  'pointerleave',\n  'pointermove',\n  'pointerout',\n  'pointerover',\n  'pointerup',\n  'reset',\n  'resize',\n  'scroll',\n  'select',\n  'selectionchange',\n  'selectstart',\n  'submit',\n  'touchcancel',\n  'touchmove',\n  'touchstart',\n  'transitioncancel',\n  'transitionend',\n  'drag',\n  'dragend',\n  'dragenter',\n  'dragexit',\n  'dragleave',\n  'dragover',\n  'dragstart',\n  'drop',\n  'focusout'\n]\n"
  },
  {
    "path": "plugin/package.json",
    "content": "{\n  \"name\": \"compat\",\n  \"version\": \"0.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n    \"build\": \"tsc index.ts\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"berial\": \"^0.0.4\"\n  }\n}\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import typescript from 'rollup-plugin-typescript2'\n\nexport default {\n  input: 'src/index.ts',\n  output: [\n    { file: 'dist/berial.d.ts', format: 'esm', exports: 'named' },\n    {\n      file: 'dist/berial.esm.js',\n      format: 'esm',\n      sourcemap: true\n    },\n    {\n      file: 'dist/berial.js',\n      format: 'umd',\n      sourcemap: true,\n      name: 'berial'\n    }\n  ],\n  plugins: [\n    typescript({\n      tsconfig: 'tsconfig.json',\n      removeComments: true,\n      useTsconfigDeclarationDir: true,\n    }),\n  ]\n}\n"
  },
  {
    "path": "src/entity.ts",
    "content": "import type { App } from './types'\nimport { mapMixin } from './mixin'\nimport { importHtml } from './html-loader'\n\nexport enum Status {\n  NOT_LOADED = 'NOT_LOADED',\n  LOADING = 'LOADING',\n  NOT_BOOTSTRAPPED = 'NOT_BOOTSTRAPPED',\n  BOOTSTRAPPING = 'BOOTSTRAPPING',\n  NOT_MOUNTED = 'NOT_MOUNTED',\n  MOUNTING = 'MOUNTING',\n  MOUNTED = 'MOUNTED',\n  UPDATING = 'UPDATING',\n  UPDATED = 'UPDATED',\n  UNMOUNTING = 'UNMOUNTING'\n}\n\nlet apps: App[] = []\n\nexport function register(appArray: any[]): void {\n  appArray.forEach((app: any) => (app.status = Status.NOT_LOADED))\n  apps = appArray\n  hack()\n  reroute()\n}\n\nfunction reroute(): void {\n  const { loads, mounts, unmounts } = getAppChanges()\n  perform()\n  async function perform(): Promise<void> {\n    unmounts.map(runUnmount)\n\n    loads.map(async (app) => {\n      app = await runLoad(app)\n      app = await runBootstrap(app)\n      return runMount(app)\n    })\n\n    mounts.map(async (app) => {\n      app = await runBootstrap(app)\n      return runMount(app)\n    })\n  }\n}\n\nfunction getAppChanges(): {\n  unmounts: App[]\n  loads: App[]\n  mounts: App[]\n} {\n  const unmounts: App[] = []\n  const loads: App[] = []\n  const mounts: App[] = []\n\n  apps.forEach((app: any) => {\n    const isActive =\n      typeof app.path === 'function'\n        ? app.path(window.location)\n        : window.location.pathname === app.path\n    switch (app.status) {\n      case Status.NOT_LOADED:\n      case Status.LOADING:\n        isActive && loads.push(app)\n        break\n      case Status.NOT_BOOTSTRAPPED:\n      case Status.BOOTSTRAPPING:\n      case Status.NOT_MOUNTED:\n        isActive && mounts.push(app)\n        break\n      case Status.MOUNTED:\n        !isActive && unmounts.push(app)\n        break\n    }\n  })\n  return { unmounts, loads, mounts }\n}\n\nfunction compose(\n  fns: ((app: App) => Promise<any>)[]\n): (app: App) => Promise<void> {\n  fns = Array.isArray(fns) ? fns : [fns]\n  return (app: App): Promise<void> =>\n    fns.reduce((p, fn) => p.then(() => fn(app)), Promise.resolve())\n}\n\nasync function runLoad(app: App): Promise<any> {\n  if (app.loaded) return app.loaded\n  app.loaded = Promise.resolve().then(async () => {\n    app.status = Status.LOADING\n    let mixinLife = mapMixin()\n    app.host = await loadShadowDOM(app)\n    const { dom, lifecycles } = await importHtml(app)\n    app.host?.appendChild(dom)\n    app.status = Status.NOT_BOOTSTRAPPED\n    app.bootstrap = compose(mixinLife.bootstrap.concat(lifecycles.bootstrap))\n    app.mount = compose(mixinLife.mount.concat(lifecycles.mount))\n    app.unmount = compose(mixinLife.unmount.concat(lifecycles.unmount))\n    delete app.loaded\n    return app\n  })\n  return app.loaded\n}\n\nfunction loadShadowDOM(app: App): Promise<DocumentFragment> {\n  return new Promise((resolve, reject) => {\n    class Berial extends HTMLElement {\n      static get tag(): string {\n        return app.name\n      }\n      constructor() {\n        super()\n        resolve(this.attachShadow({ mode: 'open' }))\n      }\n    }\n    const hasDef = window.customElements.get(app.name)\n    if (!hasDef) {\n      customElements.define(app.name, Berial)\n    }\n  })\n}\n\nasync function runUnmount(app: App): Promise<App> {\n  if (app.status != Status.MOUNTED) {\n    return app\n  }\n  app.status = Status.UNMOUNTING\n  await app.unmount(app)\n  app.status = Status.NOT_MOUNTED\n  return app\n}\n\nasync function runBootstrap(app: App): Promise<App> {\n  if (app.status !== Status.NOT_BOOTSTRAPPED) {\n    return app\n  }\n  app.status = Status.BOOTSTRAPPING\n  await app.bootstrap(app)\n  app.status = Status.NOT_MOUNTED\n  return app\n}\n\nasync function runMount(app: App): Promise<App> {\n  if (app.status !== Status.NOT_MOUNTED) {\n    return app\n  }\n  app.status = Status.MOUNTING\n  await app.mount(app)\n  app.status = Status.MOUNTED\n  return app\n}\n\nfunction hack(): void {\n  window.addEventListener = hackEventListener(window.addEventListener)\n  window.removeEventListener = hackEventListener(window.removeEventListener)\n\n  window.history.pushState = hackHistory(window.history.pushState)\n  window.history.replaceState = hackHistory(window.history.replaceState)\n\n  window.addEventListener('hashchange', reroute)\n  window.addEventListener('popstate', reroute)\n}\n\nconst captured = {\n  hashchange: [],\n  popstate: []\n} as any\n\nfunction hackEventListener(func: any): any {\n  return function (name: any, fn: any): any {\n    if (name === 'hashchange' || name === 'popstate') {\n      if (!captured[name].some((l: any) => l == fn)) {\n        captured[name].push(fn)\n        return\n      } else {\n        captured[name] = captured[name].filter((l: any) => l !== fn)\n        return\n      }\n    }\n    return func.apply(this, arguments as any)\n  }\n}\n\nfunction hackHistory(fn: any): () => void {\n  return function (): void {\n    const before = window.location.href\n    fn.apply(window.history, arguments)\n    const after = window.location.href\n    if (before !== after) {\n      new PopStateEvent('popstate')\n      reroute()\n    }\n  }\n}\n"
  },
  {
    "path": "src/html-loader.ts",
    "content": "import { request } from './util'\nimport { runScript } from './sandbox'\n\nconst ALL_SCRIPT_REGEX = /<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi\nconst SCRIPT_TAG_REGEX = /<(script)\\s+((?!type=('|')text\\/ng-template\\3).)*?>.*?<\\/\\1>/is\nconst SCRIPT_SRC_REGEX = /.*\\ssrc=('|\")?([^>'\"\\s]+)/\nconst SCRIPT_ENTRY_REGEX = /.*\\sentry\\s*.*/\nconst LINK_TAG_REGEX = /<(link)\\s+.*?>/gi\nconst LINK_IGNORE_REGEX = /.*ignore\\s*.*/\nconst LINK_PRELOAD_OR_PREFETCH_REGEX = /\\srel=('|\")?(preload|prefetch)\\1/\nconst LINK_HREF_REGEX = /.*\\shref=('|\")?([^>'\"\\s]+)/\nconst STYLE_TAG_REGEX = /<style[^>]*>[\\s\\S]*?<\\/style>/gi\nconst STYLE_TYPE_REGEX = /\\s+rel=('|\")?stylesheet\\1.*/\nconst STYLE_HREF_REGEX = /.*\\shref=('|\")?([^>'\"\\s]+)/\nconst STYLE_IGNORE_REGEX = /<style(\\s+|\\s+.+\\s+)ignore(\\s*|\\s+.*)>/i\nconst HTML_COMMENT_REGEX = /<!--([\\s\\S]*?)-->/g\nconst SCRIPT_IGNORE_REGEX = /<script(\\s+|\\s+.+\\s+)ignore(\\s*|\\s+.*)>/i\n\nexport function getInlineCode(match: any): string {\n  const start = match.indexOf('>') + 1\n  const end = match.lastIndexOf('<')\n  return match.substring(start, end)\n}\n\nfunction hasProtocol(url: string): any {\n  return (\n    url.startsWith('//') ||\n    url.startsWith('http://') ||\n    url.startsWith('https://')\n  )\n}\n\nfunction getEntirePath(path: string, baseURI: string): string {\n  return new URL(path, baseURI).toString()\n}\n\nexport const genLinkReplaceSymbol = (linkHref: any): string =>\n  `<!-- link ${linkHref} replaced by import-html-entry -->`\nexport const genScriptReplaceSymbol = (scriptSrc: any): string =>\n  `<!-- script ${scriptSrc} replaced by import-html-entry -->`\nexport const inlineScriptReplaceSymbol = `<!-- inline scripts replaced by import-html-entry -->`\nexport const genIgnoreAssetReplaceSymbol = (url: any): string =>\n  `<!-- ignore asset ${url || 'file'} replaced by import-html-entry -->`\n\nexport function parse(tpl: string, baseURI: string): any {\n  let scripts: string[] = []\n  const styles: string[] = []\n  let entry: any = null\n\n  const template = tpl\n    .replace(HTML_COMMENT_REGEX, '')\n    .replace(LINK_TAG_REGEX, (match) => {\n      const styleType = !!match.match(STYLE_TYPE_REGEX)\n      console.log(styleType)\n      if (styleType) {\n        const styleHref = match.match(STYLE_HREF_REGEX)\n        const styleIgnore = match.match(LINK_IGNORE_REGEX)\n\n        if (styleHref) {\n          const href = styleHref && styleHref[2]\n          let newHref = href\n          if (href && !hasProtocol(href)) {\n            newHref = getEntirePath(href, baseURI)\n          }\n          if (styleIgnore) {\n            return genIgnoreAssetReplaceSymbol(newHref)\n          }\n\n          styles.push(newHref)\n          return genLinkReplaceSymbol(newHref)\n        }\n      }\n\n      const preloadOrPrefetchType = !!match.match(\n        LINK_PRELOAD_OR_PREFETCH_REGEX\n      )\n      if (preloadOrPrefetchType) {\n        const linkHref = match.match(LINK_HREF_REGEX)\n\n        if (linkHref) {\n          const href = linkHref[2]\n          if (href && !hasProtocol(href)) {\n            const newHref = getEntirePath(href, baseURI)\n            return match.replace(href, newHref)\n          }\n        }\n      }\n\n      return match\n    })\n    .replace(STYLE_TAG_REGEX, (match) => {\n      if (STYLE_IGNORE_REGEX.test(match)) {\n        return genIgnoreAssetReplaceSymbol('style file')\n      }\n      return match\n    })\n    .replace(ALL_SCRIPT_REGEX, (match) => {\n      const scriptIgnore = match.match(SCRIPT_IGNORE_REGEX)\n      if (SCRIPT_TAG_REGEX.test(match) && match.match(SCRIPT_SRC_REGEX)) {\n        const matchedScriptEntry = match.match(SCRIPT_ENTRY_REGEX)\n        const matchedScriptSrcMatch = match.match(SCRIPT_SRC_REGEX)\n        let matchedScriptSrc = matchedScriptSrcMatch && matchedScriptSrcMatch[2]\n\n        if (entry && matchedScriptEntry) {\n          throw new SyntaxError('You should not set multiply entry script!')\n        } else {\n          if (matchedScriptSrc && !hasProtocol(matchedScriptSrc)) {\n            matchedScriptSrc = getEntirePath(matchedScriptSrc, baseURI)\n          }\n\n          entry = entry || (matchedScriptEntry && matchedScriptSrc)\n        }\n\n        if (scriptIgnore) {\n          return genIgnoreAssetReplaceSymbol(matchedScriptSrc || 'js file')\n        }\n\n        if (matchedScriptSrc) {\n          scripts.push(matchedScriptSrc)\n          return genScriptReplaceSymbol(matchedScriptSrc)\n        }\n\n        return match\n      } else {\n        if (scriptIgnore) {\n          return genIgnoreAssetReplaceSymbol('js file')\n        }\n        const code = getInlineCode(match)\n        const isPureCommentBlock = code\n          .split(/[\\r\\n]+/)\n          .every((line) => !line.trim() || line.trim().startsWith('//'))\n\n        if (!isPureCommentBlock) {\n          scripts.push(match)\n        }\n\n        return inlineScriptReplaceSymbol\n      }\n    })\n\n  scripts = scripts.filter((s: string) => !!s)\n\n  return {\n    template,\n    scripts,\n    styles,\n    entry: entry || scripts[scripts.length - 1]\n  }\n}\nexport async function importHtml(app: any): Promise<any> {\n  let template = '',\n    scripts,\n    styles\n  if (app.scripts) {\n    scripts = app.scripts || []\n    styles = app.styles || []\n  } else {\n    const tpl = await request(app.url as string)\n    let res = parse(tpl, '')\n    scripts = res.scripts\n    styles = res.styles\n    template = res.template\n  }\n\n  scripts = await Promise.all(\n    scripts.map((s: string) =>\n      hasProtocol(s)\n        ? request(s)\n        : s.endsWith('.js') || s.endsWith('.jsx')\n        ? request(window.origin + s)\n        : s\n    )\n  )\n  styles = styles.map((s: string) =>\n    hasProtocol(s) || s.endsWith('.css')\n      ? `<link rel=\"stylesheet\" href=\"${s}\" ></link>`\n      : `<style>${s}<style>`\n  )\n  template = template\n\n  let lifecycles = null\n  scripts.forEach(async (script: any) => {\n    lifecycles = runScript(script, app.allowList)[app.name]\n  })\n\n  const dom = document.createDocumentFragment()\n  const body = document.createElement('template')\n  let out = ''\n  styles.forEach((s: string) => (out += s))\n  out += template\n  body.innerHTML = out\n  dom.appendChild(body.content.cloneNode(true))\n  return { dom, lifecycles }\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "import { register } from './entity'\nimport { mixin, use } from './mixin'\nimport { importHtml } from './html-loader'\nimport { runScript } from './sandbox'\n\nexport { register, importHtml, runScript, mixin, use }\n"
  },
  {
    "path": "src/mixin.ts",
    "content": "import type { Lifecycles, Mixin, Plugin } from './types'\n\nconst mixins: Set<Mixin> = new Set()\nconst plugins: Set<Plugin> = new Set()\n\nexport function use(plugin: Plugin, ...args: any[]): void {\n  if (!plugins.has(plugin)) {\n    plugins.add(plugin)\n    plugin(...args)\n  }\n}\n\nexport function mixin(mix: Mixin): void {\n  if (!mixins.has(mix)) {\n    mixins.add(mix)\n  }\n}\n\nexport function mapMixin(): Lifecycles {\n  const out: Lifecycles = {\n    load: [],\n    bootstrap: [],\n    mount: [],\n    unmount: []\n  }\n  mixins.forEach((item: Mixin) => {\n    item.load && out.load!.push(item.load)\n    item.bootstrap && out.bootstrap.push(item.bootstrap)\n    item.mount && out.mount.push(item.mount)\n    item.unmount && out.unmount.push(item.unmount)\n  })\n  return out\n}\n"
  },
  {
    "path": "src/sandbox.ts",
    "content": "export function runScript(code: string, allow: any = []): any {\n  const allowObj = allow.reduce((obj: any, cur: any): any => {\n    obj[cur] = window[cur]\n    return obj\n  }, {}) as any\n\n  try {\n    const handler = {\n      get(obj: any, prop: string): any {\n        return Reflect.has(obj, prop) ? obj[prop] : null\n      },\n      set(obj: any, prop: string, value: any): boolean {\n        Reflect.set(obj, prop, value)\n        return true\n      },\n      has(obj: any, prop: string): boolean {\n        return obj && Reflect.has(obj, prop)\n      }\n    }\n    const captureHandler = {\n      get(obj: any, prop: string): any {\n        return Reflect.get(obj, prop)\n      },\n      set(): boolean {\n        return true\n      },\n      has(): boolean {\n        return true\n      }\n    }\n\n    const allowList = {\n      IS_BERIAL_SANDBOX: true,\n      __proto__: null,\n      console,\n      String,\n      Number,\n      Array,\n      Symbol,\n      Math,\n      Object,\n      Promise,\n      RegExp,\n      JSON,\n      Date,\n      Function,\n      parseInt,\n      document,\n      location,\n      performance,\n      MessageChannel,\n      SVGElement,\n      HTMLElement,\n      HTMLIFrameElement,\n      history,\n      Map,\n      Set,\n      WeakMap,\n      WeakSet,\n      Error,\n      localStorage,\n      decodeURI,\n      encodeURI,\n      decodeURIComponent,\n      encodeURIComponent,\n      fetch: fetch.bind(window),\n      setTimeout: setTimeout.bind(window),\n      clearTimeout: clearTimeout.bind(window),\n      setInterval: setInterval.bind(window),\n      clearInterval: clearInterval.bind(window),\n      requestAnimationFrame: requestAnimationFrame.bind(window),\n      cancelAnimationFrame: cancelAnimationFrame.bind(window),\n      addEventListener: addEventListener.bind(window),\n      removeEventListener: removeEventListener.bind(window),\n      // eslint-disable-next-line no-shadow\n      eval: function (code: string): any {\n        return runScript('return ' + code, {})\n      },\n      alert: function (): void {\n        alert('Sandboxed alert:' + arguments[0])\n      },\n      // position related properties\n      innerHeight,\n      innerWidth,\n      outerHeight,\n      outerWidth,\n      pageXOffset,\n      pageYOffset,\n      screen,\n      screenLeft,\n      screenTop,\n      screenX,\n      screenY,\n      scrollBy,\n      scrollTo,\n      scrollX,\n      scrollY,\n      // custom allow list\n      ...allowObj\n    }\n\n    if (!Object.isFrozen(String.prototype)) {\n      for (const k in allowList) {\n        const fn = allowList[k]\n        if (typeof fn === 'object' && fn.prototype) {\n          Object.freeze(fn.prototype)\n        }\n        if (typeof fn === 'function') {\n          Object.freeze(fn)\n        }\n      }\n    }\n    const proxy = new Proxy(allowList, handler)\n    const capture = new Proxy(\n      {\n        __proto__: null,\n        proxy,\n        globalThis: new Proxy(allowList, handler),\n        window: new Proxy(allowList, handler),\n        self: new Proxy(allowList, handler)\n      },\n      captureHandler\n    )\n    return Function(\n      'proxy',\n      'capture',\n      `with(capture) {     \n            with(proxy) {  \n              return (function(){                                               \n                ${code};\n                return window\n              })();\n            }\n        }`\n    )(proxy, capture)\n  } catch (e) {\n    throw e\n  }\n}\n"
  },
  {
    "path": "src/types.ts",
    "content": "import type { Status } from './entity'\n\nexport type Lifecycles = ToArray<Lifecycle>\n\nexport type Lifecycle = {\n  bootstrap: PromiseFn\n  unmount: PromiseFn\n  mount: PromiseFn\n  load?: PromiseFn\n}\n\nexport type Mixin = {\n  load?: PromiseFn\n  mount?: PromiseFn\n  unmount?: PromiseFn\n  bootstrap?: PromiseFn\n}\n\nexport type Plugin = (...args: any[]) => any\n\nexport type App = {\n  name: string\n  node: HTMLElement\n  url: ((props: App['props']) => Lifecycle) | string\n  match: (location: Location) => boolean\n  host: DocumentFragment\n  props: Record<string, unknown>\n  status: Status\n  loaded?: any\n  store?: any\n  loadLifecycle: any\n  unmount: PromiseFn\n  mount: PromiseFn\n  bootstrap: PromiseFn\n}\n\nexport type PromiseFn = (...args: any[]) => Promise<any>\n\nexport type ArrayType<T> = T extends (infer U)[] ? U : T\n\nexport type ToArray<T> = T extends Record<any, any>\n  ? {\n      [K in keyof T]: T[K][]\n    }\n  : unknown\n\nexport type ProxyType = Omit<ProxyConstructor, keyof ProxyConstructor>\n"
  },
  {
    "path": "src/util.ts",
    "content": "import type { Lifecycle, Lifecycles } from './types'\n\nexport function warn(trigger: string): void\nexport function warn(trigger: boolean, msg?: string): void\nexport function warn(trigger: any, msg?: any): void {\n  if (typeof trigger === 'string') msg = trigger\n  if (!trigger) return\n  throw new Error(`[Berial: Warning]: ${msg}`)\n}\n\nexport function error(trigger: string): void\nexport function error(trigger: boolean, msg?: string): void\nexport function error(trigger: any, msg?: any): void {\n  if (typeof trigger === 'string') msg = trigger\n  if (!trigger) return\n  throw new Error(`[Berial: Error]: ${msg}`)\n}\n\nexport function request(url: string, option?: RequestInit): Promise<string> {\n  return fetch(url, {\n    mode: 'cors',\n    ...option\n  }).then((res) => res.text())\n}\n\nexport function reverse<T>(arr: T[]): T[] {\n  return Array.from(arr).reverse()\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"strict\": true,\n    \"sourceMap\": true,\n    \"noImplicitAny\": true,\n    \"removeComments\": true,\n    \"target\": \"es6\",\n    \"module\": \"es6\",\n    \"moduleResolution\": \"node\",\n    \"esModuleInterop\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"downlevelIteration\": true,\n    \"noImplicitThis\": false,\n    \"jsx\": \"react\",\n    \"lib\": [\"es6\", \"dom\"],\n    \"baseUrl\": \".\",\n    \"outDir\": \"./dist\",\n    \"paths\": {\n      \"@/*\": [\"./src/*\"]\n    }\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"test/*\", \"node_modules\"]\n}\n"
  }
]