@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.
Files changed (358) hide show
  1. package/LICENSE.md +46 -0
  2. package/README.md +175 -0
  3. package/bin/cli.js +439 -0
  4. package/package.json +43 -0
  5. package/src/colors.js +100 -0
  6. package/src/presets.js +241 -0
  7. package/src/theme.js +253 -0
  8. package/template/.claude/agents/assets-marca.md +40 -0
  9. package/template/.claude/agents/avaliador-visual.md +48 -0
  10. package/template/.claude/agents/branding-briefing.md +401 -0
  11. package/template/.claude/agents/conteudo-secao.md +46 -0
  12. package/template/.claude/agents/paginas-legais.md +44 -0
  13. package/template/.claude/settings.json +3 -0
  14. package/template/.claude/skills/frontend-design/ATTRIBUTION.md +3 -0
  15. package/template/.claude/skills/frontend-design/LICENSE.txt +177 -0
  16. package/template/.claude/skills/frontend-design/SKILL.md +55 -0
  17. package/template/.claude/skills/web-design-guidelines/ATTRIBUTION.md +3 -0
  18. package/template/.claude/skills/web-design-guidelines/SKILL.md +50 -0
  19. package/template/.claude/skills/web-design-guidelines/guidelines-snapshot.md +180 -0
  20. package/template/.env.example +158 -0
  21. package/template/.mcp.json.example +14 -0
  22. package/template/.nvmrc +1 -0
  23. package/template/CLAUDE.md +333 -0
  24. package/template/COVERAGE.md +80 -0
  25. package/template/DEPLOY.md +179 -0
  26. package/template/QA.md +108 -0
  27. package/template/README.md +152 -0
  28. package/template/agents/CONSTRUCAO.md +207 -0
  29. package/template/agents/MANAGER.md +192 -0
  30. package/template/agents/PADROES.md +255 -0
  31. package/template/agents/definitions/00-scaffold.md +56 -0
  32. package/template/agents/definitions/01-layout.md +47 -0
  33. package/template/agents/definitions/02-homepage.md +70 -0
  34. package/template/agents/definitions/03-catalog.md +86 -0
  35. package/template/agents/definitions/04-pdp.md +97 -0
  36. package/template/agents/definitions/05-cart.md +116 -0
  37. package/template/agents/definitions/06-checkout.md +146 -0
  38. package/template/agents/definitions/07-auth.md +130 -0
  39. package/template/agents/definitions/08-customer.md +55 -0
  40. package/template/agents/definitions/09-promotions.md +62 -0
  41. package/template/agents/definitions/10-feedback.md +128 -0
  42. package/template/agents/definitions/11-seo-infra.md +159 -0
  43. package/template/agents/definitions/12-deploy.md +173 -0
  44. package/template/agents/definitions/13-cro.md +275 -0
  45. package/template/agents/definitions/14-seo.md +388 -0
  46. package/template/agents/definitions/15-branding.md +26 -0
  47. package/template/agents/definitions/16-qa-visual.md +138 -0
  48. package/template/agents/definitions/17-aeo.md +257 -0
  49. package/template/app/(loja)/busca/page.tsx +72 -0
  50. package/template/app/(loja)/carrinho/oferta/page.tsx +57 -0
  51. package/template/app/(loja)/carrinho/page.tsx +160 -0
  52. package/template/app/(loja)/categoria/[tagSlug]/layout.tsx +31 -0
  53. package/template/app/(loja)/categoria/[tagSlug]/loading.tsx +17 -0
  54. package/template/app/(loja)/categoria/[tagSlug]/page.tsx +49 -0
  55. package/template/app/(loja)/checkout/page.tsx +80 -0
  56. package/template/app/(loja)/checkout/pix/[ref]/page.tsx +133 -0
  57. package/template/app/(loja)/conta/assinaturas/[referenceId]/page.tsx +112 -0
  58. package/template/app/(loja)/conta/assinaturas/page.tsx +51 -0
  59. package/template/app/(loja)/conta/enderecos/page.tsx +20 -0
  60. package/template/app/(loja)/conta/entrar/page.tsx +119 -0
  61. package/template/app/(loja)/conta/page.tsx +45 -0
  62. package/template/app/(loja)/conta/pedidos/[referenceId]/page.tsx +49 -0
  63. package/template/app/(loja)/conta/pedidos/page.tsx +104 -0
  64. package/template/app/(loja)/conta/preferencias/page.tsx +24 -0
  65. package/template/app/(loja)/devolucoes/page.tsx +36 -0
  66. package/template/app/(loja)/layout.tsx +22 -0
  67. package/template/app/(loja)/oferta/page.tsx +57 -0
  68. package/template/app/(loja)/page.tsx +86 -0
  69. package/template/app/(loja)/pedido/[referenceId]/page.tsx +78 -0
  70. package/template/app/(loja)/privacidade/page.tsx +113 -0
  71. package/template/app/(loja)/produto/[productSlug]/layout.tsx +23 -0
  72. package/template/app/(loja)/produto/[productSlug]/loading.tsx +20 -0
  73. package/template/app/(loja)/produto/[productSlug]/page.tsx +372 -0
  74. package/template/app/(loja)/produtos/loading.tsx +16 -0
  75. package/template/app/(loja)/produtos/page.tsx +33 -0
  76. package/template/app/(loja)/termos/page.tsx +104 -0
  77. package/template/app/acesso/page.tsx +129 -0
  78. package/template/app/api/account/addresses/route.ts +39 -0
  79. package/template/app/api/account/exists/route.ts +21 -0
  80. package/template/app/api/account/me/route.ts +25 -0
  81. package/template/app/api/account/otp/route.ts +26 -0
  82. package/template/app/api/account/preferences/route.ts +19 -0
  83. package/template/app/api/account/signin/route.ts +27 -0
  84. package/template/app/api/account/signout/route.ts +9 -0
  85. package/template/app/api/acesso/route.ts +171 -0
  86. package/template/app/api/capi/route.ts +34 -0
  87. package/template/app/api/cart/coupon/route.ts +49 -0
  88. package/template/app/api/cart/items/route.ts +45 -0
  89. package/template/app/api/cart/link/route.ts +69 -0
  90. package/template/app/api/cart/route.ts +113 -0
  91. package/template/app/api/cart/share/route.ts +21 -0
  92. package/template/app/api/cep/[code]/route.ts +18 -0
  93. package/template/app/api/checkout/address/route.ts +24 -0
  94. package/template/app/api/checkout/email/route.ts +41 -0
  95. package/template/app/api/checkout/installments/route.ts +16 -0
  96. package/template/app/api/checkout/route.ts +110 -0
  97. package/template/app/api/checkout/shipping/route.ts +65 -0
  98. package/template/app/api/checkout-destination/route.ts +31 -0
  99. package/template/app/api/order/[ref]/route.ts +17 -0
  100. package/template/app/api/payment-link/route.ts +28 -0
  101. package/template/app/api/revalidate/route.ts +52 -0
  102. package/template/app/api/shipping/quote/route.ts +29 -0
  103. package/template/app/api/subscriptions/[id]/route.ts +52 -0
  104. package/template/app/api/track/route.ts +28 -0
  105. package/template/app/api/unbox/catalogo/route.ts +136 -0
  106. package/template/app/api/unbox/paginas/route.ts +167 -0
  107. package/template/app/api/unbox/vitrine/route.ts +84 -0
  108. package/template/app/api/webhooks/unbox/route.ts +97 -0
  109. package/template/app/apple-icon.svg +5 -0
  110. package/template/app/error.tsx +15 -0
  111. package/template/app/globals.css +457 -0
  112. package/template/app/icon.svg +4 -0
  113. package/template/app/layout.tsx +90 -0
  114. package/template/app/llms.txt/route.ts +43 -0
  115. package/template/app/manifest.ts +21 -0
  116. package/template/app/not-found.tsx +18 -0
  117. package/template/app/opengraph-image.tsx +35 -0
  118. package/template/app/robots.ts +31 -0
  119. package/template/app/sitemap.ts +42 -0
  120. package/template/bootstrap.sh +87 -0
  121. package/template/components/account/account-shell.tsx +47 -0
  122. package/template/components/account/address-book.tsx +147 -0
  123. package/template/components/account/preferences-form.tsx +77 -0
  124. package/template/components/account/reorder-button.tsx +47 -0
  125. package/template/components/account/signout-button.tsx +20 -0
  126. package/template/components/account/subscription-actions.tsx +204 -0
  127. package/template/components/account-nav.tsx +41 -0
  128. package/template/components/address-fields.tsx +102 -0
  129. package/template/components/analytics/data-layer-ready.tsx +16 -0
  130. package/template/components/analytics/purchase-tracker.tsx +32 -0
  131. package/template/components/brand-search.tsx +34 -0
  132. package/template/components/cart/cart-provider.tsx +340 -0
  133. package/template/components/cart/mini-cart.tsx +228 -0
  134. package/template/components/cart-button.tsx +35 -0
  135. package/template/components/catalog/catalog-client.tsx +523 -0
  136. package/template/components/catalog/grid-skeleton.tsx +19 -0
  137. package/template/components/catalog/pager.tsx +66 -0
  138. package/template/components/catalog/product-grid-card.tsx +52 -0
  139. package/template/components/catalog/product-grid.tsx +27 -0
  140. package/template/components/checkout/checkout-client.tsx +1333 -0
  141. package/template/components/chrome/announce-bar.tsx +57 -0
  142. package/template/components/chrome/chrome-recipe.ts +13 -0
  143. package/template/components/chrome/footers/captura-botao.tsx +28 -0
  144. package/template/components/chrome/footers/colunas.tsx +110 -0
  145. package/template/components/chrome/footers/conversao.tsx +90 -0
  146. package/template/components/chrome/footers/editorial.tsx +98 -0
  147. package/template/components/chrome/footers/minimal.tsx +55 -0
  148. package/template/components/chrome/header-bar-mobile.tsx +64 -0
  149. package/template/components/chrome/headers/centralizado.tsx +71 -0
  150. package/template/components/chrome/headers/classico.tsx +64 -0
  151. package/template/components/chrome/headers/compacto.tsx +66 -0
  152. package/template/components/chrome/headers/equilibrado.tsx +73 -0
  153. package/template/components/chrome/headers/imersivo.tsx +62 -0
  154. package/template/components/chrome/registry.ts +70 -0
  155. package/template/components/chrome/solid-on-scroll.tsx +39 -0
  156. package/template/components/empty-state.tsx +22 -0
  157. package/template/components/home/combo-card-compact.tsx +67 -0
  158. package/template/components/home/combos-home.tsx +64 -0
  159. package/template/components/home/combos-section.tsx +177 -0
  160. package/template/components/home/home-recipe.ts +41 -0
  161. package/template/components/home/sections/attributes-marquee.tsx +178 -0
  162. package/template/components/home/sections/benefits.tsx +80 -0
  163. package/template/components/home/sections/bloco-html.tsx +47 -0
  164. package/template/components/home/sections/catalogo.ts +165 -0
  165. package/template/components/home/sections/category-pills.tsx +32 -0
  166. package/template/components/home/sections/combos-carousel.tsx +141 -0
  167. package/template/components/home/sections/comparison.tsx +73 -0
  168. package/template/components/home/sections/founder-story.tsx +40 -0
  169. package/template/components/home/sections/hero.tsx +187 -0
  170. package/template/components/home/sections/kits.tsx +11 -0
  171. package/template/components/home/sections/media-cards.tsx +70 -0
  172. package/template/components/home/sections/newsletter.tsx +62 -0
  173. package/template/components/home/sections/product-showcase.tsx +175 -0
  174. package/template/components/home/sections/purchase-hero.tsx +187 -0
  175. package/template/components/home/sections/quote-banner.tsx +50 -0
  176. package/template/components/home/sections/registry.ts +186 -0
  177. package/template/components/home/sections/reviews-carousel.tsx +66 -0
  178. package/template/components/home/sections/reviews.tsx +68 -0
  179. package/template/components/home/sections/ritual.tsx +96 -0
  180. package/template/components/home/sections/savings.tsx +47 -0
  181. package/template/components/home/sections/social-row.tsx +65 -0
  182. package/template/components/home/sections/spec-table.tsx +117 -0
  183. package/template/components/home/sections/stars.tsx +12 -0
  184. package/template/components/home/sections/stats-grid.tsx +54 -0
  185. package/template/components/home/sections/trust-bar.tsx +56 -0
  186. package/template/components/home/sections/trust-strip.tsx +40 -0
  187. package/template/components/home/sections/video-wall.tsx +59 -0
  188. package/template/components/landing/landing-recipe.ts +20 -0
  189. package/template/components/landing/oferta-sections.tsx +40 -0
  190. package/template/components/landing/product-picker.tsx +257 -0
  191. package/template/components/mobile-nav.tsx +71 -0
  192. package/template/components/order-status.tsx +163 -0
  193. package/template/components/powered-by-unbox.tsx +27 -0
  194. package/template/components/product/pdp/buy-box.tsx +604 -0
  195. package/template/components/product/pdp/catalog-grid.tsx +113 -0
  196. package/template/components/product/pdp/faq-modelo.ts +86 -0
  197. package/template/components/product/pdp/gallery.tsx +87 -0
  198. package/template/components/product/pdp/interactive.tsx +200 -0
  199. package/template/components/product/pdp/newsletter.tsx +64 -0
  200. package/template/components/product/pdp/payment-chips.tsx +22 -0
  201. package/template/components/product/pdp/pdp-view.tsx +227 -0
  202. package/template/components/product/pdp/recommendations.tsx +144 -0
  203. package/template/components/product/pdp/sections.tsx +173 -0
  204. package/template/components/quantity-stepper.tsx +36 -0
  205. package/template/components/search-box.tsx +37 -0
  206. package/template/components/site-footer.tsx +59 -0
  207. package/template/components/site-header.tsx +64 -0
  208. package/template/components/ui/accordion.tsx +72 -0
  209. package/template/components/ui/alert-dialog.tsx +187 -0
  210. package/template/components/ui/alert.tsx +76 -0
  211. package/template/components/ui/aspect-ratio.tsx +22 -0
  212. package/template/components/ui/avatar.tsx +109 -0
  213. package/template/components/ui/badge.tsx +52 -0
  214. package/template/components/ui/breadcrumb.tsx +125 -0
  215. package/template/components/ui/button.tsx +58 -0
  216. package/template/components/ui/card.tsx +103 -0
  217. package/template/components/ui/carousel.tsx +242 -0
  218. package/template/components/ui/command.tsx +196 -0
  219. package/template/components/ui/dialog.tsx +160 -0
  220. package/template/components/ui/dropdown-menu.tsx +268 -0
  221. package/template/components/ui/foto.tsx +54 -0
  222. package/template/components/ui/input-group.tsx +158 -0
  223. package/template/components/ui/input-otp.tsx +87 -0
  224. package/template/components/ui/input.tsx +20 -0
  225. package/template/components/ui/label.tsx +20 -0
  226. package/template/components/ui/navigation-menu.tsx +168 -0
  227. package/template/components/ui/pagination.tsx +132 -0
  228. package/template/components/ui/popover.tsx +90 -0
  229. package/template/components/ui/progress.tsx +83 -0
  230. package/template/components/ui/radio-group.tsx +38 -0
  231. package/template/components/ui/scroll-area.tsx +55 -0
  232. package/template/components/ui/select.tsx +201 -0
  233. package/template/components/ui/separator.tsx +25 -0
  234. package/template/components/ui/sheet.tsx +138 -0
  235. package/template/components/ui/skeleton.tsx +13 -0
  236. package/template/components/ui/sonner.tsx +44 -0
  237. package/template/components/ui/switch.tsx +32 -0
  238. package/template/components/ui/table.tsx +116 -0
  239. package/template/components/ui/tabs.tsx +82 -0
  240. package/template/components/ui/textarea.tsx +18 -0
  241. package/template/components/ui/toggle-group.tsx +89 -0
  242. package/template/components/ui/toggle.tsx +45 -0
  243. package/template/components/ui/tooltip.tsx +66 -0
  244. package/template/components.json +25 -0
  245. package/template/eslint.config.mjs +36 -0
  246. package/template/gitignore +21 -0
  247. package/template/lib/analytics.ts +369 -0
  248. package/template/lib/api.ts +60 -0
  249. package/template/lib/capi.ts +87 -0
  250. package/template/lib/cart-link.ts +51 -0
  251. package/template/lib/cart-normalize.ts +142 -0
  252. package/template/lib/cart-recovery.ts +23 -0
  253. package/template/lib/cart-response.ts +48 -0
  254. package/template/lib/catalog-map.ts +54 -0
  255. package/template/lib/catalog.ts +3 -0
  256. package/template/lib/checkout-lock.ts +21 -0
  257. package/template/lib/checkout-nav.ts +22 -0
  258. package/template/lib/config.ts +51 -0
  259. package/template/lib/crm.ts +40 -0
  260. package/template/lib/customer-session.ts +21 -0
  261. package/template/lib/dataloader.ts +23 -0
  262. package/template/lib/editable/config.ts +11 -0
  263. package/template/lib/editable/document.ts +1762 -0
  264. package/template/lib/editable/index.ts +3 -0
  265. package/template/lib/editable/primitives.tsx +821 -0
  266. package/template/lib/editable/provider.tsx +861 -0
  267. package/template/lib/editable/rastreio-navegacao.tsx +65 -0
  268. package/template/lib/editable/rastreio.tsx +168 -0
  269. package/template/lib/editable/server.ts +189 -0
  270. package/template/lib/editable/tokens.ts +22 -0
  271. package/template/lib/editable/verify.ts +24 -0
  272. package/template/lib/enrichment/combos.ts +184 -0
  273. package/template/lib/enrichment/index.ts +188 -0
  274. package/template/lib/enrichment/products.json +1 -0
  275. package/template/lib/env-check.ts +47 -0
  276. package/template/lib/format.ts +130 -0
  277. package/template/lib/icons.ts +67 -0
  278. package/template/lib/json-ld.ts +9 -0
  279. package/template/lib/llms-txt.ts +75 -0
  280. package/template/lib/mockup.ts +24 -0
  281. package/template/lib/newsletter.ts +12 -0
  282. package/template/lib/orders.ts +160 -0
  283. package/template/lib/queries.ts +66 -0
  284. package/template/lib/ratelimit.ts +47 -0
  285. package/template/lib/rotas-editaveis.ts +192 -0
  286. package/template/lib/sanitize.ts +36 -0
  287. package/template/lib/schemas.ts +78 -0
  288. package/template/lib/session.ts +98 -0
  289. package/template/lib/store-config.ts +67 -0
  290. package/template/lib/unbox/client.ts +829 -0
  291. package/template/lib/unbox/customer.ts +224 -0
  292. package/template/lib/unbox/errors.ts +97 -0
  293. package/template/lib/unbox/index.ts +10 -0
  294. package/template/lib/unbox/store.ts +113 -0
  295. package/template/lib/unbox/types.ts +195 -0
  296. package/template/lib/unbox/webhooks.ts +66 -0
  297. package/template/lib/utils.ts +6 -0
  298. package/template/lib/vitrine.ts +242 -0
  299. package/template/lib/webhook-store.ts +22 -0
  300. package/template/middleware.ts +178 -0
  301. package/template/next.config.ts +63 -0
  302. package/template/package.json +62 -0
  303. package/template/postcss.config.mjs +5 -0
  304. package/template/public/brand/coll/default.png +0 -0
  305. package/template/public/brand/hero-desktop.svg +12 -0
  306. package/template/public/brand/hero-mobile.svg +12 -0
  307. package/template/public/brand/heros/boutique-desktop.svg +11 -0
  308. package/template/public/brand/heros/boutique-mobile.svg +11 -0
  309. package/template/public/brand/heros/editorial-desktop.svg +11 -0
  310. package/template/public/brand/heros/editorial-mobile.svg +11 -0
  311. package/template/public/brand/heros/essencial-desktop.svg +12 -0
  312. package/template/public/brand/heros/essencial-mobile.svg +12 -0
  313. package/template/public/brand/heros/promocional-desktop.svg +12 -0
  314. package/template/public/brand/heros/promocional-mobile.svg +12 -0
  315. package/template/public/brand/logo-chrome.svg +6 -0
  316. package/template/public/brand/logo-white.svg +6 -0
  317. package/template/public/brand/logo.svg +7 -0
  318. package/template/public/brand/pay-amex.webp +0 -0
  319. package/template/public/brand/pay-elo.webp +0 -0
  320. package/template/public/brand/pay-mastercard.webp +0 -0
  321. package/template/public/brand/pay-pix.webp +0 -0
  322. package/template/public/brand/pay-visa.webp +0 -0
  323. package/template/public/brand/ph/avatar-a.svg +5 -0
  324. package/template/public/brand/ph/avatar-b.svg +5 -0
  325. package/template/public/brand/ph/avatar-c.svg +5 -0
  326. package/template/public/brand/ph/avatar-d.svg +5 -0
  327. package/template/public/brand/ph/photo-a.svg +7 -0
  328. package/template/public/brand/ph/photo-b.svg +6 -0
  329. package/template/public/brand/ph/photo-c.svg +6 -0
  330. package/template/public/brand/ph/poster-a.svg +6 -0
  331. package/template/public/brand/ph/poster-b.svg +6 -0
  332. package/template/public/brand/ph/poster-c.svg +6 -0
  333. package/template/public/unbox/powered-by-fundo-preto.png +0 -0
  334. package/template/public/unbox/powered-by-transparente.png +0 -0
  335. package/template/public/unbox/powered-by.png +0 -0
  336. package/template/scripts/abandoned-cart.ts +82 -0
  337. package/template/scripts/check-editable.mjs +467 -0
  338. package/template/scripts/check-honestidade.mjs +111 -0
  339. package/template/scripts/check-placeholder.mjs +200 -0
  340. package/template/scripts/check-recipe.mjs +76 -0
  341. package/template/scripts/check-unbox-brand.mjs +298 -0
  342. package/template/scripts/contraste.js +177 -0
  343. package/template/scripts/dump-catalog.ts +95 -0
  344. package/template/scripts/load-env.ts +13 -0
  345. package/template/scripts/medir-sistema.mjs +56 -0
  346. package/template/scripts/place-order-pix.ts +61 -0
  347. package/template/scripts/qa-screenshots.mjs +150 -0
  348. package/template/scripts/sistema.py +231 -0
  349. package/template/scripts/subscribe-webhook.ts +36 -0
  350. package/template/scripts/test-live.ts +186 -0
  351. package/template/scripts/vocabulario.py +264 -0
  352. package/template/tsconfig.json +27 -0
  353. package/tools/LEIA-ME.md +39 -0
  354. package/tools/check-template-neutro.mjs +111 -0
  355. package/tools/notion-doc.mjs +191 -0
  356. package/tools/tokenize-neutrals.mjs +0 -0
  357. package/tools/tokenize-radius.mjs +52 -0
  358. 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
+ })();