{"id":17309614,"url":"https://github.com/jcubic/wayne","last_synced_at":"2025-05-15T01:09:58.868Z","repository":{"id":43769682,"uuid":"511553611","full_name":"jcubic/wayne","owner":"jcubic","description":"Service Worker Routing library for in browser HTTP requests","archived":false,"fork":false,"pushed_at":"2024-11-24T19:40:19.000Z","size":3609,"stargazers_count":576,"open_issues_count":2,"forks_count":30,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-04-14T02:59:04.528Z","etag":null,"topics":["browser","fake","filesystem","filesystem-library","http","http-server","http-server-library","javascript","promise","routing","service-worker","wayne","web-server"],"latest_commit_sha":null,"homepage":"https://jcubic.github.io/wayne","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/jcubic.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"jcubic","ko_fi":"jcubic","custom":"https://www.paypal.me/jcubic"}},"created_at":"2022-07-07T14:10:45.000Z","updated_at":"2025-04-03T16:40:55.000Z","dependencies_parsed_at":"2024-02-10T17:32:06.664Z","dependency_job_id":"bb044f96-7ffd-4123-b0fc-dc6a1505dbf5","html_url":"https://github.com/jcubic/wayne","commit_stats":{"total_commits":159,"total_committers":6,"mean_commits":26.5,"dds":0.04402515723270439,"last_synced_commit":"5cf16805a4c570f4a46b3181ab37db094166565a"},"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcubic%2Fwayne","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcubic%2Fwayne/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcubic%2Fwayne/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcubic%2Fwayne/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jcubic","download_url":"https://codeload.github.com/jcubic/wayne/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254254043,"owners_count":22039792,"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":["browser","fake","filesystem","filesystem-library","http","http-server","http-server-library","javascript","promise","routing","service-worker","wayne","web-server"],"created_at":"2024-10-15T12:30:29.560Z","updated_at":"2025-05-15T01:09:53.846Z","avatar_url":"https://github.com/jcubic.png","language":"JavaScript","readme":"\u003ch1 align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/jcubic/wayne/blob/master/assets/wayne-logo.svg?raw=true\"\n       alt=\"Logo of Wayne library - it represents construction worker helmet and text with the name of the library\" /\u003e\n\u003c/h1\u003e\n\n[![npm](https://img.shields.io/badge/npm-0.19.0-blue.svg)](https://www.npmjs.com/package/@jcubic/wayne)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://makeapullrequest.com)\n[![jSDelivr](https://data.jsdelivr.com/v1/package/npm/@jcubic/wayne/badge)](https://www.jsdelivr.com/package/npm/@jcubic/wayne)\n\n\n[Service Worker Routing library for in-browser HTTP requests](https://github.com/jcubic/wayne/)\n\nIt's like an Express inside Service Worker.\n\nMost of the time Service Worker is used for caching HTTP requests and making the app work when there\nis no internet (mostly for [PWA](https://en.wikipedia.org/wiki/Progressive_web_application)), but in\nfact, you can create completely new responses to requests that never leave the browser. This library\nmakes that easier by adding a simple API similar to Express.\n\n## Usage\n\nInstallation from npm:\n\n```bash\nnpm install @jcubic/wayne\n```\n\n```bash\nyarn add @jcubic/wayne\n```\n\nThe standard way of installing the service worker\n\n```javascript\nif ('serviceWorker' in navigator) {\n    const scope = location.pathname.replace(/\\/[^\\/]+$/, '/');\n    navigator.serviceWorker.register('sw.js', { scope, type: 'module' })\n             .then(function(reg) {\n                 reg.addEventListener('updatefound', function() {\n                     const installingWorker = reg.installing;\n                     console.log('A new service worker is being installed:',\n                                 installingWorker);\n                 });\n                 // registration worked\n                 console.log('Registration succeeded. Scope is ' + reg.scope);\n             }).catch(function(error) {\n                 // registration failed\n                 console.log('Registration failed with ' + error);\n             });\n}\n```\n\nIf you want to support browsers that don't support ES Modules in Service Worker use this instead:\n\n```javascript\nif ('serviceWorker' in navigator) {\n    const scope = location.pathname.replace(/\\/[^\\/]+$/, '/');\n    navigator.serviceWorker.register('sw.js', { scope })\n             .then(function(reg) {\n                 reg.addEventListener('updatefound', function() {\n                     const installingWorker = reg.installing;\n                     console.log('A new service worker is being installed:',\n                                 installingWorker);\n                 });\n                 // registration worked\n                 console.log('Registration succeeded. Scope is ' + reg.scope);\n             }).catch(function(error) {\n                 // registration failed\n                 console.log('Registration failed with ' + error);\n             });\n}\n```\n\nInside the same file you can send [AJAX](https://en.wikipedia.org/wiki/Ajax_(programming)) requests with standard\n[fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).\n\n```javascript\nfunction get(url) {\n    fetch(url)\n      .then(res =\u003e res.text())\n      .then(text =\u003e output.innerHTML = text);\n}\n\ninput.addEventListener('click', () =\u003e {\n    get(`./user/${user_id.value}`);\n});\n\nerror.addEventListener('click', () =\u003e {\n    get(`./error`);\n});\n```\n\nService worker - **sw.js** file\n\nImporting Wayne module:\n\n* when worker created as ES Module\n\n```javascript\nimport { Wayne } from 'https://cdn.jsdelivr.net/npm/@jcubic/wayne';\n\nconst app = new Wayne();\n```\n\n* When the Service Worker created as normal script\n\n```javascript\nimportScripts('https://cdn.jsdelivr.net/npm/@jcubic/wayne/index.umd.min.js');\n\nconst app = new wayne.Wayne();\n```\n\n* When using bundlers like Vite:\n\n```javascript\nimport { Wayne } from '@jcubic/wayne';\n```\n\nUsing the library\n\n```javascript\nconst users = {\n  1: 'Jakub T. Jankiewicz',\n  2: 'John Doe',\n  3: 'Jane Doe'\n};\n\napp.get('/user/{id}', function(req, res) {\n  const user = users[req.params.id];\n  if (user) {\n    res.json({result: user});\n  } else {\n    res.json({error: 'User Not Found'});\n  }\n});\n\napp.get('/error', function(req, res) {\n  nonExisting();\n});\n\napp.get('/redirect', function(req, res) {\n  res.redirect(301, '/message');\n});\n\napp.get('/message', function(req, res) {\n  res.text('Lorem Ipsum');\n});\n\napp.get('/404', function(req, res) {\n    res.text('Not Found', { status: 404, statusText: 'Not Found' });\n});\n\napp.get('/external', function(req, res) {\n  // lorem ipsum API\n  res.redirect('https://api.buildable.dev/@62d55492951509001abc363e/live/lorem-ipsum');\n});\n```\n\n### Handle the same extension for all requests\n\n```javascript\nimportScripts(\n  'https://cdn.jsdelivr.net/npm/@jcubic/wayne/index.umd.min.js',\n  'https://cdn.jsdelivr.net/gh/jcubic/static@master/js/path.js'\n);\n\nconst app = new Wayne();\n\napp.get('*', function(req, res) {\n  const url = new URL(req.url);\n  const extension = path.extname(url.pathname);\n  const accept = req.headers.get('Accept');\n  if (extension === '.js' \u0026\u0026 accept.match(/text\\/html/)) {\n    res.text('// Sorry no source code for you');\n  } else {\n    res.fetch(req);\n  }\n});\n```\n\nThis code will show the comment `// Sorry no source code for you` for every request to JavaScript\nfiles from the browser (if open in a new tab).  When you want to view the file the browser sends\n`Accept: text/html` HTTP header.\n\n### File system middleware\n\n```javascript\nimport { Wayne, FileSystem } from 'https://cdn.jsdelivr.net/npm/@jcubic/wayne';\nimport FS from \"https://cdn.skypack.dev/@isomorphic-git/lightning-fs\";\nimport mime from \"https://cdn.skypack.dev/mime\";\nimport path from \"https://cdn.skypack.dev/path-browserify\";\n\nconst { promises: fs } = new FS(\"__wayne__\");\n\nconst app = new Wayne();\n\napp.use(FileSystem({ path, fs, mime, prefix: '__fs__' }));\n```\n\nWhen not using a module the code will be similar. When you access URLs with\nthe prefix `__fs__` like `./__fs__/foo` it will read files from the indexedDB file\nsystem named `__wayne__`. See [Lightning-FS](https://github.com/isomorphic-git/lightning-fs) repo for details about the library.\n\nFrom version 0.12 you can use `test` callback option to check if the file should serve from the filesystem. Note that it will receive URLs from all domains.\n\nFrom version 0.13.0 you can use `dir` callback function that allow to dynamically change directory of served files.\n\n```javascript\nconst test = url =\u003e {\n    const path = url.pathname;\n    // return true if pathname should go to filesystem\n    return path.match(/__fs__/);\n};\n\nconst dir = () =\u003e '/';\n\napp.use(wayne.FileSystem({ path, fs, mime, test, dir }));\n```\n\nFrom version 0.14.0 both functions `dir` and `test` can be async. So you can use data from IndexedDB\ne.g. using [idb-keyval](https://github.com/jakearchibald/idb-keyval) by Jake Archibald.\n\nA patch in 0.14.3 allow putting interceptors to inject something into output HTML from FileSystem\nmiddleware. You do this by adding middleware before FileSystem and patch `res.send` method:\n\n```javascript\nfunction fs_interecept(callback) {\n    return function(req, res, next) {\n        const send = res.send.bind(res);\n        res.send = function(data, ...rest) {\n            const url = new URL(req.url);\n            if (test(url)) {\n                data = callback(data);\n            }\n            return send(data, ...rest);\n        };\n        next();\n    };\n}\n\napp.use(fs_interecept(function(html) {\n    return html.replace(/\u003c\\/body\u003e/, `\u003cscript\u003econsole.log('intercepted')\u003c/script\u003e\u003c/body\u003e`);\n}));\n```\n\nYou should use the same `test` function to make sure that you patch only those requests that came\nfrom FS.\n\n#### Serving files from Cache\n\nSince version 0.19.0 you can use Cache instead of indexedDB to serve the file from Service Worker.\nYou still need file system on main thread to save the files, but then you can use:\n\n```javascript\nwayne.make_cache({ fs, path, mime, dir: '/', prefix: '__fs__', cache: '__wayne__' });\n```\n\nThis function will cache all requests from filesystem, so you can use in service worker:\n\n```javascript\nconst app = new Wayne();\n\napp.use(FileSystem({ path, prefix: '__fs__', cache: '__wayne__' }));\n```\n\nOnly path is required in service worker. Parameters `cache` and `prefix` needs to be the same on both calls.\n\n### [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call) mechanism\n\nIn Service Worker, you create a generic route that sends data to the BroadcastChannel:\n\n```javascript\nimport { send } from 'https://cdn.jsdelivr.net/npm/@jcubic/wayne';\n\nconst channel = new BroadcastChannel('__rpc__');\n\napp.get('/rpc/{name}/*', async (req, res) =\u003e {\n    const args = req.params[0].split('/');\n    const method = req.params.name;\n    try {\n        const data = await send(channel, method, args);\n        res.json(data);\n    } catch(e) {\n        res.text(e.message);\n    }\n});\n```\n\nand in the main thread, you create the other side of the channel and the remote methods:\n\n```javascript\nimport { rpc } from 'https://cdn.jsdelivr.net/npm/@jcubic/wayne';\n\nconst channel = new BroadcastChannel('__rpc__');\n\nrpc(channel, {\n    ping: function() {\n        return 'pong';\n    },\n    sin: function(x) {\n        return Math.sin(x);\n    },\n    random: function() {\n        return Math.random();\n    },\n    json: function() {\n        return fetch('https://api.npoint.io/8c7cc24b3fd405b775ce').then(res =\u003e res.json());\n    }\n});\n```\n\nWhen you send a request `/rpc/ping` you will get a response from `methods.ping` function.\n\n```javascript\nfetch('./rpc/ping')\n  .then(res =\u003e res.text())\n  .then(text =\u003e {\n     console.log({ text });\n  });\n```\n\nWith this setup, you can create new functions/methods that will map to HTTP requests.\n\nThe demo below uses random requests:\n\n```javascript\nlet index = 0;\nconst requests = [\n    './rpc/ping/',\n    './rpc/json/',\n    './rpc/random/',\n    './rpc/sin/10'\n];\n\nrpc.addEventListener('click', () =\u003e {\n    get(random_request() );\n});\n\nfunction random_request() {\n    const next_index = index++ % requests.length;\n    return requests[next_index];\n}\n```\n\n\u003e [!WARNING]\n\u003e Above code will only work when main page is open, so you can't navigate away. It may freeze the browser,\n\u003e becasue it will not able to find other side of the BroadcastChannel. If you want to send data persistently,\n\u003e it's better to use IndexedDB to share data between main thread and Service Worker. You can use library like\n\u003e [idb-keyval](https://github.com/jakearchibald/idb-keyval) by Jake Archibald for this.\n\u003e \n\u003e But it will work when you use RPC with fetch (like in above example) that originate from the same page\n\u003e that create the channel.\n\n### Server-Sent Events\n\n[Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)\nis the way to stream data in the browser.  It's a native browser implementation of Long\nPolling. Here is an example of how to use SSE with Wayne:\n\n**Service Worker**\n\n```javascript\napp.get('/sse', function(req, res) {\n  const stream = res.sse({\n    onClose() {\n      clearInterval(timerId);\n    }\n  });\n  var timerId = setInterval(function() {\n    const now = (new Date()).toString();\n    stream.send({ data: now });\n  }, 1000);\n});\n```\n\n**Main tread**\n\n```javascript\nlet see_source;\n\nsse_start.addEventListener('click', () =\u003e {\n    see_source = new EventSource(\"./sse\");\n    see_source.onmessage = event =\u003e {\n        console.log(event.data);\n    };\n});\n\nsse_stop.addEventListener('click', () =\u003e {\n    if (see_source) {\n        see_source.close();\n        see_source = null;\n    }\n});\n```\n\n### 3rd party URL\nService Worker allows intercepting everything that origineted from the page that has service worker\ninclding URLs from different origin. From version 0.15.0 Wayne allow to inrecept such URLs. You just use\nfull URL instead of just path as a route:\n\n```javascript\napp.get(`https://github.com/{user}/{repo}`, (req, res) =\u003e {\n    res.text(`Sorry, you can't fetch ${req.params.user} repo named ${req.params.repo}`);\n});\n```\n\nIf you run fetch in browser:\n\n```javascript\nawait fetch('https://github.com/jcubic/wayne').then(res =\u003e res.text());\n```\n\nyou will get the string:\n\n```javascript\n\"Sorry, you can't fetch jcubic repo named wayne\"\n```\n\nIf you want to restrict the request to only same origin you can do this with filter option:\n\n```javascript\nconst app = new Wayne({\n    filter: req =\u003e {\n        const url = new URL(req.url);\n        return url.host === self.location.host;\n    }\n});\n```\n\nYou can also use middleware for this\n\n```javascript\napp.use((req, res, next) =\u003e {\n    const url = new URL(req.url);\n    if (url.origin === location.origin) {\n        next();\n    } else {\n        res.fetch(req);\n    }\n});\n```\n\nWith middleware you can also intercept all requests to remote servers and block those that are not\non the list:\n\n```javascript\nfunction is_chrome_extension(url) {\n  return url.match(/chrome-extension:/);\n}\n\nfunction is_valid_request(url) {\n  if (is_chrome_extension(url)) {\n    return true;\n  }\n  const valid_hosts = ['localhost', 'cdn.jsdelivr.net'];\n  const host = new URL(url).host;\n\n  return valid_hosts.includes(host);\n}\n\napp.use((req, res, next) =\u003e {\n    const url = new URL(req.url);\n    if (url.origin === location.origin) {\n        next();\n    } else {\n        if (is_valid_request(req.url)) {\n          res.fetch(req);\n        } else {\n          res.html(`\u003c!DOCTYPE HTML\u003e\u003chtml\u003e\u003cbody\u003e\u003ch1\u003eThis request is blocked\u003c/h1\u003e\u003c/body\u003e\u003c/html\u003e`, { status: 403 });\n        }\n    }\n});\n```\n\nIf you want to set the valid URLS in your main script, you can save them in indexedDB. As with\nfilesystem, you can use [idb-keyval](https://github.com/jakearchibald/idb-keyval) by Jake Archibald,\nfor this.\n\n### Overwrite HTML code\n\n```javascript\napp.get('*', async (req, res) =\u003e {\n  const _res = await fetch(req);\n  const type = _res.headers.get('Content-Type');\n  let response;\n  if (type.match(/text\\/html/i)) {\n      const html = await _res.text();\n      response = html.replace(/\u003c\\/body\u003e/, '\u003cp\u003ePatched by Wayne\u003cp\u003e\u003c/body\u003e');\n  } else {\n      response = await _res.arrayBuffer();\n  }\n  res.send(response, { type });\n});\n```\n\nThis code will intercept every page and if it's HTML it will add HTML code at the end (before\nclosing body tag).\n\n### Using with ES Modules\nYou can intercept the import of ES Module with Wayne. Here is example:\n\n**Main tread**\n```html\n\u003cscript\u003e\nwindow.ready = navigator.serviceWorker.register('./sw.js', { scope: location.pathname })\n\u003c/script\u003e\n\u003cscript type=\"module\"\u003e\n// wait for Service Woker\nawait window.ready;\n// next tick delay is require for the worker to intitialize properly\nawait new Promise(resolve =\u003e setTimeout(resolve, 0));\n\n// static imports works only when you install and refresh the browser\n// they probbaly run just after the code is parsed\nconst { default: $ } = await import('./@jquery');\n\n$('body').css('background', 'rebeccapurple');\n\u003c/script\u003e\n```\n\n**Service Worker**\n\n```javascript\napp.get('*', (req, res) =\u003e {\n  const url = new URL(req.url);\n  const name = req.url.replace(/.*@/, '');\n  if (url.pathname.match(/\\+esm/)) {\n    res.fetch(`https://cdn.jsdelivr.net${url.pathname}`);\n  } else if (url.pathname.match(/@/)) {\n     if (name.match(/css/)) {\n        res.fetch(`https://cdn.jsdelivr.net/npm/${name}`);\n     } else {\n        res.fetch(`https://esm.run/${name}`);\n     }\n  } else {\n     res.fetch(req);\n  }\n});\n```\n\nThe code checks if the URL contain `@` in the path and redirect them to\n[https://esm.run](https://www.jsdelivr.com/esm). If the script import other scrips they usually look like this:\n\n```javascript\nimport require$$0 from\"/npm/jquery@3.7.1/+esm\"\n```\n\nAnd needs to be imported from jsDelivr, the same if you import CSS file.\nSee [example of loading jQuery Terminal](https://jcubic.github.io/wayne/esm/) where this code is used.\n\n### PWA\n\nTo use with PWA and cache you need to use a custom middleware:\n\n```javascript\nconst app = new Wayne();\n\napp.use(async (req, res, next) =\u003e {\n    const url = new URL(req.url);\n\n    const cache = await get_cache();\n    const cache_response = await cache.match(req);\n\n    if (cache_response) {\n        if (navigator.onLine) {\n            const net_response = await fetch(req);\n            cache.put(req, net_response.clone());\n            res.respond(net_response);\n        } else {\n            res.respond(cache_response);\n        }\n    } else {\n       next();\n    }\n});\n\nconst cache_url = [\n  '/'\n  /* add any files you want to cache here */\n];\n\nself.addEventListener('install', (event) =\u003e {\n   event.waitUntil(cache_all());\n});\n\nfunction get_cache() {\n    return caches.open('pwa-assets');\n}\n\nasync function cache_all() {\n    const cache = await get_cache();\n    return cache.addAll(cache_url);\n}\n```\n\nThis approach is recommended by the answer to this StackOverflow question:\n\n* [Service-worker force update of new assets](https://stackoverflow.com/a/33266296/387194)\n\nIt always fetch a new value of the assets when there is internet connection and serve cached value\nwhen user is offline. When there are no cached value it do default action (which can be normal\nfetch outside of cache or Wayne route).\n\nThis is just example of using cache with Wayne middleware, you can use different caching approach.\n\n## First load\n\nAccording to the spec, the default behavior of the Service Worker is to control the HTTP requests\nafter reloading the page. To make the SW always in control use this code in your SW:\n\n```javascript\n// take control of uncontrolled clients on first load\n// ref: https://web.dev/service-worker-lifecycle/#clientsclaim\nself.addEventListener('activate', (event) =\u003e {\n  event.waitUntil(clients.claim());\n});\n```\n\nYou can read more in the article [The service worker lifecycle](https://web.dev/service-worker-lifecycle/)\nby [Jake Archibald](https://twitter.com/jaffathecake).\n\n## Demo\n* [All in one demo](https://jcubic.github.io/wayne/demo).\n* Proof of Concept of [ReactJS application inside Service Worker](https://jcubic.github.io/wayne/jsx/public/).\n* [Filesystem demo](https://jcubic.github.io/wayne/fs/).\n* [Server-Sent Events Proxy demo](https://jcubic.github.io/wayne/sse/).\n* [Offline demo](https://jcubic.github.io/wayne/offline/).\n* [Download demo](https://jcubic.github.io/wayne/download/).\n* [Source Code Syntax highlight demo](https://jcubic.github.io/wayne/code/).\n* [Using with React and Vite](https://jcubic.github.io/react-wayne-auth/)\n* [PWA/Cache](https://jcubic.github.io/wayne/pwa/)\n\nThe source code for the demos can be found\n[in the docs' directory at the gh-pages branch](https://github.com/jcubic/wayne/tree/gh-pages/docs).\n\n## API reference\n\nWayne constrcutor accept object with options:\n* `filter` - a function that is called with request object, and should return false if the request\n  should not be proxied with Service Worker.\n\nWayne object has those methods that correspond to HTTP methods\n* `get`\n* `post`\n* `put`\n* `delete`\n* `patch`\n\nEach method accepts a URL with markers inside curly brackets, those markers will be available from\n**Request.params** object.  The request object is the browser native object of a given request see\n[MDN for details](https://developer.mozilla.org/en-US/docs/Web/API/Request). The only change to the\nnative API is that the object has property **params**.\n\nHere are a few most important Request properties:\n\n* `headers` - Headers object to get key/value pairs use `Object.fromEntires(req.headers.entries())`.\n* `method` - request method as a string.\n* `url` - string with full URL.\n* `referrer` - HTTP referer.\n* `arrayBuffer()` - Returns a promise that resolves with an [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) representation of the request body.\n* `blob()` - Returns a promise that resolves with a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) representation of the request body.\n* `formData()` - Returns a promise that resolves with a [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) representation of the request body.\n* `json()` - Returns a promise that resolves with the result of parsing the request body as [JSON](https://developer.mozilla.org/en-US/docs/Web/API/Request/json).\n* `text()` - Returns a promise that resolves with a text representation of the request body.\n\n**Response** object is an instance of `HTTPResponse` those have methods:\n\n* `html()`\n* `json()`\n* `text()`\n* `send()`\n\neach of those methods accepts string as the first argument. The second argument is options:\n\n* `headers` - any headers as key-value pairs or you can pass [Headers object](https://developer.mozilla.org/en-US/docs/Web/API/Headers).\n* `statusText` - The status message associated with the status code, e.g., OK.\n* `status` - The status code for the response, e.g., 200.\n* `type` - Content-Type of the response (MIME).\n\nAdditional methods:\n* `redirect()` - accept URL or optional first argument that is the number of HTTP code\n* `respond(res)` - accept Response object in case you want to use a different [Response object](https://developer.mozilla.org/en-US/docs/Web/API/Response).\n* `sse([options])` - function creates Server-Sent Event stream, the return object has a method `send` that sends a new event.\n* `fetch(url | Request)` - method will send a normal HTTP request to the server and return the result to the client. You can use the default Request object from the route.\n* `download(data, { filename })` - a method that can be used to trigger file download. The data can be a `string` or `arrayBuffer` you can use native fetch API and call `await res.text()` or `await res.arrayBuffer()` and pass the result as data.\n\nThe application also has middleware as in Express.js\n\n* `use(function(err,  req, res, next) {})` 4 parameters it's an error handler\n* `use(function(req, res, next) {})` 3 parameters it's a middleware\n\nAdditional exported functions:\n* `FileSystem({ path: string, fs: \u003cFS Module\u003e, prefix: string })` - a function that creates a middleware for the file system. You should use FS that supports Service Worker like the one that uses [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) e.g. [BrowserFS](https://github.com/jvilk/BrowserFS) or [LightingFS](https://github.com/isomorphic-git/lightning-fs).\n* `rpc(channel, object)` - a function that should be used in the main thread that creates an RPC-like mechanism. The first argument is an instance of a broadcast channel and the second is an object with remote functions.\n* `send(channel, method: string, args: any[])` - function sends remote procedure to the main thread.\n\n## Story\n\nThe idea of using a Service worker to serve pure in-browser HTTP requests has a long history. I\nfirst used this technique for my [Git Web Terminal](https://git-terminal.js.org/) and described the\nusage of it in the article from 2018:\n[How to create Web Server in Browser](https://jcubic.wordpress.com/2018/05/23/how-to-create-web-server-from-browser/).\nIn June 2022, I came up with a cool new way of using this technique. While creating PoC for the\narticle I'm going to write (will update this story when ready), I realized that I can extract all\nthe logic of creating those fake HTTP requests into a library. This is how Wayne was born.\n\nThe name of the library was inspired by the scene in\n[Wayne's World 2](https://en.wikipedia.org/wiki/Wayne's_World_2) in which Wayne dresses up as a construction\nworker.\n\n[![Watch the video](https://github.com/jcubic/wayne/blob/master/assets/wayne's-world-screen-capture.png?raw=true)](https://youtu.be/89W-lCTFT2o)\n\nI highly recommend both movies if you haven't seen them already.\n\n## Contribution\nIf you have any ideas for an improvement, don't hesitate to create an issue.\nCode contributions are also welcome.\n\n**Working on your first Pull Request?** You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://kcd.im/pull-request)\n\n## Article about or mention Wayne\n* [Comparing Wayne.js with Express.js for service worker routing](https://blog.logrocket.com/comparing-wayne-js-express-js-service-worker-routing/)\n* [Hack to Run React Application inside Service Worker](https://dev.to/jcubic/hack-to-run-react-application-inside-service-worker-4p2f)\n* [How to create Web Server in Browser](https://itnext.io/how-to-create-web-server-in-browser-ffaa371d2b53)\n* [Hack for Syntax Highlighting of Source Code](https://jakub.jankiewicz.org/blog/display-source-files-in-color/)\n* [How to Create a REST API Without a Server](https://www.freecodecamp.org/news/how-to-create-a-rest-api-without-a-server/)\n\n## Press\n* [Architecture Weekly](https://github.com/oskardudycz/ArchitectureWeekly)\n* [JavaScript Weekly](https://javascriptweekly.com/issues/597)\n* [Web Tools Weekly](https://webtoolsweekly.com/archives/issue-471/)\n* [Vue.js Developers Newsletter](https://vuejsdevelopers.com/newsletter/issue/265/)\n\n## Acknowledge\n* Part of the content of this README was based on text from [MDN](https://developer.mozilla.org/).\n* Logo uses an illustration from [OpenClipart](https://openclipart.org/detail/320906/hard-hat).\n* This article was helpful [SSEGWSW: Server-Sent Events Gateway by Service Workers](https://medium.com/its-tinkoff/ssegwsw-server-sent-events-gateway-by-service-workers-6212c1c55184)\n\n## License\n\nReleased with [MIT](http://opensource.org/licenses/MIT) license\u003cbr/\u003e\nCopyright (c) 2022-2024 [Jakub T. Jankiewicz](https://jcubic.pl/me)\n","funding_links":["https://github.com/sponsors/jcubic","https://ko-fi.com/jcubic","https://www.paypal.me/jcubic"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcubic%2Fwayne","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjcubic%2Fwayne","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcubic%2Fwayne/lists"}