wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

Higher Level Custom Events


A common occurence in web applications are button bars that execute various commands. So I was thinking of a pattern how to make this flexible and repeatable.

Separate Event and Actions

The typical setup we find is like:

const btn = document.querySelector('#myButton');
btn.addEventListener('click', (event) => {
  event.preventPropagation();
  // Business logic goes here
  doStuff();
});

This ties the logic to a low-level event of a low level component. To increase the usefulness we need to move from "mechanical" events to business level events, kind of.

An anology: imagine a fastfood kitchen. The cook gets orders "2 burgers, large fries" that don't contain: customer tapped on the order kiosk, drive thru order or delivery request. This information iss handled by the front-desk.

Similarly we can approach the button event. Instead tying our business logic to the click event, we tie it to a higher level event.

Components send events

const btn = document.querySelector('#myButton');
btn.addEventListener('click', (event) => {
  window.dispatchEvent(new CustomEvent('update-request', { detail: { button: btn }, bubbles: true, composed: true }));
});

This is oblivious where the business logic happens, while the logic still knows where it came from by inspecting the detail object. Decoupling using custom events makes testing easier.

window.addEventListener('update-request', updateStuff);
const updateStuff = async (event) => {
  await doStuff;
};

So far, so good. In the next installment I'll tie it together with a custom component.


Posted by on 13 November 2025 | Comments (0) | categories: Javascript WebComponents WebDevelopment

A flexible filter for collections or maps


Building a proxy for http requests I came across an interesting generic problem: filter or mutate a map but only for the occurence of some given set of keys.

That's a lot of if - else - switch

The going approach is to have a specialized method with a switch:

  Map<String, String> transformMap(Map<String, String> input, String joker) {
    Map<String, String> result = new HashMap<>();

    input.entrySet().forEach(entry -> {
      String key = entry.getKey();
      String value = entry.getValue();
      switch (key) {
        case "a":
          result.put(key, "alpha");
          break;
        case "b":
          result.put(key, "beta");
          break;
        case "j":
          result.put(key, joker + "!");
        default:
          result.put(key, value);
      }
    });

    return result;
  }

This gets big and hard to maintain fast, so I was thinking of a more neutral approach entertaining Function and Optional, hear me out.


Read more

Posted by on 04 October 2025 | Comments (0) | categories: Java

Arguing with Claude about StableCoins


Winding down after a busy week, I decided to have som fun with Claude, so I asked

Explain to an A-Level student what stable coins are

Explain how they are created, distributed and backed. List the business model for coin issuers and traders. Discuss their advantages and risks compared to other cryptocurrencies as well as fiat currencies.


Read more

Posted by on 26 September 2025 | Comments (1) | categories: After hours AI

Authentication with PKCE and vanilla JavaScript


Finally you got rid of user management in your application since your organisation has standardized on an IdP. There are plenty to choose from. I usually develop using a Keycloak IdP before I test it with the target IdP (and yes: Azure Entrada tries to be different).

So it's time to adjust your SPA to use the IdP. The admins tell you, you need to use PKCE, part of the OIDC specification

Where is the fun in ready made?

A quick search on npmjs shows, the topic is popular and AWS takes the crown. But for deeper understanding, let's roll our own.

Prerequisites

There are just four items you need. The first three are provided by your IdP admins, the forth one you have to provide to them.

  • The URL of your IdP, the full path where .well-known can be found. That's usually the root, but does differ for Entrada or Keycloak
  • The issuer. That typically is the URL, except for Entrada
  • The clientId. A String, usually opaque
  • The callback urls. Pick those wisely. Typically you want three: http://localhost:3000/myApp, https://localhost:8443/myApp and https://final.place.com/myApp The first two enable you to test your application locally with both http and https

Read more

Posted by on 01 September 2025 | Comments (0) | categories: JavaScript WebDevelopment

Installing a macOS developer workstation


It is sitting on my desk, a shiny M4 MacBook. It wants to be configured. I can take the easy route and use the Migration Assistant. But that would carry forward all the cruft the various incarnations of the Migration Assistant have accumulated over the decade. So I went the hard way.

Manual with a dash of AppStore

First Stop: update macOS. In my case Sequoia 15.1 -> 15.6.1

Installation falls into 3 categories

  • command line tooling
  • settings & data
  • GUI applications

Followed by the reconfiguration of license keys


Read more

Posted by on 29 August 2025 | Comments (0) | categories: Development macOS

CouchDB, JWKS and PEM public keys


Depending on how deep you authenticate, you might be tasked maintaining a user base in _users (and welcome to "I forgot my password" hell). The standing recommendation is to implement a single source of identity using a directory as Identity Provider (IdP). My favorite NoSQL database can be configured to trust JWT signed by known IdPs, so let's do that.

Some assembly required

CouchDB can be configured in three ways: Edit the respective ini file, use the Fauxton UI or use the REST API. I like the later since I'm comfortable with curl and Bruno (not a fan of Postman anymore). The steps are:

  • configure a client on your identity provider
  • enable JWT authentication
  • specify what claims are mandatory
  • specify how to map roles
  • add trustedd public keys
  • restart your node

Read more

Posted by on 30 July 2025 | Comments (0) | categories: CouchDB JWT

Report your CSP (violations)


I'm a big fan of a strict Content Security Policy (CSP). It can be a pain to setup (but there is help, here, here, here and here) and evaluate.

Let's report it

You won't know if a policy was tried to be violated unless the attempt is reported back to your application. For that purpose we use the CSP directives report-to and the older, deprecated report-uri. Until Firefox catches up, use both. When a browser supports report-to, report-uri gets ignored.

Reporting isn't only useful in production, but already during development and especially during retrofitting. There you can swap the Content-Security-Policy header for Content-Security-Policy-Report-Only. It will allow all content to load, but report back all violations.

You then run your E2E Tests (You have those, haven't you?) and get a free overview what loads from where. Adjust the CSP, rinse and repeat.

Where to report to?

There are numerous SaaS providers with fancy dashboards, that offer ready made solutions. When you are pressed for time, that might be your best option. I haven't evaluated them, so I can't recommend or endorse them.


Read more

Posted by on 07 July 2025 | Comments (0) | categories: Java JavaScript Rust WebDevelopment

HTML's template element in single page applications


"We need to use [insert-framework-of-the-day]!" is the typical answer when asking for a light web single page application (SPA).

It doesn't need to be that way, Paul and myself shared in a recent webinar.

Serving the long tail

Long Tail Apps tend to be outside of IT control, since they bypass the (usually heavy) standup of an IT development project.

That standup could be way lighter, when your application just consists of one html file, one css file, one js file, one manifest and (optionally) one or more image files as well as a definded point of deployment.

The typical objection goes: "But it is never one HTML file, I need a login, a list and a form in read and edit mode"

template to the rescue

The <template> element is part of the WebComponents specification and it is useful in simple SPA applications.


Read more

Posted by on 23 June 2025 | Comments (0) | categories: WebComponents WebDevelopment

Fun with AI system prompts


AI Chatbots are a popular topic. As part of a session at Engage 2025 I was looking for an easy to grasp example to understand the difference of user prompt and system prompt, the later typically invisible to the user. My goal was to make it abundantly clear how important the system prompt is.

I settled sending the same user prompt with vastly different system prompts. Here is what I got.

Romeo at the balcony

Mercutio’s mocking my love and he’s never been in love himself.

Wait. What’s that light coming from the window over there? It’s like the east, with Juliet as the morning sun! Rise, Juliet, beautiful sun, and kill the jealous moon who’s already fading and sad because you are far more beautiful than she is. Don’t swear off men like the virgin moon goddess Diana—the moon envies you anyway. Her virginal appearance is weak and pale and only fools want to emulate it. Get rid of it.

It’s my lady! Oh, it’s my love. Oh, if only she knew I love her. She’s talking but I can’t hear anything. What does it matter?

Her expression means something and I can answer that. No, I’m being too forward. She’s not talking to me. Oh, if two of the most beautiful stars had to leave heaven on important business, they’d ask her eyes to do the twinkling for them while they were gone! What if her eyes took their places in the sky and those stars became her eyes? Her beautiful face would outshine those stars in her head like daylight outshines lamps, while her eyes in the sky would be so bright at nighttime that birds would be convinced it was day. Look at how she leans her cheek on her hand. I wish I were a glove on her hand so I could touch her cheek!


Read more

Posted by on 07 May 2025 | Comments (0) | categories: AI

Deploying a Single Page Application using the Domino REST API


The Domino REST API not only provides secure access to "jsonified" Domino data,
but also comes with capabilities to ease integration. This enables one to quickly cater to the long tail of applications, giving them a home instead of loosing them to the shadow IT.

Once you know the steps, you can deploy new Single Purpose Applications (I modified the meaning of SPA a little) in no time.

No CORS, no headache

DRAPI allows to host static applications in the keepweb.d directory. "Static" might be a little misnomer (it only relates to the source files, not the interaction) since a JS file can make your experience quite interactive. Since you run on the same Domain and port as the API, you don't need to worry about CORS

Preparation

Your SPA will live in a sub directory of keepweb.d, so think about a name, we shall use demo42 here. Add a suitable icon (e.g. 72x72 px png), name it demo42.png and you are ready to roll. Let's assume our Domino API is running on https://api.demo.io

Work faster with vitejs

viteJS is one of the fronteand tools you want to learn. It features "hot module reload" to speed up development and, when done, packages your application nice and tidy.

It is easy to get started. You need a current version (22.x at time of writing) of nodeJS installed as development tooling.

npm create vite@latest demo42 -- --template vanilla
cd demo42
npm install

This will create the demo42 directory and scaffold the project for you. Before we get started with athe development, let's adjust the environment. In the root of the project create a file vite.config.js

import { defineConfig } from 'vite';

export default defineConfig({
  base: '/keepweb/demo42/',
  server: {
    proxy: {
      '/api': {
        target: 'https://api.demo.io',
        changeOrigin: true
      }
    }
  }
});

This allows you to develop in the comfort of your local machine's hot module reload which refreshes your app on svae automagically. It also fixes the path matching to its final destination. Next create in public the file manifest.json. This file defines the tile layout for the landing page.

{
  "short_name": "Demo 42",
  "name": "The final answer to all Demos",
  "start_url": ".",
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#aacccc",
  "icon": "vite.svg"
}

You can play with colors and icons as you deem fit. Now we are ready to run the application:

npm run dev

Read more

Posted by on 17 March 2025 | Comments (2) | categories: Domino DRAPI WebDevelopment