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

@reintersect/sdk

v0.1.3

Published

The official Reintersect SDK for TypeScript applications.

Downloads

774

Readme

@reintersect/sdk

The official TypeScript SDK for reading Reintersect, authoring rich reply content with JSX, and creating or updating replies. The default entrypoint is an async client that returns discriminated { data, error } results. Applications already using Effect can use the Effect-native client from @reintersect/sdk/effect.

The package also includes the complete document JSX surface, API response types, schemas, and direct semantic errors. The async entrypoint bundles its Effect runtime dependencies, so most applications only install @reintersect/sdk.

[!IMPORTANT] Choose one SDK family for the whole application. Async imports and JSX use @reintersect/sdk; Effect imports and JSX use @reintersect/sdk/effect. Values and components from the two bundled families are intentionally incompatible, so mixing them will not work.

Install

pnpm add @reintersect/sdk

Create a read-write API key in Settings → Developer → API keys, then expose it to your server-side application as REINTERSECT_API_KEY. Do not ship a Reintersect API key in public browser code.

Effect applications also install the optional peers used by @reintersect/sdk/effect:

pnpm add @reintersect/sdk @effect/platform effect

Configure TypeScript JSX

Async applications use the root package for the automatic JSX transform:

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@reintersect/sdk"
  }
}

For a single file, use a pragma instead:

/** @jsxImportSource @reintersect/sdk */

Effect applications must select the Effect entrypoint instead:

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@reintersect/sdk/effect"
  }
}
/** @jsxImportSource @reintersect/sdk/effect */

Quick start

Async client

/** @jsxImportSource @reintersect/sdk */

import { Bold, Mention, createReintersectClient } from "@reintersect/sdk";

await using reintersect = createReintersectClient();

const { data, error } = await reintersect.replies.create({
  conversationId,
  content: (
    <>
      Hello <Bold>world</Bold>! Thanks <Mention type="person" id={memberId} />.
    </>
  ),
});

if (error) {
  console.error(error._tag, error.message, error.path);
} else {
  console.log(data.id);
}

Expected document, SDK, network, and API failures are returned as { data: null, error }. Successes are returned as { data, error: null }. Caller cancellation and unexpected defects reject the Promise.

Effect client

/** @jsxImportSource @reintersect/sdk/effect */

import { Bold, Mention, Reintersect, ReintersectClient } from "@reintersect/sdk/effect";
import { Effect } from "effect";

const program = Effect.gen(function* () {
  const reintersect = yield* Reintersect;

  return yield* reintersect.replies.create({
    conversationId,
    content: (
      <>
        Hello <Bold>world</Bold>! Thanks <Mention type="person" id={memberId} />.
      </>
    ),
  });
});

const reply = await Effect.runPromise(
  program.pipe(Effect.provide(ReintersectClient.layerConfig())),
);

layerConfig() reads REINTERSECT_API_KEY, optionally reads REINTERSECT_BASE_URL, and uses the global Fetch implementation. Effect callers receive ordinary typed effects and use fiber interruption, schedules, tracing, and error operators normally.

Async and Effect clients

Both clients expose the same resource namespaces and decode the same response schemas. Request objects are strict: unknown keys are rejected before the request is sent. Successful response DTOs are forward-compatible: additive unknown object properties are removed recursively, including from page items and nested authors. Missing fields, invalid known-field values, unknown closed literals, and malformed JSON remain invalid-response failures.

| Concern | Async client | Effect client | | --- | --- | --- | | Import | @reintersect/sdk | @reintersect/sdk/effect | | Construct | createReintersectClient() | ReintersectClient.layer() or .layerConfig() | | Access | returned client object | yield* Reintersect | | Success | { data, error: null } | Effect success channel | | Expected failure | { data: null, error } | typed Effect error channel | | Cancellation | pass { signal }; rejection is AbortError | interrupt the fiber | | Cleanup | close() or await using | layer scope | | Composition | ordinary async/await | Effect operators and layers | | Effect packages | bundled | optional peers supplied by the application |

Plain configuration

createReintersectClient() accepts plain values:

import { createReintersectClient } from "@reintersect/sdk";

const options = {
  apiKey: "rei_…",
  baseUrl: "https://api.reintersect.com",
  fetch: customFetch,
};

const client = createReintersectClient(options);

ReintersectClient.layer() accepts the same values from the Effect entrypoint:

import { ReintersectClient } from "@reintersect/sdk/effect";

const layer = ReintersectClient.layer(options);

| Option | Fallback | | --- | --- | | apiKey | REINTERSECT_API_KEY in Node-like environments | | baseUrl | REINTERSECT_BASE_URL, then https://api.reintersect.com | | fetch | globalThis.fetch |

Missing keys, missing Fetch support, and malformed base URLs are semantic configuration errors. The async client throws them immediately during construction. The Effect layer exposes them while the layer is built.

Effect Config

Use layerConfig() when configuration should remain in Effect:

import { ReintersectClient } from "@reintersect/sdk/effect";
import { Config, Effect } from "effect";

const layer = ReintersectClient.layerConfig({
  apiKey: Config.redacted("REINTERSECT_API_KEY"),
  baseUrl: Config.string("REINTERSECT_BASE_URL"),
});

await Effect.runPromise(program.pipe(Effect.provide(layer)));

Client operations

| Namespace | Method | Input | Result | | --- | --- | --- | --- | | identity | whoami() | none | API-key identity and access | | circles | list() | { cursor?, limit? } | circle page | | circles | get() | { slug } | circle | | conversations | list() | { slug, cursor?, limit? } | conversation-summary page | | conversations | get() | { conversationId } | conversation with content | | replies | list() | { conversationId, cursor?, limit? } | top-level reply page | | replies | listSubreplies() | { replyId, cursor?, limit? } | direct subreply page | | replies | get() | { replyId } | reply | | replies | create() | { conversationId, content, parentReplyId? } | created reply | | replies | update() | { replyId, content } | updated reply | | files | get() | { fileId } | file metadata and fresh URL | | files | download() | { fileId } | { file, bytes: Uint8Array } |

Async methods take a second { signal? } argument. replies.create and replies.update additionally accept strict there. Effect service methods return ordinary Effect values.

Pagination

List operations return { items, nextCursor }. The default page size is 50 and limit may be between 1 and 100. nextCursor is null on the final page; otherwise, pass it back unchanged.

const firstResult = await reintersect.conversations.list({ slug: "all-hands", limit: 25 });
if (firstResult.error) throw firstResult.error;

const first = firstResult.data;
if (first.nextCursor !== null) {
  const secondResult = await reintersect.conversations.list({
    slug: "all-hands",
    cursor: first.nextCursor,
    limit: 25,
  });
  if (secondResult.error) throw secondResult.error;
}

Response timestamps such as createdAt, updatedAt, and downloadUrlExpiresAt are decoded to JavaScript Date values.

Create and edit replies

Reply mutations accept either JSX or a typed ReintersectDocument. JSX is the ergonomic default:

const createdResult = await reintersect.replies.create({
  conversationId,
  content: <>A concise reply with <Bold>one important point</Bold>.</>,
});
if (createdResult.error) throw createdResult.error;

const updatedResult = await reintersect.replies.update({
  replyId: createdResult.data.id,
  content: <>A clearer replacement.</>,
});
if (updatedResult.error) throw updatedResult.error;

Set parentReplyId during creation to create a subreply. Reintersect supports one subreply level; the API returns ReintersectApiReplyNestingLimitError when a parent would exceed it.

Updates replace the complete reply content. A raw ReintersectDocument is submitted as supplied; the strict option only affects JSX.

Automatic paragraphs

Freestanding inline runs at the root, inside Document, inside Blockquote, and inside ListItem are grouped into paragraphs automatically:

const shorthand = (
  <>
    Hello <Bold>world</Bold>.
    <Blockquote>This also becomes a paragraph.</Blockquote>
  </>
);

Explicit paragraphs remain supported and preserve exact boundaries:

const explicit = (
  <Document>
    <Paragraph>First paragraph.</Paragraph>
    <Paragraph>Second paragraph.</Paragraph>
  </Document>
);

Strict JSX

Pass { strict: true } when JSX must include an explicit Document root, explicit paragraphs in block containers, and a leading Paragraph in every ListItem.

const content = (
  <Document>
    <Paragraph>Everything is explicit.</Paragraph>
  </Document>
);

const result = await reintersect.replies.create(
  { conversationId, content },
  { signal: controller.signal, strict: true },
);

The Effect client accepts the same strict option without signal; interrupt its fiber to cancel:

const reply = yield* reintersect.replies.update(
  { replyId, content },
  { strict: true },
);

JSX is validated before source-backed assets are uploaded or the reply request begins.

JSX component reference

JSX components create opaque authoring values; they do not render browser UI. Fragments, nested arrays, mapped arrays, nullable conditionals, strings, finite numbers, and function components are supported where their parent allows them. Lowercase HTML elements are intentionally rejected.

Blocks

Document

An optional explicit root. It requires meaningful content. The SDK supplies a root in default mode; strict mode requires this component.

<Document>
  <Paragraph>Hello.</Paragraph>
</Document>

Paragraph

An explicit paragraph and paragraph boundary.

A paragraph rendered in Reintersect

<Paragraph>A complete thought.</Paragraph>

Blockquote

An indented quotation. Inline content inside it receives an inferred paragraph in default mode.

A block quotation rendered in Reintersect

<Blockquote>Clarity is kindness.</Blockquote>

UnorderedList

A bulleted list. Its children must be ListItem elements.

An unordered list rendered in Reintersect

<UnorderedList>
  <ListItem>First item</ListItem>
  <ListItem>Second item</ListItem>
</UnorderedList>

OrderedList

A numbered list. start defaults to 1 and must be a positive integer.

An ordered list rendered in Reintersect

<OrderedList start={3}>
  <ListItem>Third item</ListItem>
  <ListItem>Fourth item</ListItem>
</OrderedList>

ListItem

An item for either list type. Inline-first content becomes its leading paragraph automatically. When a valid block appears first, default rendering inserts the required empty leading paragraph. Strict mode requires an explicit leading Paragraph.

A list item rendered in Reintersect

<ListItem>
  An inferred paragraph
  <UnorderedList>
    <ListItem>A nested item</ListItem>
  </UnorderedList>
</ListItem>

Heading

A heading with level={1 | 2 | 3}. The default level is 1.

The supported heading levels rendered in Reintersect

<Heading level={2}>Implementation notes</Heading>

CodeBlock

A plain-text block with an optional supported language. language is statically typed as ReintersectCodeBlockLanguage and checked again at runtime. Elements and marks are not valid children.

A highlighted code block rendered in Reintersect

<CodeBlock language="typescript">{"const answer = 42;"}</CodeBlock>

Use ReintersectCodeBlockLanguages to inspect the generated catalog at runtime.

HorizontalRule

A thematic divider with no children.

A horizontal rule rendered in Reintersect

<HorizontalRule />

Inline elements

HardBreak

A line break that does not end the surrounding paragraph.

A hard line break rendered in Reintersect

<Paragraph>
  First line
  <HardBreak />
  Second line
</Paragraph>

Emoji

An emoji selected by its canonical ReintersectEmojiName.

An emoji rendered in Reintersect

<Emoji name="sparkles" />

Use ReintersectEmojiNames to inspect the generated catalog at runtime.

Mention

A reference to a person, circle, or conversation. Every type accepts an id; only circles may use an exact, case-sensitive slug instead. Exactly one reference is required.

Person, circle, and conversation mentions rendered in Reintersect

<Mention type="person" id={memberId} />
<Mention type="circle" id={circleId} />
<Mention type="circle" slug="all-hands" />
<Mention type="conversation" id={conversationId} />

Circle slugs contain 1–128 ASCII letters, digits, _, or -. They are not trimmed or normalized. The API resolves a circle slug and returned replies contain its canonical ID.

Assets

Source-backed assets require source, name, and a generated ReintersectMimeType. Accepted sources are Uint8Array, ArrayBuffer, and Blob; Node.js Buffer works through its Uint8Array inheritance. A typed Blob must agree with the explicit type.

The SDK uploads source-backed assets before it creates or updates the reply. Use the mutually exclusive assetId form to preserve an existing asset reference, such as while editing the same reply.

Image

An uploaded image with optional accessible alternative text.

An image rendered in Reintersect

<Image
  source={bytes}
  name="architecture.png"
  type="image/png"
  alt="Architecture diagram"
/>

<Image assetId={existingImageId} alt="Architecture diagram" />

File

An uploaded file attachment.

A file attachment rendered in Reintersect

<File source={bytes} name="notes.txt" type="text/plain" />
<File assetId={existingFileId} />

Use ReintersectMimeTypes to inspect the accepted MIME catalog at runtime.

Marks

Marks wrap inline content and may be nested unless a mark documents a specific incompatibility.

Bold

Bold text rendered in Reintersect

<Bold>This is important.</Bold>

Italic

Italic text rendered in Reintersect

<Italic>A little emphasis goes a long way.</Italic>

Strike

Struck-through text rendered in Reintersect

Ship on <Strike>Friday</Strike> Thursday.

Underline

Underlined text rendered in Reintersect

Remember the <Underline>important detail</Underline>.

Code

Inline code. It cannot be combined with other marks.

Inline code rendered in Reintersect

Run <Code>pnpm typecheck</Code> before shipping.

Link

A safe link with required href and optional title. HTTP, HTTPS, email, telephone, relative-path, query, and fragment destinations are supported. Protocol-relative URLs, backslashes, control characters, and unsupported protocols are rejected before a request begins.

A link rendered in Reintersect

Visit <Link href="https://reintersect.com" title="Reintersect">Reintersect</Link>.

Fragments and control flow

Fragment and the shorthand <>…</> group children without introducing a document node.

Show chooses content using JavaScript truthiness:

<Show when={member} fallback="Anonymous">
  {(currentMember) => currentMember.displayName}
</Show>

For maps a readonly collection and supports a fallback:

<For each={items} fallback="No items yet.">
  {(item, index) => <Paragraph>{index + 1}. {item.label}</Paragraph>}
</For>

Both helpers are evaluated immediately and produce transparent authoring content; they are not reactive UI primitives. User-defined function components may return any supported JSX child.

Files and downloads

files.get({ fileId }) returns metadata with a short-lived downloadUrl and downloadUrlExpiresAt. Use files.download({ fileId }) when you need bytes; it obtains fresh metadata and returns:

interface ReintersectFileDownload {
  readonly file: ReintersectFile;
  readonly bytes: Uint8Array;
}

Normal requests time out after 30 seconds. Uploads and downloads time out after five minutes. Timeouts are returned as ReintersectSdkTimeoutError.

Cancellation and cleanup

Async callers pass an AbortSignal as the second argument:

const controller = new AbortController();

const pending = reintersect.conversations.get(
  { conversationId },
  { signal: controller.signal },
);

controller.abort();
await pending; // rejects with an error whose name is "AbortError"

Close the async client when it is no longer needed:

const reintersect = createReintersectClient();

try {
  // use the client
} finally {
  await reintersect.close();
}

await using calls the same cleanup through Symbol.asyncDispose.

Effect callers cancel by interrupting the running fiber; layer scopes own client cleanup.

Semantic errors

All modeled failures are direct tagged errors with a stable _tag, human-readable message, and precise path. Catch the semantic leaf you can handle instead of parsing messages.

Async

const { data, error } = await reintersect.replies.create({ conversationId, content });

if (error) {
  switch (error._tag) {
    case "ReintersectApiCircleMentionUnavailableError":
      console.error(`No accessible circle has slug ${error.slug}.`);
      break;
    case "ReintersectDocumentUnsafeLinkError":
      console.error(`Unsafe link at ${error.path.join(".")}: ${error.href}`);
      break;
    default:
      console.error(error.message);
  }
}

Effect

const reply = yield* reintersect.replies.create({ conversationId, content }).pipe(
  Effect.catchTag("ReintersectApiCircleMentionUnavailableError", (error) =>
    Effect.fail(new CircleReferenceError({ slug: error.slug, path: error.path })),
  ),
  Effect.catchTag("ReintersectDocumentUnsafeLinkError", (error) =>
    Effect.fail(new UnsafeCustomerLinkError({ href: error.href, path: error.path })),
  ),
);

Error families

  • ReintersectDocument…Error reports local JSX root, hierarchy, attribute, mention, link, emoji, language, number, cycle, and asset failures.
  • ReintersectSdk…Error reports configuration, request encoding, network, timeout, response, and asset-preparation failures.
  • ReintersectApi…Error reports authentication, authorization, request, resource, document, asset, rate-limit, and service failures returned by Reintersect. An unrecognized non-success response becomes ReintersectApiUnknownError, which exposes only a generic message, empty path, and HTTP status.
  • Operation types such as ReintersectRepliesCreateError compose exactly the failures modeled by that method.

Direct error tags

| Boundary | Direct tags | | --- | --- | | JSX structure | ReintersectDocumentInvalidRootError, ReintersectDocumentEmptyError, ReintersectDocumentInvalidElementError, ReintersectDocumentInvalidStructureError, ReintersectDocumentNonFiniteNumberError, ReintersectDocumentCyclicChildrenError | | JSX attributes | ReintersectDocumentInvalidAttributesError, ReintersectDocumentInvalidCodeBlockLanguageError, ReintersectDocumentInvalidEmojiNameError, ReintersectDocumentInvalidMentionError, ReintersectDocumentUnsafeLinkError | | JSX assets | ReintersectDocumentInvalidAssetError | | SDK configuration | ReintersectSdkMissingApiKeyError, ReintersectSdkMissingFetchError, ReintersectSdkInvalidConfigurationError | | SDK transport | ReintersectSdkInvalidInputError, ReintersectSdkNetworkError, ReintersectSdkTimeoutError, ReintersectSdkInvalidResponseError | | SDK assets | ReintersectSdkUnreadableAssetSourceError | | API authentication | ReintersectApiKeyMissingError, ReintersectApiKeyInvalidError, ReintersectApiKeyExpiredError | | API authorization | ReintersectApiInsufficientAccessError, ReintersectApiMemberInactiveError, ReintersectApiSubscriptionRequiredError | | API requests | ReintersectApiInvalidRequestError, ReintersectApiInvalidCursorError, ReintersectApiRateLimitExceededError | | API resources | ReintersectApiCircleNotFoundError, ReintersectApiConversationNotFoundError, ReintersectApiReplyNotFoundError, ReintersectApiFileNotFoundError, ReintersectApiReplyNotOwnedError, ReintersectApiReplyNestingLimitError | | API documents | ReintersectApiInvalidDocumentError, ReintersectApiMentionUnavailableError, ReintersectApiCircleMentionUnavailableError, ReintersectApiInvalidEmojiNameError, ReintersectApiUnsafeLinkError, ReintersectApiInvalidAssetReferenceError | | API assets | ReintersectApiInvalidAssetNameError, ReintersectApiInvalidAssetMimeTypeError, ReintersectApiEmptyAssetError, ReintersectApiUploadFailedError | | API services | ReintersectApiInternalServerError, ReintersectApiServiceUnavailableError, ReintersectApiUnknownError |

Runtime schema unions are exported for validation and matching:

| Family | Runtime unions | | --- | --- | | JSX | ReintersectDocumentStructureError, ReintersectDocumentAttributeError, ReintersectDocumentAssetError, ReintersectDocumentRenderError | | SDK | ReintersectSdkConfigurationError, ReintersectSdkTransportError, ReintersectSdkAssetError, ReintersectSdkClientError | | API | ReintersectApiAuthenticationError, ReintersectApiAuthorizationError, ReintersectApiRequestError, ReintersectApiResourceError, ReintersectApiDocumentError, ReintersectApiAssetError, ReintersectApiServiceError, ReintersectApiError |

Public response types and schemas

Every response type has a matching Effect Schema value, so applications can reuse the SDK contract at their own boundaries. These schemas remove additive unknown object properties recursively while continuing to validate every known field.

| Values | Types | Schemas | | --- | --- | --- | | Identity | ReintersectIdentity | ReintersectIdentitySchema | | Circle | ReintersectCircle | ReintersectCircleSchema | | Conversation summary | ReintersectConversationSummary | ReintersectConversationSummarySchema | | Conversation detail | ReintersectConversation | ReintersectConversationSchema | | Reply | ReintersectReply | ReintersectReplySchema | | File | ReintersectFile | ReintersectFileSchema | | Public author | ReintersectPublicAuthor | ReintersectPublicAuthorSchema | | Circle page | ReintersectCirclePage | ReintersectCirclePageSchema | | Conversation page | ReintersectConversationPage | ReintersectConversationPageSchema | | Reply page | ReintersectReplyPage | ReintersectReplyPageSchema | | Structured document | ReintersectDocument | ReintersectDocumentSchema |

Branded identifier types and their schemas are also exported for users, members, workspaces, circles, conversations, replies, files, circle slugs, and cursors. ReintersectApiAccess is "read" | "readWrite". JSON-safe raw document helpers are exported as ReintersectJsonValue and ReintersectJsonObject.

The SDK exports opaque ReintersectElement and ReintersectDocumentInput types, child and component prop types, RenderReintersectOptions, and the generated ReintersectCodeBlockLanguage, ReintersectEmojiName, and ReintersectMimeType catalogs.

Package entrypoints

| Entrypoint | Purpose | | --- | --- | | @reintersect/sdk | self-contained async client, JSX, response schemas and types, semantic errors | | @reintersect/sdk/effect | Effect-native client, JSX, schemas, types, and errors; requires the optional Effect peers | | @reintersect/sdk/jsx-runtime | automatic production JSX transform | | @reintersect/sdk/jsx-dev-runtime | automatic development JSX transform | | @reintersect/sdk/effect/jsx-runtime | Effect-family automatic production JSX transform | | @reintersect/sdk/effect/jsx-dev-runtime | Effect-family automatic development JSX transform |

The TypeScript JSX transform selects runtime entrypoints automatically; application code should normally import the async client and JSX from @reintersect/sdk. Effect applications import their client, components, schemas, and errors from @reintersect/sdk/effect and set jsxImportSource to @reintersect/sdk/effect. Do not pass JSX elements, components, schemas, errors, or client types between the two families.