npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@magusbyte/t2dclient

v2.5.0

Published

The client for the todo2d.com platform, written in TypeScript.

Readme

Todo2d.com TypeScript Client

This TypeScript client provides a comprehensive set of types, DTOs, and API interfaces for interacting with the Todo2d.com backend services. It is designed to enable developers to build custom automations, integrations, and backend services that communicate with the Todo2d platform in a type-safe and maintainable way.

Purpose

  • Automation: Build scripts or backend services that automate workflows using Todo2d.com APIs.
  • Integration: Integrate Todo2d.com with other systems or services using a strongly-typed client.
  • Type Safety: Leverage TypeScript types and interfaces for robust, error-resistant development.

API Change Management

We are committed to maintaining backward compatibility for the v1 API. You can safely use this library as we continue to add new features and endpoints to v1. If a breaking change is required that would cause the client to stop functioning, we will introduce a new API path (e.g., v2) rather than breaking existing integrations.

For a detailed list of all changes and updates, please refer to our changelog: https://docs.todo2d.com/reference/changelog/

Important Notes

  1. Not all API calls are allowed; some require elevated access levels.
  2. The origin of the call is inspected, so this library is not intended for frontend/browser use.

Getting started

Within the library you will find a set of interfaces and types that define the API endpoints, request and response structures.

There are two versions of the client:

  • ApiClient: This is a client that can be used to make API calls. It requires you to implement the IApiClient interface to provide the actual HTTP request logic, such as using fetch.
  • HubClient: This is a client that can be used to connect with the signalr backend. It requires you to implement the IHubClient.

First things first

Login in to the Todo2d.com website and create a workspace token. This token can be used to authenticate API calls.

NOTE
You can only use the workspace token to interact with a single workspace.

Store it somewhere safe.

Implement the IApiClient and IHubClient

See the example at the end of this file. We recommend you copy it.

Wrap the ApiClient and HubClient

import { formatToken, WorkHub, WorkspaceApi } from "@magusbyte/t2dclient";

import { ApiClient } from "./ApiClient"; // See the example at the end
import { HubClient } from "./HubClient";

const apiClient = new ApiClient(
  formatToken(process.env.WORKSPACE_TOKEN, "workspace"),
);
const hubClient = new HubClient(
  formatToken(process.env.WORKSPACE_TOKEN, "workspace"),
);

const workHub = new WorkHub(hubClient);
const workspaceClient = new WorkspaceApi(apiClient);

// Use the hub for live updates
workHub.addEventHandler("OnTaskSet", (ev) => {
  if (ev.item.version == 0) {
    console.log(`Task created: ${ev.item.name} (id: ${ev.item.id})`);
  } else {
    console.log(
      `Task updated: ${ev.item.name} (id: ${ev.item.id}, version: ${ev.item.version})`,
    );
  }
});

hubClient.start().then(async () => {
  // Retrieve the workspace
  var workspace = (await workspaceClient.list()).items[0]!;
  // Tell the hub subscribe to the workspace
  await workHub.subscribeToWorkspace(workspace.id);

  // Create a task (the Workhub is faster than using the TaskApi)
  await workHub.createTask({
    name: "My first task",
    workspaceId: workspace.id,
    properties: [],
    attributes: [],
  });
});

Demo

You can find the full demo at github.com/MagusByte/t2d-cli-demo

Example implementation

HubClient

import { type IHubClient } from "@magusbyte/t2dclient";
const signalr = require("@microsoft/signalr");

// An example implementation of the HubClient
export class HubClient implements IHubClient {
  constructor(private readonly token: string) {}
  private readonly prefix = "https://app.todo2d.com/api/hubs/work";
  private readonly connection = new signalr.HubConnectionBuilder()
    .withUrl(this.prefix, {
      accessTokenFactory: () => Promise.resolve(this.token),
    })
    .build();

  async start() {
    await this.connection.start();
  }

  invoke<T>(methodName: string, ...args: any[]): Promise<T> {
    return this.connection.invoke(methodName, ...args);
  }

  on(methodName: string, newMethod: (...args: any[]) => any): void {
    return this.connection.on(methodName, newMethod);
  }
}

ApiClient

import {
  ApiDeleteConfig,
  ApiFetchConfig,
  ApiGetConfig,
  ApiPatchConfig,
  ApiPostConfig,
  ApiPutConfig,
  formatToken,
  type IApiClient,
} from "@magusbyte/t2dclient";

export class ApiClient implements IApiClient {
  prefix: string = "https://app.todo2d.com";
  constructor(private token: string) {}

  async getJson<R>(
    url: string,
    config?: Partial<ApiGetConfig & ApiFetchConfig>,
  ): Promise<R> {
    const response = await fetch(this.prefix + url, {
      method: "GET",
      headers: this.createHeaders(),
    });

    return this.getData<R>(response);
  }

  async postJson<R, T = object>(
    url: string,
    body: T,
    config?: Partial<ApiPostConfig & ApiFetchConfig>,
  ): Promise<R> {
    const response = await fetch(this.prefix + url, {
      method: "POST",
      headers: this.createHeaders(),
      body: JSON.stringify(body),
    });

    return this.getData<R>(response);
  }

  async putJson<R, T = object>(
    url: string,
    body: T,
    config?: Partial<ApiPutConfig & ApiFetchConfig>,
  ): Promise<R> {
    const response = await fetch(this.prefix + url, {
      method: "PUT",
      headers: this.createHeaders(),
      body: JSON.stringify(body),
    });

    return this.getData<R>(response);
  }

  async patchJson<R, T = object>(
    url: string,
    body: T,
    config?: Partial<ApiPatchConfig & ApiFetchConfig>,
  ): Promise<R> {
    const response = await fetch(this.prefix + url, {
      method: "PATCH",
      headers: this.createHeaders(),
      body: JSON.stringify(body),
    });
    return this.getData<R>(response);
  }

  async deleteJson<R>(
    url: string,
    config?: Partial<ApiDeleteConfig & ApiFetchConfig>,
  ): Promise<R> {
    const response = await fetch(this.prefix + url, {
      method: "DELETE",
      headers: this.createHeaders(),
    });
    return this.getData<R>(response);
  }

  private async getData<R>(response: Response) {
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return (await response.json()) as R;
  }

  private createHeaders(): HeadersInit | undefined {
    return {
      "Content-Type": "application/json",
      Authorization: `Bearer ${this.token}`,
    };
  }
}