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

closai-partner-integration

v1.0.3

Published

Closai Partner Integration

Downloads

433

Readme

Closai Partner Integration Library

Use the Closai Partner Integration Library to authenticate users with Closai from your web app.This guide shows how to install, configure, and use the Library, and how to handle the OAuth callback after login.

Overview

The Library exposes a configure function that returns two main helpers:

  1. login() — redirects the user to the Closai login flow
  2. parseOAuthCallback(search) — reads the callback query string and extracts the userId

Installation

Install the package in your project:

npm install closai-partner-integration

# or

yarn add closai-partner-integration

# or

pnpm add closai-partner-integration

Requirements

Before using the library, make sure you have:

  1. A valid Closai Client ID
  2. A valid Redirect URI registered with Closai

The redirect URI is the URL Closai will redirect the user back to after authentication.

Example:

VITE_CLOSAI_CLIENT_ID=your_client_id
VITE_CLOSAI_REDIRECT_URI=http://localhost:3000/auth/closai/callback

Library Configuration

Create a small setup file to configure the Library once and reuse it across your app.

closai.ts

import { configure } from "closai-partner-integration";

const config = {
  clientId: import.meta.env.VITE_CLOSAI_CLIENT_ID || "",
  redirectUri: import.meta.env.VITE_CLOSAI_REDIRECT_URI || "",
};

const closai = configure(config);

export { closai };

Config Shape

The library expects the following configuration object:

interface Config {
  // The client ID provided by Closai
  clientId: string;

  // The redirect URI used as the callback URL after login
  redirectUri: string;
}

Basic Usage

After configuring the auth config, call closai.login() when the user clicks the login button.

import { closai } from "./closai";

function handleClosaiLogin() {
  closai.login();
}

Example in React:

import { closai } from "./closai";

export function LoginButton() {
    return (
        <button onClick={() => closai.login()}>
            Continue with Closai
        </button>
    );
}

How Login Works

When login() is called, the library:

  1. Validates the generated authentication URL
  2. Redirects the browser to the Closai authentication page
  3. After successful login, Closai redirects the user back to your redirectUri

Internally, the library performs a browser redirect using:

window.location.href = uri;

Because of that, login() should only be called in browser environments.

Handling the Callback

After Closai redirects the user back to your app, read the query string from the current URL and parse it with parseOAuthCallback.

Example callback URL

http://localhost:3000/auth/closai/callback?userId=123

Parse the callback

import { closai } from "./closai";

const result = closai.parseOAuthCallback(window.location.search);

console.log(result.userId);

Return type

{
  userId: string | null;
}

If the query string contains userId, it will be returned. Otherwise, the value will be null.

Example Callback Page in React

import { useEffect } from "react";
import { closai } from "./closai";

export function ClosaiCallbackPage() {

    // Parse the callback query string
    useEffect(() => {
        const { userId } = closai.parseOAuthCallback(window.location.search);

        if (!userId) {
            console.error("Closai login failed: missing userId");
            return;
        }

        console.log("Closai user authenticated:", userId);

        // Example:
        // - send the userId to your backend
        // - exchange it for a session
        // - fetch the user profile
        // - redirect the user to the authenticated area

    }, []);

    return <p>Signing you in with Closai...</p>;
}

Suggested Application Flow

A common integration flow looks like this:

  1. User clicks Continue with Closai
  2. App calls closai.login()
  3. User authenticates with Closai
  4. Closai redirects back to your app
  5. App reads window.location.search
  6. App calls closai.parseOAuthCallback(...)
  7. App uses the returned userId to complete authentication in its own system

Full Example

import { configure } from "closai-partner-integration";

const closai = configure({
  clientId: import.meta.env.VITE_CLOSAI_CLIENT_ID || "",
  redirectUri: import.meta.env.VITE_CLOSAI_REDIRECT_URI || "",
});

document.getElementById("login-btn")?.addEventListener("click", () => {
  closai.login();
});

const { userId } = closai.parseOAuthCallback(window.location.search);

if (userId) {
  console.log("Authenticated user:", userId);
}

🔐 Popup Login (loginWithPopup)

In addition to the default redirect-based login, the library also provides a popup-based authentication flow using loginWithPopup().

This allows users to authenticate with Closai without leaving your current page.


When to use

Use loginWithPopup() when you want:

  • A smoother UX (no full page redirect)
  • To keep the user on the same screen
  • To handle authentication asynchronously

Basic Usage

import { closai } from "./closai";

async function handleClosaiLoginPopup() {
  try {
    const result = await closai.loginWithPopup();

    console.log("Closai user authenticated:", result.userId);

    // Example:
    // - send userId to your backend
    // - create a session
    // - fetch user profile
  } catch (error) {
    console.error("Closai popup login failed:", error);
  }
}

Example in React:

import { closai } from "./closai";

export function LoginButton() {
  return (
    <button onClick={() => closai.loginWithPopup()}>
      Continue with Closai
    </button>
  );
}

⚠️ Important

loginWithPopup() does NOT require a redirectUri.

Unlike login(), which depends on a full OAuth redirect flow, the popup method handles authentication entirely through window communication (postMessage).

This means:

  • ✅ You do not need to configure a redirect URI to use loginWithPopup()
  • ✅ No callback route is required
  • ✅ No need to call parseOAuthCallback

Key Differences

| Requirement | login() | loginWithPopup() | | ------------------ | --------- | ------------------ | | redirectUri | Required | ❌ Not required | | Callback page | Required | ❌ Not required | | parseOAuthCallback | Required | ❌ Not required | | Page reload | Yes | ❌ No |


API Reference

configure(config)

Creates a configured Closai Library instance.

Parameters

configure({
  clientId: string,
  redirectUri?: string,
});

Returns

{
login: () => void;
loginWithPopup: () => Promise<{ userId: string | null }>;
parseOAuthCallback: (search: string) => { userId: string | null };
}

login()

Redirects the current browser window to the Closai authentication URL.

Example

closai.login();

Notes

  • must run in the browser
  • depends on a valid clientId and redirectUri
  • if the authentication URI is not available, the library shows an error

parseOAuthCallback(search)

Parses the callback query string and returns the userId.

Parameters

parseOAuthCallback(search: string);

Example

const { userId } = closai.parseOAuthCallback(window.location.search);

Error Handling

If the library cannot generate a valid authentication URI, login() will not redirect and will show an error instead.

Example internal behavior:

if (!uri) {
  showError(ERROR_MESSAGES.URI_REQUIRED);
  return;
}

To avoid that, ensure:

  1. clientId is defined
  2. redirectUri is defined
  3. your environment variables are loaded correctly

Best Practices

  1. Configure once:

Create the library instance once and export it from a shared file.

export const closai = configure({
  clientId: import.meta.env.VITE_CLOSAI_CLIENT_ID || "",
  redirectUri: import.meta.env.VITE_CLOSAI_REDIRECT_URI || "",
});

Notes

  • clientId and redirectUri must be defined to the login behavior (No popup)
  • your environment variables must be loaded correctly
  1. Keep secrets out of the frontend

Only public integration values such as client ID and redirect URI should live in the frontend. Any session creation or secure validation should happen on your backend.

  1. Validate callback results

Always check whether userId exists before continuing.

const { userId } = closai.parseOAuthCallback(window.location.search);

if (!userId) {
  // handle failed login
}
  1. Use a dedicated callback route

Create a route only for Closai auth callbacks, such as:

/auth/closai/callback

This keeps the flow easier to maintain.

Troubleshooting

userId is null

Check whether:

  1. Closai redirected to the correct redirectUri
  2. the callback URL includes ?userId=...
  3. you passed window.location.search into parseOAuthCallback

Clicking login does nothing

Check whether:

  1. VITE_CLOSAI_CLIENT_ID is defined
  2. VITE_CLOSAI_REDIRECT_URI is defined
  3. the library was configured correctly
  4. the code is running in a browser

Redirect URI mismatch

Make sure the exact same callback URL is registered on the Closai side and used in your app configuration.

Example README Snippet for Consumers

import { configure } from "closai-partner-integration";

const closai = configure({
  clientId: import.meta.env.VITE_CLOSAI_CLIENT_ID || "",
  redirectUri: import.meta.env.VITE_CLOSAI_REDIRECT_URI || "",
});

function loginWithClosai() {
  closai.login();
}

function handleClosaiCallback() {
  const { userId } = closai.parseOAuthCallback(window.location.search);

  if (!userId) {
    throw new Error("Missing Closai userId");
  }

  return userId;
}

Components

Closai Components is a framework-free JavaScript UI library designed to render closet-based product interfaces such as carousel, grid, and list views using pure HTML, CSS, and JavaScript.

No React. No Vue. No dependencies.


✨ Features

  • ⚡ Zero dependencies (vanilla JS)

  • 🧩 Easy to integrate into any project

  • 🎨 Fully customizable via props and styles

  • 🛍️ Built for product/closet UIs

  • 📦 Supports:

    • Carousel
    • Grid
    • List
  • 🧠 Flexible data mapping (custom keys)


🎨 Required CSS (Important)

Since this is a pure JavaScript library, you must manually include the base styles.

Option 1 — Using HTML

<link
  rel="stylesheet"
  href="node_modules/closai-partner-integration/dist/styles/base.css"
/>

Option 2 — Using bundlers (Vite, Webpack, etc.)

import "closai-partner-integration/dist/styles/base.css";

⚠️ Important

  • This CSS file is required for proper layout and styling
  • Without it, components may render incorrectly
  • You can override styles after importing it

🚀 Quick Start

Include the library:

<script src="/src/index.js"></script>

or

import { renderClosetGrid } from "closai-partner-integration";

Create a container:

<div id="grid"></div>

Render a component:

renderClosetGrid("#grid", products);

📊 Data Structure

Your product data should follow a structure like:

const products = [
  {
    id: "1",
    name: "Black Jacket",
    brand: "Zara",
    price: 129.9,
    image: "https://via.placeholder.com/300x400",
  },
];

🧩 Components

1. Carousel

renderClosetCarousel("#carousel", products, {
  onItemClick: function (product) {
    console.log(product);
  },
});

2. Grid

renderClosetGrid("#grid", products);

3. List

renderClosetList("#list", products);

⚙️ Configuration Options

You can fully customize how data is mapped and displayed:

renderClosetGrid("#grid", products, {
  idKey: "id",
  titleKey: "name",
  subtitleKey: "brand",
  priceKey: "price",
  imageKey: "image",
  currency: "$",
});

🎨 Custom Props

You can pass additional props to customize behavior and styling.

Example:

renderClosetGrid("#grid", products, {
  props: {
    id: "custom-card",
    customStyle: {
      border: "2px solid black",
      borderRadius: "20px",
    },
  },
});

Supported Props

| Prop | Type | Description | | ------------- | ------------- | ------------------------------ | | id | string | Custom DOM id for each card | | customStyle | object/string | Inline styles applied to cards |


🖱️ Events

onItemClick

renderClosetGrid("#grid", products, {
  onItemClick: function (product) {
    console.log("Clicked:", product);
  },
});

🧠 Advanced Usage

Custom Data Keys

If your API returns different field names:

renderClosetGrid("#grid", products, {
  titleKey: "product_name",
  subtitleKey: "designer",
  imageKey: "thumbnail",
  priceKey: "amount",
});

Empty State

If no products are passed:

renderClosetGrid("#grid", [], {
  emptyMessage: "No items in your closet yet",
});

🎯 Use Cases

  • Fashion apps
  • Closet / wardrobe systems
  • E-commerce widgets
  • Embedded product UIs
  • SDK integrations (like Closai 👀)