Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
    Daniel Ashcraft
    @dashcraft
    I’ll get that imported in! Thanks!
    Peter Hoddie
    @phoddie
    I posted some notes in an issue in your repository. I hope that's OK. If anything is unclear, just let me know. dashcraft/express-js-mod#2
    Daniel Ashcraft
    @dashcraft
    Absolutely
    I haven’t used Object.freeze but maybe a handful of times to enforce some semblance of immutability in react, that’s pretty neat about using it for storing in ROM
    Daniel Ashcraft
    @dashcraft

    Did some work with it today, got it working decent for posted data now and put/patch data. Took a lot of your suggestions and implanted them.

    Need to work on more in the future, but get/post json and text are my two main use cases and this drastically simplifies the process of listening to and responding to requests.

    POST: sensor.local/home?id=1

    raw json body: { "test" : "control"}

    {
        "test": "test",
        "request": {
            "state": "done",
            "headers": {
                "content-type": "application/json",
                "user-agent": "PostmanRuntime/7.28.0",
                "accept": "*/*",
                "cache-control": "no-cache",
                "postman-token": "83dae780-2ef5-491d-8a3d-b2ea217c042e",
                "host": "sensor.local",
                "accept-encoding": "gzip, deflate, br",
                "connection": "keep-alive",
                "content-length": "24"
            },
            "path": "/home",
            "httpMethod": "post",
            "data": {
                "test": "control"
            },
            "params": {
                "id": "1"
            },
            "rawQuery": "id=1",
            "location": "/home?id=1"
        }
    }

    This is about as easy as it gets when it comes to receiving data in the form of json or query params.
    Need to send files and other content types in the future,

    let express = new Express(Server)
    express.get('/home', function(req,res) {
        res.json({test: 'test', request: req})
    })
    
    express.post('/home', function(req,res) {
        res.json({test: 'test', request: req})
    })
    express.listen(80)
    Peter Hoddie
    @phoddie
    Looks straightforward to use. I see the update is up on your repository. I'll try give that a look later.
    FWIW - the use of headers above runs into the same runtime symbol problem I mentioend in my notes yesterday. A Map would be better. But, I suppose that's not API compatible with Express? If that's the case, maybe a workaround to minimize the problem by pre-defining the common headers names so that they are already baked into the firmware.
    Daniel Ashcraft
    @dashcraft
    I need to work on a way to be able to print the headers when returned, but i can find a work around for that.
    for some reason the Map got stripped when i sent the string json response, i need to take a look at how it's implemented
    or figure out a way to parse them when returned
    shouldn't be too hard

    There's some other things i need to do as well, like defining params in the path such as /home/1
    and also setting outbound headers
    res.header('something', "value")
    sending static HTML
    res.send('index.html')

    etc

    Peter Hoddie
    @phoddie
    For send, the obvious thing to do is send a file. But, actually, we usually build without a file system. Instead we have resources which are just named pieces of data. So, it could work with the same API (e.g. res.send) though perhaps you would want some prefix to indicate "this is a resource" (res.send("resource:index.html") so you can also unambiguously use files too.
    Daniel Ashcraft
    @dashcraft
    I need to also setup something to support an event stream strategy, to write text to an open socket as long as its active
    Daniel Ashcraft
    @dashcraft
    Is there a package registry for moddable? Seems like npm would be cumbersome.
    Peter Hoddie
    @phoddie
    Good question. There isn't yet, though there's been discussion. We are open to PRs to the Moddable SDK. There are details in contributing. The $MODDABLE/contributed directory might be a fit for your Express.js work.
    Daniel Ashcraft
    @dashcraft
    Gotcha, that sounds good, if there were a way to let people easily pull in moddable compatible packages from a somewhat reputable
    package manager, that'd be pretty sweet. There was a BME280 sensor package I used recently that would be a good fit for that with a little
    cleanup. Both the power and downfall of npm/yarn is how easy it is for people to install packages and manage dependencies.
    Right now, for instance, i'm rolling an event emitter class. I'm not sure if that's already been done to moddable's spec, but it's possible it has been and I'm
    rewriting the wheel. Which is okay, but ideally others could benefit from standing on the backs of giants
    Peter Hoddie
    @phoddie
    No argument. For now, that is manageable in our repository. That won't scale indefinitely.
    Daniel Ashcraft
    @dashcraft
    Not going to lie, i don't know many package managers that don't get a lot of grief from their users haha
    Daniel Ashcraft
    @dashcraft
    Simple event emitter following some others in naming conventions. This is very low brow. Nothing fancy.
    https://github.com/dashcraft/event-emitter-mod
    Peter Hoddie
    @phoddie

    It uses Map! Since Map.prototype.get returns undefined if there's no entry, instead of the exists check here...

        listeners(event) {
            let exists = this.exists(event);
            if (!exists)
                return [];
            return this.#events.get(event);
        }

    ...could you just do this?

    listeners(event) {
        return this.#events.get(event) ?? [];
    }
    (Just did a quick read... need to try running to really understand)
    Daniel Ashcraft
    @dashcraft
    Nice catch! I’ll take a look and update the interface.
    Peter Hoddie
    @phoddie

    I have a style question. For on you implement it this way...

    on = (event: string, listener: Function) => this.addListener(event, listener)

    ...instead of the more common class function form:

    on(event: string, listener: Function) {
        this.addListener(event, listener);
    }

    There are some subtle differences between the two forms. What's the reason to use the arrow function for on but not elsewhere?

    Daniel Ashcraft
    @dashcraft

    Good question, in my mind i was thinking about arrow functions auto binding to the function scope. So in this case I didn't want to
    run into interesting edge cases around function scope, whereas the arrow function will always bind this from the enclosing scope.

    I'm not sure if it's a solution in search of a problem, it's really just habit from dealing with bind in react js and handling higher order function scope issues.

    Peter Hoddie
    @phoddie

    I don't know enough about React to understand its habits. ;) I don't see how on benefits from this approach over any other call, unless it is common to write:

    const on = emitter.on;
    on("click", () => ...);
    on("key", () => ...);
    on("sensor", () => ...);

    Or, if there's some reason that developers commonly retarget this, as in emitter.on.call(somethingElse, "click", () =>...). But, if they do that, when on calls addListener it will throw on the attempt to read the private field #events.

    Otherwise, it is more efficient (memory use and execution speed) to write it as a regular public class function. That avoids creating an arrow function at runtime for each instance of the emitter.

    Daniel Ashcraft
    @dashcraft
    Yeah, i'm sure there are places we could identify, that this would be problematic, I think a class method would be fine here and I'll do that instead of this.
    I'm sure as people start to use EventStream either via extension or direct usage, areas will popup that i can address at that time.
    Daniel Ashcraft
    @dashcraft
    Also, thank you @phoddie for the code reviews! Hopefully I can get up to speed on best practices here shortly!
    Peter Hoddie
    @phoddie
    Hey @bterlson, stepping through TypeScript generated code that uses private fields, I noticed that it is not generating code that uses ECMAScript private fields, but uses WeakMap. That works with XS (of course!) but isn't as efficient as our private fields implementation. Now that private fields are Stage 4, is this support in the plans for TypeScript? (I know it isn't your job.... but I thought you might know.)
    Peter Hoddie
    @phoddie

    Since TypeScript generates inefficient code for private fields (for the moment) and since the implementation of EventEmitter doesn't use TypeScript features beyond type checking, you can use JavaScript for the implementation and have a parallel *.d.ts file with the type declarations. That seems to work.

    declare module "event-emitter-mod" {
        class EventEmitter {
            on: (event: string, listener: Function) => Boolean | null | undefined;
            emit: (event:string, args?: any) => Error | Boolean;
            addListener: (event: string, listener: Function) => Boolean | null | undefined;
            removeListener: (event:string, listener: Function) => Boolean | null | undefined;
        }
    
        export {EventEmitter as default};
    }

    And

    export default class {
        #events = new Map();
        #maxListeners;
        constructor(options) {
            this.#maxListeners = options?.maxListeners ?? 10;
        }
    
        on(event, listener) {
            return this.addListener(event, listener);
        }
    
        emit(event, args) {
            const listeners = this.#events.get(event);
            if (!listeners) return false;
            listeners.forEach(triggerCallback(args))
            return true
        }
    
        addListener(event, listener) {
            const listeners = this.#events.get(event);
    
            if (!listeners) {
                this.#events.set(event, [listener]);
            } else {
                if ((listeners.length >= this.#maxListeners) || listeners.includes(listener))
                    return false;
    
                    listeners.push(listener)
            }
            return true
        }
    
        removeListener(event, listener) {
            const listeners = this.#events.get(event);
            if (!listeners)
                return false;
    
            const index = listeners.indexOf(listener);
            if (index < 0)
                return false;
    
            listeners.splice(index, 1);
            if (!listeners.length)
                this.#events.delete(event);
    
            return true
        }
    }
    
    function triggerCallback(args) {
        return cb => {
            try {
                cb(args)
            } catch(err) {
                throw new Error(err)
            }
        }
    }
    Daniel Ashcraft
    @dashcraft
    100000%
    I need to get back and add file support to the express like package I made. I’d like to get a decently performant web server up here in the next 30 days.
    Peter Hoddie
    @phoddie
    That would be excellent.
    Andy Carle
    @PrototypingAndy_twitter
    For my fellow Windows users: I'm really happy with the Windows Terminal Preview that is available on the Microsoft Store. It (finally) incorporates tabs for Command Prompt. And it allows you to set up a custom profile for Command Prompt that uses the settings we need for ESP32 builds. It's quite nice.
    Daniel Ashcraft
    @dashcraft
    Oooooh i was just commenting about how the multiple dev environments I have are starting to drive me a tad bonkers!
    Andy Carle
    @PrototypingAndy_twitter

    The profile settings I'm using (taken from the settings.json file for the Terminal Preview app) are:

                {
                    "altGrAliasing": true,
                    "antialiasingMode": "grayscale",
                    "closeOnExit": "graceful",
                    "colorScheme": "Campbell",
                    "commandline": "%comspec% /k \"\"%ProgramFiles(x86)%\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars32.bat\" && pushd %IDF_PATH% && \"%IDF_TOOLS_PATH%\\idf_cmd_init.bat\" \"%LOCALAPPDATA%\\Programs\\Python\\Python37\\\" \"%ProgramFiles%\\Git\\cmd\\\" && popd\"",
                    "cursorShape": "bar",
                    "fontFace": "Cascadia Mono",
                    "fontSize": 12,
                    "hidden": false,
                    "historySize": 9001,
                    "icon": "%MODDABLE%\\contributed\\window-display-moddable-one\\assets\\moddable-box.png",
                    "name": "Moddable Command Prompt",
                    "padding": "8, 8, 8, 8",
                    "snapOnInput": true,
                    "startingDirectory": "%MODDABLE%",
                    "useAcrylic": false
                }

    Paths likely need some per-system tweaking.

    Daniel Ashcraft
    @dashcraft
    I'll see what i can do!
    Hopefully with minimal tweaking i can get it setup and try it out
    Daniel Ashcraft
    @dashcraft
    Thanks for the shoutout on twitter!
    Peter Hoddie
    @phoddie
    You're very welcome. Looking forward to having more to tweet about. ;)
    Daniel Ashcraft
    @dashcraft
    you can now send resources using the
    sendResource method such as:
    let  app = new Express(Server)
    app.get('/home', function(req,res) {
        res.sendResource(new Resource("index.html"))
    })
    let port = 80 // 80 is the default port
    app.listen(port)
    Peter Hoddie
    @phoddie
    Nice. Resources are very convenient for bundling binary data into a project.
    Daniel Ashcraft
    @dashcraft
    I'll need to clean and write tests for a lot but it works.
    Peter Hoddie
    @phoddie
    The Resource constructor returns a Host Buffer which is very similar to a SharedArrayBuffer. Maybe sendResource should be a more general sendBuffer that could also accept an ArrayBuffer? Alternatively. sendResource could just accept a string ("index.html") and invoke new Resource for the caller to avoid every call to sendResource also including new Resource.
    Daniel Ashcraft
    @dashcraft
    Yeah, i need to write some generalized functions for handling buffer/chunked data. That's one of the primary cleanup items I have,
    same with parsers. I need to invest some type generalizing various parsers and investigating the body-parser libraries
    that exist for web.
    As of now, the server doesn't need to know what "kind" of buffer you're sending, you can dictate the content-type,
    this was my attempt to start generalizing that behavior.
    Peter Hoddie
    @phoddie
    Understood. I'm just reacting to your code snippet. I don't have a sense of what is good style for Express.
    Daniel Ashcraft
    @dashcraft
    It's got a few features that are going to give me a headache, mostly around middleware and their router, for
    separation of concerns. There are likely some performance shortcuts I'll need to make when it comes time.