Announcing @mightylittle TypeScript Libraries

I’m pleased to announce the open-source release of four TypeScript/JavaScript libraries, each made available under the Apache-2.0 license. These libraries can provide a simple foundation for building browser applications, or can be used as components of a larger browser application framework.

The source for each package is hosted on GitHub, under the GitHub organization mightylittle.

@mightylittle/router

GitHub: https://github.com/mightylittle/router

NPM: https://www.npmjs.com/package/@mightylittle/router

Package @mightylittle/router is a simple, regex-based URL dispatcher. I have been experimenting lately with defining route handlers within web-workers and shared-workers, so I developed this library to provide a flexible routing solution which does not assume that the router is running in the main window. You can find an example of a SPA, featuring dynamic imports of route handlers, in the demo subdirectory here.

Snippet from the demo, defining a router:

const router = new Router<RoutePaths, Promise<Element>, DispatchData>({
  notFound: async (context: RouteContext<DispatchData>): Promise<Element> => {
    const NotFound = (await import("./view/NotFound.js")).default;
    return NotFound(context);
  },
  routes: {
    "/": async (context: RouteContext<DispatchData>): Promise<Element> => {
      const Home = (await import("./view/Home.js")).default;
      return Home(context);
    },
    "/about": async (context: RouteContext<DispatchData>): Promise<Element> => {
      const About = (await import("./view/About.js")).default;
      return About(context);
    },
    "/widgets/:widget": async (context: RouteContext<DispatchData>): Promise<Element> => {
      const Widget = (await import("./view/Widget.js")).default;
      return Widget(context);
    }
  }
});

@mightylittle/event-emitter

GitHub: https://github.com/mightylittle/event-emitter

NPM: https://www.npmjs.com/package/@mightylittle/event-emitter

Package @mightylittle/event-emitter provides a small and simple API for registering and triggering callbacks.

type Foo = {
  foo: string;
}

const emitter = new EventEmitter();

const onFooHandler = (data?: Foo) => console.log("'on' triggered", data);

emitter.once<Foo>("foo", (data?: Foo) => console.log("'once' triggered", data));
emitter.on<Foo>("foo", onFooHandler);
emitter.emit<Foo>("foo", {foo: "bar"});
emitter.emit<Foo>("foo", {foo: "baaz"});
emitter.off<Foo>("foo", onFooHandler);
emitter.emit<Foo>("foo", {foo: "quux"});

// Output:
// 'once' triggered { foo: 'bar' }
// 'on' triggered { foo: 'bar' }
// 'on' triggered { foo: 'baaz' }

@mightylittle/transaction-log

GitHub: https://github.com/mightylittle/transaction-log

NPM: https://www.npmjs.com/package/@mightylittle/transaction-log

Package @mightylittle/transaction-log provides two types of in-memory, replayable transaction logs, simple and batched. For the simple type, calling append writes to the log, whereas the batched version requires calling commit() to write buffered transactions to the log. An in-memory transaction log likely has only limited utility: the purpose of this library is to define interfaces implemented by persistent adapters, i.e. @mightylittle/transaction-log-idb; the in-memory logs can also be used as substitutes for persistent adapters in automated tests where IndexedDB is unavailable.

Simple log JavaScript example:

import { MemorySimpleTransactionLog } from "@mightylittle/transaction-log";

async function start () {
  const log = new MemorySimpleTransactionLog();
  await log.open();
  await log.append("foo");
  await log.append("bar");
  await log.countTransactions(); // => 2
  await log.replay((data) => console.log("data", data));
  console.log("transactions", await log.getSeqRangeTransactions(1, 2)); // returns the first and second entries
  await log.close();
  await log.clear();
}

Batched log JavaScript example:

import { MemoryBatchedTransactionLog } from "@mightylittle/transaction-log";

async function start () {
  const log = new MemoryBatchedTransactionLog();
  await log.open();
  log.append("foo");
  log.append("bar");
  await log.commit();
  await log.countTransactions(); // => 2
  await log.countCommits(); // => 1
  await log.replay((data) => console.log("data", data), true);
  console.log("transactions", await log.getSeqRangeTransactions(1, 2)); // prints the first and second entries
  console.log("commits", await log.getSeqRangeCommits(1)); // prints the first and any later commits
  await log.close();
  await log.clear();
}

@mightylittle/transaction-log-idb

GitHub: https://github.com/mightylittle/transaction-log-idb

NPM: https://www.npmjs.com/package/@mightylittle/transaction-log-idb

Package @mightylittle/transaction-log-idb implements the same interfaces as the in-memory implementations defined in @mightylittle/transaction-log, but writes data to IndexedDB. This library can be used as the basis of an event-sourcing system within browser applications.

Simple log JavaScript example:

import { IDBSimpleTransactionLog } from "@mightylittle/transaction-log-idb";

async function start () {
  const log = new IDBSimpleTransactionLog();
  await log.open();
  await log.append("foo");
  await log.append("bar");
  await log.countTransactions(); // => 2
  await log.replay((data) => console.log("data", data));
  console.log("transactions", await log.getSeqRangeTransactions(1, 2)); // returns the first and second entries
  await log.close();
  await log.clear();
}

Batched log JavaScript example:

import { IDBBatchedTransactionLog } from "@mightylittle/transaction-log-idb";

async function start () {
  const log = new IDBBatchedTransactionLog();
  await log.open();
  log.append("foo");
  log.append("bar");
  await log.commit();
  await log.countTransactions(); // => 2
  await log.countCommits(); // => 1
  await log.replay((data) => console.log("data", data), true);
  console.log("transactions", await log.getSeqRangeTransactions(1, 2)); // prints the first and second entries
  console.log("commits", await log.getSeqRangeCommits(1)); // prints the first and any later commits
  await log.close();
  await log.clear();
}