Nanoservices in no time with seamless TypeScript support.

Overview

Nanolith

CircleCI

npm Libraries.io dependency status for latest release npm npm bundle size GitHub issues

Nanolith logo

Nanoservices in no time with seamless TypeScript support.

Table of Contents

About

Threadz gets the job done, but after getting various feedback on its APIs, I realized that it is overly complex. You have the Threadz API for running one-off tasks within short-term workers, the Interact API for running one-off tasks, but sending messages to them, the BackgroundThreadzWorker API for running workers that are long-running services, the Communicate API for communicating between workers, etc. Each of these APIs has their own methods that need to be learned by reading the documentation. Additionally, the configuration of workers was placed poorly, and did not allow for flexibility. Overall, Threadz has turned into a hot coupled mess.

So how's Nanolith any different? Other than being more performant, more reliable, and having even more seamless TypeScript support, Nanolith has just two APIs. The Nanolith API can be used to call one-off workers, and directly on that API, the launchService() function can be called to launch a long-running worker that has access to your function definitions that will only finish once it's been told to terminate(). When you launch a service, you are immediately able to communicate back and forth between the worker and the main thread with no other APIs needed.

Enough talk though, let's look at how this thing works.

Defining a set of tasks

No matter what your use-case of Nanolith is, you will always start with the define() function. This function takes an object containing (sync or async) task definitions, and returns an object representing the Nanolith API, which can be used to access Nanolith's main functionalities.

To get started, create a separate file dedicated to task definitions and export a variable pointing to the awaited value of the define() function containing your definitions.

// definitions.ts
import { define } from 'nanolith';

const subtract = (x: number, y: number) => x - y;

// Exporting the variable is not a requirement, but is necessary in order to
// have access to the API later on.
export const api = await define({
    add(x: number, y: number) {
        return x + y;
    },
    async waitThenAdd(x: number, y: number) {
        await new Promise((resolve) => setTimeout(resolve, 5e3))
        return x + y;
    },
    // Functions don't have to be directly define within the object parameter,
    // they can be defined elsewhere, or even imported.
    subtract,
});

The add(), waitThenAdd(), and subtract() functions are now ready to be run on a thread.

Creating multiple sets of definitions in the same file with identifiers

Because of Nanolith runs workers directly within the same file you created your task definitions, you need to provide any second or third sets of definitions with a constant and unique identifier so Nanolith knows which code to run within the worker. This information can be provided in the options parameter of the define() function.

// definitions.ts
import { define } from 'nanolith';

const subtract = (x: number, y: number) => x - y;

// The identifier for this set of definitions will be "default"
export const api = await define({
    add(x: number, y: number) {
        return x + y;
    },
    async waitThenAdd(x: number, y: number) {
        await new Promise((resolve) => setTimeout(resolve, 5e3))
        return x + y;
    },
    subtract,
});

// The identifier for this set will be "logger"
export const logger = await define({
    sayHello: () => console.log('hello'),
}, { identifier: 'logger' });

Issues will occur when multiple sets of definitions are present in the same file, but unique identifiers aren't assigned.

Dealing with "Cannot find module" with the file option

Though it shouldn't happen, in some strange cases there is a chance that an error like this will occur when the Nanolith pool instance tries to spawn a worker:

Error: Cannot find module '/some/path/to/some/file.js'

If this occurs, it means that Nanolith failed to correctly determine the location of the file in which you called define(). Correct this error by providing the proper path under the file option.

// definitions.ts
import { define } from 'nanolith';

const subtract = (x: number, y: number) => x - y;

export const api = await define({
    add(x: number, y: number) {
        return x + y;
    },
    async waitThenAdd(x: number, y: number) {
        await new Promise((resolve) => setTimeout(resolve, 5e3))
        return x + y;
    },
    subtract,
}, { file: '/correct/path/to/some/file.js' });

Running a task

Tasks are one-off workers that are spawned, run the specified task function, then are immediately terminated automatically. The return value of the task function is available back on the main thread when using the Nanolith API.

All tasks are async, regardless of whether or not the defined task function is async.

Considering the task definitions from the section above, this is how the add() task would be run on a separate thread.

// index.ts
import { api } from './definitions.js';

// This spawns a new worker, runs the "add" function, sends the
// return value back to the main thread, then terminates the worker.
const sum = await api({
    name: 'add',
    params: [4, 5],
})

// This also spawns a new worker and runs the same workflow as
// described above
const sum2 = await api({
    name: 'add',
    params: [10, 6],
})

console.log(sum) // -> 9
console.log(sum2) // -> 16

That's it! Simply call the Nanolith API directly providing the name of the task along with the parameters (if any) to pass to the task function, and the function will be run on a separate thread. Any errors thrown by the function can be safely handled by using a try...catch block.

Configuring a task

When running a task, there are more configurations available other than the name and params of the task function.

Option Type Default Description
name string - The name of the task function to run.
params array [] The parameters to pass to the task function.
priority boolean false Whether or not to push the worker to the front of the pool's queue and treat it as a priority task.
reffed boolean true When true, worker.ref() will be called. When false, worker.unref() will be called.
options WorkerOptions {} An object containing most of the options available on the Worker constructor.
messengers Messenger[] [] An array of Messenger objects to expose to the task's worker.

Launching a service

Services differ from tasks, as they are not one-off workers. They are long-running workers that will continue running until they are close()d. A service has access to all of the task functions in the set of definitions it is using.

// index.ts
import { api } from './definitions.js';

// Spawns a service worker and waits for it to go
// "online" before resolving
const service = await api.launchService({
    // Provide an exception handler to know when an uncaught exception
    // is thrown by the underlying worker, and handle it as needed.
    // If this is not provided, uncaught exceptions will go unseen.
    exceptionHandler: ({ error }) => {
        console.error('oops!', error.message);
    }
});

// Runs the "add" function in the worker created by the
// "launchService" function
const sum = await service.call({
    name: 'add',
    params: [4, 5],
})

// Also runs the "add" function in the worker created by
// the "launchService" function
const sum2 = await service.call({
    name: 'add',
    params: [10, 6],
})

console.log(sum) // -> 9
console.log(sum2) // -> 16

// Terminates the worker
await service.close();

Configuring a service

Similar to running a task, various options are available when configuring a service. The launchService() function accepts all of the following options.

Option Type Default Description
exceptionHandler Function - An optional but recommended option that allows for the catching of uncaught exceptions within the service.
priority boolean false Whether or not to push the worker to the front of the pool's queue and treat it as a priority task.
reffed boolean true When true, worker.ref() will be called. When false, worker.unref() will be called.
options WorkerOptions {} An object containing most of the options available on the Worker constructor.
messengers Messenger[] [] An array of Messenger objects to expose to the service worker.

Using a service initializer task function

There may be times when you want to have a task function be automatically called right when the service goes online. This is called a service initializer function, and it can be used to register listeners on the parent or on a Messenger instance, or to do any other internal configuration of the service before any tasks can be called on it.

To create a service initializer function, simply name one of your task definitions __initializeService and define your initialization logic there. The function will be run immediately after the service goes online, and the launchService() function will only resolve after the initialization function has completed its work.

Using a service

The main method on launched services that you'll be using is .call(); however, there are many more properties and methods available.

Name Type Description
closed Property Whether or not the underlying worker has exited its process. This will be true after calling await service.close()
call() Method Call a task to be run within the service worker.
close() Method Terminates the worker, ending its process and marking the Service instance as closed.
sendMessage() Method Send a message to the service worker.
onMessage() Method Receive messages from the service worker.
offMessage() Method Remove a callback function added with onMessage().
sendMessenger() Method Dynamically send a Messenger object to the service worker.

Managing concurrency

To keep things safe, Nanolith automatically manages the creation of workers with a single instance of the internal Pool class. By default, the concurrency of the pool is equal to the number of cores on the machine currently running the process; however, it can be changed by using the .setConcurrency() method and ConcurrencyOption.

// index.ts
import { pool, ConcurrencyOption } from 'nanolith';

// One thread per four cores.
pool.setConcurrency(ConcurrencyOption.Quarter);
// One thread per two cores.
pool.setConcurrency(ConcurrencyOption.Half);
// Default concurrency. One thread per core (x1).
pool.setConcurrency(ConcurrencyOption.Default);
// One thread per core.
pool.setConcurrency(ConcurrencyOption.x1);
// Two threads per core.
pool.setConcurrency(ConcurrencyOption.x2);
// Four threads per core.
pool.setConcurrency(ConcurrencyOption.x4);
// Six threads per core.
pool.setConcurrency(ConcurrencyOption.x6);
// Eight threads per core.
pool.setConcurrency(ConcurrencyOption.x8);
// Ten threads per core.
pool.setConcurrency(ConcurrencyOption.x10);

Using pool

The global pool instance has various properties and methods that can be accessed.

Name Type Description
option Property A direct reference to the ConcurrencyOption enum.
maxConcurrency Property The currency maximum concurrency of the pool. Can be changed with setConcurrency()
maxed Property Whether or not the pool has reached its max concurrency.
queueLength Property The current number of item in the pool's queue.
activeCount Property The current number of workers that are running under the pool.
idle Property A boolean defining whether or not the pool is currently doing nothing.
next Property Returns the internal PoolItemOptions for the next worker in the queue to be run.
setConcurrency() Method Modify the concurrency of the pool. Use this wisely.

Creating a service cluster

When you have multiple services using the same set of task definitions, it is difficult to manually allocate tasks to each of them. For example, if you have 3 services running that all have access to the formatVideo() function, you would ideally like to run the next call for formatVideo() on the service that is currently the least busy. This is the purpose of the ServiceCluster API.

// index.ts
import { ServiceCluster } from 'nanolith';
import { api } from './definitions.js';

const cluster = new ServiceCluster(api);

// Launch three services that will be managed by the cluster
await cluster.launchService();
await cluster.launchService();
await cluster.launchService();

// The ".use()" method returns the least busy service out of all
// the services.
const promise1 = cluster.use().call({
    name: 'waitThenAdd',
    params: [4, 3]
})

// This call will run on a different service from the first one,
// because the first one has one active call, while the others
// have zero.
const promise2 = cluster.use().call({
    name: 'waitThenAdd',
    params: [4, 5]
})

const [sum1, sum2] = await Promise.all([promise1, promise2]);

console.log(sum1) // -> 7
console.log(sum2) // -> 9

// Close all services attached to the cluster.
await cluster.closeAll();

Using ServiceCluster

Each ServiceCluster instance has access to a few methods and properties.

Name Type Description
activeServices Property The number of currently running services on the cluster.
activeServiceCalls Property The number of currently active task calls on all services on the cluster.
launchService() Method Launch a new service on the provided Nanolith API, and automatically manage it with the ServiceCluster.
addService() Method Add an already running service to to the cluster.
use() Method Returns the Service instance on the cluster that is currently the least active. If no services are active on the cluster, an error will be thrown.
closeAll() Method Runs the close() method on all Service instances on the cluster.

Communicating between threads

These docs are still under construction! This section will soon be fleshed out. If you're eager to learn how to communicate amongst threads in Nanolith, check out the JSDoc examples for the exported values parent, messages, Messenger, and Service.

Fun example

// worker.ts
import { define } from 'nanolith';

export const worker = await define({
    // Classic example. Let's "promisify" a for-loop!
    // Note that these "task functions" can be either async or sync - doesn't matter.
    bigForLoop: (msg: string) => {
        for (const _ of Array(900000000).keys()) {
        }
        return msg;
    },
});

Notice that there's no bloat. Just keys and values (the "task functions").

In our index file (or wherever else), we can simple import the worker variable and call it. This worker variable is our Nanolith API.

// index.ts
import { worker } from './worker.js';

// This will spin up a worker and run our bigForLoop function
// inside of it. Once the function returns, the promise will
// resolve with the return value and the worker's process will
// be exited.
const promise = worker({
    name: 'bigForLoop',
    params: ['test'],
});

console.log('hello world');

const result = await promise;

console.log(result);
You might also like...

Multiple file upload plugin with image previews, drag and drop, progress bars. S3 and Azure support, image scaling, form support, chunking, resume, pause, and tons of other features.

Multiple file upload plugin with image previews, drag and drop, progress bars. S3 and Azure support, image scaling, form support, chunking, resume, pause, and tons of other features.

Fine Uploader is no longer maintained and the project has been effectively shut down. For more info, see https://github.com/FineUploader/fine-uploader

Jan 2, 2023

The official proxy of Titanium Network with enhanced support for a large majority of sites with hCAPTCHA support. Successor to Alloy Proxy.

Corrosion Titanium Networks main web proxy. Successor to Alloy Installation: npm i corrosion Example: const Corrosion = require('corrosion'); const p

Dec 21, 2022

🟢 OneForAll Support Bot - Is a support bot for the discord server of OFA!

🟢 OneForAll Support Bot - Is a support bot for the discord server of OFA! Setup You can setup OneForAll Support Bot by simply opening your terminal/c

Oct 15, 2022

A website for tracking community support for BIP21 QR codes that support on-chain and lightning bitcoin payments.

BIP21 Microsite This is a WIP microsite to promote the usage of a BIP21 payment URI QR code that can include lightning invoices or offers. Wallet supp

Nov 27, 2022

Persistent key/value data storage for your Browser and/or PWA, promisified, including file support and service worker support, all with IndexedDB. Perfectly suitable for your next (PWA) app.

Persistent key/value data storage for your Browser and/or PWA, promisified, including file support and service worker support, all with IndexedDB. Perfectly suitable for your next (PWA) app.

BrowstorJS 🚀 💾 🔒 Persistent key/value data storage for your Browser and/or PWA, promisified, including file support and service worker support, all

Aug 5, 2022

A enhanced web storage with env support, expire time control, change callback and LRU storage clear strategy.

enhanced-web-storage A enhanced web storage with env support, expire time control, change callback and LRU storage clear strategy. How to Start import

Sep 10, 2021

A time-based one-time password (TOTP) generator and authenticator for Gun DB

A time-based one-time password (TOTP) generator and authenticator for Gun DB

Entangler A time-based one-time password (TOTP) generator and authenticator for Gun DB Entangler generates a 6 digit passcode every 30 seconds. It gen

Nov 9, 2022

🤖 An action that fetches the list of malicious domains on Discord in different providers and creates/updates a JSON file with them from time to time.

Discord Guardian Action 🤖  This action fetches the list of malicious domains on Discord in different providers and creates/updates a JSON file with t

Nov 30, 2022

Don't waste time looking at what you are typing, spend time thinking about the meaning.

Don't waste time looking at what you are typing, spend time thinking about the meaning.

LETA Don't waste time looking at what you are typing, spend time thinking about the meaning. About You will be able to: Practice touch typing Pick bes

Dec 15, 2022

A jQuery Plug-in to select the time with a clock inspired by the Android time picker.

A jQuery Plug-in to select the time with a clock inspired by the Android time picker.

Clock Timepicker Plugin for jQuery See a demo here A free jQuery Plug-in to select the time with a clock inspired by the Android time picker. This plu

Dec 22, 2022

Compile-time tests for types. Useful to make sure types don't regress into being overly-permissive as changes go in over time.

expect-type Compile-time tests for types. Useful to make sure types don't regress into being overly-permissive as changes go in over time. Similar to

Jan 8, 2023

⚡️ Lightning Time: a new way to measure time

Lightning Time ⚡️ Lightning Time ⚡️ is a new way to measure time. It's a spin on hexadecimal time: the day is split into 16 parts over and over. The f

Nov 22, 2022

Use full ES2015+ features to develop Node.js applications, Support TypeScript.

Use full ES2015+ features to develop Node.js applications, Support TypeScript.

ThinkJS Use full ES2015+ features to develop Node.js applications, Support TypeScript. 简体中文文档 Installation npm install -g think-cli Create Application

Dec 30, 2022

Functional, simple and customizable UI buttons for react native. Also contains loading functionality and automatically changes color for dual tone buttons. TypeScript support.

React Native UI Buttons ✨ Installation If you want to use icons make sure you have react-native-vector-icons installed in your project. npm install --

Dec 5, 2022

A svelte action for creating tippy.js tooltips with full typescript support

A svelte action for creating tippy.js tooltips with full typescript support

Tippy.js for svelte A svelte action for creating tippy.js tooltips. Getting started # Pnpm pnpm add svelte-tippy tippy.js # Yarn yarn add svelte-tipp

Jul 19, 2022

A typescript data mapping tool. To support mutual transforming between domain model and orm entity.

ts-data-mapper A typescript mapping tool supports mutual transforming between domain model and orm entity. In most case, domain model is not fully com

Mar 26, 2022

Simple, lightweight at-runtime type checking functions, with full TypeScript support

pheno Simple, lightweight at-runtime type checking functions, with full TypeScript support Features Full TypeScript integration: TypeScript understand

Sep 5, 2022

Protobuf RPC for TypeScript and Go with streaming support.

Stream RPC starpc implements Proto3 services (server & client) in both TypeScript and Go. Supports client-to-server streaming RPCs in the web browser,

Dec 14, 2022
Comments
  • Shared memory solution

    Shared memory solution

    In Threadz, there is the SharedMemory class, which allows for the reading and writing of shared memory space.

    The Threadz solution works, but is a bit bloated and unoptimal. Nanolith needs a slick shared memory solution that is more performant and reliable than the previous solution, and intuitive to use.

    feature 
    opened by mstephen19 1
  • `parent.onMessengerReceived` not working in `__initializeService` hook

    `parent.onMessengerReceived` not working in `__initializeService` hook

    worker.ts

    import { define, parent, messengers } from 'nanolith';
    
    export const api = await define({
        __initializeService() {
            parent.onMessengerReceived((messenger) => {
                console.log(messenger);
            });
        },
    });
    

    index.ts

    import { Messenger } from 'nanolith';
    import { api } from './worker.js';
    
    const service = await api.launchService();
    
    const messenger = new Messenger('foo');
    
    await service.sendMessenger(messenger);
    

    The console.log is of undefined, despite registering the onMessengerReceived listener. However, this works perfectly: worker.ts

    import { define, parent, messengers } from 'nanolith';
    
    export const api = await define({
        registerListener() {
            parent.onMessengerReceived((messenger) => {
                console.log(messenger);
            });
        },
    });
    

    index.ts

    import { Messenger } from 'nanolith';
    import { api } from './worker.js';
    
    const service = await api.launchService();
    
    await service.call({ name: 'registerListener' });
    
    const messenger = new Messenger('foo');
    
    await service.sendMessenger(messenger);
    
    bug 
    opened by mstephen19 0
  • `activeCalls` on `Service`

    `activeCalls` on `Service`

    Currently, ServiceCluster has a very roundabout way of detecting how many calls are currently active on a Service by listening for events on each instance. This can be improved (performance-wise as well, slightly) by simply having an activeCalls property right on a service.

    improvement 
    opened by mstephen19 0
  • Strict typing for `TaskFunction`

    Strict typing for `TaskFunction`

    Currently, the type definition for TaskFunction looks like this:

    type TaskFunction = (...args: any[]) => Awaitable<any>;
    

    Though this works, it is problematic because only certain basic data types can be sent to a worker via posting a message. Because the type is any, it does not prevent runtime errors caused by passing in things like functions or random class instances into these functions and expecting it to work.

    The TaskFunction type should only accept certain acceptable data types that workers can receive, along with functions like parent.sendMessage or messenger.sendMessage. It must be strict.

    feature good first issue 
    opened by mstephen19 1
Releases(0.3.0)
  • 0.3.0(Dec 28, 2022)

    Removed

    • __beforeServiceTask and __afterServiceTask hooks in favor of universal __beforeTask and __afterTask hooks with new context.

    Changed

    • README streaming examples to use .shift() instead of .splice().
    • Context of TaskHooks to contain the name of the task being called and whether or not it's being called within a service.
    • ServiceCluster.launch to disallow negative or non-whole numbers.
    • Rename parent to MainThread, which makes much more sense anyways since the parent thread is always the main thread in Nanolith.
    • Rename messengers to MessengerList.
    • Rename MessengerList.seek() to MessengerList.list().
    • General overall refactor + minor performance improvements.
    • README revamp.

    Fixed

    • __afterTask hook being called after the returned value was posted back to the main thread instead of before.
    • Weird exclusion of HookDefinitions keys in Tasks type.
    • Needing to close all SharedMap instances, otherwise the thread would hang even if nothing else is happening.
    • Errors when initializing SharedMap with an empty object.

    Added

    • Functionality for setting new keys on SharedMap instances rather than throwing an error.
    • The ability to set new values on SharedMap based on the previous value. This is fantastic for high-concurrency parallel operations and eliminates all race conditions.
    • Nanolith.clusterize method for easy creation of a service cluster and launching services all at the same time.
    Source code(tar.gz)
    Source code(zip)
  • 0.2.5(Dec 22, 2022)

    Features 🆕

    Fixes & improvements 🛠️

    • Add a timeout of 15 seconds to allow a stream to be accepted before the promise is rejected with createStream() on Messenger.
    • Change TaskWorkerOptions and ServiceWorkerOptions types to be exported as LaunchTaskOptions and LaunchServiceOptions instead.
    • Export SharedArrayPair, MessengerTransfer, and SharedMapTransfer types.
    • Switch messenger.transfer() to be a getter property instead of a method.
    • Change pool.option to be a static property.
    Source code(tar.gz)
    Source code(zip)
  • 0.2.4(Dec 8, 2022)

    Features 🆕

    • Streaming data between the main thread and a service.
    • Streaming data between threads with Messenger
    • Replaced offMessage() with listener-remover functions returned from onMessage() calls

    Fixes & improvements 🛠️

    • MessengerTransferObject issues within __initializeService() hook calls
    • Minor performance improvements to Messenger
    Source code(tar.gz)
    Source code(zip)
  • 0.2.3(Dec 6, 2022)

    Features 🆕

    • waitForMessage() method on Messenger and `Service1
    • Streaming data between threads!

    Fixes & improvements 🛠️

    • parent.waitForMessenger() not working when registered in an __initializeService() hook call.
    Source code(tar.gz)
    Source code(zip)
  • 0.2.1(Dec 3, 2022)

    Features 🆕

    • Added automatically generated identifiers to the define() function. Manually providing them is still possible, but no longer necessary.
    • Removed messages object in favor of the new identical messengers object.
    • Removed launchService() on ServiceCluster in favor of the new launch() method.

    Fixes & improvements 🛠️

    • Eradicated the maxListeners error when calling a high volume of tasks on a Service.
    • Various performance improvements.
    • Lowered bundle size by disabling declaration map files.
    Source code(tar.gz)
    Source code(zip)
  • 0.2.0(Nov 30, 2022)

    Features 🆕

    • TaskDefinitions, Nanolith, TaskWorkerOptions, and ServiceWorkerOptions types now available at the top-level.
    • closeAll() and setRef() methods on Messenger.
    • Deprecated messages object in favor of the new identical messengers object.
    • Deprecated launchService() on ServiceCluster in favor of the new launch() method.

    Fixes & improvements 🛠️

    • Large performance improvements for ServiceCluster.
    • Slight performance improvements for Service.
    • Slight performance improvements for Messenger.
    • Disabled the max-listeners memory leak warning for all Worker instances (this is temporary).
    Source code(tar.gz)
    Source code(zip)
  • 0.1.4(Nov 2, 2022)

    Added

    • activeCalls property to Service + docs.
    • __beforeServiceTask and __afterServiceTask hooks + docs.
    • safeMode option to DefineOptions + docs.
    • "License" section to the README

    Changed

    • Fixed various typos in README and JSDoc comments + general improvements.
    • Removed calling and called events from Service.
    • Same-file calling to be handled on the define() level instead of within runTaskWorker or runServiceWorker.
    • Description in package.json to "Multithreaded nanoservices in no time with seamless TypeScript support."
    • "About" section in README.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.1(Oct 24, 2022)

    Features

    • Support for an automatically called __initializeService hook when launching a service.
    • Support for new __beforeTask and __afterTask hooks when calling a task.
    • closeAllIdle() method and currentServices property on ServiceCluster.
    • Support for an identifier parameter in the .use() method on ServiceCluster.
    • threadID and raw worker properties now available on Service instances.
    • New waitForMessage() function under parent.
    • New seek() function under messages.

    Fixes

    • Possible EventEmitter memory leak detected error (thrown from Worker instances when calling many tasks on a service) fixed by cleaning up all listeners and increasing the limit with setMaxListeners.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Oct 11, 2022)

    After making some final improvements and fixes on the 0.0.1 beta releases, I'm extremely proud to announce the first stable and ready-to-go version of Nanolith - v0.1.0.

    This version brings a new feature to the table - ServiceCluster, which allows you to manage and allocate task loads amongst multiple services with just a few lines of code.

    Source code(tar.gz)
    Source code(zip)
  • 0.0.1-beta3(Oct 4, 2022)

Owner
Matt Stephens
Full-Stack Web-Engineer | UC Berkeley Extension Graduate | TypeScript, Node.js, Express.js, React.js, Next.js, MySQL, MongoDB, GraphQL, Docker | MERNG
Matt Stephens
Add partytown support to WordPress sites.

WP Partytown This plugin is a POC for ticket #176 in WordPress/performance repo. Goals Allow plugin developers to execute scripts inside a Partytown W

rtCamp 31 Dec 12, 2022
This project demonstrates how you can use the multi-model capabilities of Redis to create a real-time stock watchlist application.

Introduction This project demonstrates how you can use Redis Stack to create a real-time stock watchlist application. It uses several different featur

Redis Developer 43 Jan 2, 2023
Template project for ComputerCraft programs written in TypeScript.

cc-tstl-template Template project for ComputerCraft programs written in TypeScript. Uses TypeScriptToLua to compile with ComputerCraft typing declarat

JackMacWindows 22 Dec 4, 2022
TypeScript Data Structures that you need!

TSDS TypeScript Data Structures that you need! Doc Website Introduction A data structure is a way to store and organize data in order to facilitate ac

Ehsan Samavati 25 Dec 8, 2022
Opinionated, type-safe, zero-dependency max/min priority queue for JavaScript and TypeScript projects.

qewe qewe is an opinionated, type-safe, zero-dependency max/min priority queue for JavaScript and TypeScript projects. Installation Add qewe to your p

Jamie McElwain 2 Jan 10, 2022
:eyes: Vue in React, React in Vue. Seamless integration of the two. :dancers:

vuera NOTE: This project is looking for a maintainer! Use Vue components in your React app: import React from 'react' import MyVueComponent from './My

Aleksandr Komarov 4k Dec 30, 2022
replacement for comma.ai backend and useradmin dashboard. comes bundled with a modified cabana instance for seamless drive reviewing.

retropilot-server Replacement for comma.ai backend and useradmin dashboard. Bundled with a modified version of comma's cabana to allow viewing & analy

Florian Brede 39 Dec 4, 2022
Seamless and lightweight parallax scrolling library implemented in pure JavaScript utilizing Hardware acceleration for extra performance.

parallax-vanilla.js Seamless and lightweight parallax scrolling library implemented in pure JavaScript utilizing Hardware acceleration for extra perfo

Erik Engervall 91 Dec 16, 2022
A tool help you generate documentation lightweight, seamless, fast and beautiful

一个 轻量、飞快、美观 的 React 组件文档生成套件 English | 简体中文 Features Vite 内核,高效极速的开发体验 使用 Markdown 写法,让你快速为项目编写说明文档的同时,自动生成预览,在调试的同时生成文档 自动分析 TypeScript 类型定义并生成 API 文

Vitdoc 73 Dec 15, 2022
Query for CSS brower support data, combined from caniuse and MDN, including version support started and global support percentages.

css-browser-support Query for CSS browser support data, combined from caniuse and MDN, including version support started and global support percentage

Stephanie Eckles 65 Nov 2, 2022