@mira-arandu/commerce-client-sdk 0.0.0-stage → 47.20.4
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 +406 -2
- package/dist/address.d.ts +27 -0
- package/dist/address.d.ts.map +1 -0
- package/dist/address.js +29 -0
- package/dist/address.js.map +1 -0
- package/dist/analytics/cdp.d.ts +14 -0
- package/dist/analytics/cdp.d.ts.map +1 -0
- package/dist/analytics/cdp.js +94 -0
- package/dist/analytics/cdp.js.map +1 -0
- package/dist/analytics/create-analytics.d.ts +3 -0
- package/dist/analytics/create-analytics.d.ts.map +1 -0
- package/dist/analytics/create-analytics.js +184 -0
- package/dist/analytics/create-analytics.js.map +1 -0
- package/dist/analytics/data-layer.d.ts +14 -0
- package/dist/analytics/data-layer.d.ts.map +1 -0
- package/dist/analytics/data-layer.js +58 -0
- package/dist/analytics/data-layer.js.map +1 -0
- package/dist/analytics/ga4.d.ts +18 -0
- package/dist/analytics/ga4.d.ts.map +1 -0
- package/dist/analytics/ga4.js +105 -0
- package/dist/analytics/ga4.js.map +1 -0
- package/dist/analytics/gtm.d.ts +6 -0
- package/dist/analytics/gtm.d.ts.map +1 -0
- package/dist/analytics/gtm.js +32 -0
- package/dist/analytics/gtm.js.map +1 -0
- package/dist/analytics/index.d.ts +6 -0
- package/dist/analytics/index.d.ts.map +1 -0
- package/dist/analytics/index.js +5 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/types.d.ts +128 -0
- package/dist/analytics/types.d.ts.map +1 -0
- package/dist/analytics/types.js +2 -0
- package/dist/analytics/types.js.map +1 -0
- package/dist/cart.d.ts +27 -0
- package/dist/cart.d.ts.map +1 -0
- package/dist/cart.js +59 -0
- package/dist/cart.js.map +1 -0
- package/dist/client-config.d.ts +7 -0
- package/dist/client-config.d.ts.map +1 -0
- package/dist/client-config.js +9 -0
- package/dist/client-config.js.map +1 -0
- package/dist/client.d.ts +10 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +21 -0
- package/dist/client.js.map +1 -0
- package/dist/documents.d.ts +17 -0
- package/dist/documents.d.ts.map +1 -0
- package/dist/documents.js +77 -0
- package/dist/documents.js.map +1 -0
- package/dist/errors.d.ts +19 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +48 -0
- package/dist/errors.js.map +1 -0
- package/dist/etag.d.ts +32 -0
- package/dist/etag.d.ts.map +1 -0
- package/dist/etag.js +53 -0
- package/dist/etag.js.map +1 -0
- package/dist/experiences.d.ts +21 -0
- package/dist/experiences.d.ts.map +1 -0
- package/dist/experiences.js +42 -0
- package/dist/experiences.js.map +1 -0
- package/dist/first-touch.d.ts +87 -0
- package/dist/first-touch.d.ts.map +1 -0
- package/dist/first-touch.js +222 -0
- package/dist/first-touch.js.map +1 -0
- package/dist/generated/client/client.gen.d.ts +3 -0
- package/dist/generated/client/client.gen.d.ts.map +1 -0
- package/dist/generated/client/client.gen.js +217 -0
- package/dist/generated/client/client.gen.js.map +1 -0
- package/dist/generated/client/index.d.ts +11 -0
- package/dist/generated/client/index.d.ts.map +1 -0
- package/dist/generated/client/index.js +7 -0
- package/dist/generated/client/index.js.map +1 -0
- package/dist/generated/client/types.gen.d.ts +121 -0
- package/dist/generated/client/types.gen.d.ts.map +1 -0
- package/dist/generated/client/types.gen.js +3 -0
- package/dist/generated/client/types.gen.js.map +1 -0
- package/dist/generated/client/utils.gen.d.ts +38 -0
- package/dist/generated/client/utils.gen.d.ts.map +1 -0
- package/dist/generated/client/utils.gen.js +231 -0
- package/dist/generated/client/utils.gen.js.map +1 -0
- package/dist/generated/client.gen.d.ts +13 -0
- package/dist/generated/client.gen.d.ts.map +1 -0
- package/dist/generated/client.gen.js +5 -0
- package/dist/generated/client.gen.js.map +1 -0
- package/dist/generated/core/auth.gen.d.ts +26 -0
- package/dist/generated/core/auth.gen.d.ts.map +1 -0
- package/dist/generated/core/auth.gen.js +15 -0
- package/dist/generated/core/auth.gen.js.map +1 -0
- package/dist/generated/core/bodySerializer.gen.d.ts +26 -0
- package/dist/generated/core/bodySerializer.gen.d.ts.map +1 -0
- package/dist/generated/core/bodySerializer.gen.js +58 -0
- package/dist/generated/core/bodySerializer.gen.js.map +1 -0
- package/dist/generated/core/params.gen.d.ts +44 -0
- package/dist/generated/core/params.gen.d.ts.map +1 -0
- package/dist/generated/core/params.gen.js +110 -0
- package/dist/generated/core/params.gen.js.map +1 -0
- package/dist/generated/core/pathSerializer.gen.d.ts +34 -0
- package/dist/generated/core/pathSerializer.gen.d.ts.map +1 -0
- package/dist/generated/core/pathSerializer.gen.js +107 -0
- package/dist/generated/core/pathSerializer.gen.js.map +1 -0
- package/dist/generated/core/queryKeySerializer.gen.d.ts +19 -0
- package/dist/generated/core/queryKeySerializer.gen.d.ts.map +1 -0
- package/dist/generated/core/queryKeySerializer.gen.js +93 -0
- package/dist/generated/core/queryKeySerializer.gen.js.map +1 -0
- package/dist/generated/core/serverSentEvents.gen.d.ts +72 -0
- package/dist/generated/core/serverSentEvents.gen.d.ts.map +1 -0
- package/dist/generated/core/serverSentEvents.gen.js +133 -0
- package/dist/generated/core/serverSentEvents.gen.js.map +1 -0
- package/dist/generated/core/types.gen.d.ts +84 -0
- package/dist/generated/core/types.gen.d.ts.map +1 -0
- package/dist/generated/core/types.gen.js +3 -0
- package/dist/generated/core/types.gen.js.map +1 -0
- package/dist/generated/core/utils.gen.d.ts +20 -0
- package/dist/generated/core/utils.gen.d.ts.map +1 -0
- package/dist/generated/core/utils.gen.js +88 -0
- package/dist/generated/core/utils.gen.js.map +1 -0
- package/dist/generated/index.d.ts +3 -0
- package/dist/generated/index.d.ts.map +1 -0
- package/dist/generated/index.js +4 -0
- package/dist/generated/index.js.map +1 -0
- package/dist/generated/sdk.gen.d.ts +17391 -0
- package/dist/generated/sdk.gen.d.ts.map +1 -0
- package/dist/generated/sdk.gen.js +42178 -0
- package/dist/generated/sdk.gen.js.map +1 -0
- package/dist/generated/storefront-api.gen.d.ts +2 -0
- package/dist/generated/storefront-api.gen.d.ts.map +1 -0
- package/dist/generated/storefront-api.gen.js +4 -0
- package/dist/generated/storefront-api.gen.js.map +1 -0
- package/dist/generated/types.gen.d.ts +157745 -0
- package/dist/generated/types.gen.d.ts.map +1 -0
- package/dist/generated/types.gen.js +4938 -0
- package/dist/generated/types.gen.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/media/index.d.ts +71 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media/index.js +162 -0
- package/dist/media/index.js.map +1 -0
- package/dist/money.d.ts +12 -0
- package/dist/money.d.ts.map +1 -0
- package/dist/money.js +31 -0
- package/dist/money.js.map +1 -0
- package/dist/passkeys.d.ts +48 -0
- package/dist/passkeys.d.ts.map +1 -0
- package/dist/passkeys.js +189 -0
- package/dist/passkeys.js.map +1 -0
- package/dist/payment.d.ts +20 -0
- package/dist/payment.d.ts.map +1 -0
- package/dist/payment.js +56 -0
- package/dist/payment.js.map +1 -0
- package/dist/shipping.d.ts +22 -0
- package/dist/shipping.d.ts.map +1 -0
- package/dist/shipping.js +66 -0
- package/dist/shipping.js.map +1 -0
- package/dist/storefront/appointments.d.ts +36 -0
- package/dist/storefront/appointments.d.ts.map +1 -0
- package/dist/storefront/appointments.js +85 -0
- package/dist/storefront/appointments.js.map +1 -0
- package/dist/storefront/customer-documents.d.ts +6 -0
- package/dist/storefront/customer-documents.d.ts.map +1 -0
- package/dist/storefront/customer-documents.js +19 -0
- package/dist/storefront/customer-documents.js.map +1 -0
- package/dist/storefront/index.d.ts +6 -0
- package/dist/storefront/index.d.ts.map +1 -0
- package/dist/storefront/index.js +6 -0
- package/dist/storefront/index.js.map +1 -0
- package/dist/storefront/plp.d.ts +23 -0
- package/dist/storefront/plp.d.ts.map +1 -0
- package/dist/storefront/plp.js +45 -0
- package/dist/storefront/plp.js.map +1 -0
- package/dist/storefront/rental.d.ts +11 -0
- package/dist/storefront/rental.d.ts.map +1 -0
- package/dist/storefront/rental.js +53 -0
- package/dist/storefront/rental.js.map +1 -0
- package/dist/storefront/variants.d.ts +57 -0
- package/dist/storefront/variants.d.ts.map +1 -0
- package/dist/storefront/variants.js +123 -0
- package/dist/storefront/variants.js.map +1 -0
- package/dist/storefront-api.d.ts +21 -0
- package/dist/storefront-api.d.ts.map +1 -0
- package/dist/storefront-api.js +19 -0
- package/dist/storefront-api.js.map +1 -0
- package/dist/storefront-cart.d.ts +74 -0
- package/dist/storefront-cart.d.ts.map +1 -0
- package/dist/storefront-cart.js +148 -0
- package/dist/storefront-cart.js.map +1 -0
- package/dist/wishlist.d.ts +10 -0
- package/dist/wishlist.d.ts.map +1 -0
- package/dist/wishlist.js +20 -0
- package/dist/wishlist.js.map +1 -0
- package/package.json +37 -4
package/README.md
CHANGED
|
@@ -1,3 +1,407 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @mira-dev-tech/commerce-client-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Cliente TypeScript da API aranduOS, gerado do contrato OpenAPI
|
|
4
|
+
(`services/api/openapi/spec.yaml`). Tipos, funções e erros vêm todos do
|
|
5
|
+
contrato — cada versão do SDK corresponde a um contrato publicado.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i @mira-dev-tech/commerce-client-sdk@npm:@mira-arandu/commerce-client-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Não precisa de `.npmrc` nem de token. No npm público o pacote chama-se
|
|
12
|
+
`@mira-arandu/commerce-client-sdk`; o apelido acima instala-o com o nome
|
|
13
|
+
`@mira-dev-tech/commerce-client-sdk`, que é o dos imports desta página. No
|
|
14
|
+
`package.json` fica
|
|
15
|
+
`"@mira-dev-tech/commerce-client-sdk": "npm:@mira-arandu/commerce-client-sdk@<versão>"`.
|
|
16
|
+
No GitHub Packages o nome é `@mira-dev-tech/commerce-client-sdk`, sem apelido.
|
|
17
|
+
|
|
18
|
+
## Começar
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { createCommerceClient, listProducts } from "@mira-dev-tech/commerce-client-sdk"
|
|
22
|
+
|
|
23
|
+
const client = createCommerceClient({
|
|
24
|
+
baseUrl: "https://api.mira-dev.tech/v1",
|
|
25
|
+
token: process.env.COMMERCE_TOKEN, // servidor apenas
|
|
26
|
+
memberId: process.env.MEMBER_ID, // enviado como X-Tenant-ID
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const { data } = await listProducts({ client })
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
O `memberId` identifica o tenant e vai sozinho no header `X-Tenant-ID` — não o
|
|
33
|
+
adicione à mão.
|
|
34
|
+
|
|
35
|
+
## Superfície da vitrine (`/storefront-api`)
|
|
36
|
+
|
|
37
|
+
O pacote full re-exporta **todas** as operações da API (admin, POS, ERP, …).
|
|
38
|
+
Uma vitrine deve importar o subpath `/storefront-api`, que traz num import só o
|
|
39
|
+
que um site precisa — **não** é um segundo codegen:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import {
|
|
43
|
+
createCommerceClient,
|
|
44
|
+
listStorefrontProducts,
|
|
45
|
+
composeCheckout,
|
|
46
|
+
getRentalAvailabilityCalendar,
|
|
47
|
+
requestCustomerMagicLink,
|
|
48
|
+
customerListAppointments,
|
|
49
|
+
listAvailableScheduleSlots,
|
|
50
|
+
groupAvailableSlotsByDay,
|
|
51
|
+
} from "@mira-dev-tech/commerce-client-sdk/storefront-api"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
O que está lá:
|
|
55
|
+
|
|
56
|
+
- as operações das tags `storefront`, `checkout`, `booking` e `customers` (#1469),
|
|
57
|
+
sem os writes de staff que partilham essas tags (frota, CRM, heuristics);
|
|
58
|
+
- **toda a operação do cliente final**: login, conta, pedidos, assinaturas,
|
|
59
|
+
marcações, documentos e solicitações. Entra sozinha, pelo contrato (#4319);
|
|
60
|
+
- o que o site chama sem login: catálogo, horários livres, frete, pagamento,
|
|
61
|
+
link de pagamento, formulários (`sdkOperationIds` em `storefront-surface.yaml`);
|
|
62
|
+
- os tipos do contrato e as funções prontas abaixo.
|
|
63
|
+
|
|
64
|
+
**A garantia:** o que um site alcança está neste pacote. Operação pública, ou que
|
|
65
|
+
o bearer do BFF alcança, fora do pacote e sem motivo declarado reprova o CI
|
|
66
|
+
(Garantia 6, `tests/bff/storefront-sdk-surface.test.ts`). Se faltar algo aqui, é
|
|
67
|
+
defeito: abra uma issue.
|
|
68
|
+
|
|
69
|
+
Exportar a função não abre rota. Quem decide o acesso é o proxy e o RBAC, e a
|
|
70
|
+
[`storefront-bff-allowlist.json`](../../public/openapi/storefront-bff-allowlist.json)
|
|
71
|
+
é um conjunto menor: não leva a sessão do comprador.
|
|
72
|
+
|
|
73
|
+
Checkout com `throwOnError` e interceptor de sessão continua em
|
|
74
|
+
`@mira/commerce-checkout-sdk`. Storefronts actuais podem ficar no pacote full
|
|
75
|
+
até migrar.
|
|
76
|
+
|
|
77
|
+
### Agenda e documentos do cliente
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const { data } = await listAvailableScheduleSlots({
|
|
81
|
+
client,
|
|
82
|
+
query: { warehouse_id: unidadeId, product_id: servicoId },
|
|
83
|
+
})
|
|
84
|
+
// O fuso é o da empresa: no do visitante, um horário às 23h podia mudar de dia.
|
|
85
|
+
const dias = groupAvailableSlotsByDay(data?.data ?? [], "America/Sao_Paulo")
|
|
86
|
+
|
|
87
|
+
const { error } = await customerRescheduleAppointment({
|
|
88
|
+
client,
|
|
89
|
+
path: { slot_id: marcacao.id },
|
|
90
|
+
body: { scheduled_for: novoHorario },
|
|
91
|
+
})
|
|
92
|
+
const recusa = appointmentChangeRefusal(error)
|
|
93
|
+
if (recusa?.team_request) {
|
|
94
|
+
// Fora da regra da empresa: ofereça falar com a equipe, com a mensagem do cliente.
|
|
95
|
+
const body = teamRequestCaseBody(recusa, { submissionId: crypto.randomUUID(), message })
|
|
96
|
+
if (body) await customerOpenCase({ client, body })
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Também: `appointmentIsAwaitingPayment` (horário reservado à espera do pagamento),
|
|
101
|
+
`cancelRefundLabel` (o que acontece com o valor pago), `customerDocumentIsNew` e
|
|
102
|
+
`formatDocumentSize`. O documento baixa-se com `customerDownloadDocument`, que
|
|
103
|
+
devolve uma URL assinada de 5 minutos: peça de novo a cada download.
|
|
104
|
+
|
|
105
|
+
> **O token é de servidor.** Ele vale por todo o tenant: exposto no browser, dá
|
|
106
|
+
> acesso a pedidos com dados pessoais dos seus clientes. Chame a API do seu
|
|
107
|
+
> backend ou de um BFF, nunca de código entregue ao navegador.
|
|
108
|
+
|
|
109
|
+
## Mídia de produto / SKU (PDP + stories, otimizado para web)
|
|
110
|
+
|
|
111
|
+
Imagem e vídeo podem viver no **produto pai** e/ou na **SKU** (filho). A PDP
|
|
112
|
+
consolida isso em `variants[].effective_media` (media da SKU se não vazia,
|
|
113
|
+
senão do pai). Cada item tem `role`: `gallery` (carrossel) ou `story` (9:16 /
|
|
114
|
+
shorts). Imagens trazem `variants[]` (imagor / ADR 0125) para `srcset`.
|
|
115
|
+
|
|
116
|
+
Helpers no SDK — use-os em vez de montar URLs à mão:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import {
|
|
120
|
+
createCommerceClient,
|
|
121
|
+
getStorefrontProductDetail,
|
|
122
|
+
buildPdpWebMedia,
|
|
123
|
+
plpCoverWebMedia,
|
|
124
|
+
} from "@mira-dev-tech/commerce-client-sdk"
|
|
125
|
+
|
|
126
|
+
const client = createCommerceClient({ baseUrl, token, memberId })
|
|
127
|
+
const { data: pdp } = await getStorefrontProductDetail({
|
|
128
|
+
client,
|
|
129
|
+
path: { slug_or_id: "meu-produto" },
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
// Variante seleccionada (SKU ou product_id do filho)
|
|
133
|
+
const { gallery, stories, cover } = buildPdpWebMedia(pdp!, selectedSku)
|
|
134
|
+
|
|
135
|
+
// Galeria — srcset + sizes prontos para <img> / next/image
|
|
136
|
+
gallery.map((m) => (
|
|
137
|
+
<img
|
|
138
|
+
key={m.key}
|
|
139
|
+
src={m.src}
|
|
140
|
+
srcSet={m.srcSet}
|
|
141
|
+
sizes={m.sizes}
|
|
142
|
+
alt={m.alt}
|
|
143
|
+
width={m.width}
|
|
144
|
+
height={m.height}
|
|
145
|
+
style={{ aspectRatio: m.aspectRatio }}
|
|
146
|
+
/>
|
|
147
|
+
))
|
|
148
|
+
|
|
149
|
+
// Stories / live commerce — rail vertical
|
|
150
|
+
stories.map((m) =>
|
|
151
|
+
m.kind === "video" ? (
|
|
152
|
+
<video
|
|
153
|
+
key={m.key}
|
|
154
|
+
src={m.src}
|
|
155
|
+
poster={m.poster}
|
|
156
|
+
style={{ aspectRatio: m.aspectRatio }}
|
|
157
|
+
muted
|
|
158
|
+
playsInline
|
|
159
|
+
preload="metadata"
|
|
160
|
+
/>
|
|
161
|
+
) : (
|
|
162
|
+
<img key={m.key} src={m.src} srcSet={m.srcSet} alt={m.alt} style={{ aspectRatio: m.aspectRatio }} />
|
|
163
|
+
),
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
// PLP thumb
|
|
167
|
+
const thumb = plpCoverWebMedia(product)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Também disponíveis: `resolveEffectiveMedia`, `resolveEffectiveMediaFromPdp`,
|
|
171
|
+
`galleryMedia` / `storyMedia`, `toWebProductMedia` / `toWebProductMediaList`.
|
|
172
|
+
|
|
173
|
+
Upload (admin/BFF): `uploadProductMedia` com multipart `file` + opcional
|
|
174
|
+
`role`, `poster`, `duration_ms` — `product_id` do pai **ou** do filho.
|
|
175
|
+
|
|
176
|
+
## Storefront helpers (ciclo PLP → PDP → checkout)
|
|
177
|
+
|
|
178
|
+
O SDK inclui helpers **sem I/O** (excepto `fetchWithEtag`) para não reimplementar
|
|
179
|
+
regras no storefront:
|
|
180
|
+
|
|
181
|
+
| Área | API |
|
|
182
|
+
|------|-----|
|
|
183
|
+
| Variantes | `createVariantPicker(pdp)` — eixos → SKU, preço, stock, mídia |
|
|
184
|
+
| PLP | `toPlpCardView(card)` — `priceLabel`, cover, rental |
|
|
185
|
+
| Locação | `rentalFromPrice`, `formatRentalFromLabel` |
|
|
186
|
+
| Dinheiro | `formatMoney`, `formatPriceRange`, `formatInstallments` |
|
|
187
|
+
| Cache | `fetchWithEtag` / `createEtagMemoryCache` (`If-None-Match` / 304) |
|
|
188
|
+
| Documento | `normalizeDocument`, `isValidCPF` / `isValidCNPJ`, `validateDocument` |
|
|
189
|
+
| CEP | `normalizeZip`, `addressFromZipLookup` (campos locked) |
|
|
190
|
+
| Frete | `ShippingQuoteSession` — trocar CEP invalida cotação |
|
|
191
|
+
| Pagamento | `paymentMethodLabel`, `pixPendingFromResponse`, `pixPollIntervalMs` |
|
|
192
|
+
| Wishlist | `isInWishlist`, `normalizeWishlistCreate` |
|
|
193
|
+
| Carrinho | `normalizeCartLines`, `cartLinesSubtotal` |
|
|
194
|
+
| Experiences | `sortExperienceBlocks`, `resolvedExperienceBlocks` |
|
|
195
|
+
| Erros | `apiErrorMessage`, `isStockConflictError` |
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import {
|
|
199
|
+
createVariantPicker,
|
|
200
|
+
toPlpCardView,
|
|
201
|
+
fetchWithEtag,
|
|
202
|
+
ShippingQuoteSession,
|
|
203
|
+
normalizeDocument,
|
|
204
|
+
validateDocument,
|
|
205
|
+
} from "@mira-dev-tech/commerce-client-sdk"
|
|
206
|
+
|
|
207
|
+
const picker = createVariantPicker(pdp!)
|
|
208
|
+
const resolved = picker.resolve({ Cor: "PRETO", Tamanho: "M" })
|
|
209
|
+
if (resolved.complete && resolved.inStock) {
|
|
210
|
+
// add to cart: resolved.variant.sku + resolved.gallery
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const cards = plp.data.map(toPlpCardView)
|
|
214
|
+
|
|
215
|
+
const ship = new ShippingQuoteSession()
|
|
216
|
+
ship.setZip(cep)
|
|
217
|
+
// após resolveShippingQuotes:
|
|
218
|
+
ship.applyResult(quotes, cep)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Checkout headless completo (sessão/compose/place-order) continua em
|
|
222
|
+
`@mira/commerce-checkout-sdk` — este pacote não duplica esse fluxo.
|
|
223
|
+
|
|
224
|
+
## Mudanças que afetam integrações
|
|
225
|
+
|
|
226
|
+
### CNPJ alfanumérico (em emissão desde 31/07/2026)
|
|
227
|
+
|
|
228
|
+
O CNPJ passou a ter **12 posições alfanuméricas + 2 dígitos verificadores
|
|
229
|
+
numéricos** (IN RFB 2.229). O tipo de `document_number` continua `string`; o que
|
|
230
|
+
muda é o tratamento:
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
// ERRADO — descarta as letras e produz outro documento
|
|
234
|
+
const doc = input.replace(/\D/g, "")
|
|
235
|
+
|
|
236
|
+
// CERTO — remove só pontuação, preserva letras, MAIÚSCULAS
|
|
237
|
+
const doc = input.toUpperCase().replace(/[^0-9A-Z]/g, "")
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Validação com `^\d{14}$` rejeita toda empresa aberta a partir dessa data. A API
|
|
241
|
+
confere o dígito verificador (módulo 11 com valor `ASCII-48`) e recusa documento
|
|
242
|
+
inválido — inclusive os numéricos com DV errado que passavam antes.
|
|
243
|
+
|
|
244
|
+
### Endereço de entrega é conferido contra o CEP
|
|
245
|
+
|
|
246
|
+
O frete é cotado a partir do CEP, então um endereço incoerente com ele (CEP de
|
|
247
|
+
uma zona, cidade de outra) é recusado com **422**.
|
|
248
|
+
|
|
249
|
+
Busque o endereço oficial e deixe o comprador preencher **apenas número e
|
|
250
|
+
complemento**:
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
import { getAddressByZip } from "@mira-dev-tech/commerce-client-sdk"
|
|
254
|
+
|
|
255
|
+
const { data } = await getAddressByZip({ client, query: { zip: "01310-100" } })
|
|
256
|
+
// data.street / data.neighborhood / data.city / data.state → campos bloqueados
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`404` significa "não foi possível resolver o CEP agora" (inexistente, ou o
|
|
260
|
+
provedor externo fora do ar), não "CEP inválido". É raro — a base se preenche
|
|
261
|
+
sozinha na primeira consulta de cada CEP — e nesse caso deixe preencher à mão:
|
|
262
|
+
o pedido não é bloqueado por isso.
|
|
263
|
+
|
|
264
|
+
**Trocar o CEP invalida a cotação.** Recote com `resolveShippingQuotes` e
|
|
265
|
+
descarte o método anterior: um `shipping_method_id` cotado para outro CEP dá um
|
|
266
|
+
preço que a API não aceita.
|
|
267
|
+
|
|
268
|
+
### O pedido guarda um snapshot do comprador
|
|
269
|
+
|
|
270
|
+
`order.customer_snapshot` traz nome, email, WhatsApp e documento **como estavam
|
|
271
|
+
no momento da compra**. Use-o em nota fiscal e pós-venda em vez de reconsultar o
|
|
272
|
+
cadastro, que muda com o tempo.
|
|
273
|
+
|
|
274
|
+
## Erros
|
|
275
|
+
|
|
276
|
+
As funções devolvem `{ data, error }` — status HTTP não lança exceção:
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
const { data, error, response } = await createOrder({ client, body })
|
|
280
|
+
if (error) {
|
|
281
|
+
if (response.status === 422) {
|
|
282
|
+
// regra de negócio: endereço incoerente com o CEP, estoque insuficiente, …
|
|
283
|
+
}
|
|
284
|
+
return
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Versões e notificações
|
|
289
|
+
|
|
290
|
+
Versionamento semântico, uma versão por contrato publicado:
|
|
291
|
+
|
|
292
|
+
- **patch** — correção sem mudar contrato
|
|
293
|
+
- **minor** — campos ou operações novas, retrocompatíveis
|
|
294
|
+
- **major** — mudança que quebra, sempre com nota de migração no release
|
|
295
|
+
|
|
296
|
+
Para ser avisado de cada versão, no repositório
|
|
297
|
+
[mira-dev-tech/mira-commerce-core](https://github.com/mira-dev-tech/mira-commerce-core)
|
|
298
|
+
use **Watch → Custom → Releases**. Toda publicação nasce de um GitHub Release
|
|
299
|
+
com o changelog — as do SDK usam tags `sdk-vX.Y.Z`.
|
|
300
|
+
|
|
301
|
+
Pré-releases saem na dist-tag `next`
|
|
302
|
+
(`npm i @mira-dev-tech/commerce-client-sdk@npm:@mira-arandu/commerce-client-sdk@next`)
|
|
303
|
+
e nunca viram o `latest`.
|
|
304
|
+
|
|
305
|
+
O pacote é publicado já compilado (JS + `.d.ts`), então funciona tanto com
|
|
306
|
+
bundler quanto em Node puro.
|
|
307
|
+
|
|
308
|
+
## Referência
|
|
309
|
+
|
|
310
|
+
- Catálogo de operações da API em execução: <https://api.mira-dev.tech/mcp/catalog>
|
|
311
|
+
- Contrato OpenAPI: `public/openapi/mira-commerce.openapi.yaml`
|
|
312
|
+
- Analytics, comissões e returns: [`docs/sdk-wiring.md`](../../docs/sdk-wiring.md)
|
|
313
|
+
|
|
314
|
+
### Analytics (Mira dataLayer + GTM/GA4)
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
import { createMiraAnalytics } from "@mira-dev-tech/commerce-client-sdk"
|
|
318
|
+
|
|
319
|
+
const analytics = createMiraAnalytics({ gtmId: "GTM-XXXX", gaMeasurementId: "G-XXXX" })
|
|
320
|
+
analytics.setConsent({ analytics: true })
|
|
321
|
+
analytics.viewItem({ currency: "BRL", items: [{ item_id: "SKU-1" }] })
|
|
322
|
+
analytics.viewItemList({ item_list_id: "catalog", item_list_name: "Catálogo", items })
|
|
323
|
+
// Loja sem checkout (venda por WhatsApp): o lead é a conversão
|
|
324
|
+
analytics.generateLead({ currency: "BRL", value: 99 }, { lead_source: "whatsapp", placement: "pdp" })
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Guia: [`docs/storefront-analytics.md`](../../docs/storefront-analytics.md) · ADR 0067.
|
|
328
|
+
|
|
329
|
+
### Primeiro toque da sessão no pedido — ADR 0198 C4
|
|
330
|
+
|
|
331
|
+
O core guarda o primeiro toque da sessão em `core.order_attribution` e nunca o
|
|
332
|
+
reescreve, mas só o browser sabe por onde a pessoa chegou. Duas linhas:
|
|
333
|
+
|
|
334
|
+
```ts
|
|
335
|
+
import { captureFirstTouch, firstTouchMarketingData } from "@mira-dev-tech/commerce-client-sdk"
|
|
336
|
+
|
|
337
|
+
captureFirstTouch() // em TODO o carregamento de página (layout/root)
|
|
338
|
+
|
|
339
|
+
// ao gravar a sessão de checkout, junto com a UTM do último toque que a loja já envia:
|
|
340
|
+
marketing_data: { ...lastTouchUtm, ...firstTouchMarketingData() }
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
- Grava UTM (`source`/`medium`/`campaign`/`content`/`term`), `gclid` e
|
|
344
|
+
`first_touch_at` (RFC3339) **uma vez**, na primeira visita conhecida — uma
|
|
345
|
+
primeira visita directa também conta, e a campanha posterior não vira a
|
|
346
|
+
descoberta.
|
|
347
|
+
- `touch_count` só cresce quando chega UTM/gclid **novo** (recarregar a mesma
|
|
348
|
+
URL não conta).
|
|
349
|
+
- `anon_id` é o `mira_anon_id` do `localStorage` — o mesmo token do header
|
|
350
|
+
`X-Mira-Anon` de `/events/track` (`core.web_events`). Para o header, usar
|
|
351
|
+
`getOrCreateAnonId()` em vez de gerar outro id.
|
|
352
|
+
- Chave desconhecida é **omitida**, nunca string vazia; sem storage (SSR, modo
|
|
353
|
+
privado) devolve `{}` sem lançar.
|
|
354
|
+
|
|
355
|
+
### Carrinho dual-write (session + local) — ADR 0142
|
|
356
|
+
|
|
357
|
+
Sessões de checkout expiram (~30 min). O helper `createCartStore` grava linhas
|
|
358
|
+
cotadas na sessão **e** num snapshot local; na leitura reidrata a sessão se
|
|
359
|
+
estiver vazia. Tokens (`cks_tok`) **nunca** vão para o storage do browser.
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
import { createCartStore } from "@mira/commerce-client-sdk"
|
|
363
|
+
|
|
364
|
+
const cart = createCartStore({
|
|
365
|
+
storageKey: "loja-cart-v1",
|
|
366
|
+
ensureSession: () => ensureCheckoutSession(),
|
|
367
|
+
getSessionState: () => fetchCheckoutSessionState(),
|
|
368
|
+
patchSessionState: (partial) => patchCheckoutSessionState(partial),
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
await cart.write({ lines: [{ sku: "SKU-1", qty: 1 }], updatedAt: new Date().toISOString() })
|
|
372
|
+
const current = await cart.read()
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Forma recomendada de `state.cart`: `{ lines, updatedAt }` (array legado ainda
|
|
376
|
+
aceite no core). Detalhe: [`docs/adr/0142-storefront-cart-dual-write.md`](../../docs/adr/0142-storefront-cart-dual-write.md)
|
|
377
|
+
· também em `@mira/commerce-checkout-sdk`.
|
|
378
|
+
|
|
379
|
+
### Dinheiro em centavos (ADR 0151)
|
|
380
|
+
|
|
381
|
+
Quote, compose, card da PLP e `rental_meta` devolvem o major (reais, deprecated)
|
|
382
|
+
e o irmão `*_cents`. Na vitrine, preferir os centavos:
|
|
383
|
+
|
|
384
|
+
```ts
|
|
385
|
+
import { formatMoneyFromCents } from "@mira-dev-tech/commerce-client-sdk"
|
|
386
|
+
|
|
387
|
+
formatMoneyFromCents(16990) // "R$ 169,90"
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
## Desenvolvimento (interno)
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
npm run sdk:generate # regenera src/generated do spec
|
|
394
|
+
npm run build --workspace=@mira/commerce-client-sdk # dist JS + .d.ts (o que é publicado)
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
No repositório o `package.json` aponta para `src/` — é o que o monorepo consome
|
|
398
|
+
via workspace, sem passo de build. O workflow de publicação reescreve
|
|
399
|
+
`main`/`types`/`exports` para `dist/` no tarball.
|
|
400
|
+
|
|
401
|
+
Não editar `src/generated/` à mão. No monorepo, preferir a facade
|
|
402
|
+
[`lib/commerce-client/`](../../lib/commerce-client/), que roteia mock vs live via
|
|
403
|
+
`NEXT_PUBLIC_COMMERCE_API_MODE`.
|
|
404
|
+
|
|
405
|
+
Publicação: `.github/workflows/publish-commerce-sdk.yml` (npm público e GitHub
|
|
406
|
+
Packages, disparado por GitHub Release). ADR 0008 · runbook em
|
|
407
|
+
[`docs/ops/sdk-publish.md`](../../docs/ops/sdk-publish.md).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** CEP só dígitos (8). */
|
|
2
|
+
export declare function normalizeZip(raw: string): string;
|
|
3
|
+
export declare function isValidZip(raw: string): boolean;
|
|
4
|
+
export declare function formatZip(raw: string): string;
|
|
5
|
+
/** Subconjunto típico de `getAddressByZip`. */
|
|
6
|
+
export type ZipLookupAddress = {
|
|
7
|
+
zip?: string;
|
|
8
|
+
street?: string;
|
|
9
|
+
neighborhood?: string;
|
|
10
|
+
city?: string;
|
|
11
|
+
state?: string;
|
|
12
|
+
};
|
|
13
|
+
export type AddressFill = {
|
|
14
|
+
zip: string;
|
|
15
|
+
street: string;
|
|
16
|
+
neighborhood: string;
|
|
17
|
+
city: string;
|
|
18
|
+
state: string;
|
|
19
|
+
/** Campos que o comprador não deve editar (preenchidos pelo CEP). */
|
|
20
|
+
lockedFields: Array<"street" | "neighborhood" | "city" | "state" | "zip">;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Monta formulário de entrega a partir do lookup de CEP.
|
|
24
|
+
* Comprador preenche só número/complemento.
|
|
25
|
+
*/
|
|
26
|
+
export declare function addressFromZipLookup(data: ZipLookupAddress): AddressFill;
|
|
27
|
+
//# sourceMappingURL=address.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI7C;AAED,+CAA+C;AAC/C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,qEAAqE;IACrE,YAAY,EAAE,KAAK,CAAC,QAAQ,GAAG,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC,CAAA;CAC1E,CAAA;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,gBAAgB,GAAG,WAAW,CAUxE"}
|
package/dist/address.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** CEP só dígitos (8). */
|
|
2
|
+
export function normalizeZip(raw) {
|
|
3
|
+
return raw.replace(/\D/g, "").slice(0, 8);
|
|
4
|
+
}
|
|
5
|
+
export function isValidZip(raw) {
|
|
6
|
+
return /^\d{8}$/.test(normalizeZip(raw));
|
|
7
|
+
}
|
|
8
|
+
export function formatZip(raw) {
|
|
9
|
+
const z = normalizeZip(raw);
|
|
10
|
+
if (z.length !== 8)
|
|
11
|
+
return z;
|
|
12
|
+
return `${z.slice(0, 5)}-${z.slice(5)}`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Monta formulário de entrega a partir do lookup de CEP.
|
|
16
|
+
* Comprador preenche só número/complemento.
|
|
17
|
+
*/
|
|
18
|
+
export function addressFromZipLookup(data) {
|
|
19
|
+
const zip = normalizeZip(data.zip ?? "");
|
|
20
|
+
return {
|
|
21
|
+
zip,
|
|
22
|
+
street: data.street ?? "",
|
|
23
|
+
neighborhood: data.neighborhood ?? "",
|
|
24
|
+
city: data.city ?? "",
|
|
25
|
+
state: data.state ?? "",
|
|
26
|
+
lockedFields: ["street", "neighborhood", "city", "state", "zip"],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=address.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address.js","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAC3C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAA;AAC1C,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,MAAM,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,CAAA;IAC3B,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAA;IAC5B,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;AACzC,CAAC;AAqBD;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAsB;IACzD,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,CAAA;IACxC,OAAO;QACL,GAAG;QACH,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE;QACzB,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,EAAE;QACrC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE;QACvB,YAAY,EAAE,CAAC,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;KACjE,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MiraCdpBridgeOptions, MiraDataLayerEvent } from "./types.js";
|
|
2
|
+
export declare function toCdpEvents(event: MiraDataLayerEvent): Array<{
|
|
3
|
+
type: string;
|
|
4
|
+
ref?: string;
|
|
5
|
+
}>;
|
|
6
|
+
/**
|
|
7
|
+
* Fire-and-forget; falhas (401 anónimo, etc.) são ignoradas.
|
|
8
|
+
*
|
|
9
|
+
* Sem sessão de cliente, `/events/track` só grava o evento quando chega o
|
|
10
|
+
* `X-Mira-Anon` — sem ele responde 202 e descarta, e o funil anónimo (a maior
|
|
11
|
+
* parte do tráfego) ficava vazio.
|
|
12
|
+
*/
|
|
13
|
+
export declare function sendToCdp(opts: MiraCdpBridgeOptions, event: MiraDataLayerEvent): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=cdp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdp.d.ts","sourceRoot":"","sources":["../../src/analytics/cdp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAiB,MAAM,SAAS,CAAA;AAatF,wBAAgB,WAAW,CACzB,KAAK,EAAE,kBAAkB,GACxB,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA2BvC;AA+BD;;;;;;GAMG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,oBAAoB,EAC1B,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,IAAI,CAAC,CAgBf"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/** Allowlist do backend (`internal/events/track.go`). */
|
|
2
|
+
const CDP_TYPE = {
|
|
3
|
+
page_view: "page_view",
|
|
4
|
+
view_item: "product_view",
|
|
5
|
+
add_to_cart: "add_to_cart",
|
|
6
|
+
begin_checkout: "checkout_step",
|
|
7
|
+
checkout_step: "checkout_step",
|
|
8
|
+
// view_item_list / select_item / purchase / generate_lead: fora do allowlist
|
|
9
|
+
// CDP — não enviar (o core responderia 202 e descartaria)
|
|
10
|
+
};
|
|
11
|
+
export function toCdpEvents(event) {
|
|
12
|
+
const type = CDP_TYPE[event.event];
|
|
13
|
+
if (!type)
|
|
14
|
+
return [];
|
|
15
|
+
if (event.event === "page_view") {
|
|
16
|
+
return [{ type }];
|
|
17
|
+
}
|
|
18
|
+
if (event.event === "checkout_step" || event.event === "begin_checkout") {
|
|
19
|
+
const step = event.ecommerce?.checkout_step;
|
|
20
|
+
const ref = step != null
|
|
21
|
+
? String(step).slice(0, 120)
|
|
22
|
+
: event.event === "begin_checkout"
|
|
23
|
+
? "begin"
|
|
24
|
+
: undefined;
|
|
25
|
+
return [{ type, ...(ref ? { ref } : {}) }];
|
|
26
|
+
}
|
|
27
|
+
const items = event.ecommerce?.items ?? [];
|
|
28
|
+
if (items.length === 0) {
|
|
29
|
+
return [{ type }];
|
|
30
|
+
}
|
|
31
|
+
return items.map((item) => ({
|
|
32
|
+
type,
|
|
33
|
+
ref: String(item.item_id).slice(0, 120),
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Chave do token anónimo no `localStorage` e header que o leva ao core. É o
|
|
38
|
+
* MESMO token do `anon_id` do `marketing_data` (ADR 0198 C4, #2345) — um
|
|
39
|
+
* segundo identificador partiria a costura entre o funil (`core.web_events`) e
|
|
40
|
+
* o pedido (`core.order_attribution`).
|
|
41
|
+
*
|
|
42
|
+
* Leitura mínima de propósito: quem CRIA o token é o `getOrCreateAnonId` da
|
|
43
|
+
* captura do primeiro toque (PR #2368), que substitui este leitor quando
|
|
44
|
+
* entrar. Aqui só se lê — o bridge não inventa um id que a atribuição do
|
|
45
|
+
* pedido não conheça.
|
|
46
|
+
*/
|
|
47
|
+
const ANON_STORAGE_KEY = "mira_anon_id";
|
|
48
|
+
const ANON_HEADER = "X-Mira-Anon";
|
|
49
|
+
/** Tecto do core (`internal/events/track.go`). */
|
|
50
|
+
const MAX_ANON_LEN = 64;
|
|
51
|
+
function readAnonId(opts) {
|
|
52
|
+
try {
|
|
53
|
+
const explicit = typeof opts.anonId === "function" ? opts.anonId() : opts.anonId;
|
|
54
|
+
if (explicit !== undefined)
|
|
55
|
+
return explicit.trim().slice(0, MAX_ANON_LEN);
|
|
56
|
+
if (typeof window === "undefined")
|
|
57
|
+
return "";
|
|
58
|
+
// Aceder a `window.localStorage` pode LANÇAR (SecurityError com cookies bloqueados).
|
|
59
|
+
const stored = window.localStorage?.getItem(ANON_STORAGE_KEY)?.trim() ?? "";
|
|
60
|
+
return stored.length <= MAX_ANON_LEN ? stored : "";
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Fire-and-forget; falhas (401 anónimo, etc.) são ignoradas.
|
|
68
|
+
*
|
|
69
|
+
* Sem sessão de cliente, `/events/track` só grava o evento quando chega o
|
|
70
|
+
* `X-Mira-Anon` — sem ele responde 202 e descarta, e o funil anónimo (a maior
|
|
71
|
+
* parte do tráfego) ficava vazio.
|
|
72
|
+
*/
|
|
73
|
+
export async function sendToCdp(opts, event) {
|
|
74
|
+
if (opts.enabled === false)
|
|
75
|
+
return;
|
|
76
|
+
const events = toCdpEvents(event);
|
|
77
|
+
if (events.length === 0)
|
|
78
|
+
return;
|
|
79
|
+
const headers = { "Content-Type": "application/json" };
|
|
80
|
+
const anon = readAnonId(opts);
|
|
81
|
+
if (anon)
|
|
82
|
+
headers[ANON_HEADER] = anon;
|
|
83
|
+
try {
|
|
84
|
+
await opts.client.post({
|
|
85
|
+
url: "/events/track",
|
|
86
|
+
body: { events },
|
|
87
|
+
headers,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
/* CDP é best-effort */
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=cdp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdp.js","sourceRoot":"","sources":["../../src/analytics/cdp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-analytics.d.ts","sourceRoot":"","sources":["../../src/analytics/create-analytics.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,0BAA0B,EAC1B,aAAa,EAKd,MAAM,SAAS,CAAA;AAEhB,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,0BAA+B,GAAG,aAAa,CAyG3F"}
|