@bacano/sdk 1.1.15 → 1.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/STOREFRONT_CONTENT_LOCATOR.md +111 -0
- package/STOREFRONT_FORMS.md +211 -0
- package/client.d.ts +8 -1
- package/content-entry-locator.d.ts +16 -0
- package/index.cjs +195 -2
- package/index.d.ts +3 -1
- package/index.js +195 -2
- package/modules/forms.d.ts +9 -0
- package/package.json +6 -1
- package/react/form-field-control.d.ts +11 -0
- package/react/forms.cjs +460 -0
- package/react/forms.d.ts +49 -0
- package/react/forms.js +446 -0
- package/react.cjs +172 -2
- package/react.js +172 -2
- package/skills/bacano-figma-web/SKILL.md +8 -0
- package/skills/bacano-figma-web/references/bacano-mapping.md +5 -3
- package/skills/bacano-figma-web/references/content-workflow.md +11 -1
- package/skills/bacano-figma-web/references/implementation-quality.md +12 -0
- package/skills/bacano-web/SKILL.md +7 -1
- package/skills/bacano-web/references/common.md +7 -0
- package/skills/bacano-web/references/forms.md +67 -0
- package/skills/bacano-web/references/informational.md +3 -0
- package/types.d.ts +87 -0
package/README.md
CHANGED
|
@@ -112,6 +112,20 @@ Commercial forms and newsletter subscriptions must follow
|
|
|
112
112
|
Bacano owns consent evidence, identity resolution, attribution and idempotent
|
|
113
113
|
lead capture through the SDK.
|
|
114
114
|
|
|
115
|
+
Website forms defined and published from Bacano use the additive contract in
|
|
116
|
+
[`STOREFRONT_FORMS.md`](./STOREFRONT_FORMS.md). Storefronts can mount the
|
|
117
|
+
official `BacanoForm` renderer or consume the same typed definition through
|
|
118
|
+
`useBacanoForm()` without GraphQL, HTML stored in the database or a rebuild
|
|
119
|
+
after each publication.
|
|
120
|
+
|
|
121
|
+
Every visible region sourced from a Bacano `ContentEntry` must expose the
|
|
122
|
+
entry-level marker defined in
|
|
123
|
+
[`STOREFRONT_CONTENT_LOCATOR.md`](./STOREFRONT_CONTENT_LOCATOR.md). Use
|
|
124
|
+
`getContentEntryLocatorAttributes(entry)` on the outer section or card so an
|
|
125
|
+
authorized internal browser tool can show the entry name and open its Bacano
|
|
126
|
+
editor. This contract identifies entries only; it does not expose field-level
|
|
127
|
+
editing or grant backoffice access.
|
|
128
|
+
|
|
115
129
|
Static and hybrid stores must also follow
|
|
116
130
|
[`STOREFRONT_PERFORMANCE.md`](./STOREFRONT_PERFORMANCE.md). It defines the
|
|
117
131
|
official build bootstrap, live-state refresh, responsive media and request
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Localizador de entradas de Content
|
|
2
|
+
|
|
3
|
+
Este documento define el contrato público V1 que permite reconocer desde el
|
|
4
|
+
navegador qué entrada de Bacano Content produce una región visible de una web.
|
|
5
|
+
El objetivo es únicamente **identificar la entrada y abrir su editor**. No es
|
|
6
|
+
edición inline y no identifica campos individuales.
|
|
7
|
+
|
|
8
|
+
## Contrato obligatorio
|
|
9
|
+
|
|
10
|
+
Toda región visual cuyo contenido editorial provenga de un `ContentEntry` debe
|
|
11
|
+
marcar su elemento DOM exterior significativo con:
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
<section
|
|
15
|
+
data-bacano-content-entry="v1"
|
|
16
|
+
data-bacano-entry-key="seccion-de-servicios"
|
|
17
|
+
data-bacano-entry-name="Sección de servicios"
|
|
18
|
+
data-bacano-entry-locale="es-CO"
|
|
19
|
+
>
|
|
20
|
+
<!-- contenido visible -->
|
|
21
|
+
</section>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Usar el helper oficial evita diferencias en nombres, versión y normalización:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import {
|
|
28
|
+
getContentEntryLocatorAttributes,
|
|
29
|
+
type ContentEntry,
|
|
30
|
+
} from "@bacano/sdk";
|
|
31
|
+
|
|
32
|
+
function ServicesSection({ entry }: { entry: ContentEntry }) {
|
|
33
|
+
return (
|
|
34
|
+
<section {...getContentEntryLocatorAttributes(entry)}>
|
|
35
|
+
{/* La sección puede consumir un view model derivado de entry. */}
|
|
36
|
+
</section>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
El helper falla si la entrada no tiene una `key` estable o un `locale`. Una
|
|
42
|
+
región que no puede abrir una entrada inequívoca no debe aparentar que sí puede.
|
|
43
|
+
|
|
44
|
+
## Granularidad
|
|
45
|
+
|
|
46
|
+
- Marcar una vez el bloque visual administrable: `section`, `article`, banner,
|
|
47
|
+
tarjeta o contenedor equivalente.
|
|
48
|
+
- No marcar títulos, párrafos, botones ni campos individuales dentro del mismo
|
|
49
|
+
bloque.
|
|
50
|
+
- Si una tarjeta visible usa otra entrada referenciada, la tarjeta recibe su
|
|
51
|
+
propio marcador anidado. El espacio restante conserva el marcador de la
|
|
52
|
+
entrada contenedora.
|
|
53
|
+
- Si una entrada produce varias regiones independientes, cada región puede
|
|
54
|
+
repetir el mismo marcador.
|
|
55
|
+
- No marcar texto perteneciente al código/i18n, productos del catálogo,
|
|
56
|
+
configuración pública ni documentos legales como si fueran Content.
|
|
57
|
+
|
|
58
|
+
Los mappers pueden transformar `contentJson`, pero deben conservar el origen
|
|
59
|
+
editorial necesario para llamar el helper. Por ejemplo:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const section = {
|
|
63
|
+
locator: getContentEntryLocatorAttributes(entry),
|
|
64
|
+
title: readTitle(entry.contentJson),
|
|
65
|
+
};
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
<section {...section.locator}>{section.title}</section>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Responsabilidades del consumidor
|
|
73
|
+
|
|
74
|
+
Una extensión o herramienta de inspección:
|
|
75
|
+
|
|
76
|
+
1. busca `[data-bacano-content-entry="v1"]`;
|
|
77
|
+
2. muestra `data-bacano-entry-name` al usuario;
|
|
78
|
+
3. usa `data-bacano-entry-key` y `data-bacano-entry-locale` para construir la
|
|
79
|
+
navegación hacia el backoffice;
|
|
80
|
+
4. deja autenticación, empresa y permisos bajo control de Bacano.
|
|
81
|
+
|
|
82
|
+
El SDK no publica una URL de administración ni abre ventanas. Ese destino
|
|
83
|
+
pertenece a la herramienta interna y puede cambiar entre ambientes.
|
|
84
|
+
|
|
85
|
+
## Seguridad y salida estática
|
|
86
|
+
|
|
87
|
+
Los cuatro atributos contienen exclusivamente identidad editorial ya pública:
|
|
88
|
+
versión, key, nombre y locale de una entrada publicada. Nunca incluir tokens,
|
|
89
|
+
URLs administrativas firmadas, secretos, IDs de usuarios ni datos privados.
|
|
90
|
+
|
|
91
|
+
Los marcadores no conceden permisos. El backoffice debe autenticar y autorizar
|
|
92
|
+
cualquier lectura o modificación. En exports estáticos los atributos quedan
|
|
93
|
+
materializados junto con el contenido publicado y se actualizan en el siguiente
|
|
94
|
+
build.
|
|
95
|
+
|
|
96
|
+
## Verificación
|
|
97
|
+
|
|
98
|
+
Antes de publicar una web que usa Bacano Content:
|
|
99
|
+
|
|
100
|
+
- inventariar las entradas que producen regiones visibles;
|
|
101
|
+
- inspeccionar el HTML final, no solo JSX o componentes;
|
|
102
|
+
- comprobar que cada región tiene exactamente la key, nombre y locale de su
|
|
103
|
+
entrada fuente;
|
|
104
|
+
- probar marcadores anidados cuando una entrada contenedora renderiza entradas
|
|
105
|
+
referenciadas;
|
|
106
|
+
- comprobar que activar una herramienta de localización no altera navegación,
|
|
107
|
+
layout ni accesibilidad cuando vuelve a desactivarse.
|
|
108
|
+
|
|
109
|
+
Una región editorial sin marcador o con una key distinta de su fuente es un
|
|
110
|
+
fallo de integración, porque impide localizar de manera confiable el contenido
|
|
111
|
+
que la web declara administrable.
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Formularios de websites
|
|
2
|
+
|
|
3
|
+
Los formularios públicos de Bacano son definiciones tipadas y versionadas. El
|
|
4
|
+
storefront decide dónde se muestran y cómo lucen; Bacano controla su estructura,
|
|
5
|
+
estado y validaciones. La definición nunca contiene HTML ni JavaScript arbitrario.
|
|
6
|
+
|
|
7
|
+
## Renderer oficial para React
|
|
8
|
+
|
|
9
|
+
Monta un único `BacanoProvider` global y usa la key pública configurada en el ERP:
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { BacanoForm } from "@bacano/sdk/react/forms";
|
|
13
|
+
|
|
14
|
+
export function ContactForm() {
|
|
15
|
+
return (
|
|
16
|
+
<BacanoForm
|
|
17
|
+
formKey="contacto"
|
|
18
|
+
classNames={{ root: "contact-form", field: "contact-field" }}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`BacanoForm` soporta campos directos, secciones visuales y pasos reales,
|
|
25
|
+
validación nativa, opciones manuales, Listas de Content y el envío oficial de
|
|
26
|
+
respuestas. Si se
|
|
27
|
+
omite `onSubmit`, utiliza el mismo `BacanoClient` global, genera una clave
|
|
28
|
+
idempotente estable y llama `client.forms.submitResponse(...)`. Un formulario
|
|
29
|
+
pausado muestra su mensaje de indisponibilidad y deja de operar sin reconstruir
|
|
30
|
+
el storefront.
|
|
31
|
+
|
|
32
|
+
## Estructura del formulario
|
|
33
|
+
|
|
34
|
+
La definición tiene tres niveles explícitos:
|
|
35
|
+
|
|
36
|
+
- `fields`: campos directos de un formulario continuo.
|
|
37
|
+
- `sections`: agrupaciones visuales opcionales dentro del formulario continuo.
|
|
38
|
+
- `steps`: etapas reales de un formulario multipaso. Cada paso puede contener
|
|
39
|
+
campos directos y secciones.
|
|
40
|
+
|
|
41
|
+
Un formulario continuo puede tener campos, secciones o ambos. Cuando existen
|
|
42
|
+
pasos, `fields` y `sections` de la raíz deben estar vacíos: todo el contenido
|
|
43
|
+
pertenece a un paso. Las secciones no crean navegación ni cambian el envío; los
|
|
44
|
+
pasos sí muestran avance y controles para continuar o regresar. Un formulario
|
|
45
|
+
también puede existir sin secciones ni pasos, usando únicamente campos directos.
|
|
46
|
+
|
|
47
|
+
`onSubmit` permanece disponible para experiencias personalizadas. No debe
|
|
48
|
+
usarse para llamar GraphQL, endpoints internos ni crear una persistencia
|
|
49
|
+
paralela.
|
|
50
|
+
|
|
51
|
+
## Renderer personalizado
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { useBacanoState } from "@bacano/sdk/react";
|
|
55
|
+
import { useBacanoForm } from "@bacano/sdk/react/forms";
|
|
56
|
+
|
|
57
|
+
export function BrandedContactForm() {
|
|
58
|
+
const { client } = useBacanoState();
|
|
59
|
+
const { form, loading, error, refresh } = useBacanoForm("contacto");
|
|
60
|
+
|
|
61
|
+
if (loading || !client) return null;
|
|
62
|
+
if (error || !form) return <p>Formulario no disponible.</p>;
|
|
63
|
+
if (form.status === "PAUSED") {
|
|
64
|
+
return <p>{form.definition.unavailableMessage}</p>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const submit = async (values: Record<string, string | string[] | boolean>) =>
|
|
68
|
+
client.forms.submitResponse({
|
|
69
|
+
formKey: form.key,
|
|
70
|
+
formVersion: form.version,
|
|
71
|
+
definitionHash: form.definitionHash,
|
|
72
|
+
idempotencyKey: crypto.randomUUID(),
|
|
73
|
+
values,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return <CustomRenderer definition={form.definition} onSubmit={submit} />;
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
El renderer personalizado puede cambiar componentes y estilos, pero debe usar
|
|
81
|
+
los campos, keys, opciones y validaciones entregados por el SDK. No debe crear
|
|
82
|
+
un segundo cliente, resolver el website nuevamente ni usar GraphQL directo.
|
|
83
|
+
|
|
84
|
+
## Honeypot y confirmación de persistencia
|
|
85
|
+
|
|
86
|
+
`honeypot` es un campo trampa contra bots, no un token de seguridad. No es una
|
|
87
|
+
marca de tiempo, un UUID, un número aleatorio ni un identificador del visitante.
|
|
88
|
+
Un usuario real debe enviarlo siempre vacío.
|
|
89
|
+
|
|
90
|
+
- `BacanoForm` administra el honeypot internamente. El storefront no debe crear
|
|
91
|
+
otro ni asignarle un valor.
|
|
92
|
+
- Un renderer personalizado debe omitir `honeypot` en
|
|
93
|
+
`client.forms.submitResponse(...)` salvo que implemente deliberadamente un
|
|
94
|
+
input trampa oculto. El SDK lo enviará como cadena vacía por defecto.
|
|
95
|
+
- Si se implementa el input trampa, debe quedar fuera del flujo visual y del
|
|
96
|
+
teclado, con autocompletado desactivado. Nunca debe reutilizar un campo real ni
|
|
97
|
+
recibir sugerencias del navegador o de un gestor de contraseñas.
|
|
98
|
+
- Bacano responde de manera deliberadamente genérica ante un honeypot lleno:
|
|
99
|
+
`accepted: true` y `responseId: null`. La respuesta no se persiste y el bot no
|
|
100
|
+
descubre que fue detectado.
|
|
101
|
+
- Para una prueba funcional o aprobación de producción, el envío debe devolver
|
|
102
|
+
`accepted: true` y un UUID en `responseId`, y debe aparecer una sola vez en
|
|
103
|
+
Respuestas dentro de Bacano.
|
|
104
|
+
|
|
105
|
+
No uses `responseId: null` para mostrar información distinta al visitante. Es
|
|
106
|
+
una señal técnica para pruebas y observabilidad segura, no un mensaje que deba
|
|
107
|
+
revelar la protección antispam.
|
|
108
|
+
|
|
109
|
+
## Tipos semánticos de contacto
|
|
110
|
+
|
|
111
|
+
La etiqueta y la key no sustituyen el tipo del campo. Si un dato representa un
|
|
112
|
+
correo o un teléfono, el formulario debe definirlo como `email` o `phone`:
|
|
113
|
+
|
|
114
|
+
- Bacano valida el formato según el tipo declarado.
|
|
115
|
+
- Las respuestas conservan todos los valores, pero solo los campos `email` y
|
|
116
|
+
`phone` alimentan los datos de contacto normalizados de la respuesta.
|
|
117
|
+
- Un campo `text` llamado `correo`, `email`, `telefono` o similar continúa
|
|
118
|
+
siendo texto libre. No debe dependerse de su nombre para CRM, leads o futuras
|
|
119
|
+
automatizaciones.
|
|
120
|
+
|
|
121
|
+
Antes de publicar, revisa que los campos destinados a identificar o contactar a
|
|
122
|
+
la persona tengan el tipo correcto. Cambiar únicamente su etiqueta no corrige el
|
|
123
|
+
contrato de datos.
|
|
124
|
+
|
|
125
|
+
## Next.js y Static First
|
|
126
|
+
|
|
127
|
+
Obtén la definición durante el build o desde un Server Component usando el
|
|
128
|
+
mismo cliente configurado para el website. Después entrégala al renderer como
|
|
129
|
+
`initialForm`:
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
const initialForm = await bacano.forms.getDefinition("contacto");
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<BacanoForm
|
|
136
|
+
formKey="contacto"
|
|
137
|
+
initialForm={initialForm}
|
|
138
|
+
/>
|
|
139
|
+
);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Así, el HTML inicial contiene el formulario aunque JavaScript todavía no haya
|
|
143
|
+
hidratado. El provider global reutiliza esa definición y la revalida en runtime
|
|
144
|
+
para reflejar una publicación o pausa sin rebuild. No ocultes el formulario
|
|
145
|
+
estático detrás de un `loadingFallback` global ni crees otro `BacanoClient` para
|
|
146
|
+
consultarlo.
|
|
147
|
+
|
|
148
|
+
## Contrato operativo
|
|
149
|
+
|
|
150
|
+
- `DRAFT`: no es público.
|
|
151
|
+
- `ACTIVE`: entrega la última versión publicada.
|
|
152
|
+
- `PAUSED`: conserva definición y mensaje, pero no acepta nuevos envíos.
|
|
153
|
+
- `ARCHIVED`: deja de ser público y conserva auditoría.
|
|
154
|
+
- Publicar crea una versión inmutable; editar modifica únicamente el borrador.
|
|
155
|
+
- Los cambios publicados se revalidan en runtime y no requieren rebuild.
|
|
156
|
+
- La revalidación usa caché corta y ETag; una falla temporal no elimina una
|
|
157
|
+
definición válida que ya fue generada estáticamente.
|
|
158
|
+
- Una Lista de Content se resuelve en Bacano. El storefront recibe opciones
|
|
159
|
+
públicas listas para renderizar.
|
|
160
|
+
- Cada envío incluye la versión y huella exactas de la definición publicada.
|
|
161
|
+
Una página desactualizada recibe `FORM_VERSION_MISMATCH` y debe refrescar.
|
|
162
|
+
- `idempotencyKey` identifica un intento lógico. Repetir la misma clave y el
|
|
163
|
+
mismo contenido devuelve la respuesta existente; reutilizarla con otro
|
|
164
|
+
contenido se rechaza.
|
|
165
|
+
- Bacano deriva empresa y website desde `websiteSlug`; el storefront no envía
|
|
166
|
+
identificadores internos ni controla el tenant.
|
|
167
|
+
- La API vuelve a validar tipos, requeridos, rangos y opciones de listas. La
|
|
168
|
+
validación del navegador mejora la experiencia, pero no es autoritativa.
|
|
169
|
+
- URL, referidor y atribución se sanean. Las respuestas y datos personales no
|
|
170
|
+
se escriben en logs.
|
|
171
|
+
- `pageUrl`, `referrerUrl` y la URL de atribución aceptan únicamente HTTP o
|
|
172
|
+
HTTPS. No envíes protocolos locales, ejecutables o de datos.
|
|
173
|
+
- Los teléfonos admiten un `+` inicial y separadores habituales; Bacano los
|
|
174
|
+
normaliza antes de resolver identidad y rechaza letras o signos mal ubicados.
|
|
175
|
+
- Los números usan notación decimal o científica explícita. Valores ambiguos
|
|
176
|
+
como `0x10` o `3 unidades` se rechazan aunque JavaScript pudiera convertirlos.
|
|
177
|
+
- Un checkbox opcional omitido se conserva como `null`; uno requerido debe
|
|
178
|
+
enviarse expresamente como `true`.
|
|
179
|
+
- Una falla de correo, CRM o automatización no revierte la respuesta.
|
|
180
|
+
- `STORE_RESPONSE` conserva la respuesta sin crear un lead.
|
|
181
|
+
- `CAPTURE_LEAD` conserva primero la respuesta y después resuelve internamente
|
|
182
|
+
la identidad canónica. Reutiliza un lead o cliente existente, crea un lead
|
|
183
|
+
cuando corresponde y manda a revisión las coincidencias contradictorias de
|
|
184
|
+
correo y teléfono sin fusionarlas automáticamente.
|
|
185
|
+
- El resultado público continúa siendo deliberadamente genérico. El storefront
|
|
186
|
+
no recibe ni debe inferir si Bacano creó un lead, encontró un cliente o marcó
|
|
187
|
+
una identidad para revisión.
|
|
188
|
+
|
|
189
|
+
## Checklist mínimo del storefront
|
|
190
|
+
|
|
191
|
+
- Usar un solo `BacanoProvider` y el cliente global ya inicializado.
|
|
192
|
+
- Renderizar con `BacanoForm` o con `useBacanoForm`; nunca copiar la definición
|
|
193
|
+
a un esquema local ni consultar GraphQL directamente.
|
|
194
|
+
- Pueden coexistir varios `BacanoForm` en una página. El renderer oficial aísla
|
|
195
|
+
los identificadores de sus controles; no construyas IDs globales solo con la
|
|
196
|
+
key del campo.
|
|
197
|
+
- Entregar `initialForm` cuando la página se genera de forma estática.
|
|
198
|
+
- Respetar `ACTIVE`, `PAUSED`, versión y `definitionHash` sin inferir estados.
|
|
199
|
+
- Mantener una `idempotencyKey` estable durante los reintentos del mismo envío.
|
|
200
|
+
- Probar formulario activo, pausado, inexistente, versión desactualizada,
|
|
201
|
+
reintento idempotente y error de red.
|
|
202
|
+
- Confirmar en Bacano que una respuesta real quedó persistida antes de aprobar
|
|
203
|
+
el flujo; no basta con mostrar el mensaje de éxito del navegador.
|
|
204
|
+
- Probar autocompletado del navegador y gestores de contraseñas: ningún campo
|
|
205
|
+
real puede llenar el honeypot. Un envío real debe devolver un UUID como
|
|
206
|
+
`responseId`; `null` no demuestra persistencia.
|
|
207
|
+
- Verificar que correos y teléfonos utilicen los tipos `email` y `phone`, no
|
|
208
|
+
campos de texto con una etiqueta parecida.
|
|
209
|
+
- No prometer correo, cupón, solicitud ni automatización mientras la capacidad
|
|
210
|
+
no esté implementada y activa en Bacano. Aunque `CAPTURE_LEAD` esté activo,
|
|
211
|
+
el mensaje público no debe afirmar qué identidad interna se creó o reutilizó.
|
package/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BacanoClientConfig, BacanoClientContext, Brand, Cart, CartItem, CatalogAttributeList, CatalogCategoryList, CatalogPageResult, CheckoutResult, CheckoutSubmission, ContentEntry, CustomerAddress, CustomerProfile, CustomerProfileUpdate, GetAttributeListOptions, GetCatalogPageOptions, GetCategoryListOptions, GetContentOptions, GetOrdersOptions, GetProductFiltersOptions, GetProductRecommendationsOptions, GetProductSeoEntriesOptions, GetProductsOptions, GetSearchSuggestionsOptions, GetStaticCatalogBootstrapOptions, GetStaticCatalogSnapshotOptions, Order, Product, ProductAvailability, ProductCategory, ProductContentBlock, ProductFilters, ProductRecommendationsResult, ProductSeoEntriesResult, ProductVariantLiveState, PublicOrder, PublicOrderLookupInput, SearchSuggestion, StartGuestCheckoutInput, StaticCatalogBootstrap, StaticCatalogSnapshot, WaitForPublicOrderPaymentInput, WebsiteCampaignCaptureInput, WebsiteCampaignCaptureResult, WebsiteCheckoutAddress, WebsiteCheckoutItem, WebsiteCouponActionResult, WebsiteDeliveryOption, WebsiteFreeShippingProgress, WebsiteFulfillmentFamily, WebsiteGuestContact, WebsiteLocationOptions, WebsitePaymentMethod, WebsitePaymentOption, WebsitePaymentSessionResult, WebsiteShippingPolicy, WebsiteShippingQuote } from "./types";
|
|
1
|
+
import type { BacanoClientConfig, BacanoClientContext, Brand, Cart, CartItem, CatalogAttributeList, CatalogCategoryList, CatalogPageResult, CheckoutResult, CheckoutSubmission, ContentEntry, CustomerAddress, CustomerProfile, CustomerProfileUpdate, GetAttributeListOptions, GetCatalogPageOptions, GetCategoryListOptions, GetContentOptions, GetOrdersOptions, GetProductFiltersOptions, GetProductRecommendationsOptions, GetProductSeoEntriesOptions, GetProductsOptions, GetSearchSuggestionsOptions, GetStaticCatalogBootstrapOptions, GetStaticCatalogSnapshotOptions, Order, Product, ProductAvailability, ProductCategory, ProductContentBlock, ProductFilters, ProductRecommendationsResult, ProductSeoEntriesResult, ProductVariantLiveState, PublicOrder, PublicOrderLookupInput, SearchSuggestion, StartGuestCheckoutInput, StaticCatalogBootstrap, StaticCatalogSnapshot, WaitForPublicOrderPaymentInput, WebsiteCampaignCaptureInput, WebsiteCampaignCaptureResult, WebsiteCheckoutAddress, WebsiteCheckoutItem, WebsiteCouponActionResult, WebsiteDeliveryOption, WebsiteFormResponseInput, WebsiteFormResponseResult, WebsiteFreeShippingProgress, WebsiteFulfillmentFamily, WebsiteGuestContact, WebsiteLocationOptions, WebsitePaymentMethod, WebsitePaymentOption, WebsitePaymentSessionResult, WebsitePublishedForm, WebsiteShippingPolicy, WebsiteShippingQuote } from "./types";
|
|
2
2
|
export interface BacanoClient {
|
|
3
3
|
/** Initialize the client and resolve its website context. */
|
|
4
4
|
init(): Promise<void>;
|
|
@@ -34,6 +34,13 @@ export interface BacanoClient {
|
|
|
34
34
|
getByTag(tag: string, opts?: GetContentOptions): Promise<ContentEntry[]>;
|
|
35
35
|
getForProduct(productId: string, opts?: GetContentOptions): Promise<ProductContentBlock[]>;
|
|
36
36
|
};
|
|
37
|
+
forms: {
|
|
38
|
+
/** Returns the current published definition or null when it is unavailable. */
|
|
39
|
+
getDefinition(formKey: string, options?: {
|
|
40
|
+
force?: boolean;
|
|
41
|
+
}): Promise<WebsitePublishedForm | null>;
|
|
42
|
+
submitResponse(input: WebsiteFormResponseInput): Promise<WebsiteFormResponseResult>;
|
|
43
|
+
};
|
|
37
44
|
cart: {
|
|
38
45
|
getOrCreate(contact?: WebsiteGuestContact, fulfillmentFamily?: WebsiteFulfillmentFamily): Promise<Cart>;
|
|
39
46
|
startCheckout(input: StartGuestCheckoutInput): Promise<Cart>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ContentEntry } from "./types";
|
|
2
|
+
export declare const CONTENT_ENTRY_LOCATOR_VERSION: "v1";
|
|
3
|
+
export declare const CONTENT_ENTRY_LOCATOR_SELECTOR: "[data-bacano-content-entry=\"v1\"]";
|
|
4
|
+
export type ContentEntryLocatorSource = Pick<ContentEntry, "key" | "locale" | "name">;
|
|
5
|
+
export interface ContentEntryLocatorAttributes {
|
|
6
|
+
"data-bacano-content-entry": typeof CONTENT_ENTRY_LOCATOR_VERSION;
|
|
7
|
+
"data-bacano-entry-key": string;
|
|
8
|
+
"data-bacano-entry-locale": string;
|
|
9
|
+
"data-bacano-entry-name": string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Adds the public, non-authoritative marker consumed by Bacano's content
|
|
13
|
+
* locator. Spread the result only on the outermost meaningful DOM element
|
|
14
|
+
* whose visible editorial content is owned by the entry.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getContentEntryLocatorAttributes(entry: ContentEntryLocatorSource): ContentEntryLocatorAttributes;
|
package/index.cjs
CHANGED
|
@@ -21,6 +21,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
AuthError: () => AuthError,
|
|
24
|
+
CONTENT_ENTRY_LOCATOR_SELECTOR: () => CONTENT_ENTRY_LOCATOR_SELECTOR,
|
|
25
|
+
CONTENT_ENTRY_LOCATOR_VERSION: () => CONTENT_ENTRY_LOCATOR_VERSION,
|
|
24
26
|
CheckoutError: () => CheckoutError,
|
|
25
27
|
GraphQLRequestError: () => GraphQLRequestError,
|
|
26
28
|
MARKETING_SCHEMA_VERSION: () => MARKETING_SCHEMA_VERSION,
|
|
@@ -38,6 +40,7 @@ __export(index_exports, {
|
|
|
38
40
|
createWebsiteMarketingRuntime: () => createWebsiteMarketingRuntime,
|
|
39
41
|
formatMediaSrcSet: () => formatMediaSrcSet,
|
|
40
42
|
getAllowedMarketingParameters: () => getAllowedMarketingParameters,
|
|
43
|
+
getContentEntryLocatorAttributes: () => getContentEntryLocatorAttributes,
|
|
41
44
|
getPrimaryProductImage: () => getPrimaryProductImage,
|
|
42
45
|
getProductDetailVariants: () => getProductDetailVariants,
|
|
43
46
|
getProductImages: () => getProductImages,
|
|
@@ -66,10 +69,10 @@ var WebsiteError = class extends Error {
|
|
|
66
69
|
}
|
|
67
70
|
};
|
|
68
71
|
var RateLimitError = class extends WebsiteError {
|
|
69
|
-
constructor(message,
|
|
72
|
+
constructor(message, retryAfterSeconds2) {
|
|
70
73
|
super(message, "RATE_LIMITED", {
|
|
71
74
|
status: 429,
|
|
72
|
-
retryAfterSeconds
|
|
75
|
+
retryAfterSeconds: retryAfterSeconds2
|
|
73
76
|
});
|
|
74
77
|
this.name = "RateLimitError";
|
|
75
78
|
}
|
|
@@ -6115,6 +6118,171 @@ function createWebsiteMarketingRuntime(options) {
|
|
|
6115
6118
|
};
|
|
6116
6119
|
}
|
|
6117
6120
|
|
|
6121
|
+
// libs/sdk/src/modules/forms.ts
|
|
6122
|
+
function retryAfterSeconds(response) {
|
|
6123
|
+
const value = Number(response.headers.get("Retry-After") ?? "1");
|
|
6124
|
+
return Number.isFinite(value) && value >= 0 ? Math.ceil(value) : 1;
|
|
6125
|
+
}
|
|
6126
|
+
function isFormKey(value) {
|
|
6127
|
+
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value);
|
|
6128
|
+
}
|
|
6129
|
+
function createFormsModule(apiUrl, websiteSlug, getContext) {
|
|
6130
|
+
const cache = /* @__PURE__ */ new Map();
|
|
6131
|
+
function getServerOrigin() {
|
|
6132
|
+
if (typeof window !== "undefined") return null;
|
|
6133
|
+
const domain = getContext().domain?.trim();
|
|
6134
|
+
if (!domain) return null;
|
|
6135
|
+
try {
|
|
6136
|
+
return new URL(domain.includes("://") ? domain : `https://${domain}`).origin;
|
|
6137
|
+
} catch {
|
|
6138
|
+
return null;
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
return {
|
|
6142
|
+
async getDefinition(formKey, options) {
|
|
6143
|
+
const normalizedKey = formKey.trim();
|
|
6144
|
+
if (!isFormKey(normalizedKey)) {
|
|
6145
|
+
throw new WebsiteError(
|
|
6146
|
+
"formKey has an invalid format",
|
|
6147
|
+
"INVALID_FORM_KEY"
|
|
6148
|
+
);
|
|
6149
|
+
}
|
|
6150
|
+
const cached = cache.get(normalizedKey);
|
|
6151
|
+
if (!options?.force && cached && cached.expiresAt > Date.now()) {
|
|
6152
|
+
return cached.value;
|
|
6153
|
+
}
|
|
6154
|
+
const url = new URL(
|
|
6155
|
+
`${apiUrl.replace(/\/+$/, "")}/api/v1/website/forms/definition`
|
|
6156
|
+
);
|
|
6157
|
+
url.searchParams.set("websiteSlug", websiteSlug);
|
|
6158
|
+
url.searchParams.set("formKey", normalizedKey);
|
|
6159
|
+
let response;
|
|
6160
|
+
try {
|
|
6161
|
+
const serverOrigin = getServerOrigin();
|
|
6162
|
+
const headers = new Headers();
|
|
6163
|
+
if (serverOrigin) headers.set("Origin", serverOrigin);
|
|
6164
|
+
if (cached?.etag) headers.set("If-None-Match", cached.etag);
|
|
6165
|
+
response = await fetch(url, {
|
|
6166
|
+
...serverOrigin || cached?.etag ? { headers } : {},
|
|
6167
|
+
...options?.force ? { cache: "no-cache" } : {}
|
|
6168
|
+
});
|
|
6169
|
+
} catch (error) {
|
|
6170
|
+
throw new WebsiteError(
|
|
6171
|
+
`Could not load the website form: ${error instanceof Error ? error.message : String(error)}`,
|
|
6172
|
+
"NETWORK_ERROR"
|
|
6173
|
+
);
|
|
6174
|
+
}
|
|
6175
|
+
if (response.status === 304 && cached) {
|
|
6176
|
+
cache.set(normalizedKey, {
|
|
6177
|
+
...cached,
|
|
6178
|
+
expiresAt: Date.now() + 3e4
|
|
6179
|
+
});
|
|
6180
|
+
return cached.value;
|
|
6181
|
+
}
|
|
6182
|
+
if (response.status === 404) {
|
|
6183
|
+
cache.set(normalizedKey, {
|
|
6184
|
+
etag: null,
|
|
6185
|
+
expiresAt: Date.now() + 15e3,
|
|
6186
|
+
value: null
|
|
6187
|
+
});
|
|
6188
|
+
return null;
|
|
6189
|
+
}
|
|
6190
|
+
if (response.status === 429) {
|
|
6191
|
+
throw new RateLimitError(
|
|
6192
|
+
"Rate limit exceeded. Please try again later.",
|
|
6193
|
+
retryAfterSeconds(response)
|
|
6194
|
+
);
|
|
6195
|
+
}
|
|
6196
|
+
if (!response.ok) {
|
|
6197
|
+
throw new WebsiteError(
|
|
6198
|
+
`Could not load the website form (status ${response.status})`,
|
|
6199
|
+
response.status === 403 ? "ORIGIN_NOT_ALLOWED" : "FORM_DEFINITION_ERROR",
|
|
6200
|
+
{ status: response.status }
|
|
6201
|
+
);
|
|
6202
|
+
}
|
|
6203
|
+
const value = await response.json();
|
|
6204
|
+
if (value.schemaVersion !== 1 || value.key !== normalizedKey || !Array.isArray(value.definition?.fields) || !Array.isArray(value.definition?.sections) || !Array.isArray(value.definition?.steps)) {
|
|
6205
|
+
throw new WebsiteError(
|
|
6206
|
+
"Website form returned an invalid definition",
|
|
6207
|
+
"INVALID_FORM_DEFINITION"
|
|
6208
|
+
);
|
|
6209
|
+
}
|
|
6210
|
+
cache.set(normalizedKey, {
|
|
6211
|
+
etag: response.headers.get("ETag"),
|
|
6212
|
+
expiresAt: Date.now() + 3e4,
|
|
6213
|
+
value
|
|
6214
|
+
});
|
|
6215
|
+
return value;
|
|
6216
|
+
},
|
|
6217
|
+
async submitResponse(input) {
|
|
6218
|
+
const normalizedKey = input.formKey.trim();
|
|
6219
|
+
if (!isFormKey(normalizedKey)) {
|
|
6220
|
+
throw new WebsiteError(
|
|
6221
|
+
"formKey has an invalid format",
|
|
6222
|
+
"INVALID_FORM_KEY"
|
|
6223
|
+
);
|
|
6224
|
+
}
|
|
6225
|
+
const context = getContext();
|
|
6226
|
+
const attribution = getWebsiteMarketingAttribution(context.websiteId);
|
|
6227
|
+
const pageUrl = typeof window !== "undefined" ? window.location.href : void 0;
|
|
6228
|
+
const referrerUrl = typeof document !== "undefined" && document.referrer ? document.referrer : void 0;
|
|
6229
|
+
let response;
|
|
6230
|
+
try {
|
|
6231
|
+
response = await fetch(
|
|
6232
|
+
`${apiUrl.replace(/\/+$/, "")}/api/v1/website/forms/responses`,
|
|
6233
|
+
{
|
|
6234
|
+
method: "POST",
|
|
6235
|
+
headers: { "Content-Type": "application/json" },
|
|
6236
|
+
body: JSON.stringify({
|
|
6237
|
+
websiteSlug,
|
|
6238
|
+
formKey: normalizedKey,
|
|
6239
|
+
formVersion: input.formVersion,
|
|
6240
|
+
definitionHash: input.definitionHash,
|
|
6241
|
+
idempotencyKey: input.idempotencyKey,
|
|
6242
|
+
source: input.source ?? "WEBSITE",
|
|
6243
|
+
values: input.values,
|
|
6244
|
+
pageUrl,
|
|
6245
|
+
referrerUrl,
|
|
6246
|
+
attribution: attribution ? serializeWebsiteMarketingAttribution(attribution) : null,
|
|
6247
|
+
honeypot: input.honeypot ?? ""
|
|
6248
|
+
})
|
|
6249
|
+
}
|
|
6250
|
+
);
|
|
6251
|
+
} catch (error) {
|
|
6252
|
+
throw new WebsiteError(
|
|
6253
|
+
`Could not submit the website form: ${error instanceof Error ? error.message : String(error)}`,
|
|
6254
|
+
"NETWORK_ERROR"
|
|
6255
|
+
);
|
|
6256
|
+
}
|
|
6257
|
+
if (response.status === 429) {
|
|
6258
|
+
throw new RateLimitError(
|
|
6259
|
+
"Rate limit exceeded. Please try again later.",
|
|
6260
|
+
retryAfterSeconds(response)
|
|
6261
|
+
);
|
|
6262
|
+
}
|
|
6263
|
+
const result = await response.json().catch(() => null);
|
|
6264
|
+
if (!response.ok) {
|
|
6265
|
+
throw new WebsiteError(
|
|
6266
|
+
result?.message ?? `Could not submit the website form (status ${response.status})`,
|
|
6267
|
+
result?.code ?? (response.status === 403 ? "ORIGIN_NOT_ALLOWED" : "FORM_RESPONSE_ERROR"),
|
|
6268
|
+
{ status: response.status }
|
|
6269
|
+
);
|
|
6270
|
+
}
|
|
6271
|
+
if (typeof result?.accepted !== "boolean" || typeof result.message !== "string") {
|
|
6272
|
+
throw new WebsiteError(
|
|
6273
|
+
"Website form returned an invalid response",
|
|
6274
|
+
"INVALID_RESPONSE"
|
|
6275
|
+
);
|
|
6276
|
+
}
|
|
6277
|
+
return {
|
|
6278
|
+
accepted: result.accepted,
|
|
6279
|
+
responseId: typeof result.responseId === "string" ? result.responseId : null,
|
|
6280
|
+
message: result.message
|
|
6281
|
+
};
|
|
6282
|
+
}
|
|
6283
|
+
};
|
|
6284
|
+
}
|
|
6285
|
+
|
|
6118
6286
|
// libs/sdk/src/modules/marketing.ts
|
|
6119
6287
|
function readRetryAfter(response) {
|
|
6120
6288
|
const value = Number(response.headers.get("Retry-After") ?? "1");
|
|
@@ -6641,6 +6809,7 @@ function createBacanoClient(config) {
|
|
|
6641
6809
|
config.websiteSlug,
|
|
6642
6810
|
getContext
|
|
6643
6811
|
);
|
|
6812
|
+
const formsModule = createFormsModule(apiUrl, config.websiteSlug, getContext);
|
|
6644
6813
|
return {
|
|
6645
6814
|
async init() {
|
|
6646
6815
|
if (context) return;
|
|
@@ -6693,6 +6862,10 @@ function createBacanoClient(config) {
|
|
|
6693
6862
|
getByTag: contentModule.getByTag.bind(contentModule),
|
|
6694
6863
|
getForProduct: contentModule.getForProduct.bind(contentModule)
|
|
6695
6864
|
},
|
|
6865
|
+
forms: {
|
|
6866
|
+
getDefinition: formsModule.getDefinition.bind(formsModule),
|
|
6867
|
+
submitResponse: formsModule.submitResponse.bind(formsModule)
|
|
6868
|
+
},
|
|
6696
6869
|
cart: {
|
|
6697
6870
|
getOrCreate: cartModule.getOrCreate.bind(cartModule),
|
|
6698
6871
|
startCheckout: cartModule.startCheckout.bind(cartModule),
|
|
@@ -6734,3 +6907,23 @@ function createBacanoClient(config) {
|
|
|
6734
6907
|
}
|
|
6735
6908
|
};
|
|
6736
6909
|
}
|
|
6910
|
+
|
|
6911
|
+
// libs/sdk/src/content-entry-locator.ts
|
|
6912
|
+
var CONTENT_ENTRY_LOCATOR_VERSION = "v1";
|
|
6913
|
+
var CONTENT_ENTRY_LOCATOR_SELECTOR = `[data-bacano-content-entry="${CONTENT_ENTRY_LOCATOR_VERSION}"]`;
|
|
6914
|
+
function getContentEntryLocatorAttributes(entry) {
|
|
6915
|
+
const key = entry.key?.trim();
|
|
6916
|
+
if (!key) {
|
|
6917
|
+
throw new Error("A Bacano Content locator requires a stable entry key.");
|
|
6918
|
+
}
|
|
6919
|
+
const locale = entry.locale.trim();
|
|
6920
|
+
if (!locale) {
|
|
6921
|
+
throw new Error("A Bacano Content locator requires an entry locale.");
|
|
6922
|
+
}
|
|
6923
|
+
return {
|
|
6924
|
+
"data-bacano-content-entry": CONTENT_ENTRY_LOCATOR_VERSION,
|
|
6925
|
+
"data-bacano-entry-key": key,
|
|
6926
|
+
"data-bacano-entry-locale": locale,
|
|
6927
|
+
"data-bacano-entry-name": entry.name.trim() || key
|
|
6928
|
+
};
|
|
6929
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export type { BacanoClient } from "./client";
|
|
2
2
|
export { createBacanoClient } from "./client";
|
|
3
|
+
export type { ContentEntryLocatorAttributes, ContentEntryLocatorSource, } from "./content-entry-locator";
|
|
4
|
+
export { CONTENT_ENTRY_LOCATOR_SELECTOR, CONTENT_ENTRY_LOCATOR_VERSION, getContentEntryLocatorAttributes, } from "./content-entry-locator";
|
|
3
5
|
export { AuthError, CheckoutError, GraphQLRequestError, NotInitializedError, RateLimitError, WebsiteError, } from "./errors";
|
|
4
6
|
export type { WebsiteAdvertisingConsent, WebsiteMarketingAttribution, WebsiteMarketingBrowserEvent, WebsiteMarketingBrowserEventName, WebsiteMarketingContactPlacement, WebsiteMarketingDiagnostic, WebsiteMarketingItem, WebsiteMarketingRuntime, WebsiteMarketingRuntimeOptions, WebsiteMarketingScriptLoadStrategy, WebsiteMarketingTouchpoint, } from "./marketing";
|
|
5
7
|
export { captureWebsiteMarketingAttribution, createWebsiteMarketingRuntime, getAllowedMarketingParameters, getWebsiteMarketingAttribution, MARKETING_SCHEMA_VERSION, sanitizeMarketingUrl, WEBSITE_MARKETING_CONTACT_PLACEMENTS, } from "./marketing";
|
|
@@ -7,4 +9,4 @@ export type { MediaResponsiveSource, MediaResponsiveSourceOptions, MediaTransfor
|
|
|
7
9
|
export { buildStorageContentMediaUrls, buildStorageFileUrl, buildStorageMediaUrls, buildStorageResponsiveSources, buildStorageTransformedFileUrl, formatMediaSrcSet, } from "./media";
|
|
8
10
|
export { getWebsiteContextFromPublicConfig, getWebsitePublicConfig, } from "./modules/auth";
|
|
9
11
|
export { getPrimaryProductImage, getProductDetailVariants, getProductImages, getProductMediaByKind, getProductMediaKind, getProductVideos, resolveProductPricing, } from "./modules/catalog";
|
|
10
|
-
export type { AccessTokenProvider, AttributeFilter, BacanoClientConfig, BacanoClientContext, Brand, Cart, CartItem, CatalogAttributeList, CatalogAttributeListFilters, CatalogAttributeListItem, CatalogAttributeListValue, CatalogCategoryList, CatalogCategoryListItem, CatalogCategoryListItemSourceType, CheckoutResult, CheckoutSubmission, ContentEntry, ContentJson, ContentMedia, ContentTypeSummary, CustomerAddress, CustomerAddressType, CustomerProfile, CustomerProfileUpdate, GetAttributeListOptions, GetCategoryListOptions, GetContentOptions, GetOrdersOptions, GetProductFiltersOptions, GetProductSeoEntriesOptions, GetProductsOptions, GetSearchSuggestionsOptions, GetStaticCatalogBootstrapOptions, GetStaticCatalogSnapshotOptions, GetWebsitePublicConfigOptions, Order, OrderItem, Product, ProductAttribute, ProductAvailability, ProductCategory, ProductContentBlock, ProductFilters, ProductMedia, ProductMediaKind, ProductOptionGroup, ProductOptionGroupMember, ProductPrice, ProductPriceType, ProductRelatedProduct, ProductSeoEntriesResult, ProductSeoEntry, ProductSortBy, ProductSpecification, ProductSpecificationFacet, ProductSpecificationField, ProductSpecificationFilterSelection, ProductSpecificationMeasure, ProductSpecificationOption, ProductSpecificationSection, ProductSubcategory, ProductVariant, ProductVariantLiveState, ProductVariantPricing, PublicOrder, PublicOrderDeliverySummary, PublicOrderItem, PublicOrderLookupInput, PublicOrderPaymentSummary, ResolvedWebsitePublicConfig, SearchSuggestion, StartGuestCheckoutInput, StaticCatalogBootstrap, StaticCatalogSnapshot, VariantAttribute, WaitForPublicOrderPaymentInput, WebsiteBrandingMedia, WebsiteBrandingPublicConfig, WebsiteCampaignCaptureContact, WebsiteCampaignCaptureInput, WebsiteCampaignCaptureResult, WebsiteCampaignCaptureSource, WebsiteCheckoutAddress, WebsiteCheckoutItem, WebsiteCouponActionResult, WebsiteDeliveryOption, WebsiteDepartmentOption, WebsiteFreeShippingProgress, WebsiteFulfillmentFamily, WebsiteGuestContact, WebsiteLegalPublicConfig, WebsiteLocationOptions, WebsiteMunicipalityOption, WebsitePaymentMethod, WebsitePaymentMode, WebsitePaymentOption, WebsitePaymentOptionMethod, WebsitePaymentSessionResult, WebsitePickupInfo, WebsitePublicConfig, WebsiteShippingPolicy, WebsiteShippingQuote, } from "./types";
|
|
12
|
+
export type { AccessTokenProvider, AttributeFilter, BacanoClientConfig, BacanoClientContext, Brand, Cart, CartItem, CatalogAttributeList, CatalogAttributeListFilters, CatalogAttributeListItem, CatalogAttributeListValue, CatalogCategoryList, CatalogCategoryListItem, CatalogCategoryListItemSourceType, CheckoutResult, CheckoutSubmission, ContentEntry, ContentJson, ContentMedia, ContentTypeSummary, CustomerAddress, CustomerAddressType, CustomerProfile, CustomerProfileUpdate, GetAttributeListOptions, GetCategoryListOptions, GetContentOptions, GetOrdersOptions, GetProductFiltersOptions, GetProductSeoEntriesOptions, GetProductsOptions, GetSearchSuggestionsOptions, GetStaticCatalogBootstrapOptions, GetStaticCatalogSnapshotOptions, GetWebsitePublicConfigOptions, Order, OrderItem, Product, ProductAttribute, ProductAvailability, ProductCategory, ProductContentBlock, ProductFilters, ProductMedia, ProductMediaKind, ProductOptionGroup, ProductOptionGroupMember, ProductPrice, ProductPriceType, ProductRelatedProduct, ProductSeoEntriesResult, ProductSeoEntry, ProductSortBy, ProductSpecification, ProductSpecificationFacet, ProductSpecificationField, ProductSpecificationFilterSelection, ProductSpecificationMeasure, ProductSpecificationOption, ProductSpecificationSection, ProductSubcategory, ProductVariant, ProductVariantLiveState, ProductVariantPricing, PublicOrder, PublicOrderDeliverySummary, PublicOrderItem, PublicOrderLookupInput, PublicOrderPaymentSummary, ResolvedWebsitePublicConfig, SearchSuggestion, StartGuestCheckoutInput, StaticCatalogBootstrap, StaticCatalogSnapshot, VariantAttribute, WaitForPublicOrderPaymentInput, WebsiteBrandingMedia, WebsiteBrandingPublicConfig, WebsiteCampaignCaptureContact, WebsiteCampaignCaptureInput, WebsiteCampaignCaptureResult, WebsiteCampaignCaptureSource, WebsiteCheckoutAddress, WebsiteCheckoutItem, WebsiteCouponActionResult, WebsiteDeliveryOption, WebsiteDepartmentOption, WebsiteFormAction, WebsiteFormDefinition, WebsiteFormField, WebsiteFormFieldType, WebsiteFormOption, WebsiteFormOptionSource, WebsiteFormResponseInput, WebsiteFormResponseResult, WebsiteFormResponseSource, WebsiteFormResponseValue, WebsiteFormSection, WebsiteFormStatus, WebsiteFormStep, WebsiteFreeShippingProgress, WebsiteFulfillmentFamily, WebsiteGuestContact, WebsiteLegalPublicConfig, WebsiteLocationOptions, WebsiteMunicipalityOption, WebsitePaymentMethod, WebsitePaymentMode, WebsitePaymentOption, WebsitePaymentOptionMethod, WebsitePaymentSessionResult, WebsitePickupInfo, WebsitePublicConfig, WebsitePublishedForm, WebsiteShippingPolicy, WebsiteShippingQuote, } from "./types";
|