More On Scripting In Dynamo-Browse
Making some more progress on the adding scripting of dynamo-browse. It helps being motivated to be able to write some scripts for myself, just to be able to make my own working life easier. So far the following things can be done within user scripts:
- Define new commands
- Display messages to, and get input from, the user
- Read the attributes of, and set the attributes of, the currently selected row (only string attributes at this stage)
- Run queries and get result sets, which can optionally be displayed to the user
Here’s a modified example of a script I’ve been working on that does most of what is listed above:
const db = require("audax:dynamo-browse");
db.session.registerCommand("cust", () => {
db.ui.prompt("Enter user ID: ").then((userId) => {
return db.session.query(`userId="${userId}"`);
}).then((resultSet) => {
db.session.currentResultSet = resultSet;
});
});
db.session.registerCommand("sub", () => {
let currentItem = db.session.selectedRow;
let currentUserId = currentItem.item["userId"];
if (!currentUserId) {
db.ui.print("No 'userId' on current record");
return;
}
db.session.query(`pk="ID#${currentUserId}"`, {
table: "billing-subscriptions"
}).then((rs) => {
db.session.currentResultSet = rs;
});
});
The process of loading this script is pretty basic at the moment. I’ve added a loadscript
command, which reads and executes a particular JavaScript file taken as an argument. Scripts can be loaded on startup by putting these loadscript
commands (along with any other commands) in a simple RC file that is read on launch.
I’m not sure how much I like this approach. I would like to put some structure around how these scripts are written and loaded, so that they’re not just running atop of one another. However, I don’t want too much structure in place either. To me, having the floor for getting a script written and executed too high is a little demotivating, and sort of goes against the ability to automate these one-off tasks you sometimes come across. That’s the downsides I see with IDEs like GoLand and, to a lesser degree, VS Code: they may have vibrant plugin ecosystems, but the minimum amount of effort to automate something is quite high, that it may not be worth the hassle.
A nice middleground I’m considering is to make it to have two levels of automation: one using JavaScript, and the other closer to something like Bash or TCL. The JavaScript would be akin to plugins, and would have a rich enough API and runtime to support a large number of sophisticated use-cases, with the cost being a fair degree of effort to get something usable. The Bash or TCL level would have a lower level of sophistication, but it would be quicker to do these one-off tasks that you can bang out and throw away. This would mean splitting the effort between the scripting API and command line language, which would fall on me to do, so I’m not sure if I’d go ahead with this (the command line “language” is little more than a glorified whitespace split at the moment).
In the meantime, I’ll focus on making sure the JavaScript environment is good enough to use. This will take some time and dog-fooding so they’ll be plenty to do.
I’ve also started sharing the tool with those that I work with, which is exciting. I’ve yet to get any feedback from them yet, but they did seem quite interested when they saw me using it. I said this before, but I’ll say it again: I’m just glad that I put a bit of polish into the first release and got the website up with a link I can share with them. I guess this is something to remember for next time: what you’re working on doesn’t need to be complete, but it helps that it’s in a state that is easy enough to share with others.
Currently enjoying the sound of an eastern rosella singing it’s heart out, helping to make a long tiring week just that little bit easier to deal with.
It wasn’t that long ago where a Google search would return useful results that were not found by either Ecosia or DuckDuckGo. That’s really not happening as often nowadays. In fact, if Ecosia/DDG cannot find anything, there’s very little reason to bother trying Google at all.
Trying to find which countries require 3D Secure for online payments is annoying. Each website only list two or three countries as examples: “3DS is required in some countries like X and Y…”, and do not link to any sources. I need to do like a set union to get the full list.
For the past several weeks, these two have been foraging in front of my place. They’re quite skittish, and will fly off when anyone approaches (it’s difficult to get a picture of them). But after a while, they’ll always return to the same spot.

Evening walk, with the wattles in full bloom.
Kyneton Botanical Gardens
Went to Kyneton with Mum and Dad today. While they went off for a bike ride, I had the opportunity to go for a walk around the botanical gardens. This was my first time there, and although the gardens themselves were not very big, it was still a pleasant experience. Here are some photos I took of that visit.
This was followed by lunch at Little Swallow Cafe. The place was quite busy — I suspect that their reputation is such that it would be busy most of the time — but the food was very nice. We then went for a short walk around Kyneton and then drove to Malmsbury for a coffee and Devonshire tea.
It was a little cold but the rain held off. All in all, a good day for it.
Kyneton Railway Station.



Work was the reason I was unable to go for an afternoon walk last two Fridays. The wet weather was not going to be the reason why I was unable go for one today.

This week’s earworm: The Songs Of Distant Earth by Mike Oldfield 🎵
Not necessarily a new one in my rotation, but I’ve been listening to it non-stop all week.
In this new work-from-home world we now inhabit, it’s coming to the point where my productivity is directly proportional to how many good podcasts are available today.
DynamoDB JSON Attribute Types Quick Reference
Because apparently it’s too difficult for AWS to provide an easy way to find this information.
Atomic Types
Type | JSON | Value |
---|---|---|
Binary | B |
String value containing the Base64-encoded binary data. |
Boolean | BOOL |
Either true or false |
String | S |
String value |
Number | N |
String with the numerical value |
Null | NULL |
Should always be true |
Collection Types
Type | JSON | Value |
---|---|---|
List | L |
A JSON array, with each element being an attribute with a type. |
Map | M |
A JSON object, with the keys being the map keys, and the values being an attribute with a type. |
Set Types
Type | JSON | Value |
---|---|---|
Binary Set | BS |
A JSON array of Base-64-encoded binary data. |
Number Set | NS |
A JSON array of string with the numerical values. |
String Set | SS |
A JSON array of strings. |
Sources:
- AttributeValue API documentation: this the definitive guide from AWS.
- DynamoDB Item size and format: more info about the particulars of each attribute type.
Scripting In Dynamo-Browse
Making some progress with adding scripting capabilities to dynamo-browse. I’ve selected a pretty full-featured JS interpreter called goja. It has full support for ECMAScript 5.1 and some pretty good coverage of ES6 as well. It also has an event loop, which means that features such as setTimeout
and promises come out of the box, which is nice. Best of all, it’s written in Go, meaning a single memory space and shared garbage collector.
I’ve started working the extension interface. I came up with a bit of a draft during the weekend and now just implementing the various bindings. I don’t want to go too fast here. I’m just thinking about all the cautionary tales from those that were responsible for making APIs, and introducing mistakes that they had to live with. I imagine one way to avoid this is to play with the extension API for as long as it make sense, smoothing out any rough edges when I find them.
It’s still early days, but here are some of the test scripts I’ve been writing:
// Define a new "hello" command which will ask for your name and
// print it at the bottom.
const dynamobrowse = require("audax:dynamo-browse");
dynamobrowse.session.registerCommand("hello", () => {
dynamobrowse.ui.prompt("What is your name? ").then((name) => {
dynamobrowse.ui.alert("Hello, " + name);
});
});
The prompt
method here asks the user for a line of text, and will return the user’s input. It acts a bit like the prompt function in the browser, except that it uses promises. Unfortunately, Goja does not yet support async
/await
so that means that anonymous functions will need to do for the time being (at least Goja supports arrow functions).
Here’s another script, which prints out the name of the table, and set the value of the address
field on the first row:
const dynamobrowse = require("audax:dynamo-browse");
dynamobrowse.session.registerCommand("bla", () => {
let rs = dynamobrowse.session.currentResultSet;
let tableName = rs.table.name;
dynamobrowse.ui.alert("Current table name = " + tableName);
rs.rows[0].item.address = "123 Fake St.";
});
The rows
array, and item
object, makes use of DynamicArray and DyanmicObject, which is a nice feature in Goja in which you do not need to set the elements in the array, or fields on the object explicitly. Instead, when a field or element is looked up, Goja will effectively call a method on a Go interface with the field name or element index as a parameter. This is quite handy, particularly as it cuts down on memory copies.
These scripts work but they do require some improvements. For example, some of these names could possibly be shortened, just to reduce the amount of typing involved. They also need a lot of testing: I have no idea if the updated address in the example above will be saved properly.
Even so, I’m quite happy about how quickly this is coming along. Hopefully soon I’ll be in a position to write the test script I’m planning for work.
Also, I got my first beta user. Someone at work saw me use dynamo-browse today and asked where he could get a copy. I’m just so glad I got the website finished in time.
Coming up with a naming convention for things like metadata keys, customer HTTP headers, or log tags; and not writing it down and sharing it with others, is a mistake I’m paying for in spades at the moment.
A little while ago, when we were setting up Stripe and it came time to decide how metadata keys should look, I thought it would be enough to come up with a style in my head (e.g. snake case with a particular prefix) and simply start using it. I guess I was expecting that others would follow it, simply because they would be motivated to maintain the observed style, with the keys I were defining being a bit of an example here.
Well, I now know that not only did this not happen, but when someone does want to know how these names should look (and I include myself here) there’s no definitive guide available to refer to. The result: no convention at all. Different cases, keys without a prefix, etc. Not be a huge problem, but being unable to rely on a convention does mean more cognitive load when it comes time to figuring out how a name is expected to be formed. Plus, it just looks untidy.
I guess it would have been a good idea to document it somewhere. Doing this is not being overly specific or dictating convention from above. It doesn’t have to be as elaborate as an IETF standards: just a few paragraphs and examples that is accessible to everyone. And if others disagree with the proposed convention, then work it through until everyone is happy with it.
I’ll try to remember this for next time.
P.S. More services really need to support user-defined metadata fields like Stripe does.
I discovered Goomics by Manu Cornet this morning after reading this Ars Technica article. There are some pretty amusing comics there about “life at Google.” Here’s a few that I particuarily liked:
Credit: Manu Cornet
I started writing a comment in GitLab today for a pull request I was working on. I left the comment unfinished to do something else. I was planning to finish it when I returned, but before I could do that, my laptop crashed and I feared that I completely lost it.
Fortunately, when I rebooted, the comment remained as it was in the text-box where I left it.
I don’t know if this was GitLab or Vivaldi or something else. But to whoever program that behaviour: thank you! You saved me 5-10 minutes going back through my thoughts and retyping it (I spent that time writing up this post 😄).
A little hard to get through my regular Sunday routine today. I’ll do my best to keep at it though. It won’t solve all my troubles but sometimes just doing what you can to feel a little better will have to do.
Useful tip I learnt today about screenshots on MacOS: you can take a screenshot of a window without the drop shadow by holding down the Option key when clicking the mouse.
I wish I looked this up sooner, and not just turn to ImageMagick to try and remove the shadow myself.
Release Preparation & Next Steps
Finally finished the website for the Audax toolset and cut an initial release (v0.0.2). I’ve also managed to get binary releases for Linux and Windows made. I’ve started to work on binary releases for MacOS with the intention of distributing it via Homebrew. This is completely new to me so I’m not sure what progress I’ve made so far: probably nothing. For one thing, I’ll need a separate machine to test with, since I’ve just been installing them from source on the machine’s I’ve been using. Code signing is going to be another thing that will be fun to deal with.
I’ll slip that into a bit of a background task for the moment. For now, I’d like to start work on a feature for “dynamo-browse” that I’ve been thinking of for a while: the ability for users to extend it with scripts.
Some might argue that it may be a little early for this feature, but I’m motivated to do it now as I have a personal need for this, which is navigating amongst a bunch of DynamoDB tables that all store related data. This is all work related, so can’t say too much here. But if I were just to say that doing this now is a little annoying. It involves copying and pasting keys, changing tables, and running filters and queries manually: all possible, but very time consuming. Having the ability to do all this with a single command or keybinding would be so much better. And since this is all work related, I can’t simply modify the source code directly as it will give too much away. Perfect candidate for using an embedded scripting language.
So I think that’s what I’ll start work on next.
I also need to think about starting work on “sqs-browse”. This would be a complete rewrite of what is currently there: which is something that can be used for polling SQS queues and viewing the messages there. I’m hoping for something a little more sophisticated, involving a workspace for writing and pushing messages to queues, along with pulling, saving and routing them from queues. I haven’t got an immediate need for this yet, but I’ve come close on occasion so I can’t leave this too late.
My Nonna bought me a set of winter gloves. This is actually my first ever set, and I can’t believe it took me this long to get a pair.

Made from a blend of wool, possum fur, and silk. Tried them out this morning in temperatures just above freezing. Wonderfully warm.