GithubHelp home page GithubHelp logo

jcubic / wayne Goto Github PK

View Code? Open in Web Editor NEW
548.0 13.0 27.0 2.81 MB

Service Worker Routing library for in browser HTTP requests

Home Page: https://jcubic.github.io/wayne

License: MIT License

JavaScript 96.67% Makefile 3.33%
fake http http-server http-server-library service-worker javascript promise routing wayne filesystem

wayne's People

Contributors

claudiosdc avatar jcubic avatar karlhorky avatar notangelmario avatar r8bhavneet avatar wenqingl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wayne's Issues

Create POC for JSX => HTML

The task is to test if you can have React application (or a different library) that generates static HTML files.

The application should live in Service Worker and HTTP requests should return different part of the application.

This is just to test if something like this is possible.

How can I custom root_url to normalize_url normally?

With the following line:

const root_url = location.pathname.replace(/\/[^\/]+$/, '');

my root_url could be '/s', which lead to a failed route match for my request starts with '/s', but I know my root url should be '/', how can I fix this problem?

Create PoC for WebRTC chat

Create Proof of concept of WebRTC chat (two people only) that uses GET request and SSE and the rest is in Service Worker.

Body Parser middleware

This is a basic implementation of the body parser:

async function stream_to_string(stream) {
    const result = [];
    const decoder = new TextDecoder();
    for await (const chunk of stream) {
      result.push(decoder.decode(chunk, { stream: true }));
    }
    return result.join('');
}

async function stream_to_uint8Array(stream) {
    return uint8_concat(read_chunks(stream));
}

function read_chunks(stream) {
    const chunks = [];
    for await (const chunk of stream) {
      chunks.push(chunk);
    }
    return chunks;
}

function uint8_concat(chunks) {
    const len = chunks_len(chunks);
    const result = new Uint8Array();
    let offset = 0;
    for (const chunk of chunks) {
      result.set(chunk, offset);
      offset += chunk.length;
    }
    return result;
}

function chunks_len(chunks) {
    return chunks.reduce((acc, chunk) => acc + chunk.length, 0);
}

app.use(function(req, res, next) {
    req.json = async function() {
        return JSON.parse(await stream_to_string(req.body));
    };
    req.raw = function() {
        return stream_to_uint8Array(req.body);
    };
    req.text = function() {
        return stream_to_string(req.body);
    };
    next();
});

Add support for glob path

express.js allow to use this syntax:

app.get('/__fs__/*', function(req, res) {
  res.send(req.params[0]);
});

Wayne should also support this syntax. Found why investigating #3

Waiting for promise inside get route

I'm building an app that includes a service worker (that I'm trying to manage with Wayne) and a dedicated worker (that runs a lightning-fs database). The dedicated worker must communicate with the main thread and with the service worker via broadcast channels. Among many other things, the application opens new tabs that get content fetched from the database by the dedicated worker and served by the service worker.

Here's an example of how a non-Wayne service worker would fetch data from the database through the dedicated worker in my app:

const channel = new BroadcastChannel('my-channel');

self.addEventListener('fetch', event => {
    if (event.request.url.endsWith('/yo')) {
        event.respondWith(
            makeResponse()
        )
    }
});


const makeResponse = async function() {
    let x = await getMessage();
    let y = await new Response(x, {
        headers: {
            'Content-Type': 'text/html'
        }
    });
    return y
}

async function getMessage() {
    return new Promise((resolve) => {
        channel.addEventListener('message', (event) => {
            channel.removeEventListener('message', event.currentTarget);
            resolve(event.data);
        });
        channel.postMessage('/core/');
    });
}

I have code on the dedicated worker that responds to that message with the content of /core/ :

const channel = new BroadcastChannel('my-channel');

channel.onmessage = async function(event) {
      const ind = await fs.promises.readdir(event.data);
  channel.postMessage(ind);
}

This example works fine (at least as long the main page stays open, which is how the app is intended to be used). But when I try to fit this inside a Wayne app.get, I incur into problems. I tried formulating the code in different ways, but I've incurred into weird behavior (the most common one is the "/yo" page not loading at the first attempt, loading on reload, and then alternating between not-loading&loading at every reload; I've noticed that if I wait a few seconds after a failed reload, the next reload is a failure too: a failed reload guarantees a successful reload at the next attempt only when the next attempt happens immediately after the failure).

I've tried a few things, including experimenting with code that should make the page load forever (something that's pretty easy to accoplish with a normal bootstrapped service worker), but it didn't work inside app.get, the page loads immediately and I get either a reload of "./" or a successful load of "./yo" (as I said, often alternating between the two). This made me wonder if the library abstracts away some of the control one would usually have over the http response?

I've looked inside the Wayne code, and while I get a general idea of it (besides including path manipulation with regex, you register routes in the Wayne object that associate a path with a function, and there's a "fetch" event listener that returns a blob with metadata based on the res method called), I deemed it more efficient to ask here instead of trying to reconstruct the entire code line by line (that could still be my last option, if you tell me that for some reason Wayne does not include ways to wait for the message from the dedicated worker, but I still decided to use it as a library, I'd might need to edit it). I suspect there's probably a fairly simple solution to this?

Create a website

There should be a website and documentation.

This is time-consuming, but I think it will be a good first issue.

Add middlewere

add overloaded use API function similar to express.js:

app.use((req, res, next) => {

});

and

app.use((err, req, res, next) => {

});

This will help in #10

Add a way to easily call fetch inside route

This came out in #20. There is a need for simple pass thru mechanism.

You can probably use something like this:

app.get('/foo/*', (req, res) => {
    if (req.params[0].match(/.txt$/)) {
        fs.promises.readFile(req.params[0], 'utf8').then(text => {
            res.text(text);
        });
    } else {
        fetch(req).then(text => res.text(text));
    }
});

This should be tested if it works and maybe simplified.

bug: "not found" handler intercepts already matched routes

I have a handler defined for my index page route as below:

app.get('/', function(req, res) {
  res.html(`<h1>Index Page</h1>`);
});

Then I want to add handler to show "not found" page for all other routes

app.get('*', function(req, res) {
  const accept = req.headers.get('Accept');
  if (accept.match(/text\/html/)) {
    res.html(render(`<h1>Page not found</h1>`));
  } else {
    res.fetch(req);
  }
});

Unfortunately when added this wildcard handler following after index route, then index route is not working anymore as expected and instead it shows "not found" message for defined index route as well.

Error retrieving binary resources

When using the FileSystem middleware and a binary resource, like an image file for example, is retrieved, the returned data is broken.

Add route capture handler in fetch listener

What is it

There is a need for me to not proxy all routes by fetch listener, if a route capture handler is provided outside event.respondWith, that could be helpful.

self.addEventListener("fetch", (event) => {
  if(routeCapture(event.request)) {
    event.respondWith(promise.catch(() => {}));
  }
});

routeCapture could be like this:

({ request }) => isMatch(request)

In workbox, a capture is like:

RegExp | string | RouteMatchCallback | Route

Why I want this

In current case, a normal route of request that not registered by app could resolve by fetch as following:

fetch(event.request).then(resolve).catch(reject);

In my experience, a fetch proxy by service worker could lead to some unknown problems that's what concerns me. For example, the referer header of request could be modified to sw.js if it's proxy by fetch.

How to write a POST endpoint would really help

Ciao,

I'm enjoying your little library but still cannot wrap my mind around an example of using a POST endpoint. You provided plenty of GET examples, and they helped, but POST and how to handle the body would really help.

I'm working on it, when (if) I'll succeed I'll provide a PR!

Best,
rosdec

Add redirects

In orinal article code there is:

resolve(Response.redirect(url + '/', 301));

The library should be able to do that.

Possible API:

app.get('/something', (req, res) => {
  res.redirect(`${req.url}/`, 301);
});

TODO:

  • Test infinite redirects
  • Test redirects for different domain/scope

Support ignore case sensitive when match route

What will be like?

app.get('/message', function(req, res) {
  res.text('Lorem Ipsum');
}, { ignoreCaseSensitive: true });

when ignoreCaseSensitive param provided, we can match routes like /message or /MESSAGE that case not sensitive any more.

Why we need this?

Sometime we want to match some requests that url case pattern not sure, but we know case not sensitive on service. So we don't
need to register paths for all different cases.

/restapi/v1/getUsers
/restapi/v1/getusers

Unicode filenames

While working on a demo to validate that the #26 fix works.

Found that the Unicode filenames don't work in the demo.

Przechwycenie obrazu ekranu_2023-06-02_16-34-35

SSE should support event names

I am using wayne along with htmx and I would like elements to subscribe for specific events as in docs

<div hx-sse="connect:/sse">
  <div hx-sse="swap:eventName1">
    ...
  </div>
  <div hx-sse="swap:eventName2">
    ...
  </div>
</div>

then accordingly broadcast named events

app.get('/sse', function(req, res) {
//...
      stream.send({ name: 'eventName1', data: '' } );
});

Add parenetneses and question mark syntax

There is should be a way to have what's you can in express.js:

'/login(/:token)?'

with the Wayne syntax:

'/login(/{token})?'

Add support for:

  • parentheses
  • Optionals
  • numerics \d
  • plus quantificator
  • dot operator

Add convenient passthru method

There is a need for the API that will just use the same request but to the server. This similar to #22 that is not enough.

Consider code of syntax highlighting:

app.get('*', async function(req, res) {
  const url = new URL(req.url);
  const extension = path.extname(url.pathname).substring(1);
  const language = language_map[extension];
  const accept = req.headers.get('Accept');
  if (language && Prism.languages[language] && accept.match(/text\/html/)) {
      const code = await fetch(req.url).then(res => res.text());
      const grammar = Prism.languages[language];
      const tokens = Prism.tokenize(code, grammar);
      const output = Prism.Token.stringify(tokens, language);
      res.html(`${style}\n<pre><code class="language-${language}">${output}</code></pre>`);
  } else {
      const fetch_res = await fetch(req.url);
      res._resolve(fetch_res);
  }
});

from gh-pages branch: sw.js.

So there is a need to use hidden _resolve method that accept response object.

Using without ES Modules

It seems that ES Modules in Service Worker are not widely supported. So for this case, there is a need to generate a UMD file, while we are at it we can also transpile to a more widely supported ECMAScript.

  • Add transpiler to UMD
  • Add support for more widely supported ECMAScript
  • Update documentation

app.get without res method

Is there a proper way to call app.get without including a res method? Basically using the route just to execute arbitrary code. This seemed easier before the last version: now every fetch event seems to cause loading?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.