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

@qorus-test3/site-components

v0.1.9

Published

Reusable React components for Qorus sites

Readme

@qorus/site-components

Librería piloto de componentes React reutilizables para páginas Qorus/Base44. No incluye router, autenticación de producto ni prerender.

Uso

npm install
npm run dev

El segundo comando abre el playground con los tres componentes y un submit simulado. Para consumir la librería:

import {
  QorusSiteProvider,
  QorusItemList,
  QorusAppointment,
  QorusContactForm,
} from "@qorus/site-components";

const appConfig = {
  publicHost: import.meta.env.VITE_PXP_PUBLIC_HOST,
  // Puede ser la URL temporal de Base44 o del ambiente de pruebas.
  siteUrl: "https://profile.9jul6-dev.qorus.io",
};

<QorusSiteProvider
  config={appConfig}
>
  <QorusItemList title="Portfolio" items={items} />
  <QorusAppointment label="Reservar cita" />
  <QorusContactForm />
</QorusSiteProvider>;

QorusSiteProvider acepta esas claves mediante config o como props directas, además de submitPath, siteLookupPath y submitAdapter.

La URL del sitio se resuelve en este orden:

  1. config.siteUrl, para Base44, sandboxes y pruebas manuales;
  2. VITE_SITE_URL, para el pipeline de prerender;
  3. window.location.href, únicamente después de montar en el navegador;
  4. "" en Node si no existe ninguno de los anteriores.

El bundle ESM preserva literalmente import.meta.env, para que estas variables sean resueltas por el build Vite de la aplicación consumidora y no queden congeladas al momento de publicar @qorus-test3/site-components.

La detección del navegador se difiere hasta useEffect, por lo que el primer render del cliente coincide con el HTML generado en Node y no causa errores de hidratación. En prerender, los efectos de red y la inyección del script de citas no se ejecutan.

Cuando hay una URL resuelta, el provider abre una sesión PKCE del API Gateway (POST /Public/session → POST /Public/token) y recién entonces consulta DEFAULT_SITE_LOOKUP_PATH?baseUrl=<siteUrl> con Authorization: Bearer. La sesión inicial usa gatewayEmbedId (o el embed de onboarding por defecto). La respuesta alimenta siteId, vendorId, locationId, baseUrl, site y env. Los embeds quedan separados de forma explícita:

  • gatewayEmbedId: sesión PKCE del API Gateway (EMBED_ID_QORD_PROFILE, o config.gatewayEmbedId / config.embedId / VITE_ONBOARDING_EMBED_ID). embedId se mantiene como alias de este valor para compatibilidad.
  • appointmentEmbedId: widget de citas (EMBED_ID_APPOINTMENT_WIDGET). Nunca cae al embed del gateway: si el lookup no lo trae, usa ap_de8a96b911fddd94ad9f9e0bc6ee4734_mrdjnamn.
  • formEmbedId: metadata del formulario (EMBED_ID_QORD_BASIC_FORM); la sesión del submit usa gatewayEmbedId, no este valor.

Cada componente usa el identificador que le corresponde. El provider aplica de inmediato los fallbacks siteId 1708, vendorId "1538" y locationId 75 para no bloquear la UI; si el lookup llega, los reemplaza. Si falla, esos valores se mantienen y el error queda en useQorusSite().error.

Para uso directo del SDK, getSiteByBaseUrl(options) es la variante segura: retorna null si falta URL o si falla sesión/red. La función estricta getSiteConfigByBaseUrl(options) conserva errores para consumidores que necesitan manejarlos explícitamente.

Si el gateway de ese sitio requiere otro embed, embedIdEnvKey permite indicar la clave de env, por ejemplo "EMBED_ID_PROFILE". Los valores explícitos en props del componente conservan la máxima prioridad. El provider también acepta locationImpl, siteLookupClient y fetchImpl para pruebas. El playground muestra el patrón de variables en playground/src/appConfig.js.

Componentes

QorusItemList

  • title, subtitle: encabezado opcional.
  • visible: al ser false no renderiza nada.
  • preset: "promotions" | "members" | "packages". Usa el endpoint real, el método y los defaultParams de ITEM_PRESETS. Los overrides de props (limit, _vendorId, _locationId, locationId, params, …) se combinan encima de esos defaults.
  • endpoint / method: sustituyen el path o el verbo del preset.
  • items: array o respuesta envolvente ({ data }, { result }) cuando no hay fetch. autoMapList extrae la lista y, si hay preset, aplica el mapeo unificado.
  • Los presets aceptan las dos convenciones de Qorus para un mismo campo: packageName o PACKAGE_NAME, promoCode o PROMO_CODE, etc. El precio se busca en price, amount, PACKAGE_PRICE y variantes; si el endpoint no devuelve precio, la tarjeta omite esa línea en vez de quedar vacía.
  • Sin fields ni renderItem, cada tarjeta usa las propiedades normalizadas title, subtitle, badge, date y price.
  • renderItem={(item) => …} o fields={["title", "raw.email"]} permiten leer cualquier campo original en item.raw[fieldName].
  • className, itemClassName: clases para que el sitio aplique su diseño.

Los ítems con order se ordenan primero, de menor a mayor. La lista conserva HTML semántico (section, ul, li, article).

Las herramientas no-code pueden leer @qorus/site-components/schemas/QorusItemList.schema.json.

<QorusItemList
  title="Promotions"
  preset="promotions"
  _vendorId="19"
  limit={10}
/>;

<QorusItemList
  title="Members"
  preset="members"
  renderItem={(item) => (
    <article>
      <h3>{item.title}</h3>
      <p>{item.raw.email}</p>
    </article>
  )}
/>;

QorusAppointment

Renderiza un button, no un enlace. Props principales: label, onTrigger, widgetClass (default appointment-widget), eventName (default qorus:appointment-trigger), className, disabled, siteId y embedId.

La URL del script y la clase salen de APP_CONFIG: primero site.config.APPOINTMENT_WIDGET_SCRIPT_URL / APPOINTMENT_WIDGET_DEFAULT_CLASS, luego VITE_APPOINTMENT_WIDGET_SCRIPT_URL, y por último https://widgets-dev.qorus.io/appointment/appointment-widget.js y appointment-widget. El botón incluye data-qorus-widget="appointment", data-site-id (fallback 1708), data-embed-id y data-vendor-id. data-embed-id usa appointmentEmbedId (nunca el embed del gateway): prop embedId → site.config.appointmentEmbedId (EMBED_ID_APPOINTMENT_WIDGET) → ap_de8a96b911fddd94ad9f9e0bc6ee4734_mrdjnamn. Ese mismo valor viaja como config.embedId en onTrigger / el CustomEvent, que es lo que el widget manda a getProviderDetails. vendorId se resuelve: prop → site.config.vendorId → VITE_VENDOR_ID → "1538".

El host puede integrar el widget de dos formas:

<QorusAppointment
  label="Agendar"
  onTrigger={({ config, widgetClass, event }) => openWidget(config)}
/>;

Si no se pasa onTrigger, el botón emite en window un CustomEvent con el nombre de eventName; su detail contiene { config, widgetClass, nativeEvent }. También conserva widgetClass como clase CSS para scripts existentes que escuchan clicks delegados. El playground pasa un callback y muestra un stub visible.

QorusContactForm

Por defecto incluye name, email, phone y message. El teléfono se formatea mientras se escribe como xxx-xxx-xxxx (hasta 10 dígitos) y usa placeholder/pattern nativos para ese formato. fields acepta objetos con name, label, type (text, email, tel, textarea o select), required, placeholder, autoComplete, rows, options, defaultValue e inputProps.

También acepta defaultValues, submitLabel, source, pageUrl, submitAdapter, onSuccess, onError y textos de éxito/error. className y slots permiten controlar el look sin imponerlo:

<QorusContactForm
  fields={[
    { name: "fullName", label: "Nombre", required: true },
    { name: "topic", label: "Tema", type: "select", options: ["Ventas", "Otro"] },
  ]}
  slots={{
    form: "form",
    field: "field",
    label: "label",
    input: "input",
    actions: "actions",
    submit: "button",
    status: "status",
  }}
/>;

El estado mínimo incluye loading, success y error.

Flujo real de envío

createQorusSubmitAdapter implementa el wiring real:

  1. crea PKCE y hace POST /Public/session con config.gatewayEmbedId (no con appointmentEmbedId);
  2. intercambia el verifier con POST /Public/token;
  3. resuelve un siteId entero positivo desde fields.siteId, config.siteId, VITE_SITE_ID o el fallback 1708, en ese orden;
  4. hace POST /api/hq-nd/ContentType/submitOpenHouseOptin con { siteId, ...fields }, preservando todos los campos del formulario.

Las llamadas autenticadas llevan el bearer token del gateway. Se puede inyectar fetchImpl, cryptoImpl o un client para pruebas. El adapter mock del playground permite probar la UI sin credenciales; quitándolo del provider se activa el adapter real. Las APIs de navegador se consultan sólo al disparar el flujo, no durante importación o render.

Build

npm run build
npm run build:playground

React y React DOM son peerDependencies; no se empaquetan en la librería.

Datos para agentes

useQorusData({ endpoint, method, params, preset, siteConfig }) (o la forma corta useQorusData(endpoint, siteConfig)) aisla PKCE/gateway y devuelve { items, loading, error } ya normalizados. autoMapList(response, preset) desenvuelve { data } / { result } / arrays y mapea promotions, members o packages a title, subtitle, badge, date, price y raw.