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

@trindtech/posts

v0.1.5

Published

Cliente JS/TS pra geração de carrosséis pra Instagram via IA. Integrável em qualquer app via API key.

Readme

@trindtech/posts

Cliente TS/JS pra API do Posties. Geração de carrosséis pra Instagram via IA, em qualquer aplicação.

Instalação

yarn add @trindtech/posts
# ou
npm install @trindtech/posts
# ou
pnpm add @trindtech/posts

Funciona em Node.js 18+, edge runtimes (Cloudflare Workers, Vercel Edge), browsers (Vite, webpack, etc — desde que a API key fique no servidor).

Uso

import { PostiesClient } from "@trindtech/posts"

const posties = new PostiesClient({
  apiKey: process.env.POSTIES_API_KEY!, // formato: pst_...
  // baseUrl: "https://posts.trindtech.com.br",  // default; troque em dev
})

// 1. Pega/inicializa a brand da workspace
const brand = await posties.brand.get()

// 2. Atualiza brand (parcial)
await posties.brand.update({
  name: "Minha Empresa",
  niche: "SaaS B2B",
  colorPrimary: "#3b82f6",
})

// 3. Gera ideias de tópicos
const { ideas } = await posties.ideas.generate(10)

// 4. Gera carrossel completo (sync, ~10-30s)
const carousel = await posties.carousels.generate({
  topic: "5 erros que travam onboarding de SaaS",
  numSlides: 8,
  template: "MODERN_BG", // gera bg image via IA
})

// 5. Regenera 1 slide específico OU a imagem
await posties.carousels.regenerate(carousel.id, {
  slide: { index: 2, instruction: "deixa mais punchy, com exemplo concreto" },
})

await posties.carousels.regenerate(carousel.id, {
  image: { instruction: "tons mais escuros, paleta mais minimalista" },
})

// 6. Lista carrosséis
const list = await posties.carousels.list()

// 7. Atualiza caption/hashtags manualmente
await posties.carousels.update(carousel.id, {
  caption: "Minha caption editada",
  hashtags: ["#saas", "#onboarding"],
})

// 8. Deleta
await posties.carousels.delete(carousel.id)

// 9. Análise de perfil IG: cola bio + posts e calibra a brand
const insights = await posties.analyze.profile({
  bio: "Te ajudo a organizar suas finanças sem planilha.",
  samplePosts: [
    "Caption do primeiro post...",
    "Caption do segundo...",
  ],
})
console.log(insights.toneAnalysis, insights.suggestedAdjustments)

Auth

A API key vai como Authorization: Bearer pst_.... Crie em /settings/api-keys dentro do app Posties.

A key é mostrada apenas uma vez na criação. Guarde em variável de ambiente segura.

Erros

Falhas viram PostiesError com status e message:

import { PostiesError } from "@trindtech/posts"

try {
  await posties.carousels.generate({ topic: "..." })
} catch (err) {
  if (err instanceof PostiesError) {
    console.log(err.status, err.message)
    // 401: api key inválida/revogada/expirada
    // 422: validação (tópico curto demais, etc)
    // 500: falha interna (LLM/Imagen down, etc)
  }
}

Hooks React (@trindtech/posts/react)

Sub-export que expõe um Provider + hooks do React Query envolvendo o PostiesClient. Use pra acelerar a integração em apps React (Next.js, Remix, Vite, etc).

import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import {
  PostiesProvider,
  useBrand,
  useCarousels,
  useGenerateCarousel,
  useRegenerateCarousel,
} from "@trindtech/posts/react"
import { PostiesClient } from "@trindtech/posts"

Setup do Provider

⚠️ Crítico de segurança: a API key NUNCA pode ir pro browser. Os hooks precisam de um PostiesClient — escolha um dos 2 caminhos abaixo dependendo de onde sua app roda.

Caminho A — Ambiente trusted (Node, scripts, CLI, app interno)

A app inteira roda em ambiente seguro (não tem browser exposto). Cria o client direto com a key:

const postiesClient = new PostiesClient({
  apiKey: process.env.POSTIES_API_KEY!,
})

<QueryClientProvider client={queryClient}>
  <PostiesProvider client={postiesClient}>
    <App />
  </PostiesProvider>
</QueryClientProvider>

Caminho B — Web app com browser (recomendado pra Next.js / Remix)

A key fica no servidor; o client browser usa um fetch custom que chama uma rota proxy do seu app. O proxy adiciona a key.

1. Cria proxy server-side (Next.js exemplo, em app/api/posties/[...path]/route.ts):

import { NextRequest } from "next/server"

export async function GET(req: NextRequest) {
  return proxy(req)
}
export const POST = GET
export const PATCH = GET
export const DELETE = GET

async function proxy(req: NextRequest) {
  const path = req.nextUrl.pathname.replace(/^\/api\/posties/, "")
  const url = `https://posts.trindtech.com.br/api${path}${req.nextUrl.search}`
  const body = req.method === "GET" || req.method === "DELETE"
    ? undefined
    : await req.text()
  return fetch(url, {
    method: req.method,
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.POSTIES_API_KEY}`,
    },
    body,
  })
}

2. Cria client browser apontando pro proxy:

"use client"
import { PostiesClient } from "@trindtech/posts"

export const postiesClient = new PostiesClient({
  apiKey: "ignored",  // não usa, key fica no proxy
  baseUrl: "/api/posties",  // proxy do app
  apiPath: "",  // CRÍTICO: o proxy já recebe path sem `/api` extra
})

// Detalhe: o SDK ainda tenta mandar `Authorization: Bearer ignored` —
// o proxy ignora e adiciona o seu próprio. Funciona.
//
// `apiPath: ""` evita que o SDK prepende `/api` à URL — sem isso, a
// chamada chega no proxy como `/api/posties/api/autopost/...` (com dup).

3. Setup do Provider (igual caminho A):

<QueryClientProvider client={queryClient}>
  <PostiesProvider client={postiesClient}>
    <App />
  </PostiesProvider>
</QueryClientProvider>

Hooks disponíveis

function MyApp() {
  // Queries — cache automático via React Query
  const { data: brand, isLoading } = useBrand()
  const { data: carousels = [] } = useCarousels()
  const { data: carousel } = useCarousel(carouselId)

  // Mutations — invalidam cache relacionado automaticamente no onSuccess
  const updateBrand = useUpdateBrand()
  const generate = useGenerateCarousel()
  const regenerate = useRegenerateCarousel(carouselId)
  const updateCarousel = useUpdateCarousel(carouselId)
  const deleteCarousel = useDeleteCarousel()
  const generateIdeas = useGenerateIdeas()
  const analyze = useAnalyzeProfile()

  return (
    <button
      onClick={() => generate.mutate({
        topic: "3 erros que matam reserva",
        template: "MODERN_BG",
      })}
    >
      {generate.isPending ? "Gerando..." : "Gerar"}
    </button>
  )
}

Cada mutation aceita options (onSuccess, onError) que rodam depois da invalidação interna de cache:

const generate = useGenerateCarousel({
  onSuccess: (carousel) => {
    toast.success(`Gerado: ${carousel.id}`)
    router.push(`/autopost/${carousel.id}`)
  },
})

Query keys

Pra invalidação manual:

import { postiesQueryKeys } from "@trindtech/posts/react"

queryClient.invalidateQueries({ queryKey: postiesQueryKeys.brand() })
queryClient.invalidateQueries({ queryKey: postiesQueryKeys.carousels() })
queryClient.invalidateQueries({
  queryKey: postiesQueryKeys.carousel(carouselId),
})

Peer deps

react ^18 || ^19 e @tanstack/react-query ^5. Marcadas como optional — se você só usa o SDK vanilla (sem hooks), não precisa instalar.

⚠️ Aviso pra desenvolvimento local com file: path

Se você está consumindo o SDK via "file:../caminho/do/sdk" (em vez do npm), yarn copia o node_modules inteiro do SDK pro seu projeto — incluindo @tanstack/react-query em devDeps. Isso cria duas instâncias de React Query, causando erro No QueryClient set mesmo com Provider configurado.

Fix: depois de yarn install, limpa o duplicado:

rm -rf node_modules/@trindtech/posts/node_modules

Ou usa via npm (yarn add @trindtech/posts) — peer deps resolvem corretamente do parent, sem duplicação.

Curl direto

Tudo abaixo funciona sem o SDK (útil pra debug e linguagens sem cliente):

# Lista carrosséis
curl https://posts.trindtech.com.br/api/autopost/carousels \
  -H "Authorization: Bearer pst_..."

# Gera novo
curl -X POST https://posts.trindtech.com.br/api/autopost/carousels \
  -H "Authorization: Bearer pst_..." \
  -H "Content-Type: application/json" \
  -d '{"topic":"3 erros que matam onboarding","template":"MODERN_BG"}'

Comportamento sync

A geração de carrossel é síncrona — o request bloqueia até o LLM terminar (texto: ~5-10s) e o Imagen renderizar (~10-15s pra MODERN_BG). Total: 10-30s por carrossel.

Pra workloads em batch (>10 carrosséis em sequência), considere paralelizar com Promise.all ou implementar fila no seu lado. Webhooks chegam na v2.

Limites

  • Workspaces: cada API key é escopo de UMA workspace. Pra multi-tenant no seu lado, gere uma key por tenant.
  • Carrosséis: máx 10 slides cada
  • Tópico: 3 a 200 caracteres
  • Hashtags: máx 30

Publicar nova versão (mantenedor)

# 1. Bump da versão no package.json (semver)
# 2. Build + publish (prepublishOnly roda build automático)
yarn npm publish --access public
# ou (se o login npm já está feito):
npm publish

# 3. Tag git pra rastreabilidade
git tag -a sdk-v0.0.2 -m "sdk 0.0.2: …"
git push --tags

prepublishOnly garante que o dist/ está atualizado antes de publicar. Se quiser ver o que será publicado sem subir:

npm pack --dry-run