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

@myco-dev/sdk

v0.1.14

Published

TypeScript SDK for building apps on the Myco platform.

Readme

@myco/sdk

TypeScript SDK for building apps on the Myco platform.

Installation

npm install @myco/sdk

Peer Dependencies

npm install react better-auth swr

Quick Start

import { MycoSDK } from "@myco/sdk";

// Optional: import generated types for type-safe entity access
import type { EntityTypes } from "./types/myco.generated";

const myco = new MycoSDK<EntityTypes>("your-app-key");

// Fetch records - automatically waits for SDK to be ready
const { records } = await myco.data.getRecords("property");

Client SDK

Initialization

import { MycoSDK } from "@myco/sdk";

const myco = new MycoSDK<EntityTypes>("your-app-key", {
  // Optional: override the base URL
  baseUrl: "https://api.myco.com",
  // Optional: disable auto-redirect on unauthenticated (default: true)
  redirectOnUnauth: false,
});

// Optional: await ready if you need to know when initialization completes
// All API calls automatically wait for ready internally
await myco.ready;

The SDK automatically:

  • Detects local vs production environment
  • Handles workspace initialization via the /api/app/join endpoint
  • Persists workspace ID in localStorage
  • Redirects to login if unauthenticated (configurable)

Auth Namespace

myco.auth.login(returnTo?)

Redirects to the federated login page.

// Redirect to login, return to current page after
myco.auth.login();

// Redirect to login, return to specific URL after
myco.auth.login("/dashboard");

myco.auth.logout()

Signs out the current user and clears the session.

await myco.auth.logout();

myco.auth.useSession()

React hook for session state.

function MyComponent() {
  const { data: session, isPending, error } = myco.auth.useSession();

  if (isPending) return <div>Loading...</div>;
  if (!session) return <div>Not logged in</div>;

  return <div>Hello, {session.user.name}</div>;
}

myco.auth.getSession()

Get session data outside of React components.

const session = await myco.auth.getSession();

Workspace Namespace

The workspace namespace handles initialization automatically — no manual ensure() needed.

myco.workspace.ready

Promise that resolves when the workspace is initialized. All other SDK namespaces wait for this internally.

await myco.workspace.ready;

myco.workspace.list()

List all workspaces the user has access to.

const workspaces = await myco.workspace.list();

myco.workspace.current()

Get the current workspace details.

const workspace = await myco.workspace.current();
console.log(workspace.name);

myco.workspace.switch(workspaceId)

Switch to a different workspace.

myco.workspace.switch("ws_abc123");

myco.workspace.update(data)

Update the current workspace.

await myco.workspace.update({ name: "Renamed Workspace" });

myco.workspace.useWorkspace()

React hook for the current workspace.

function WorkspaceHeader() {
  const { data: workspace, isPending, error } = myco.workspace.useWorkspace();

  if (isPending) return <div>Loading...</div>;

  return <h1>{workspace?.name}</h1>;
}

myco.workspace.members.get()

Get members of the current workspace.

const members = await myco.workspace.members.get();

myco.workspace.members.add(userId, role?)

Add a member to the current workspace.

await myco.workspace.members.add("user_123", "admin");
await myco.workspace.members.add("user_456"); // defaults to "member"

myco.workspace.members.remove(userId)

Remove a member from the current workspace.

await myco.workspace.members.remove("user_123");

Data Namespace

Entities

myco.data.getEntities()

Get all entities for the current app.

const entities = await myco.data.getEntities();
myco.data.getEntity(entitySlug)

Get a single entity by slug.

const entity = await myco.data.getEntity("property");
console.log(entity.fields);

Records (Async Methods)

myco.data.getRecords(entitySlug, options?)

Get paginated records for an entity.

const { records, pagination } = await myco.data.getRecords("property", {
  page: 1,
  pageSize: 20,
  sort: "createdAt",
  order: "desc",
  filter: { status: "active" },
});
myco.data.getRecord(entitySlug, recordId)

Get a single record by ID.

const record = await myco.data.getRecord("property", "rec_123");
myco.data.createRecord(entitySlug, data)

Create a new record.

const record = await myco.data.createRecord("property", {
  title: "Beach House",
  price: 500000,
});
myco.data.updateRecord(entitySlug, recordId, data)

Update an existing record.

const record = await myco.data.updateRecord("property", "rec_123", {
  price: 550000,
});
myco.data.deleteRecord(entitySlug, recordId)

Delete a record.

await myco.data.deleteRecord("property", "rec_123");
myco.data.batchGetRecords(entitySlug, ids)

Batch get multiple records by IDs.

const records = await myco.data.batchGetRecords("property", [
  "rec_123",
  "rec_456",
]);

Records (React Hooks)

myco.data.useRecords(entitySlug, options?)

SWR hook for fetching records with built-in pagination.

function PropertyList() {
  const {
    data: properties,
    isLoading,
    error,
    // Pagination
    page,
    hasNextPage,
    hasPreviousPage,
    nextPage,
    previousPage,
    setPage,
  } = myco.data.useRecords("property", {
    pageSize: 10,
    initialPage: 1,
    sort: "createdAt",
    order: "desc",
    filter: { status: "active" },
    // Called when page changes (for URL sync, analytics, etc.)
    onPageChange: (page) => {
      router.push(`?page=${page}`);
    },
  });

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      {properties?.map((property) => (
        <div key={property.id}>{property.data.title}</div>
      ))}
      <button onClick={previousPage} disabled={!hasPreviousPage}>
        Previous
      </button>
      <span>Page {page}</span>
      <button onClick={nextPage} disabled={!hasNextPage}>
        Next
      </button>
    </div>
  );
}

Options:

| Option | Type | Description | |--------|------|-------------| | initialPage | number | Starting page (default: 1) | | pageSize | number | Records per page | | sort | string | Field to sort by | | order | "asc" \| "desc" | Sort direction | | filter | object | Filter criteria | | onPageChange | (page: number) => void | Callback when page changes |

Return value extends SWR response with:

| Property | Type | Description | |----------|------|-------------| | page | number | Current page number | | hasNextPage | boolean | Whether there's a next page | | hasPreviousPage | boolean | Whether there's a previous page | | nextPage | () => void | Go to next page | | previousPage | () => void | Go to previous page | | setPage | (page: number) => void | Jump to specific page |

myco.data.useRecord(entitySlug, recordId)

SWR hook for fetching a single record.

function PropertyDetail({ id }: { id: string }) {
  const { data: property, isLoading } = myco.data.useRecord("property", id);

  if (isLoading) return <div>Loading...</div>;

  return <div>{property?.data.title}</div>;
}

Supports conditional fetching by passing null or undefined:

// Only fetch when selectedId is set
const { data } = myco.data.useRecord("property", selectedId ?? null);

Server SDK

For server-side rendering and API routes (e.g., Remix loaders/actions).

import { createServerSDK } from "@myco/sdk/server";

export async function loader({ request }: LoaderArgs) {
  const myco = createServerSDK(
    { MYCO_APP_KEY: process.env.MYCO_APP_KEY! },
    request
  );

  const { user } = await myco.auth.getVerifiedUser();

  if (!user) {
    return redirect(myco.auth.getLoginUrl(request.url));
  }

  const { records } = await myco.data.getRecords("property");
  return json({ records });
}

Server Auth

myco.auth.getVerifiedUser()

Verify the JWT from cookies and return the user.

const { user, jwt } = await myco.auth.getVerifiedUser();
if (!user) {
  // Not authenticated
}

myco.auth.getLoginUrl(returnTo)

Get the federated login URL.

const loginUrl = myco.auth.getLoginUrl("https://myapp.com/dashboard");
return redirect(loginUrl);

Server Workspace

Same API as client SDK (minus switch, useWorkspace, and ready):

  • myco.workspace.list()
  • myco.workspace.current()
  • myco.workspace.update(data)
  • myco.workspace.members.get()
  • myco.workspace.members.add(userId, role?)
  • myco.workspace.members.remove(userId)

Server Data

Same API as client SDK (async methods only, no hooks):

  • myco.data.getEntities()
  • myco.data.getEntity(entitySlug)
  • myco.data.getRecords(entitySlug, options?)
  • myco.data.getRecord(entitySlug, recordId)
  • myco.data.createRecord(entitySlug, data)
  • myco.data.updateRecord(entitySlug, recordId, data)
  • myco.data.deleteRecord(entitySlug, recordId)
  • myco.data.batchGetRecords(recordIds)

Server Fetch

For custom API calls:

const result = await myco.fetch<MyType>("/api/custom-endpoint", {
  method: "POST",
  body: JSON.stringify({ data }),
});

Error Handling

The SDK exports an ApiError class for handling API errors:

import { ApiError } from "@myco/sdk";

try {
  await myco.data.getRecord("property", "invalid-id");
} catch (error) {
  if (error instanceof ApiError) {
    console.log(error.status); // HTTP status code
    console.log(error.body);   // Response body
    console.log(error.path);   // Request path
  }
}

TypeScript

Generated Types

For type-safe entity access, generate types from your Myco schema:

// types/myco.generated.ts
export interface EntityTypes {
  property: {
    title: string;
    price: number;
    bedrooms: number;
    status: "active" | "sold";
  };
  agent: {
    name: string;
    email: string;
  };
}

Then pass the type to MycoSDK:

import type { EntityTypes } from "./types/myco.generated";

const myco = new MycoSDK<EntityTypes>("your-app-key");

// Now fully typed!
const { records } = await myco.data.getRecords("property");
// records is TypedEntityRecord<{ title: string; price: number; ... }>[]

Exported Types

import type {
  MycoSDKConfig,
  JoinResponse,
  VerifiedUser,
  Workspace,
  WorkspaceMember,
  Entity,
  EntityField,
  EntityRecord,
  PaginatedResponse,
  GetRecordsOptions,
  TypedEntityRecord,
} from "@myco/sdk";