@unbox-plus/cli 0.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/LICENSE.md +46 -0
- package/README.md +175 -0
- package/bin/cli.js +439 -0
- package/package.json +43 -0
- package/src/colors.js +100 -0
- package/src/presets.js +241 -0
- package/src/theme.js +253 -0
- package/template/.claude/agents/assets-marca.md +40 -0
- package/template/.claude/agents/avaliador-visual.md +48 -0
- package/template/.claude/agents/branding-briefing.md +401 -0
- package/template/.claude/agents/conteudo-secao.md +46 -0
- package/template/.claude/agents/paginas-legais.md +44 -0
- package/template/.claude/settings.json +3 -0
- package/template/.claude/skills/frontend-design/ATTRIBUTION.md +3 -0
- package/template/.claude/skills/frontend-design/LICENSE.txt +177 -0
- package/template/.claude/skills/frontend-design/SKILL.md +55 -0
- package/template/.claude/skills/web-design-guidelines/ATTRIBUTION.md +3 -0
- package/template/.claude/skills/web-design-guidelines/SKILL.md +50 -0
- package/template/.claude/skills/web-design-guidelines/guidelines-snapshot.md +180 -0
- package/template/.env.example +158 -0
- package/template/.mcp.json.example +14 -0
- package/template/.nvmrc +1 -0
- package/template/CLAUDE.md +333 -0
- package/template/COVERAGE.md +80 -0
- package/template/DEPLOY.md +179 -0
- package/template/QA.md +108 -0
- package/template/README.md +152 -0
- package/template/agents/CONSTRUCAO.md +207 -0
- package/template/agents/MANAGER.md +192 -0
- package/template/agents/PADROES.md +255 -0
- package/template/agents/definitions/00-scaffold.md +56 -0
- package/template/agents/definitions/01-layout.md +47 -0
- package/template/agents/definitions/02-homepage.md +70 -0
- package/template/agents/definitions/03-catalog.md +86 -0
- package/template/agents/definitions/04-pdp.md +97 -0
- package/template/agents/definitions/05-cart.md +116 -0
- package/template/agents/definitions/06-checkout.md +146 -0
- package/template/agents/definitions/07-auth.md +130 -0
- package/template/agents/definitions/08-customer.md +55 -0
- package/template/agents/definitions/09-promotions.md +62 -0
- package/template/agents/definitions/10-feedback.md +128 -0
- package/template/agents/definitions/11-seo-infra.md +159 -0
- package/template/agents/definitions/12-deploy.md +173 -0
- package/template/agents/definitions/13-cro.md +275 -0
- package/template/agents/definitions/14-seo.md +388 -0
- package/template/agents/definitions/15-branding.md +26 -0
- package/template/agents/definitions/16-qa-visual.md +138 -0
- package/template/agents/definitions/17-aeo.md +257 -0
- package/template/app/(loja)/busca/page.tsx +72 -0
- package/template/app/(loja)/carrinho/oferta/page.tsx +57 -0
- package/template/app/(loja)/carrinho/page.tsx +160 -0
- package/template/app/(loja)/categoria/[tagSlug]/layout.tsx +31 -0
- package/template/app/(loja)/categoria/[tagSlug]/loading.tsx +17 -0
- package/template/app/(loja)/categoria/[tagSlug]/page.tsx +49 -0
- package/template/app/(loja)/checkout/page.tsx +80 -0
- package/template/app/(loja)/checkout/pix/[ref]/page.tsx +133 -0
- package/template/app/(loja)/conta/assinaturas/[referenceId]/page.tsx +112 -0
- package/template/app/(loja)/conta/assinaturas/page.tsx +51 -0
- package/template/app/(loja)/conta/enderecos/page.tsx +20 -0
- package/template/app/(loja)/conta/entrar/page.tsx +119 -0
- package/template/app/(loja)/conta/page.tsx +45 -0
- package/template/app/(loja)/conta/pedidos/[referenceId]/page.tsx +49 -0
- package/template/app/(loja)/conta/pedidos/page.tsx +104 -0
- package/template/app/(loja)/conta/preferencias/page.tsx +24 -0
- package/template/app/(loja)/devolucoes/page.tsx +36 -0
- package/template/app/(loja)/layout.tsx +22 -0
- package/template/app/(loja)/oferta/page.tsx +57 -0
- package/template/app/(loja)/page.tsx +86 -0
- package/template/app/(loja)/pedido/[referenceId]/page.tsx +78 -0
- package/template/app/(loja)/privacidade/page.tsx +113 -0
- package/template/app/(loja)/produto/[productSlug]/layout.tsx +23 -0
- package/template/app/(loja)/produto/[productSlug]/loading.tsx +20 -0
- package/template/app/(loja)/produto/[productSlug]/page.tsx +372 -0
- package/template/app/(loja)/produtos/loading.tsx +16 -0
- package/template/app/(loja)/produtos/page.tsx +33 -0
- package/template/app/(loja)/termos/page.tsx +104 -0
- package/template/app/acesso/page.tsx +129 -0
- package/template/app/api/account/addresses/route.ts +39 -0
- package/template/app/api/account/exists/route.ts +21 -0
- package/template/app/api/account/me/route.ts +25 -0
- package/template/app/api/account/otp/route.ts +26 -0
- package/template/app/api/account/preferences/route.ts +19 -0
- package/template/app/api/account/signin/route.ts +27 -0
- package/template/app/api/account/signout/route.ts +9 -0
- package/template/app/api/acesso/route.ts +171 -0
- package/template/app/api/capi/route.ts +34 -0
- package/template/app/api/cart/coupon/route.ts +49 -0
- package/template/app/api/cart/items/route.ts +45 -0
- package/template/app/api/cart/link/route.ts +69 -0
- package/template/app/api/cart/route.ts +113 -0
- package/template/app/api/cart/share/route.ts +21 -0
- package/template/app/api/cep/[code]/route.ts +18 -0
- package/template/app/api/checkout/address/route.ts +24 -0
- package/template/app/api/checkout/email/route.ts +41 -0
- package/template/app/api/checkout/installments/route.ts +16 -0
- package/template/app/api/checkout/route.ts +110 -0
- package/template/app/api/checkout/shipping/route.ts +65 -0
- package/template/app/api/checkout-destination/route.ts +31 -0
- package/template/app/api/order/[ref]/route.ts +17 -0
- package/template/app/api/payment-link/route.ts +28 -0
- package/template/app/api/revalidate/route.ts +52 -0
- package/template/app/api/shipping/quote/route.ts +29 -0
- package/template/app/api/subscriptions/[id]/route.ts +52 -0
- package/template/app/api/track/route.ts +28 -0
- package/template/app/api/unbox/catalogo/route.ts +136 -0
- package/template/app/api/unbox/paginas/route.ts +167 -0
- package/template/app/api/unbox/vitrine/route.ts +84 -0
- package/template/app/api/webhooks/unbox/route.ts +97 -0
- package/template/app/apple-icon.svg +5 -0
- package/template/app/error.tsx +15 -0
- package/template/app/globals.css +457 -0
- package/template/app/icon.svg +4 -0
- package/template/app/layout.tsx +90 -0
- package/template/app/llms.txt/route.ts +43 -0
- package/template/app/manifest.ts +21 -0
- package/template/app/not-found.tsx +18 -0
- package/template/app/opengraph-image.tsx +35 -0
- package/template/app/robots.ts +31 -0
- package/template/app/sitemap.ts +42 -0
- package/template/bootstrap.sh +87 -0
- package/template/components/account/account-shell.tsx +47 -0
- package/template/components/account/address-book.tsx +147 -0
- package/template/components/account/preferences-form.tsx +77 -0
- package/template/components/account/reorder-button.tsx +47 -0
- package/template/components/account/signout-button.tsx +20 -0
- package/template/components/account/subscription-actions.tsx +204 -0
- package/template/components/account-nav.tsx +41 -0
- package/template/components/address-fields.tsx +102 -0
- package/template/components/analytics/data-layer-ready.tsx +16 -0
- package/template/components/analytics/purchase-tracker.tsx +32 -0
- package/template/components/brand-search.tsx +34 -0
- package/template/components/cart/cart-provider.tsx +340 -0
- package/template/components/cart/mini-cart.tsx +228 -0
- package/template/components/cart-button.tsx +35 -0
- package/template/components/catalog/catalog-client.tsx +523 -0
- package/template/components/catalog/grid-skeleton.tsx +19 -0
- package/template/components/catalog/pager.tsx +66 -0
- package/template/components/catalog/product-grid-card.tsx +52 -0
- package/template/components/catalog/product-grid.tsx +27 -0
- package/template/components/checkout/checkout-client.tsx +1333 -0
- package/template/components/chrome/announce-bar.tsx +57 -0
- package/template/components/chrome/chrome-recipe.ts +13 -0
- package/template/components/chrome/footers/captura-botao.tsx +28 -0
- package/template/components/chrome/footers/colunas.tsx +110 -0
- package/template/components/chrome/footers/conversao.tsx +90 -0
- package/template/components/chrome/footers/editorial.tsx +98 -0
- package/template/components/chrome/footers/minimal.tsx +55 -0
- package/template/components/chrome/header-bar-mobile.tsx +64 -0
- package/template/components/chrome/headers/centralizado.tsx +71 -0
- package/template/components/chrome/headers/classico.tsx +64 -0
- package/template/components/chrome/headers/compacto.tsx +66 -0
- package/template/components/chrome/headers/equilibrado.tsx +73 -0
- package/template/components/chrome/headers/imersivo.tsx +62 -0
- package/template/components/chrome/registry.ts +70 -0
- package/template/components/chrome/solid-on-scroll.tsx +39 -0
- package/template/components/empty-state.tsx +22 -0
- package/template/components/home/combo-card-compact.tsx +67 -0
- package/template/components/home/combos-home.tsx +64 -0
- package/template/components/home/combos-section.tsx +177 -0
- package/template/components/home/home-recipe.ts +41 -0
- package/template/components/home/sections/attributes-marquee.tsx +178 -0
- package/template/components/home/sections/benefits.tsx +80 -0
- package/template/components/home/sections/bloco-html.tsx +47 -0
- package/template/components/home/sections/catalogo.ts +165 -0
- package/template/components/home/sections/category-pills.tsx +32 -0
- package/template/components/home/sections/combos-carousel.tsx +141 -0
- package/template/components/home/sections/comparison.tsx +73 -0
- package/template/components/home/sections/founder-story.tsx +40 -0
- package/template/components/home/sections/hero.tsx +187 -0
- package/template/components/home/sections/kits.tsx +11 -0
- package/template/components/home/sections/media-cards.tsx +70 -0
- package/template/components/home/sections/newsletter.tsx +62 -0
- package/template/components/home/sections/product-showcase.tsx +175 -0
- package/template/components/home/sections/purchase-hero.tsx +187 -0
- package/template/components/home/sections/quote-banner.tsx +50 -0
- package/template/components/home/sections/registry.ts +186 -0
- package/template/components/home/sections/reviews-carousel.tsx +66 -0
- package/template/components/home/sections/reviews.tsx +68 -0
- package/template/components/home/sections/ritual.tsx +96 -0
- package/template/components/home/sections/savings.tsx +47 -0
- package/template/components/home/sections/social-row.tsx +65 -0
- package/template/components/home/sections/spec-table.tsx +117 -0
- package/template/components/home/sections/stars.tsx +12 -0
- package/template/components/home/sections/stats-grid.tsx +54 -0
- package/template/components/home/sections/trust-bar.tsx +56 -0
- package/template/components/home/sections/trust-strip.tsx +40 -0
- package/template/components/home/sections/video-wall.tsx +59 -0
- package/template/components/landing/landing-recipe.ts +20 -0
- package/template/components/landing/oferta-sections.tsx +40 -0
- package/template/components/landing/product-picker.tsx +257 -0
- package/template/components/mobile-nav.tsx +71 -0
- package/template/components/order-status.tsx +163 -0
- package/template/components/powered-by-unbox.tsx +27 -0
- package/template/components/product/pdp/buy-box.tsx +604 -0
- package/template/components/product/pdp/catalog-grid.tsx +113 -0
- package/template/components/product/pdp/faq-modelo.ts +86 -0
- package/template/components/product/pdp/gallery.tsx +87 -0
- package/template/components/product/pdp/interactive.tsx +200 -0
- package/template/components/product/pdp/newsletter.tsx +64 -0
- package/template/components/product/pdp/payment-chips.tsx +22 -0
- package/template/components/product/pdp/pdp-view.tsx +227 -0
- package/template/components/product/pdp/recommendations.tsx +144 -0
- package/template/components/product/pdp/sections.tsx +173 -0
- package/template/components/quantity-stepper.tsx +36 -0
- package/template/components/search-box.tsx +37 -0
- package/template/components/site-footer.tsx +59 -0
- package/template/components/site-header.tsx +64 -0
- package/template/components/ui/accordion.tsx +72 -0
- package/template/components/ui/alert-dialog.tsx +187 -0
- package/template/components/ui/alert.tsx +76 -0
- package/template/components/ui/aspect-ratio.tsx +22 -0
- package/template/components/ui/avatar.tsx +109 -0
- package/template/components/ui/badge.tsx +52 -0
- package/template/components/ui/breadcrumb.tsx +125 -0
- package/template/components/ui/button.tsx +58 -0
- package/template/components/ui/card.tsx +103 -0
- package/template/components/ui/carousel.tsx +242 -0
- package/template/components/ui/command.tsx +196 -0
- package/template/components/ui/dialog.tsx +160 -0
- package/template/components/ui/dropdown-menu.tsx +268 -0
- package/template/components/ui/foto.tsx +54 -0
- package/template/components/ui/input-group.tsx +158 -0
- package/template/components/ui/input-otp.tsx +87 -0
- package/template/components/ui/input.tsx +20 -0
- package/template/components/ui/label.tsx +20 -0
- package/template/components/ui/navigation-menu.tsx +168 -0
- package/template/components/ui/pagination.tsx +132 -0
- package/template/components/ui/popover.tsx +90 -0
- package/template/components/ui/progress.tsx +83 -0
- package/template/components/ui/radio-group.tsx +38 -0
- package/template/components/ui/scroll-area.tsx +55 -0
- package/template/components/ui/select.tsx +201 -0
- package/template/components/ui/separator.tsx +25 -0
- package/template/components/ui/sheet.tsx +138 -0
- package/template/components/ui/skeleton.tsx +13 -0
- package/template/components/ui/sonner.tsx +44 -0
- package/template/components/ui/switch.tsx +32 -0
- package/template/components/ui/table.tsx +116 -0
- package/template/components/ui/tabs.tsx +82 -0
- package/template/components/ui/textarea.tsx +18 -0
- package/template/components/ui/toggle-group.tsx +89 -0
- package/template/components/ui/toggle.tsx +45 -0
- package/template/components/ui/tooltip.tsx +66 -0
- package/template/components.json +25 -0
- package/template/eslint.config.mjs +36 -0
- package/template/gitignore +21 -0
- package/template/lib/analytics.ts +369 -0
- package/template/lib/api.ts +60 -0
- package/template/lib/capi.ts +87 -0
- package/template/lib/cart-link.ts +51 -0
- package/template/lib/cart-normalize.ts +142 -0
- package/template/lib/cart-recovery.ts +23 -0
- package/template/lib/cart-response.ts +48 -0
- package/template/lib/catalog-map.ts +54 -0
- package/template/lib/catalog.ts +3 -0
- package/template/lib/checkout-lock.ts +21 -0
- package/template/lib/checkout-nav.ts +22 -0
- package/template/lib/config.ts +51 -0
- package/template/lib/crm.ts +40 -0
- package/template/lib/customer-session.ts +21 -0
- package/template/lib/dataloader.ts +23 -0
- package/template/lib/editable/config.ts +11 -0
- package/template/lib/editable/document.ts +1762 -0
- package/template/lib/editable/index.ts +3 -0
- package/template/lib/editable/primitives.tsx +821 -0
- package/template/lib/editable/provider.tsx +861 -0
- package/template/lib/editable/rastreio-navegacao.tsx +65 -0
- package/template/lib/editable/rastreio.tsx +168 -0
- package/template/lib/editable/server.ts +189 -0
- package/template/lib/editable/tokens.ts +22 -0
- package/template/lib/editable/verify.ts +24 -0
- package/template/lib/enrichment/combos.ts +184 -0
- package/template/lib/enrichment/index.ts +188 -0
- package/template/lib/enrichment/products.json +1 -0
- package/template/lib/env-check.ts +47 -0
- package/template/lib/format.ts +130 -0
- package/template/lib/icons.ts +67 -0
- package/template/lib/json-ld.ts +9 -0
- package/template/lib/llms-txt.ts +75 -0
- package/template/lib/mockup.ts +24 -0
- package/template/lib/newsletter.ts +12 -0
- package/template/lib/orders.ts +160 -0
- package/template/lib/queries.ts +66 -0
- package/template/lib/ratelimit.ts +47 -0
- package/template/lib/rotas-editaveis.ts +192 -0
- package/template/lib/sanitize.ts +36 -0
- package/template/lib/schemas.ts +78 -0
- package/template/lib/session.ts +98 -0
- package/template/lib/store-config.ts +67 -0
- package/template/lib/unbox/client.ts +829 -0
- package/template/lib/unbox/customer.ts +224 -0
- package/template/lib/unbox/errors.ts +97 -0
- package/template/lib/unbox/index.ts +10 -0
- package/template/lib/unbox/store.ts +113 -0
- package/template/lib/unbox/types.ts +195 -0
- package/template/lib/unbox/webhooks.ts +66 -0
- package/template/lib/utils.ts +6 -0
- package/template/lib/vitrine.ts +242 -0
- package/template/lib/webhook-store.ts +22 -0
- package/template/middleware.ts +178 -0
- package/template/next.config.ts +63 -0
- package/template/package.json +62 -0
- package/template/postcss.config.mjs +5 -0
- package/template/public/brand/coll/default.png +0 -0
- package/template/public/brand/hero-desktop.svg +12 -0
- package/template/public/brand/hero-mobile.svg +12 -0
- package/template/public/brand/heros/boutique-desktop.svg +11 -0
- package/template/public/brand/heros/boutique-mobile.svg +11 -0
- package/template/public/brand/heros/editorial-desktop.svg +11 -0
- package/template/public/brand/heros/editorial-mobile.svg +11 -0
- package/template/public/brand/heros/essencial-desktop.svg +12 -0
- package/template/public/brand/heros/essencial-mobile.svg +12 -0
- package/template/public/brand/heros/promocional-desktop.svg +12 -0
- package/template/public/brand/heros/promocional-mobile.svg +12 -0
- package/template/public/brand/logo-chrome.svg +6 -0
- package/template/public/brand/logo-white.svg +6 -0
- package/template/public/brand/logo.svg +7 -0
- package/template/public/brand/pay-amex.webp +0 -0
- package/template/public/brand/pay-elo.webp +0 -0
- package/template/public/brand/pay-mastercard.webp +0 -0
- package/template/public/brand/pay-pix.webp +0 -0
- package/template/public/brand/pay-visa.webp +0 -0
- package/template/public/brand/ph/avatar-a.svg +5 -0
- package/template/public/brand/ph/avatar-b.svg +5 -0
- package/template/public/brand/ph/avatar-c.svg +5 -0
- package/template/public/brand/ph/avatar-d.svg +5 -0
- package/template/public/brand/ph/photo-a.svg +7 -0
- package/template/public/brand/ph/photo-b.svg +6 -0
- package/template/public/brand/ph/photo-c.svg +6 -0
- package/template/public/brand/ph/poster-a.svg +6 -0
- package/template/public/brand/ph/poster-b.svg +6 -0
- package/template/public/brand/ph/poster-c.svg +6 -0
- package/template/public/unbox/powered-by-fundo-preto.png +0 -0
- package/template/public/unbox/powered-by-transparente.png +0 -0
- package/template/public/unbox/powered-by.png +0 -0
- package/template/scripts/abandoned-cart.ts +82 -0
- package/template/scripts/check-editable.mjs +467 -0
- package/template/scripts/check-honestidade.mjs +111 -0
- package/template/scripts/check-placeholder.mjs +200 -0
- package/template/scripts/check-recipe.mjs +76 -0
- package/template/scripts/check-unbox-brand.mjs +298 -0
- package/template/scripts/contraste.js +177 -0
- package/template/scripts/dump-catalog.ts +95 -0
- package/template/scripts/load-env.ts +13 -0
- package/template/scripts/medir-sistema.mjs +56 -0
- package/template/scripts/place-order-pix.ts +61 -0
- package/template/scripts/qa-screenshots.mjs +150 -0
- package/template/scripts/sistema.py +231 -0
- package/template/scripts/subscribe-webhook.ts +36 -0
- package/template/scripts/test-live.ts +186 -0
- package/template/scripts/vocabulario.py +264 -0
- package/template/tsconfig.json +27 -0
- package/tools/LEIA-ME.md +39 -0
- package/tools/check-template-neutro.mjs +111 -0
- package/tools/notion-doc.mjs +191 -0
- package/tools/tokenize-neutrals.mjs +0 -0
- package/tools/tokenize-radius.mjs +52 -0
- package/tools/workflow-storefront.legado.js +1207 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Orçamento de sistema visual — conta o que faz a loja ter "cara de IA".
|
|
3
|
+
|
|
4
|
+
POR QUE ISTO EXISTE
|
|
5
|
+
O `escala.py` irmão mede o TETO da tipografia (tipo grande demais). Este mede a
|
|
6
|
+
QUANTIDADE — que é outro defeito, com outra causa e outro sintoma. O levantamento
|
|
7
|
+
de 334 ajustes em três lojas do CLI achou a causa mecânica do diagnóstico que o
|
|
8
|
+
Bruno deu duas vezes ("ta tudo com mt cara de AI... qual design system vc ta
|
|
9
|
+
seguindo?"):
|
|
10
|
+
|
|
11
|
+
escala tipográfica 76 tamanhos distintos (alvo ~6)
|
|
12
|
+
raios 10 valores (alvo: derivados de um token)
|
|
13
|
+
cor fora de token 14+ hex soltos (alvo: zero)
|
|
14
|
+
|
|
15
|
+
Nenhum desses três é cobrado em lugar nenhum da esteira hoje. E nenhum se conserta
|
|
16
|
+
com prompt: o gerador acrescenta um tamanho novo a cada seção porque cada acréscimo
|
|
17
|
+
é individualmente defensável. Orçamento obriga a gastar; instrução deixa acumular.
|
|
18
|
+
|
|
19
|
+
O QUE CONTA COMO DEFINIÇÃO DE TOKEN (e por isso não é violação)
|
|
20
|
+
O bloco `:root` / `@theme` do globals.css é onde as cores DEVEM viver — hex ali é a
|
|
21
|
+
definição, não o vazamento. Violação é hex escrito em componente ou em regra CSS
|
|
22
|
+
fora do bloco de tokens.
|
|
23
|
+
|
|
24
|
+
USO
|
|
25
|
+
sistema.py <dir-do-projeto> [--tipos 8] [--raios 5] [--hex 0] [--secoes 8]
|
|
26
|
+
|
|
27
|
+
CÓDIGOS DE SAÍDA
|
|
28
|
+
0 dentro do orçamento
|
|
29
|
+
1 estourou algum teto
|
|
30
|
+
2 O GATE NÃO RODOU (caminho errado, ou varreu zero arquivo)
|
|
31
|
+
|
|
32
|
+
O 2 existe pela mesma razão do 2 do escala.py: gate que varre o vazio e diz "limpo"
|
|
33
|
+
aprova sem ter olhado.
|
|
34
|
+
"""
|
|
35
|
+
import re
|
|
36
|
+
import sys
|
|
37
|
+
import pathlib
|
|
38
|
+
from collections import Counter
|
|
39
|
+
|
|
40
|
+
EXT_CODIGO = {".tsx", ".ts", ".jsx", ".js"}
|
|
41
|
+
EXT_CSS = {".css"}
|
|
42
|
+
IGNORAR = {"node_modules", ".next", ".git", "shots", "reviews", "public", "marca"}
|
|
43
|
+
|
|
44
|
+
# ── tipografia ────────────────────────────────────────────────────────────────
|
|
45
|
+
# Tailwind arbitrário `text-[13px]` / `text-[clamp(...)]` e utilitários nomeados.
|
|
46
|
+
RE_TEXT_ARB = re.compile(r"text-\[([^\]]+)\]")
|
|
47
|
+
RE_TEXT_NOM = re.compile(r"\btext-(xs|sm|base|lg|xl|[2-9]xl)\b")
|
|
48
|
+
RE_CSS_FS = re.compile(r"font-size:\s*([^;}]+)")
|
|
49
|
+
|
|
50
|
+
# ── raios ─────────────────────────────────────────────────────────────────────
|
|
51
|
+
RE_ROUND_ARB = re.compile(r"rounded(?:-[a-z]{1,2})?-\[([^\]]+)\]")
|
|
52
|
+
RE_ROUND_NOM = re.compile(r"\brounded(?:-[trbl]{1,2})?-(none|sm|md|lg|xl|[2-3]xl|full)\b")
|
|
53
|
+
RE_CSS_RADIUS = re.compile(r"border-radius:\s*([^;}]+)")
|
|
54
|
+
|
|
55
|
+
# ── cor ───────────────────────────────────────────────────────────────────────
|
|
56
|
+
# ── largura de container ──────────────────────────────────────────────────────
|
|
57
|
+
# "A margem esta errada" quase nunca e margem: sao larguras de container divergentes.
|
|
58
|
+
# Na Punch o scaffold entregou cabecalho 1400, rodape 1180 e <main> 1240, e o cliente
|
|
59
|
+
# reclamou em TRES rodadas diferentes porque cada tela expunha uma combinacao.
|
|
60
|
+
# So contam as larguras de PAGINA (>=1000px): abaixo disso sao medidas de leitura e
|
|
61
|
+
# larguras de componente, que legitimamente variam.
|
|
62
|
+
RE_CONTAINER = re.compile(r"max-w-\[(\d{4,})px\]")
|
|
63
|
+
PISO_CONTAINER = 1000
|
|
64
|
+
|
|
65
|
+
RE_HEX = re.compile(r"#[0-9a-fA-F]{3,8}\b")
|
|
66
|
+
RE_FALLBACK = re.compile(r"var\(\s*(--[A-Za-z0-9-]+)\s*,\s*(#[0-9a-fA-F]{3,8})\s*\)")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def arquivos(raiz, exts):
|
|
70
|
+
for p in raiz.rglob("*"):
|
|
71
|
+
if p.suffix.lower() not in exts:
|
|
72
|
+
continue
|
|
73
|
+
if any(parte in IGNORAR for parte in p.parts):
|
|
74
|
+
continue
|
|
75
|
+
yield p
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def normalizar(valor):
|
|
79
|
+
"""`13px`, ` 13px `, `13PX` são o mesmo tamanho. clamp() conta como um valor próprio."""
|
|
80
|
+
return re.sub(r"\s+", "", valor).lower()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def bloco_de_tokens(texto):
|
|
84
|
+
"""Devolve os intervalos (ini, fim) de `:root{...}` e `@theme{...}` — onde hex é lei."""
|
|
85
|
+
faixas = []
|
|
86
|
+
for m in re.finditer(r"(:root[^{]*|@theme[^{]*)\{", texto):
|
|
87
|
+
i = m.end() - 1
|
|
88
|
+
prof = 0
|
|
89
|
+
for j in range(i, len(texto)):
|
|
90
|
+
if texto[j] == "{":
|
|
91
|
+
prof += 1
|
|
92
|
+
elif texto[j] == "}":
|
|
93
|
+
prof -= 1
|
|
94
|
+
if prof == 0:
|
|
95
|
+
faixas.append((m.start(), j))
|
|
96
|
+
break
|
|
97
|
+
return faixas
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def medir(raiz):
|
|
101
|
+
tipos, raios, hexes, fallbacks = Counter(), Counter(), Counter(), {}
|
|
102
|
+
containers = {}
|
|
103
|
+
onde_hex = {}
|
|
104
|
+
vistos = 0
|
|
105
|
+
|
|
106
|
+
for p in arquivos(raiz, EXT_CODIGO | EXT_CSS):
|
|
107
|
+
vistos += 1
|
|
108
|
+
try:
|
|
109
|
+
t = p.read_text(encoding="utf-8", errors="ignore")
|
|
110
|
+
except OSError:
|
|
111
|
+
continue
|
|
112
|
+
|
|
113
|
+
for m in RE_TEXT_ARB.finditer(t):
|
|
114
|
+
v = normalizar(m.group(1))
|
|
115
|
+
if "px" in v or "rem" in v or "clamp" in v or "vw" in v:
|
|
116
|
+
tipos[v] += 1
|
|
117
|
+
for m in RE_TEXT_NOM.finditer(t):
|
|
118
|
+
tipos["tw:" + m.group(1)] += 1
|
|
119
|
+
for m in RE_CSS_FS.finditer(t):
|
|
120
|
+
tipos[normalizar(m.group(1))] += 1
|
|
121
|
+
|
|
122
|
+
for m in RE_ROUND_ARB.finditer(t):
|
|
123
|
+
raios[normalizar(m.group(1))] += 1
|
|
124
|
+
for m in RE_ROUND_NOM.finditer(t):
|
|
125
|
+
raios["tw:" + m.group(1)] += 1
|
|
126
|
+
for m in RE_CSS_RADIUS.finditer(t):
|
|
127
|
+
raios[normalizar(m.group(1))] += 1
|
|
128
|
+
|
|
129
|
+
for m in RE_CONTAINER.finditer(t):
|
|
130
|
+
larg = int(m.group(1))
|
|
131
|
+
if larg >= PISO_CONTAINER:
|
|
132
|
+
containers.setdefault(larg, set()).add(str(p.relative_to(raiz)))
|
|
133
|
+
|
|
134
|
+
faixas = bloco_de_tokens(t) if p.suffix.lower() in EXT_CSS else []
|
|
135
|
+
# intervalos ocupados por fallback de var(): contam separado
|
|
136
|
+
faixas_fb = []
|
|
137
|
+
for m in RE_FALLBACK.finditer(t):
|
|
138
|
+
faixas_fb.append((m.start(), m.end()))
|
|
139
|
+
fallbacks.setdefault(m.group(1), Counter())[m.group(2).lower()] += 1
|
|
140
|
+
for m in RE_HEX.finditer(t):
|
|
141
|
+
if any(ini <= m.start() <= fim for ini, fim in faixas):
|
|
142
|
+
continue # definição de token, não vazamento
|
|
143
|
+
if any(ini <= m.start() <= fim for ini, fim in faixas_fb):
|
|
144
|
+
continue # fallback de var(), medido à parte
|
|
145
|
+
h = m.group(0).lower()
|
|
146
|
+
hexes[h] += 1
|
|
147
|
+
onde_hex.setdefault(h, []).append(
|
|
148
|
+
f"{p.relative_to(raiz)}:{t.count(chr(10), 0, m.start()) + 1}"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
return vistos, tipos, raios, hexes, onde_hex, fallbacks, containers
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def contar_secoes(raiz):
|
|
155
|
+
"""Momentos da home, lidos da receita. Devolve None quando não há receita."""
|
|
156
|
+
for nome in ("components/home/home-recipe.ts", "components/home/home-recipe.tsx"):
|
|
157
|
+
p = raiz / nome
|
|
158
|
+
if p.exists():
|
|
159
|
+
t = p.read_text(encoding="utf-8", errors="ignore")
|
|
160
|
+
return len(re.findall(r"\bsection:\s*[\"']", t))
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def linha(rotulo, achado, teto, amostra=""):
|
|
165
|
+
marca = "ok " if achado <= teto else "ESTOUROU"
|
|
166
|
+
print(f"{marca:9}{rotulo:<26}{achado:>4} (teto {teto}){amostra}")
|
|
167
|
+
return achado <= teto
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def main():
|
|
171
|
+
args = sys.argv[1:]
|
|
172
|
+
if not args:
|
|
173
|
+
sys.exit(__doc__)
|
|
174
|
+
raiz = pathlib.Path(args[0]).resolve()
|
|
175
|
+
tetos = {"tipos": 8, "raios": 5, "hex": 0, "secoes": 8}
|
|
176
|
+
for chave in tetos:
|
|
177
|
+
flag = f"--{chave}"
|
|
178
|
+
if flag in args:
|
|
179
|
+
tetos[chave] = int(args[args.index(flag) + 1])
|
|
180
|
+
|
|
181
|
+
if not raiz.is_dir():
|
|
182
|
+
print(f"ERRO: {raiz} nao e um diretorio — o gate NAO RODOU", file=sys.stderr)
|
|
183
|
+
sys.exit(2)
|
|
184
|
+
|
|
185
|
+
vistos, tipos, raios, hexes, onde_hex, fallbacks, containers = medir(raiz)
|
|
186
|
+
if vistos == 0:
|
|
187
|
+
print(f"ERRO: nenhum arquivo varrido em {raiz} — o gate NAO RODOU", file=sys.stderr)
|
|
188
|
+
sys.exit(2)
|
|
189
|
+
|
|
190
|
+
print(f"Orcamento de sistema · {vistos} arquivos varridos em {raiz.name}\n")
|
|
191
|
+
ok = True
|
|
192
|
+
ok &= linha("tamanhos de tipo", len(tipos), tetos["tipos"])
|
|
193
|
+
ok &= linha("raios", len(raios), tetos["raios"])
|
|
194
|
+
ok &= linha("hex fora de token", len(hexes), tetos["hex"])
|
|
195
|
+
|
|
196
|
+
secoes = contar_secoes(raiz)
|
|
197
|
+
if secoes is not None:
|
|
198
|
+
ok &= linha("momentos da home", secoes, tetos["secoes"])
|
|
199
|
+
else:
|
|
200
|
+
print(f"{'—':9}{'momentos da home':<26} ? (sem receita encontrada)")
|
|
201
|
+
|
|
202
|
+
if len(tipos) > tetos["tipos"]:
|
|
203
|
+
print("\n tipos mais usados:", ", ".join(f"{v}×{n}" for v, n in tipos.most_common(6)))
|
|
204
|
+
print(" os", len(tipos) - 6, "restantes sao a gordura:", ", ".join(list(tipos)[6:18]))
|
|
205
|
+
if len(raios) > tetos["raios"]:
|
|
206
|
+
print("\n raios:", ", ".join(f"{v}×{n}" for v, n in raios.most_common(12)))
|
|
207
|
+
if hexes:
|
|
208
|
+
print("\n hex fora de token:")
|
|
209
|
+
for h, n in hexes.most_common(12):
|
|
210
|
+
print(f" {h} {n}× {onde_hex[h][0]}")
|
|
211
|
+
|
|
212
|
+
if containers:
|
|
213
|
+
ok &= linha("larguras de container", len(containers), 1)
|
|
214
|
+
if len(containers) > 1:
|
|
215
|
+
print("\n cada largura e um alinhamento diferente na mesma pagina:")
|
|
216
|
+
for larg in sorted(containers, reverse=True):
|
|
217
|
+
arqs = sorted(containers[larg])
|
|
218
|
+
print(f" {larg}px em {len(arqs)}: " + ", ".join(arqs[:3]))
|
|
219
|
+
|
|
220
|
+
mentirosos = {tok: c for tok, c in fallbacks.items() if len(c) > 1}
|
|
221
|
+
if mentirosos:
|
|
222
|
+
print("\n fallback de var() com mais de um hex — pelo menos um mente:")
|
|
223
|
+
for tok, c in list(mentirosos.items())[:8]:
|
|
224
|
+
print(f" {tok}: " + ", ".join(f"{h} ({n}×)" for h, n in c.most_common()))
|
|
225
|
+
ok = False
|
|
226
|
+
|
|
227
|
+
sys.exit(0 if ok else 1)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
if __name__ == "__main__":
|
|
231
|
+
main()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// ⚠️ EFEITO REAL: cria uma assinatura de webhook na loja de produção.
|
|
2
|
+
// Use sob demanda. Imprime o `secret` — cole em UNBOX_WEBHOOK_SECRET no .env.local.
|
|
3
|
+
//
|
|
4
|
+
// npm run unbox:webhook:subscribe -- https://SEU-DOMINIO/api/webhooks/unbox
|
|
5
|
+
// (em dev, exponha localhost com um túnel, ex.: ngrok/cloudflared)
|
|
6
|
+
import "./load-env"; // SEMPRE o primeiro import — parser de env idêntico ao do app
|
|
7
|
+
import { UnboxClient } from "../lib/unbox/client";
|
|
8
|
+
|
|
9
|
+
const endpoint = process.argv[2] || process.env.WEBHOOK_ENDPOINT;
|
|
10
|
+
if (!endpoint) {
|
|
11
|
+
console.error("Uso: npm run unbox:webhook:subscribe -- <https://seu-dominio/api/webhooks/unbox>");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
(async () => {
|
|
16
|
+
const client = new UnboxClient({
|
|
17
|
+
apiKey: process.env.UNBOX_API_KEY ?? "",
|
|
18
|
+
shopId: process.env.UNBOX_SHOP_ID!,
|
|
19
|
+
// API de parceiros: presente = signIn e leituras compativeis roteiam pra ela
|
|
20
|
+
partnerApiKey: process.env.UNBOX_PARTNER_API_KEY,
|
|
21
|
+
partnerGqlUrl: process.env.UNBOX_PARTNER_GRAPHQL_URL,
|
|
22
|
+
captchaBypass: process.env.UNBOX_CAPTCHA_BYPASS,
|
|
23
|
+
});
|
|
24
|
+
await client.signIn(process.env.UNBOX_USER!, process.env.UNBOX_PASS!);
|
|
25
|
+
|
|
26
|
+
for (const eventType of ["ORDER_STATUS_UPDATE", "ORDER_CREATED"]) {
|
|
27
|
+
try {
|
|
28
|
+
const sub = await client.subscribeWebhook(eventType, endpoint);
|
|
29
|
+
console.log(`✅ ${eventType} → ${endpoint}`);
|
|
30
|
+
console.log(` secret: ${sub.secret}`);
|
|
31
|
+
} catch (e: any) {
|
|
32
|
+
console.error(`❌ ${eventType}: ${e.message}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
console.log("\nCole o secret em UNBOX_WEBHOOK_SECRET no .env.local.");
|
|
36
|
+
})();
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// Teste ao vivo do SDK contra a loja real (produção). Roda só leituras seguras + monta o payload
|
|
2
|
+
// de placeOrder (dry-run). NÃO cria pedido/webhook/OTP.
|
|
3
|
+
//
|
|
4
|
+
// npm run unbox:test
|
|
5
|
+
import "./load-env"; // SEMPRE o primeiro import — parser de env idêntico ao do app
|
|
6
|
+
import { UnboxClient } from "../lib/unbox/client";
|
|
7
|
+
import { friendlyError, cartEventLabel } from "../lib/unbox/errors";
|
|
8
|
+
import { orderStatusLabel } from "../lib/unbox/customer";
|
|
9
|
+
|
|
10
|
+
const SHOP = process.env.UNBOX_SHOP_ID!;
|
|
11
|
+
const SLUG = process.env.UNBOX_SHOP_SLUG ?? "minha-loja";
|
|
12
|
+
const client = new UnboxClient({
|
|
13
|
+
apiKey: process.env.UNBOX_API_KEY ?? "",
|
|
14
|
+
shopId: SHOP,
|
|
15
|
+
// API de parceiros: presente = signIn e leituras compativeis roteiam pra ela
|
|
16
|
+
partnerApiKey: process.env.UNBOX_PARTNER_API_KEY,
|
|
17
|
+
partnerGqlUrl: process.env.UNBOX_PARTNER_GRAPHQL_URL,
|
|
18
|
+
captchaBypass: process.env.UNBOX_CAPTCHA_BYPASS,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
let pass = 0,
|
|
22
|
+
fail = 0;
|
|
23
|
+
async function step<T>(name: string, fn: () => Promise<T> | T): Promise<T | undefined> {
|
|
24
|
+
try {
|
|
25
|
+
const r = await fn();
|
|
26
|
+
console.log(`✅ ${name}`);
|
|
27
|
+
pass++;
|
|
28
|
+
return r;
|
|
29
|
+
} catch (e: any) {
|
|
30
|
+
console.log(`❌ ${name}\n → ${e.message}`);
|
|
31
|
+
fail++;
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const ADDR = {
|
|
37
|
+
fullName: "Allan Teste", taxPayerId: "08383142951", postal: "04551-080",
|
|
38
|
+
address1: "Rua São Tomé", address2: "Apto 703", number: "73",
|
|
39
|
+
neighborhood: "Vila Olímpia", city: "São Paulo", region: "SP",
|
|
40
|
+
cityCode: "3550308", phone: "11999990000",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
(async () => {
|
|
44
|
+
console.log(client.usesPartnerApi
|
|
45
|
+
? `ℹ️ API de PARCEIROS ativa (${client.partnerGqlUrl}) — signIn + leituras compatíveis roteiam por ela`
|
|
46
|
+
: "ℹ️ Modo core puro (sem UNBOX_PARTNER_API_KEY) — todas as chamadas via core/REST");
|
|
47
|
+
await step("1. signIn (loja)", async () => {
|
|
48
|
+
const token = await client.signIn(process.env.UNBOX_USER!, process.env.UNBOX_PASS!);
|
|
49
|
+
// Setup só-parceiro: UNBOX_SHOP_ID pode vir vazio — extrai do JWT (mesmos claims
|
|
50
|
+
// arn:unbox:shopId que o app usa em lib/unbox/store.ts). Necessário pros passos core.
|
|
51
|
+
if (!client.shopId) {
|
|
52
|
+
try {
|
|
53
|
+
const payload = token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
54
|
+
const claims = JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
|
|
55
|
+
client.shopId = claims["arn:unbox:shopId"] ?? "";
|
|
56
|
+
if (client.shopId) console.log(` shopId (do JWT): ${client.shopId}`);
|
|
57
|
+
} catch { /* segue sem shopId — passos core vão acusar */ }
|
|
58
|
+
}
|
|
59
|
+
return token;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const cat = await step("2. getCatalog", async () => {
|
|
63
|
+
const c = await client.getCatalog({ first: 10 });
|
|
64
|
+
console.log(` totalCount=${c.totalCount}`);
|
|
65
|
+
return c;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
let picked: any;
|
|
69
|
+
if (cat) {
|
|
70
|
+
picked = (cat.nodes as any[])
|
|
71
|
+
.map((n) => n.product)
|
|
72
|
+
.find((p) => !p.isSoldOut && p.variants?.[0]?.pricing?.[0]?.price);
|
|
73
|
+
if (picked) console.log(` produto: "${picked.title}" @ ${picked.variants[0].pricing[0].displayPrice}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
await step("3. getProductBySlug", async () => {
|
|
77
|
+
if (!picked) throw new Error("sem produto");
|
|
78
|
+
const p = await client.getProductBySlug(picked.slug);
|
|
79
|
+
if (!p?.product?._id) throw new Error("produto não encontrado");
|
|
80
|
+
return p;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await step("4. getTags", async () => {
|
|
84
|
+
const t = await client.getTags(); // sem filtro isTopLevel — mesmo path que o app usa
|
|
85
|
+
console.log(` ${t.length} categorias`);
|
|
86
|
+
return t;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
await step("5. getShop (promoções + assinatura)", async () => {
|
|
90
|
+
const s = await client.getShop(SLUG);
|
|
91
|
+
console.log(` shopSales: ${(s.shopSales ?? []).map((x: any) => x.label).join(", ")}`);
|
|
92
|
+
console.log(` freqs: ${(s.recurringOrdersPolicy?.allowedFrequencies ?? []).map((f: any) => f.title).join(", ")}`);
|
|
93
|
+
return s;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await step("6. getPaymentMethods", async () => {
|
|
97
|
+
const m = await client.getPaymentMethods();
|
|
98
|
+
console.log(` ${m.map((x: any) => x.name).join(", ")}`);
|
|
99
|
+
return m;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await step("7. listDiscountCodes", async () => {
|
|
103
|
+
const d = await client.listDiscountCodes(3);
|
|
104
|
+
console.log(` totalCount=${d.totalCount}`);
|
|
105
|
+
return d;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const v = picked?.variants?.[0];
|
|
109
|
+
const cart = await step("8. createCart", async () => {
|
|
110
|
+
if (!picked) throw new Error("sem produto");
|
|
111
|
+
const r = await client.createCart([{ productId: picked.productId, productVariantId: v._id, price: v.pricing[0].price, quantity: 1 }]);
|
|
112
|
+
console.log(` cartId=${r.cartId} subtotal=${r.cart.checkout.summary.total.displayAmount}`);
|
|
113
|
+
return r;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
await step("8b. addCartItems / getCart / update / remove", async () => {
|
|
117
|
+
if (!cart || !picked) throw new Error("sem carrinho");
|
|
118
|
+
await client.addCartItems(cart.cartId, cart.cartToken, [{ productId: picked.productId, productVariantId: v._id, price: v.pricing[0].price, quantity: 1 }]);
|
|
119
|
+
const reloaded = await client.getCart(cart.cartId, cart.cartToken);
|
|
120
|
+
const itemId = reloaded.items.edges[0].node._id;
|
|
121
|
+
await client.updateItemQuantity(cart.cartId, cart.cartToken, itemId, 3);
|
|
122
|
+
await client.removeCartItems(cart.cartId, cart.cartToken, [itemId]);
|
|
123
|
+
console.log(` getCart itens=${reloaded.items.totalCount}; add/update/remove OK`);
|
|
124
|
+
return true;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
let fgId: string | undefined;
|
|
128
|
+
await step("9. setShippingAddress", async () => {
|
|
129
|
+
if (!cart) throw new Error("sem carrinho");
|
|
130
|
+
fgId = await client.setShippingAddress(cart.cartId, cart.cartToken, ADDR as any);
|
|
131
|
+
console.log(` fulfillmentGroupId=${fgId}`);
|
|
132
|
+
return fgId;
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
let fmId: string | undefined;
|
|
136
|
+
await step("10. quoteShipping", async () => {
|
|
137
|
+
if (!cart || !fgId) throw new Error("pré-requisito faltando");
|
|
138
|
+
const opts = await client.quoteShipping(cart.cartId, cart.cartToken, fgId);
|
|
139
|
+
fmId = opts[0]?.fulfillmentMethod._id;
|
|
140
|
+
console.log(` ${opts.length} opção(ões); 1ª: ${opts[0]?.fulfillmentMethod.displayName} ${opts[0]?.price?.displayAmount}`);
|
|
141
|
+
if (!fmId) throw new Error("sem opções de frete");
|
|
142
|
+
return opts;
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
let finalCart: any;
|
|
146
|
+
await step("11. selectShipping", async () => {
|
|
147
|
+
if (!cart || !fgId || !fmId) throw new Error("pré-requisito faltando");
|
|
148
|
+
finalCart = await client.selectShipping(cart.cartId, cart.cartToken, fgId, fmId);
|
|
149
|
+
console.log(` total c/ frete=${finalCart.checkout.summary.total.displayAmount}`);
|
|
150
|
+
return finalCart;
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await step("12. buildOrderItems + payload placeOrder (DRY-RUN)", async () => {
|
|
154
|
+
if (!finalCart || !cart || !fmId) throw new Error("pré-requisito faltando");
|
|
155
|
+
const items = client.buildOrderItems(finalCart);
|
|
156
|
+
const total = finalCart.checkout.summary.total.amount;
|
|
157
|
+
console.log(` payload OK: ${items.length} item(ns), total=R$${total} (placeOrder NÃO chamado)`);
|
|
158
|
+
return true;
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
await step("13. customerAccountExists", async () => {
|
|
162
|
+
const r = await client.customerAccountExists(process.env.UNBOX_TEST_EMAIL ?? "cliente@example.com");
|
|
163
|
+
console.log(` existe? ${r}`);
|
|
164
|
+
return r;
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
await step("14. getAddressByPostalCode", async () => {
|
|
168
|
+
const a = await client.getAddressByPostalCode("04551-080");
|
|
169
|
+
console.log(` ${a.address1}, ${a.neighborhood} - ${a.city}/${a.region}`);
|
|
170
|
+
return a;
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
await step("16. errors/labels", async () => {
|
|
174
|
+
const checks: Array<[string, string]> = [
|
|
175
|
+
[friendlyError({ message: "INSUFFICIENT_FUNDS_ERROR" }), "Pagamento recusado: saldo/limite insuficiente."],
|
|
176
|
+
[cartEventLabel("SALE_FREE_ITEM_ADDED"), "🎁 Você ganhou um brinde!"],
|
|
177
|
+
[orderStatusLabel("PENDING"), "Aguardando pagamento"],
|
|
178
|
+
];
|
|
179
|
+
for (const [got, exp] of checks) if (got !== exp) throw new Error(`esperava "${exp}", veio "${got}"`);
|
|
180
|
+
console.log(` ${checks.length} mapeamentos OK`);
|
|
181
|
+
return true;
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
console.log(`\n──────────── ${pass} passaram, ${fail} falharam ────────────`);
|
|
185
|
+
process.exit(fail ? 1 : 0);
|
|
186
|
+
})();
|