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

@report-platform/sdk

v0.2.0

Published

Widget embutível de relatórios/propostas da Report Platform (editor de documentos orientado a blocos).

Readme

@report-platform/sdk

Widget embutível de relatórios e propostas da Report Platform: um editor de documentos orientado a blocos que empresas de construção usam para compor, revisar e exportar propostas a partir dos dados estruturados de clientes, obras e orçamentos. O host embute o editor como biblioteca — sem router, login ou navegação próprios — passando apenas um token temporário, o reportType e os data; o usuário compõe o documento visualmente, reorganiza blocos, controla campos e colunas, salva modelos reutilizáveis e exporta o PDF.

A arquitetura é híbrida e declarativa: ProseMirror gerencia a árvore editável (seleção, transações, undo/redo), JSON Schema valida dados e configurações e uma DSL JSON descreve a composição visual dos blocos. Regras sensíveis, autorização, uploads e geração de PDF ficam no backend multiempresa da plataforma. Nenhum JSON vindo de cadastro executa código, e editor, prévia e PDF compartilham a mesma árvore de renderização.

Instalação

npm install @report-platform/sdk
# ou
pnpm add @report-platform/sdk

O pacote traz duas entradas, com o mesmo contrato de opções, callbacks e capacidades:

| Entrada | Para quem | Runtime | | --------------------------------- | ------------------------------------------------ | --------------------------------------------------------- | | @report-platform/sdk | aplicações React (18 ou 19) | react e react-dom são peer dependencies do host | | @report-platform/sdk/standalone | qualquer página/host que não é uma app React | React, ReactDOM e ProseMirror vêm embutidos no bundle |

Ambas são distribuídas em ESM (.mjs) e CommonJS (.cjs), com declarações TypeScript autocontidas (.d.ts/.d.cts) — nenhum declare module é necessário, em qualquer moduleResolution (bundler, node16/nodenext ou node). O manifesto declara react/react-dom como peer dependencies do pacote inteiro; a entrada standalone não os usa em runtime (traz o próprio React), mas gerenciadores que instalam peers automaticamente (npm 7+) vão baixá-los mesmo num host sem React — é inofensivo.

Pré-requisito: o token vem do SEU backend

O widget nunca conhece o clientSecret da sua empresa. Seu backend troca a credencial por um JWT temporário (TTL ≤ 15 min, sem refresh token) e o entrega ao navegador; é esse token que o widget recebe. A troca é servidor-a-servidor — o endpoint não responde a CORS de navegador, por construção:

curl -sS -X POST "https://api-production-7db79.up.railway.app/v1/auth/editor-tokens" \
  -H "Authorization: Basic $(printf '%s:%s' "$CLIENT_ID" "$CLIENT_SECRET" | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "usuario-42",
    "reports": [
      { "reportType": "maiscontrole.proposal",
        "scopes": ["editor:read", "templates:read", "templates:create"] }
    ]
  }'

Peça o mínimo de escopos que a tela precisa. Lista fechada: editor:read, templates:read, templates:create, templates:update-own, templates:set-default, assets:create, export:pdf.

O data do relatório também é derivado no seu backend e validado pela plataforma contra o dataSchema publicado do reportType. A sessão do editor guarda apenas o hash do data e o documento de trabalho persistido nunca contém dados; somente os snapshots de prévia/exportação congelam os dados validados (para gerar o PDF) e são purgados pela plataforma.

Uso — host sem React (mountReportEditor)

import { mountReportEditor } from "@report-platform/sdk/standalone";

const handle = mountReportEditor(document.getElementById("editor"), {
  token, // JWT temporário vindo do SEU backend
  reportType: "maiscontrole.proposal",
  data, // derivado no SEU backend
  documentKey: "orcamento-18", // opcional: a plataforma persiste e restaura o documento
  onReady(context) {
    /* sessionId, permissões efetivas, modelos disponíveis, avisos… */
  },
  onAuthRequired() {
    renovarToken().then((novo) => handle.setToken(novo));
  },
  onConflict(conflict) {
    // Três variantes, discriminadas por conflict.kind:
    //  "data-update"           → keepLocal() | acceptIncoming()
    //  "working-document-save" → keepLocal() | acceptRemote()
    //  "template-save"         → informativo (currentVersion, lastAuthor)
    // Sem resposta, o documento local é preservado.
  },
  onError(error) {
    console.warn(error.code, error.message); // código estável + mensagem pt-BR
  },
});

// mais tarde
const documento = handle.getDocument();
handle.unmount(); // limpo e idempotente

Todo o DOM do widget — inclusive o <style> com escopo e os overlays — é renderizado dentro do filho que o SDK cria no seu container. Nada é injetado em document.head; o único toque em document.body é o <a download> oculto, imediatamente removido, que dispara o download do PDF exportado.

Uso — aplicação React

import { useMemo, useRef } from "react";
import {
  ReportEditor,
  ReportSdkProvider,
  createReportClient,
  type ReportEditorHandle,
  type ReportEditorProps,
} from "@report-platform/sdk";

type Props = { token: string; data: ReportEditorProps["data"] };

export function PropostaEditor({ token, data }: Props) {
  const client = useMemo(() => createReportClient(), []); // API de produção por padrão
  const handleRef = useRef<ReportEditorHandle | null>(null);

  return (
    <ReportSdkProvider client={client}>
      <ReportEditor
        ref={handleRef}
        token={token}
        reportType="maiscontrole.proposal"
        data={data}
        documentKey="orcamento-18"
        onReady={(ctx) => console.info("sessão", ctx.sessionId)}
        onAuthRequired={() => renovarToken().then((novo) => handleRef.current?.setToken(novo))}
        onError={(error) => console.warn(error.code, error.message)}
      />
    </ReportSdkProvider>
  );
}

createReportClient é chamado uma vez por host; o provider carrega apenas o client — nenhum token, dado ou estado de sessão vive nele.

apiBaseUrl — opcional, produção por padrão

Sem apiBaseUrl, o SDK fala com a API de produção da plataforma:

import { DEFAULT_API_BASE_URL } from "@report-platform/sdk"; // também exportado de /standalone
// "https://api-production-7db79.up.railway.app"

Esse é hoje o domínio de andaime do Railway; quando o domínio definitivo da plataforma entrar no ar, o default muda em nova versão do SDK e o domínio anterior continua respondendo durante a janela de suporte. Instalações que precisam de estabilidade absoluta devem passar apiBaseUrl explicitamente.

Para outro ambiente (desenvolvimento local, homologação, instalação própria), informe a base explicitamente — em qualquer uma das entradas:

createReportClient({ apiBaseUrl: "http://localhost:3001" });
mountReportEditor(el, { apiBaseUrl: "http://localhost:3001", token, reportType, data });

Um valor em branco é rejeitado com TypeError (configuração errada falha alto; nunca há fallback silencioso). O SDK nunca deriva a URL de window.location.

Opções, callbacks e handle

Opções de mountReportEditor. As props de ReportEditor (React) são as mesmas, com três diferenças: apiBaseUrl, fetch e validationLimits pertencem a createReportClient na entrada React; previewDebounceMs (padrão 2000 ms) e templatesToolbarTarget existem só na entrada React:

| Opção | Tipo | Descrição | | -------------------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------- | | token | string | JWT temporário — só em memória, enviado como Authorization: Bearer. Trocá-lo segue o fluxo de setToken | | reportType | string | Chave estável da definição do relatório (ex.: maiscontrole.proposal) | | data | JsonObject | Dados do relatório, validados contra o schema publicado. Trocá-los (por referência) segue o fluxo updateData | | documentKey | string? | Identidade do documento de negócio (1–200 caracteres). Presente → a plataforma persiste, restaura e faz autosave | | autosaveDebounceMs | number? | Debounce do autosave quando documentKey está presente (padrão 3000 ms) | | apiBaseUrl | string? | Base da API (padrão: produção — acima) | | fetch | FetchLike? | Implementação de fetch injetável (testes, proxies) | | validationLimits | Partial<ValidationLimits>? | Ajustes dos limites estruturais de validação (bytes/profundidade/nós) |

Callbacks (ReportEditorCallbacks): onReady, onChange ({ revision, dirty }, com throttle — nunca carrega o documento, puxe com getDocument()), onDirtyChange, onTemplateSaved, onAuthRequired, onConflict, onWarning, onError, onTelemetry (100% opt-in; sem token, dado ou conteúdo de documento).

Handle (ReportEditorMountHandle no standalone; ReportEditorHandle via ref no React): getDocument(), undo(), redo(), setToken(token), updateData(data); no standalone ainda update(options) e unmount(); no React ainda getTemplateActions(), setPreviewOpen(open) e getExportController() para hosts que dirigem Prévia/Exportar do próprio toolbar.

Erros chegam por onError como ReportEditorErrorcode estável (AUTH_REQUIRED, FORBIDDEN_REPORT, INVALID_DATA, INVALID_CONFIG, INVALID_DOCUMENT, PROTOCOL_INCOMPATIBLE, PRIMITIVES_UNSUPPORTED, CONFLICT, RATE_LIMITED, INTERNAL), message em pt-BR, issues[] com JSON Pointer por campo e requestId. AUTH_REQUIRED no bootstrap é roteado para onAuthRequired.

CSP da página do host

| Diretiva | Valor necessário | Por quê | | ------------- | ----------------------------------------------------- | --------------------------------------------------------------------- | | connect-src | origem da API | bootstrap, modelos, uploads e exportação via fetch/XHR | | frame-src | origem da API | a prévia paginada é um iframe da rota SSR de impressão | | img-src | origem da API + origem do storage assinado (S3/MinIO) | imagens da galeria são servidas por URLs assinadas de curta duração | | style-src | 'unsafe-inline' | o widget injeta seu stylesheet com escopo dentro do próprio container |

Nenhum cookie é usado; o SDK não carrega script remoto.

Compatibilidade

  • Navegadores evergreen (bundle es2022; o standalone não depende de process.env).
  • React 18 ou 19 (só na entrada React).
  • O SDK negocia protocolo, schema de documento e conjunto de primitivas com a API a cada bootstrap; incompatibilidades chegam tipadas (PROTOCOL_INCOMPATIBLE, INVALID_DOCUMENT, PRIMITIVES_UNSUPPORTED) e nunca renderizam um documento pela metade — bloco desconhecido abre em fallback somente leitura com os atributos preservados.
  • Versionamento SemVer; a superfície pública tipada (.d.ts) é o contrato.

Licença

UNLICENSED — todos os direitos reservados. Uso mediante credencial emitida pela Report Platform.