{"_id":"@12-apps/discounts","_rev":"16-01709effa7fbb75782847aab3bf62534","name":"@12-apps/discounts","dist-tags":{"latest":"1.15.0"},"versions":{"1.0.0":{"name":"@12-apps/discounts","version":"1.0.0","license":"MIT","_id":"@12-apps/discounts@1.0.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"b1cea3f80fd48775a23902b7417cc3a98b9eea40","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.0.0.tgz","fileCount":23,"integrity":"sha512-+g4YljBUiyQdSilqlO9/h6lDlh2OHoTVzQkre6JjkKCGCDon5D1CrK5m9/yn4Kqj6yIQpajXxk6rddubhzGe2Q==","signatures":[{"sig":"MEYCIQDs/ZpnQGOWj8dmnlc3FWieTMSnLUS7MkYNYIwesuCmLQIhAIZJL4r/0N9P8WguEt8ZYH2x5ZntDnntJAhUmi8VsO0R","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":109703},"type":"module","engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./pt-BR":"./src/engine/pt-BR.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./package.json":"./package.json","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"54f82e7a4781eb64d3dde1bb6b564908cdd2d1f0","scripts":{"lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit"},"_npmUser":{"name":"tigredonorte","email":"tigredonorte3@gmail.com"},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.0.2","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/shared-helpers":"^2.0.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.39.1","vitest":"^3.2.4","typescript":"^5.9.2","@types/node":"^22.10.6","@12-apps/wiring":"^1.5.0","@12-apps/eslint-config":"^1.21.0","@12-apps/typescript-config":"^1.20.0","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"@12-apps/wiring":">=1.3.0"},"peerDependenciesMeta":{"@12-apps/wiring":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.0.0_1787319333198_0.3294721026155263","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@12-apps/discounts","version":"1.1.0","license":"MIT","_id":"@12-apps/discounts@1.1.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"2d16f555d0b2bcf880f35bb3ecf1517c9ca891e4","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.1.0.tgz","fileCount":26,"integrity":"sha512-BmuhcdB8Cp4I4CS3jzQexKeoQTReNV2/oxmc7Pkw3KqiojjNqhbPOmj3M/w3yJxfWQx4DRUS2OV+WNCtxLYT7A==","signatures":[{"sig":"MEYCIQD34nUgHb/ZmuscD2OZkwuRRIRlbLh2uzMG9AW+PCNTnwIhAIL+vaHRtLDjAbLCdi+F64z9UPYsOPAqfMmmWoqrZYpN","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":166102},"type":"module","engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./pt-BR":"./src/engine/pt-BR.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./package.json":"./package.json","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"a2984f6ee51ca1733af60fa6b2e2d554313c9e96","scripts":{"lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.0.2","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/shared-helpers":"^2.0.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.39.1","vitest":"^3.2.4","typescript":"^5.9.2","@types/node":"^22.10.6","@12-apps/wiring":"^1.7.0","@12-apps/eslint-config":"^1.21.1","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"@12-apps/wiring":">=1.3.0"},"peerDependenciesMeta":{"@12-apps/wiring":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.1.0_1787337785838_0.02579921445387967","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@12-apps/discounts","version":"1.2.0","license":"MIT","_id":"@12-apps/discounts@1.2.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"b94d474df1b3ef6e902de7f202bb885f582a2a86","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.2.0.tgz","fileCount":27,"integrity":"sha512-R5iz4RrnPf+ndh+8BY9PG7UIoWr+ir6o4HRRwiE8Z5GzAa3bWNgbWSk4fCaKn044qcm1v84RVslP2RdG8yd4UQ==","signatures":[{"sig":"MEQCIEngbx41E/x977H0vVsEUROL6Icqu2BS9tfrgh18AMftAiB9YF0gD+ji2XyLZuZVY7cgAMTGPcEL3vehYLn/oJFCRw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":176271},"type":"module","engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./pt-BR":"./src/engine/pt-BR.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./package.json":"./package.json","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"c202a44e593f086d5cf8e65326f0c9b4a41230b5","scripts":{"lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.0.2","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/shared-helpers":"^2.0.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.39.1","vitest":"^3.2.4","typescript":"^5.9.2","@types/node":"^22.10.6","@12-apps/wiring":"^1.10.0","@12-apps/eslint-config":"^1.21.1","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"@12-apps/wiring":">=1.3.0"},"peerDependenciesMeta":{"@12-apps/wiring":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.2.0_1787348961205_0.736218510753617","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@12-apps/discounts","version":"1.3.0","license":"MIT","_id":"@12-apps/discounts@1.3.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"4e0066840fbe08bdc5aa72418f16d559ae50e9bf","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.3.0.tgz","fileCount":50,"integrity":"sha512-npNyeG64WdHZRTznnYEuWihTewpyZXDkIbZVVCfRSGdo3kapnGsd2lUFdIZr6tU3XWa8MVxYVAXwCsDHwJV06A==","signatures":[{"sig":"MEYCIQDNxk3OsbCxIKXox58tj6mNfomPm5cYNIXHYojoXCG4OQIhALfs+z0k+W4E1NXrLYlQLpHHjPBOa6AvJfQ0iw9qgfmH","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":339682},"type":"module","wiring":{"db":{"partial":"prisma/discounts.prisma","migrations":"prisma/migrations"}},"engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./pt-BR":"./src/engine/pt-BR.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./react/pt-BR":"./src/react/pt-BR.ts","./manifest/web":"./src/manifest/web.ts","./package.json":"./package.json","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"15c437065d0ec83f4e535ef7caa4d036327df460","scripts":{"dev":"storybook dev -p 6009","lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit","prisma:sync":"node scripts/sync-discounts-schema.mjs","storybook:build":"storybook build","prisma:sync:check":"node scripts/sync-discounts-schema.mjs --check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.0.2","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.2.0","@12-apps/app-shell":"^5.1.0","@12-apps/forms-core":"^1.20.0","@mui/icons-material":"^6.5.0","@12-apps/shared-helpers":"^2.0.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.1","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","storybook":"^9.1.10","typescript":"^5.9.2","@types/node":"^22.10.6","@types/react":"19.2.2","@mui/material":"^6.5.0","@emotion/react":"^11.14.0","@12-apps/wiring":"^1.10.0","@emotion/styled":"^11.14.0","@types/react-dom":"19.2.2","react-router-dom":"^7.0.0","@electric-sql/pglite":"^0.2.17","@storybook/addon-docs":"^9.1.10","@storybook/react-vite":"^9.1.10","@12-apps/eslint-config":"^1.21.1","@storybook/addon-links":"^9.1.10","@testing-library/react":"^16.1.0","eslint-plugin-storybook":"9.1.10","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.3.0","react-router-dom":">=7.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"@12-apps/wiring":{"optional":true},"react-router-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.3.0_1787352732577_0.07895263301629796","host":"s3://npm-registry-packages-npm-production"}},"1.4.0":{"name":"@12-apps/discounts","version":"1.4.0","license":"MIT","_id":"@12-apps/discounts@1.4.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"3eac4b26ef6a736aa5c6a25943a682e957b9b68f","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.4.0.tgz","fileCount":52,"integrity":"sha512-GuIsGBEeXgpcZnceuv9CQzfwiJ/W0jLdMDtbnOaDDPhyV4WB80MB0wSHdWEGxI/Zg4UPT2KBWJJ13MvvUZ9qaQ==","signatures":[{"sig":"MEYCIQC+PeuhKDsxR74yxgNvH4vRl9EsNjAIpjb269XKE8MSvQIhAPqmBsN3lAi9m3nhMOK73EXyCI7pYHsulPJpFQG4trRb","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":365664},"type":"module","wiring":{"db":{"partial":"prisma/discounts.prisma","migrations":"prisma/migrations"}},"engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./pt-BR":"./src/engine/pt-BR.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./react/pt-BR":"./src/react/pt-BR.ts","./manifest/web":"./src/manifest/web.ts","./package.json":"./package.json","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"29a65a260b764eb0e2978b5151c3e1a1acd869d3","scripts":{"dev":"storybook dev -p 6009","lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit","prisma:sync":"node scripts/sync-discounts-schema.mjs","storybook:build":"storybook build","prisma:sync:check":"node scripts/sync-discounts-schema.mjs --check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.0.2","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.2.0","@12-apps/app-shell":"^5.1.0","@12-apps/forms-core":"^1.20.0","@mui/icons-material":"^6.5.0","@12-apps/shared-helpers":"^2.0.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.1","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","storybook":"^9.1.10","typescript":"^5.9.2","@types/node":"^22.10.6","@types/react":"19.2.2","@mui/material":"^6.5.0","@emotion/react":"^11.14.0","@12-apps/wiring":"^1.10.0","@emotion/styled":"^11.14.0","@types/react-dom":"19.2.2","react-router-dom":"^7.0.0","@electric-sql/pglite":"^0.2.17","@storybook/addon-docs":"^9.1.10","@storybook/react-vite":"^9.1.10","@12-apps/eslint-config":"^1.21.1","@storybook/addon-links":"^9.1.10","@testing-library/react":"^16.1.0","eslint-plugin-storybook":"9.1.10","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.3.0","react-router-dom":">=7.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"@12-apps/wiring":{"optional":true},"react-router-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.4.0_1787355568438_0.3930298282592901","host":"s3://npm-registry-packages-npm-production"}},"1.5.0":{"name":"@12-apps/discounts","version":"1.5.0","license":"MIT","_id":"@12-apps/discounts@1.5.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"35bd4eb93d919dde4f5ae9e74da5d966166558ee","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.5.0.tgz","fileCount":52,"integrity":"sha512-ccOlKzN+bSf0FcXdJH+Gmot3nrA4MCLrmTuHQ6YvTucCyDp70K31i4XpiJTTJD7tEpNzYXcsCCPPJq64bpO72Q==","signatures":[{"sig":"MEUCIQDLzJ89sNgTfi2DNXQCoJkzZtNFGXkV3sJeYsgTqjxMCQIgA9+NXBoma4LXi7S0DD4OboIkeVSR1mX+jRldBh3B9XM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":369280},"type":"module","wiring":{"db":{"partial":"prisma/discounts.prisma","migrations":"prisma/migrations"}},"engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./pt-BR":"./src/engine/pt-BR.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./react/pt-BR":"./src/react/pt-BR.ts","./manifest/web":"./src/manifest/web.ts","./package.json":"./package.json","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"bac05114e00530d9e7573297125ddd4084fdd4bb","scripts":{"dev":"storybook dev -p 6009","lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit","prisma:sync":"node scripts/sync-discounts-schema.mjs","storybook:build":"storybook build","prisma:sync:check":"node scripts/sync-discounts-schema.mjs --check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.0.2","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.3.0","@12-apps/app-shell":"^5.1.0","@12-apps/forms-core":"^1.20.0","@mui/icons-material":"^6.5.0","@12-apps/shared-helpers":"^2.0.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.1","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","storybook":"^9.1.10","typescript":"^5.9.2","@types/node":"^22.10.6","@types/react":"19.2.2","@mui/material":"^6.5.0","@emotion/react":"^11.14.0","@12-apps/wiring":"^1.10.0","@emotion/styled":"^11.14.0","@types/react-dom":"19.2.2","react-router-dom":"^7.0.0","@electric-sql/pglite":"^0.2.17","@storybook/addon-docs":"^9.1.10","@storybook/react-vite":"^9.1.10","@12-apps/eslint-config":"^1.21.1","@storybook/addon-links":"^9.1.10","@testing-library/react":"^16.1.0","eslint-plugin-storybook":"9.1.10","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.3.0","react-router-dom":">=7.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"@12-apps/wiring":{"optional":true},"react-router-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.5.0_1787422156870_0.20294594812213052","host":"s3://npm-registry-packages-npm-production"}},"1.6.0":{"name":"@12-apps/discounts","version":"1.6.0","license":"MIT","_id":"@12-apps/discounts@1.6.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"37b7e1e59e1745f79e680912abcd6fde127f9ab5","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.6.0.tgz","fileCount":56,"integrity":"sha512-OMaBf7xoj5XJ76I/IKaJYq4ABJ47Fo0LTN1/lEO9ykdyt8SAUZ+SfkYWvivfrtrJFz5DeXOX+n8SJHOqNo9jwA==","signatures":[{"sig":"MEQCIE4PDMCgumwulYbeM9uBRNIZKpF4l9xilvoj+Iy3ZlC9AiBFCl7zohM7g3vgfGWAYGrKGaGoC5A+XTp78xLHaumMdQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.6.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":394748},"type":"module","wiring":{"db":{"partial":"prisma/discounts.prisma","migrations":"prisma/migrations"}},"engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./pt-BR":"./src/engine/pt-BR.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./react/pt-BR":"./src/react/pt-BR.ts","./manifest/web":"./src/manifest/web.ts","./package.json":"./package.json","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"47e4931b7188a5c041352c9b21ebf740c543f40f","scripts":{"dev":"storybook dev -p 6009","lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit","prisma:sync":"node scripts/sync-discounts-schema.mjs","storybook:build":"storybook build","prisma:sync:check":"node scripts/sync-discounts-schema.mjs --check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.0.2","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.3.1","@12-apps/app-shell":"^5.2.0","@12-apps/forms-core":"^1.20.0","@mui/icons-material":"^6.5.0","@12-apps/shared-helpers":"^2.0.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.1","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","storybook":"^9.1.10","typescript":"^5.9.2","@types/node":"^22.10.6","@types/react":"19.2.2","@mui/material":"^6.5.0","@emotion/react":"^11.14.0","@12-apps/wiring":"^1.10.0","@emotion/styled":"^11.14.0","@types/react-dom":"19.2.2","react-router-dom":"^7.0.0","@electric-sql/pglite":"^0.2.17","@storybook/addon-docs":"^9.1.10","@storybook/react-vite":"^9.1.10","@12-apps/eslint-config":"^1.21.1","@storybook/addon-links":"^9.1.10","@testing-library/react":"^16.1.0","eslint-plugin-storybook":"9.1.10","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.3.0","react-router-dom":">=7.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"@12-apps/wiring":{"optional":true},"react-router-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.6.0_1787430296171_0.531658970285207","host":"s3://npm-registry-packages-npm-production"}},"1.7.0":{"name":"@12-apps/discounts","version":"1.7.0","license":"MIT","_id":"@12-apps/discounts@1.7.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"648910ab9d74a28c1acb41c74bc3456dea2a341b","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.7.0.tgz","fileCount":56,"integrity":"sha512-RNmgGu13miyHfT9610HL2mJUFLhTk6yL24KJtGo1Aln63oZzc+nAkqr5M83+vIWY5cqtc2dqjS9XwHTYhQuTKQ==","signatures":[{"sig":"MEYCIQDSAnfdt2HvA4BMW4BjXSA4CfyaqTRVedDZT5Y6XKwQvgIhALgoV/s8Y68pwO/qQAqQg9sDDFMKNIr+wAA4D/gnESyL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.7.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":394972},"type":"module","wiring":{"db":{"partial":"prisma/discounts.prisma","migrations":"prisma/migrations"}},"engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./pt-BR":"./src/engine/pt-BR.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./react/pt-BR":"./src/react/pt-BR.ts","./manifest/web":"./src/manifest/web.ts","./package.json":"./package.json","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"8cb5a3741c23210e92a558071eedde889c4283de","scripts":{"dev":"storybook dev -p 6009","lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit","prisma:sync":"node scripts/sync-discounts-schema.mjs","storybook:build":"storybook build","prisma:sync:check":"node scripts/sync-discounts-schema.mjs --check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.0.2","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.7.0","@12-apps/app-shell":"^5.2.0","@12-apps/forms-core":"^1.20.0","@mui/icons-material":"^6.5.0","@12-apps/shared-helpers":"^2.0.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.1","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","storybook":"^9.1.10","typescript":"^5.9.2","@types/node":"^22.10.6","@types/react":"19.2.2","@mui/material":"^6.5.0","@emotion/react":"^11.14.0","@12-apps/wiring":"^1.10.0","@emotion/styled":"^11.14.0","@types/react-dom":"19.2.2","react-router-dom":"^7.0.0","@electric-sql/pglite":"^0.2.17","@storybook/addon-docs":"^9.1.10","@storybook/react-vite":"^9.1.10","@12-apps/eslint-config":"^1.21.1","@storybook/addon-links":"^9.1.10","@testing-library/react":"^16.1.0","eslint-plugin-storybook":"9.1.10","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.3.0","react-router-dom":">=7.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"@12-apps/wiring":{"optional":true},"react-router-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.7.0_1787504566788_0.19471621060742805","host":"s3://npm-registry-packages-npm-production"}},"1.8.0":{"name":"@12-apps/discounts","version":"1.8.0","license":"MIT","_id":"@12-apps/discounts@1.8.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"017bfa67340d23300847b64d6f3cc2dac2900cb0","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.8.0.tgz","fileCount":56,"integrity":"sha512-DxquHBQHwRLYUl2tFWBc9FV8fTkdQ+KfsvieIbdNjusvP6W9v5SyarG3OPzY97bYwhnHJxReYG67NHy+sZidhA==","signatures":[{"sig":"MEYCIQCCASNfKewjccX9Y2dRNv7SXm2Exb8PIkeoYU3fXN/LGgIhAN4IZynFT0g/Jm9uC1YhGgRrH0IzXm5s2BCG0kJfgvTP","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.8.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":395800},"type":"module","wiring":{"db":{"partial":"prisma/discounts.prisma","migrations":"prisma/migrations"}},"engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./pt-BR":"./src/engine/pt-BR.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./manifest":"./src/manifest/index.ts","./react/pt-BR":"./src/react/pt-BR.ts","./manifest/web":"./src/manifest/web.ts","./package.json":"./package.json","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"3bf9eef2568a872f949d9d26981c534210979a5b","scripts":{"dev":"storybook dev -p 6009","lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit","prisma:sync":"node scripts/sync-discounts-schema.mjs","storybook:build":"storybook build","prisma:sync:check":"node scripts/sync-discounts-schema.mjs --check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.0.2","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.8.0","@12-apps/app-shell":"^5.2.0","@12-apps/forms-core":"^1.20.0","@mui/icons-material":"^6.5.0","@12-apps/shared-helpers":"^2.0.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.1","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","storybook":"^9.1.10","typescript":"^5.9.2","@types/node":"^22.10.6","@types/react":"19.2.2","@mui/material":"^6.5.0","@emotion/react":"^11.14.0","@12-apps/wiring":"^1.10.0","@emotion/styled":"^11.14.0","@types/react-dom":"19.2.2","react-router-dom":"^7.0.0","@electric-sql/pglite":"^0.2.17","@storybook/addon-docs":"^9.1.10","@storybook/react-vite":"^9.1.10","@12-apps/eslint-config":"^1.21.1","@storybook/addon-links":"^9.1.10","@testing-library/react":"^16.1.0","eslint-plugin-storybook":"9.1.10","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.3.0","react-router-dom":">=7.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"@12-apps/wiring":{"optional":true},"react-router-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.8.0_1787508578882_0.21714174292461963","host":"s3://npm-registry-packages-npm-production"}},"1.9.0":{"name":"@12-apps/discounts","version":"1.9.0","license":"MIT","_id":"@12-apps/discounts@1.9.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"ef9c1e68c8c3ac378f51bbccee1114a64dc12b90","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.9.0.tgz","fileCount":60,"integrity":"sha512-gqlAKlilyACVLXc5QSo3vR0LPqE0fRRYjA0nlns1oMzPOkcoAnrgunCMMXIJr3UbEFpg+ovv5/MM0vC8zMjCjA==","signatures":[{"sig":"MEUCIHRpqTQt5fiWDCqDYrqifkuT5L18JBDi3lqVTc9hV7RFAiEA2XniEX0pqJgAZJsEutnWodiIg0LXc+KmnsTRfaLntvA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.9.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":409749},"type":"module","wiring":{"db":{"partial":"prisma/discounts.prisma","migrations":"prisma/migrations"}},"engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./en-US":"./src/engine/en-US.ts","./pt-BR":"./src/engine/pt-BR.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./locales":"./src/locales.ts","./manifest":"./src/manifest/index.ts","./react/en-US":"./src/react/en-US.ts","./react/pt-BR":"./src/react/pt-BR.ts","./manifest/web":"./src/manifest/web.ts","./package.json":"./package.json","./server/en-US":"./src/server/en-US.ts","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"9aa6a7b4c316ceed44033000e57b1b02c316537b","scripts":{"dev":"storybook dev -p 6009","lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit","prisma:sync":"node scripts/sync-discounts-schema.mjs","storybook:build":"storybook build","prisma:sync:check":"node scripts/sync-discounts-schema.mjs --check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.0.2","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.9.0","@12-apps/app-shell":"^5.3.0","@12-apps/forms-core":"^1.20.0","@mui/icons-material":"^6.5.0","@12-apps/shared-helpers":"^2.0.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.1","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","storybook":"^9.1.10","typescript":"^5.9.2","@types/node":"^22.10.6","@types/react":"19.2.2","@12-apps/i18n":"^1.0.0","@mui/material":"^6.5.0","@emotion/react":"^11.14.0","@12-apps/wiring":"^1.10.0","@emotion/styled":"^11.14.0","@types/react-dom":"19.2.2","react-router-dom":"^7.0.0","@electric-sql/pglite":"^0.2.17","@storybook/addon-docs":"^9.1.10","@storybook/react-vite":"^9.1.10","@12-apps/eslint-config":"^1.21.1","@storybook/addon-links":"^9.1.10","@testing-library/react":"^16.1.0","eslint-plugin-storybook":"9.1.10","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.3.0","react-router-dom":">=7.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"@12-apps/wiring":{"optional":true},"react-router-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.9.0_1787547386056_0.5562807993256309","host":"s3://npm-registry-packages-npm-production"}},"1.10.0":{"name":"@12-apps/discounts","version":"1.10.0","license":"MIT","_id":"@12-apps/discounts@1.10.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"257f49d972770a103b4cd45cc2b68138bee8685d","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.10.0.tgz","fileCount":60,"integrity":"sha512-iUXe+cX8nximTLN6rKzO8JTzbQkpZ+joqo9cAxPFFYGR6h0xgktETPeOyO0J7bjgvSLL8A2f1ppW/Hc3tM8tGg==","signatures":[{"sig":"MEYCIQD9uMC11zmdzUqK7dL6mMnuzsR8RIyIXzymYrZSdv4U1QIhAIsnsPfhgcTcWa0SeI9XTZRNdRlrYtQEuPPZxFYOg6e1","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.10.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":411754},"type":"module","wiring":{"db":{"partial":"prisma/discounts.prisma","migrations":"prisma/migrations"}},"engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./en-US":"./src/engine/en-US.ts","./pt-BR":"./src/engine/pt-BR.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./locales":"./src/locales.ts","./manifest":"./src/manifest/index.ts","./react/en-US":"./src/react/en-US.ts","./react/pt-BR":"./src/react/pt-BR.ts","./manifest/web":"./src/manifest/web.ts","./package.json":"./package.json","./server/en-US":"./src/server/en-US.ts","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"7919b5bfd73132d3a8a78a6de99b0eb22575f35f","scripts":{"dev":"storybook dev -p 6009","lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit","prisma:sync":"node scripts/sync-discounts-schema.mjs","storybook:build":"storybook build","prisma:sync:check":"node scripts/sync-discounts-schema.mjs --check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.0.2","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.9.0","@12-apps/app-shell":"^5.3.0","@12-apps/forms-core":"^1.20.0","@mui/icons-material":"^6.5.0","@12-apps/shared-helpers":"^2.0.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.1","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","storybook":"^9.1.10","typescript":"^5.9.2","@types/node":"^22.10.6","@types/react":"19.2.2","@12-apps/i18n":"^1.1.0","@mui/material":"^6.5.0","@emotion/react":"^11.14.0","@12-apps/wiring":"^1.11.0","@emotion/styled":"^11.14.0","@types/react-dom":"19.2.2","react-router-dom":"^7.0.0","@electric-sql/pglite":"^0.2.17","@storybook/addon-docs":"^9.1.10","@storybook/react-vite":"^9.1.10","@12-apps/eslint-config":"^1.21.1","@storybook/addon-links":"^9.1.10","@testing-library/react":"^16.1.0","eslint-plugin-storybook":"9.1.10","@12-apps/typescript-config":"^1.20.1","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.3.0","react-router-dom":">=7.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"@12-apps/wiring":{"optional":true},"react-router-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.10.0_1787584373231_0.5046719937632529","host":"s3://npm-registry-packages-npm-production"}},"1.11.0":{"name":"@12-apps/discounts","version":"1.11.0","license":"MIT","_id":"@12-apps/discounts@1.11.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"9c67007c41a48faf9bed76f762efcbbf9e826ab4","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.11.0.tgz","fileCount":60,"integrity":"sha512-7IBfsQP31/hc5JpvRqAEDTSavOHwEUR2LY+Z4biZ9xXvs9uPVxoTOkaflTFOkuLbQETAU2HYXJ2etnhQd9MUTQ==","signatures":[{"sig":"MEYCIQCZOtDnp4VjlFsTfqdlS5noaKfxvVIQaVjVufblaATyPQIhAIzjAtNNE6X1cCsx1p6yiL2COq27E972CB+ZbNuvcQfp","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.11.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":411779},"type":"module","wiring":{"db":{"partial":"prisma/discounts.prisma","migrations":"prisma/migrations"}},"engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./en-US":"./src/engine/en-US.ts","./pt-BR":"./src/engine/pt-BR.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./locales":"./src/locales.ts","./manifest":"./src/manifest/index.ts","./react/en-US":"./src/react/en-US.ts","./react/pt-BR":"./src/react/pt-BR.ts","./manifest/web":"./src/manifest/web.ts","./package.json":"./package.json","./server/en-US":"./src/server/en-US.ts","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"4714bd10c901bcc51544843d5d6091cc1509e649","scripts":{"dev":"storybook dev -p 6009","lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit","prisma:sync":"node scripts/sync-discounts-schema.mjs","storybook:build":"storybook build","prisma:sync:check":"node scripts/sync-discounts-schema.mjs --check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.0.2","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"sideEffects":false,"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.10.0","@12-apps/app-shell":"^5.5.0","@12-apps/forms-core":"^1.21.0","@mui/icons-material":"^6.5.0","@12-apps/shared-helpers":"^2.1.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.1","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","storybook":"^9.1.10","typescript":"^5.9.2","@types/node":"^22.10.6","@types/react":"19.2.2","@12-apps/i18n":"^1.2.0","@mui/material":"^6.5.0","@emotion/react":"^11.14.0","@12-apps/wiring":"^1.14.0","@emotion/styled":"^11.14.0","@types/react-dom":"19.2.2","react-router-dom":"^7.0.0","@electric-sql/pglite":"^0.2.17","@storybook/addon-docs":"^9.1.10","@storybook/react-vite":"^9.1.10","@12-apps/eslint-config":"^1.22.0","@storybook/addon-links":"^9.1.10","@testing-library/react":"^16.1.0","eslint-plugin-storybook":"9.1.10","@12-apps/typescript-config":"^1.21.0","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"react":">=19.0.0","react-dom":">=19.0.0","@12-apps/wiring":">=1.3.0","react-router-dom":">=7.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"@12-apps/wiring":{"optional":true},"react-router-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.11.0_1787617996172_0.5447256539717205","host":"s3://npm-registry-packages-npm-production"}},"1.12.0":{"name":"@12-apps/discounts","version":"1.12.0","license":"MIT","_id":"@12-apps/discounts@1.12.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"2b6efadc6a6a35b7979239d6daa1b7839d147722","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.12.0.tgz","fileCount":81,"integrity":"sha512-9iRtitLKR12AYStXb5N89dKF5HTLZCrI+Df7sa/FZolHMvq/ykNPnFzZTaLwd8Gq5DiJ01WC8HVt71sigewZcg==","signatures":[{"sig":"MEUCIFWzg58WoweR92z5KOYAPTRF3xz5TXRA1irKziXIWQPyAiEAv8hEUzbMvpGFsaBx9/W16mRCnbYCiBxyRjuEXcLcUS0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.12.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":457863},"type":"module","wiring":{"db":{"partial":"prisma/discounts.prisma","migrations":"prisma/migrations"}},"engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./e2e":{"types":"./dist/e2e/index.d.ts","default":"./dist/e2e/index.js"},"./en-US":"./src/engine/en-US.ts","./pt-BR":"./src/engine/pt-BR.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./locales":"./src/locales.ts","./manifest":"./src/manifest/index.ts","./react/en-US":"./src/react/en-US.ts","./react/pt-BR":"./src/react/pt-BR.ts","./manifest/web":"./src/manifest/web.ts","./package.json":"./package.json","./server/en-US":"./src/server/en-US.ts","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"14d4758eea55506bae6bb0b6fe6001aa1c954e81","scripts":{"dev":"storybook dev -p 6009","lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","build":"tsc -p tsconfig.e2e.json","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit","prisma:sync":"node scripts/sync-discounts-schema.mjs","storybook:build":"storybook build","prisma:sync:check":"node scripts/sync-discounts-schema.mjs --check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.0.2","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"sideEffects":["**/e2e/**"],"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.10.0","@12-apps/app-shell":"^5.5.0","@12-apps/forms-core":"^1.21.0","@mui/icons-material":"^6.5.0","@12-apps/shared-helpers":"^2.1.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.1","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","storybook":"^9.1.10","typescript":"^5.9.2","@types/node":"^22.10.6","@types/react":"19.2.2","@12-apps/i18n":"^1.2.0","@mui/material":"^6.5.0","@emotion/react":"^11.14.0","playwright-bdd":"^9.2.0","@12-apps/wiring":"^1.14.0","@emotion/styled":"^11.14.0","@playwright/test":"^1.61.1","@types/react-dom":"19.2.2","react-router-dom":"^7.0.0","@electric-sql/pglite":"^0.2.17","@storybook/addon-docs":"^9.1.10","@storybook/react-vite":"^9.1.10","@12-apps/eslint-config":"^1.22.0","@storybook/addon-links":"^9.1.10","@testing-library/react":"^16.1.0","eslint-plugin-storybook":"9.1.10","@12-apps/typescript-config":"^1.21.0","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"react":">=19.0.0","react-dom":">=19.0.0","playwright-bdd":">=8.3.0","@12-apps/wiring":">=1.3.0","@playwright/test":">=1.55.0","react-router-dom":">=7.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"playwright-bdd":{"optional":true},"@12-apps/wiring":{"optional":true},"@playwright/test":{"optional":true},"react-router-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.12.0_1787624867444_0.15546170118946434","host":"s3://npm-registry-packages-npm-production"}},"1.13.0":{"name":"@12-apps/discounts","version":"1.13.0","license":"MIT","_id":"@12-apps/discounts@1.13.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"d0ec5256f30d44f5ba31c25c8867eaca51eaff43","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.13.0.tgz","fileCount":89,"integrity":"sha512-UfSVktIsvdGBC/kg82FigmZE6fZpIQq+Ck/ezGQv5n7A/g80X+dt82a7YcW45WTpq59f1gNCkr16JXE9LGfg8g==","signatures":[{"sig":"MEUCIE6avwHqjftrp+8eJBUTXxWNZNuBjmpf5ek1QXUU0D++AiEA8yPBqT6tnkIJE54Ox8E0j4ab466ilxwldwJZ83r2IqA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.13.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":527603},"type":"module","wiring":{"db":{"partial":"prisma/discounts.prisma","migrations":"prisma/migrations"}},"engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./e2e":{"types":"./dist/e2e/index.d.ts","default":"./dist/e2e/index.js"},"./en-US":"./src/engine/en-US.ts","./pt-BR":"./src/engine/pt-BR.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./locales":"./src/locales.ts","./manifest":"./src/manifest/index.ts","./timezone":"./src/timezone.ts","./react/en-US":"./src/react/en-US.ts","./react/pt-BR":"./src/react/pt-BR.ts","./manifest/web":"./src/manifest/web.ts","./package.json":"./package.json","./server/en-US":"./src/server/en-US.ts","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"b66900fbc38454f6ed6afb3820ae1c341037d07c","scripts":{"dev":"storybook dev -p 6009","lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","build":"tsc -p tsconfig.e2e.json","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit","prisma:sync":"node scripts/sync-discounts-schema.mjs","storybook:build":"storybook build","prisma:sync:check":"node scripts/sync-discounts-schema.mjs --check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.0.2","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"sideEffects":["**/e2e/**"],"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.15.1","@12-apps/app-shell":"^5.7.0","@12-apps/forms-core":"^1.21.0","@mui/icons-material":"^6.5.0","@12-apps/shared-helpers":"^2.1.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.1","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","storybook":"^9.1.10","typescript":"^5.9.2","@types/node":"^22.10.6","@types/react":"19.2.2","@12-apps/i18n":"^1.3.0","@mui/material":"^6.5.0","@emotion/react":"^11.14.0","playwright-bdd":"^9.2.0","@12-apps/wiring":"^1.16.0","@emotion/styled":"^11.14.0","@playwright/test":"^1.61.1","@types/react-dom":"19.2.2","react-router-dom":"^7.0.0","@electric-sql/pglite":"^0.2.17","@storybook/addon-docs":"^9.1.10","@storybook/react-vite":"^9.1.10","@12-apps/eslint-config":"^1.22.0","@storybook/addon-links":"^9.1.10","@testing-library/react":"^16.1.0","eslint-plugin-storybook":"9.1.10","@12-apps/typescript-config":"^1.21.0","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"react":">=19.0.0","react-dom":">=19.0.0","playwright-bdd":">=8.3.0","@12-apps/wiring":">=1.3.0","@playwright/test":">=1.55.0","react-router-dom":">=7.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"playwright-bdd":{"optional":true},"@12-apps/wiring":{"optional":true},"@playwright/test":{"optional":true},"react-router-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.13.0_1788073898174_0.11742829401503396","host":"s3://npm-registry-packages-npm-production"}},"1.14.0":{"name":"@12-apps/discounts","version":"1.14.0","license":"MIT","_id":"@12-apps/discounts@1.14.0","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"homepage":"https://github.com/12-apps/shared-packages#readme","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"7747834a1f1b1198a4797b00d8908c7427119fee","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.14.0.tgz","fileCount":89,"integrity":"sha512-D/C0b0O775moAIemBW0VtUmm4ZTjQSHd7RaUd5MjN2Uz4iU+vdl3HiK/P+mn+jUYSleZeQobuuO4oBuwS47HhA==","signatures":[{"sig":"MEYCIQCNOOHqX2OrTraTdxo1itZPlUwG8NszTeKPQMT3nhEziQIhALJiJnn0Vqqx9xpQwMO3Le7av7O1POZXLurDloUzjxK0","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQD78piIsl+K/wOlZN6Mkgml15Fm1cxQw/VYZhHnLj8hTgIgQEUlN3s+j/Gk/BYohyeYaHsn14IG4xrm5N1b/Xj67Mg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.14.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":527809},"type":"module","wiring":{"db":{"partial":"prisma/discounts.prisma","migrations":"prisma/migrations"}},"engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./e2e":{"types":"./dist/e2e/index.d.ts","default":"./dist/e2e/index.js"},"./en-US":"./src/engine/en-US.ts","./pt-BR":"./src/engine/pt-BR.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./locales":"./src/locales.ts","./manifest":"./src/manifest/index.ts","./timezone":"./src/timezone.ts","./react/en-US":"./src/react/en-US.ts","./react/pt-BR":"./src/react/pt-BR.ts","./manifest/web":"./src/manifest/web.ts","./package.json":"./package.json","./server/en-US":"./src/server/en-US.ts","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"f35ae5a79db04f334861bb85440bf54ae6eb395e","scripts":{"dev":"storybook dev -p 6009","lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","build":"tsc -p tsconfig.e2e.json","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit","prisma:sync":"node scripts/sync-discounts-schema.mjs","storybook:build":"storybook build","prisma:sync:check":"node scripts/sync-discounts-schema.mjs --check"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.1.0","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"sideEffects":["**/e2e/**"],"_nodeVersion":"24.21.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.42.0","@12-apps/app-shell":"^5.11.0","@12-apps/forms-core":"^1.21.0","@mui/icons-material":"^6.5.0","@12-apps/shared-helpers":"^2.1.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.1","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","storybook":"^9.1.10","typescript":"^5.9.2","@types/node":"^22.10.6","@types/react":"19.2.2","@12-apps/i18n":"^1.3.0","@mui/material":"^6.5.0","@emotion/react":"^11.14.0","playwright-bdd":"^9.2.0","@12-apps/wiring":"^1.16.0","@emotion/styled":"^11.14.0","@playwright/test":"^1.61.1","@types/react-dom":"19.2.2","react-router-dom":"^7.0.0","@electric-sql/pglite":"^0.2.17","@storybook/addon-docs":"^9.1.10","@storybook/react-vite":"^9.1.10","@12-apps/eslint-config":"^1.22.0","@storybook/addon-links":"^9.1.10","@testing-library/react":"^16.1.0","eslint-plugin-storybook":"9.1.10","@12-apps/typescript-config":"^1.21.0","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"react":">=19.0.0","react-dom":">=19.0.0","playwright-bdd":">=8.3.0","@12-apps/wiring":">=1.3.0","@playwright/test":">=1.55.0","react-router-dom":">=7.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"playwright-bdd":{"optional":true},"@12-apps/wiring":{"optional":true},"@playwright/test":{"optional":true},"react-router-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/discounts_1.14.0_1790463254564_0.9341188824302165","host":"s3://npm-registry-packages-npm-production"}},"1.15.0":{"_id":"@12-apps/discounts@1.15.0","bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"dist":{"shasum":"dcc1a57dc3f057262e179649a415f3a5fe53f025","tarball":"https://registry.npmjs.org/@12-apps/discounts/-/discounts-1.15.0.tgz","fileCount":91,"integrity":"sha512-dkWNgW4nAo5K2yi03k8SU5DAK4LXqQCbAwWdWnY1POJySMFchkNGF8z5bbKPwkjyVK/aT42JLW98yR1u2XfRrQ==","signatures":[{"sig":"MEYCIQChWYb4vlaUHxW+XUKZndXezBH0coqk7tzF7YvxUbZW+AIhAK8cF7pbm3Yl0qfyFJ52A5h3/2PP/MHNjp+EyBgLJVTC","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCICxrrkJacbJ6wkcNFOet1VXJAnFnpn/aZiyoJk5B7O1aAiEAjvC4Vs7WqB4Kgtp6kZmBXXpJVBp7Fy+vvXGbZ70Fcs0="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@12-apps%2fdiscounts@1.15.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":535179},"name":"@12-apps/discounts","type":"module","wiring":{"db":{"partial":"prisma/discounts.prisma","migrations":"prisma/migrations"}},"engines":{"node":">=22.0.0"},"exports":{".":"./src/index.ts","./e2e":{"types":"./dist/e2e/index.d.ts","default":"./dist/e2e/index.js"},"./en-US":"./src/engine/en-US.ts","./pt-BR":"./src/engine/pt-BR.ts","./react":"./src/react/index.ts","./server":"./src/server/index.ts","./locales":"./src/locales.ts","./manifest":"./src/manifest/index.ts","./timezone":"./src/timezone.ts","./react/en-US":"./src/react/en-US.ts","./react/pt-BR":"./src/react/pt-BR.ts","./manifest/web":"./src/manifest/web.ts","./package.json":"./package.json","./server/en-US":"./src/server/en-US.ts","./server/pt-BR":"./src/server/pt-BR.ts","./manifest/server":"./src/manifest/server.ts"},"gitHead":"39f4f99d70ddab880443d0bcb4bc728d8308c1e0","license":"MIT","scripts":{"dev":"storybook dev -p 6009","lint":"eslint src --max-warnings 0","test":"node ../../scripts/vitest-with-teardown.mjs run","build":"tsc -p tsconfig.e2e.json","clean":"rm -rf node_modules coverage dist","typecheck":"tsc --noEmit","test:watch":"vitest watch","check-types":"tsc --noEmit","prisma:sync":"node scripts/sync-discounts-schema.mjs","storybook:build":"storybook build","prisma:sync:check":"node scripts/sync-discounts-schema.mjs --check"},"version":"1.15.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:785ff896-fd99-4e76-b24e-00c071e94125"}},"homepage":"https://github.com/12-apps/shared-packages#readme","repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"_npmVersion":"12.1.0","description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","directories":{},"maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"sideEffects":["**/e2e/**"],"_nodeVersion":"24.21.0","dependencies":{"zod":"^4.3.5","@12-apps/ui":"^6.43.0","@12-apps/app-shell":"^5.11.0","@12-apps/forms-core":"^1.21.0","@mui/icons-material":"^6.5.0","@12-apps/shared-helpers":"^2.1.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.1","jsdom":"^25.0.1","react":"^19.2.0","eslint":"^9.39.1","vitest":"^3.2.4","react-dom":"^19.2.0","storybook":"^9.1.10","typescript":"^5.9.2","@types/node":"^22.10.6","@types/react":"19.2.2","@12-apps/i18n":"^1.3.0","@mui/material":"^6.5.0","@emotion/react":"^11.14.0","playwright-bdd":"^9.2.0","@12-apps/wiring":"^1.16.0","@emotion/styled":"^11.14.0","@playwright/test":"^1.61.1","@types/react-dom":"19.2.2","react-router-dom":"^7.0.0","@electric-sql/pglite":"^0.2.17","@storybook/addon-docs":"^9.1.10","@storybook/react-vite":"^9.1.10","@12-apps/eslint-config":"^1.22.0","@storybook/addon-links":"^9.1.10","@testing-library/react":"^16.1.0","eslint-plugin-storybook":"9.1.10","@12-apps/typescript-config":"^1.21.0","eslint-plugin-test-flakiness":"^1.4.0"},"peerDependencies":{"react":">=19.0.0","react-dom":">=19.0.0","playwright-bdd":">=8.3.0","@12-apps/wiring":">=1.3.0","@playwright/test":">=1.55.0","react-router-dom":">=7.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"playwright-bdd":{"optional":true},"@12-apps/wiring":{"optional":true},"@playwright/test":{"optional":true},"react-router-dom":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/discounts_1.15.0_1790468794656_0.2502899910750085"}}},"time":{"created":"2026-08-21T13:35:33.004Z","modified":"2026-09-27T00:26:35.206Z","1.0.0":"2026-08-21T13:35:33.393Z","1.1.0":"2026-08-21T18:43:05.990Z","1.2.0":"2026-08-21T21:49:21.364Z","1.3.0":"2026-08-21T22:52:12.733Z","1.4.0":"2026-08-21T23:39:28.583Z","1.5.0":"2026-08-22T18:09:17.026Z","1.6.0":"2026-08-22T20:24:56.309Z","1.7.0":"2026-08-23T17:02:46.929Z","1.8.0":"2026-08-23T18:09:39.028Z","1.9.0":"2026-08-24T04:56:26.200Z","1.10.0":"2026-08-24T15:12:53.365Z","1.11.0":"2026-08-25T00:33:16.303Z","1.12.0":"2026-08-25T02:27:47.737Z","1.13.0":"2026-08-30T07:11:38.357Z","1.14.0":"2026-09-26T22:54:14.657Z","1.15.0":"2026-09-27T00:26:34.739Z"},"bugs":{"url":"https://github.com/12-apps/shared-packages/issues"},"license":"MIT","homepage":"https://github.com/12-apps/shared-packages#readme","repository":{"url":"git+https://github.com/12-apps/shared-packages.git","type":"git","directory":"packages/discounts"},"description":"The discount/promotion domain: a host-agnostic evaluator (percentage or fixed amount, order/category/item scope, automatic or coupon-triggered, stacking and exclusivity, integer-cent allocation) plus the admin CRUD surface as wiring-contract route descrip","maintainers":[{"name":"tigredonorte","email":"tigredonorte3@gmail.com"}],"readme":"# @12-apps/discounts\n\nThe discount / promotion domain, extracted from its origin host and made\nhost-agnostic: an **evaluator** that decides what a cart actually pays, and an\n**admin CRUD surface** shipped as wiring-contract route descriptors over a\nstore port.\n\nNo database, no framework, no clock, and **no baked user copy**.\n\n## What is in the box\n\n| entry | what it is |\n|---|---|\n| `@12-apps/discounts` | the engine: `evaluateDiscounts`, `previewItemDiscount`, `comboOffersForItem`, `matchCombo`, the vocabulary, the rejection copy port |\n| `@12-apps/discounts/pt-BR` | the pt-BR rejection pack, imported by hand |\n| `@12-apps/discounts/server` | `createApiDiscounts`, the `DiscountStore` port, the permission contribution, the MCP tools |\n| `@12-apps/discounts/server/pt-BR` | the pt-BR pack for the admin surface's sentences |\n| `@12-apps/discounts/manifest` | the shared wiring manifest |\n| `@12-apps/discounts/manifest/server` | the `http` capability |\n\n## The model\n\nA discount is a **percentage** (basis points, 1..10000) or a **fixed amount**\n(integer cents), scoped to the whole **order**, to **categories**, to specific\n**items**, or to a **combo**, fired either **automatically** when its conditions\nhold or by the buyer typing a **coupon code**. Money is integer cents\nthroughout; a percentage is basis points, so \"12,5%\" needs no decimal column\nanywhere.\n\nThe evaluator is a pure function of `{ lines, rules, couponCode, now }`:\n\n```ts\nimport { evaluateDiscounts } from \"@12-apps/discounts\";\n\nconst outcome = evaluateDiscounts({ lines, rules, couponCode, now: new Date() });\n// outcome.subtotalCents, .discountTotalCents, .totalCents\n// outcome.applied[]   — what removed money, frozen for the order snapshot\n// outcome.lines[]     — per-line cents removed; Σ equals discountTotalCents\n// outcome.rejections[] — only for what the buyer ASKED for, or lost a stack race\n```\n\nThree properties it holds to the cent, and the suite pins each:\n\n- `Σ applied[].amountCents === discountTotalCents === Σ lines[].discountCents`;\n- narrowest scope first, so an order-wide promo can never give away money an\n  item-level promo already removed;\n- a discounted order is never worth **less than one cent**. That is a payment\n  constraint, not a pricing one — providers reject a zero-amount charge, so an\n  order discounted to nothing could never be settled at all. The floor is\n  applied *during* allocation, never as a post-hoc trim, which is what keeps\n  the sum identity above true.\n\n`previewItemDiscount` answers the other question — what a catalog card should\nstrike through *before* there is a cart — from the same rules, so a card and a\ncart cannot quote different numbers.\n\n## Combos\n\nA **combo** is a discount scoped to a group the merchant defines: a list of\n**slots**, each naming products and/or categories and how many units it needs.\nThe evaluator matches those slots against the cart's **units**, repeatedly, and\nprices each match.\n\n```ts\n// \"1 pipoca grande + 2 refrigerantes por R$ 25,00\"\n{\n  scope: \"COMBO\",\n  type: \"BUNDLE_PRICE\",\n  bundlePriceCents: 2_500,\n  comboRequirements: [\n    { menuItemIds: [\"popcorn-lg\"], categoryIds: [], quantity: 1 },\n    { menuItemIds: [], categoryIds: [\"drinks\"], quantity: 2 },\n  ],\n}\n\n// \"3 hambúrgueres pelo preço de 2\" — one slot of three, the cheapest one free\n{\n  scope: \"COMBO\",\n  type: \"FREE_UNITS\",\n  freeUnits: 1,\n  comboRequirements: [{ menuItemIds: [\"burger\"], categoryIds: [], quantity: 3 }],\n}\n```\n\nFour rewards, and the type picks which column is read:\n\n| type | column | the group's discount |\n|---|---|---|\n| `BUNDLE_PRICE` | `bundlePriceCents` | its value above the bundle price |\n| `FREE_UNITS` | `freeUnits` | its N cheapest units |\n| `PERCENTAGE` | `percentOffBp` | that rate off it |\n| `FIXED_AMOUNT` | `amountOffCents` | that many cents off it |\n\nThe last two are the ordinary columns, doing the ordinary thing against a group\ninstead of against a line. The first two only mean something against a matched\ngroup, so they are legal at `COMBO` scope and nowhere else — the write path\nrefuses the combination and a host's CHECK constraint should too.\n\nEvery reward is **per application**. A cart of seven burgers takes \"3 for the\nprice of 2\" twice and pays full price for the seventh; `maxComboApplications`\ncaps that when a merchant wants it capped.\n\nFour decisions worth knowing before you wire it up:\n\n- **Combos run first.** The pass order is `COMBO → ITEM → CATEGORY → ORDER`.\n- **What a combo consumed is opaque to `ITEM` and `CATEGORY`.** A combo price is\n  a number the merchant set deliberately; a component-targeted promotion\n  stacking on top of it is a double discount. So a line of five burgers with\n  three inside a combo offers exactly two burgers, at full price, to an\n  item-level promotion. **`ORDER` is not blocked** and applies to the combo\n  price, because an order-wide promise is about the basket, not the components.\n- **One pool.** Every combo draws from the same units, so two combos can never\n  both be paid for the same burger. The richer one claims them first.\n- **A combo is not badged.** Its price does not exist until the other components\n  are in the cart, so `previewItemDiscount` skips it. `comboOffersForItem` is\n  what a card can honestly show instead — that the item takes part in a combo,\n  as a label rather than a price.\n\nA combo the cart cannot assemble is rejected `COMBO_NOT_MATCHED`, which is the\none rejection reason this package refuses to coarsen: a buyer one soda short\ncan finish the combo, and telling them so is the difference between a dead end\nand a sale.\n\n### What a combo is NOT, here\n\nIt is a **pricing rule**, not a catalog entity. There is no `Combo` product with\nits own menu card, its own single cart line and its own order snapshot — that is\na host table with foreign keys into the host's own catalog, which is exactly why\nthis package declares no `db` capability. What travels is the rule; the sellable\nbundle, if a host wants one, stays the host's.\n\n## The admin surface\n\n`createApiDiscounts({ store, copy, logger })` returns five `WireRoute` descriptors —\nlist, read, create, re-state, archive — each **declaring the permission it\nneeds** (`discounts:read` / `discounts:write`), so a host's coverage gates can\nread the policy off the assembled table instead of scanning route files.\n\n```ts\nimport { createApiDiscounts } from \"@12-apps/discounts/server\";\nimport { PT_BR_DISCOUNTS_SERVER_COPY } from \"@12-apps/discounts/server/pt-BR\";\n\nconst { routes } = createApiDiscounts({\n  store,\n  copy: PT_BR_DISCOUNTS_SERVER_COPY,\n  logger: createFeatureLogger(\"discounts\"),\n});\n```\n\nOr, through the contract:\n\n```ts\nimport { discountsManifest } from \"@12-apps/discounts/manifest\";\nimport { discountsServerManifest } from \"@12-apps/discounts/manifest/server\";\n\nhost.adoptServer({\n  manifest: discountsManifest,\n  server: discountsServerManifest,\n  bindings: {\n    http: { mountPath: \"/api/admin/:tenantSlug\", config: { store, copy, logger } },\n  },\n});\n```\n\nDelete is a **soft archive**, and that is a product decision: the orders that\nalready redeemed a discount keep their snapshot, and its redemption counter\nstays readable for reporting.\n\n### Nothing it does is silent\n\n`logger` is **required config, with no default**, for the reason `copy` is: the\ndefault would be a no-op, and a no-op is the exact silence it exists to end.\nEvery route is wrapped, and there are three outcomes:\n\n| outcome | level | what the line names |\n|---|---|---|\n| a write that succeeded | `info` | the verb, the discount id, the tenant |\n| a refusal this package decided (400 / 404 / 422) | `warn` | the route, the status, and for a 422 the **field** a form paints red |\n| anything thrown — and it is re-thrown unchanged | `error` | the route, the tenant, the cause's message |\n\nEvery line is a **string**. A host's logger is a Winston child in practice,\nwhose formatter runs `util.inspect(…, { depth: 5 })` over an extra argument —\nwhich is how a provider error's retained response body, buyer name and CPF\nincluded, reaches a third party. The cause is folded into the sentence instead.\nA line names the tenant, the route, the discount id and the field; never the\nbody, never a coupon code, never a name an operator typed.\n\nThe `error` case **re-throws**. A uniqueness clash, a foreign target and a dead\nconnection are the store's to raise in the host's own error vocabulary; a\nsurface that folded one into a tidy response would turn a broken database into a\nrefusal nobody investigates.\n\nA host adopting through `@12-apps/wiring/consumer` already has the logger the\nbinder built from this package's declared namespace — pass\n`assembled.loggers[\"@12-apps/discounts\"]`. A host on no wiring passes any\n`createFeatureLogger`-shaped child of its own.\n\nThe evaluator is deliberately **not** wired to it. `evaluateCart` is a pure\nfunction called once per cart render; a logger there would file a line per\nkeystroke on the storefront and would need a logger threaded through a browser\nbundle. A rejected coupon is already a `DiscountRejection` in the result, which\nis the host's to report if it wants to.\n\n## Letting a host table opt in — `DiscountableCollection`\n\nA discount points at rows this package will never see: a store's categories, its\nproducts, and — the day a host wants it — its suppliers or its shelves. Every\nquestion about them (\"which exist\", \"are they this tenant's\", \"what is this one\nfiled under\") is answerable only by the host, so the host DECLARES the table:\n\n```ts\nconst categories: DiscountableCollection = {\n  targetType: \"CATEGORY\",\n  slug: \"categories\",\n  label: \"Categorias\",          // host vocabulary, host language\n  nests: true,\n  ops: {\n    list: (clientId) => …,      // the picker's rows\n    ownsAll: (clientId, ids) => …,   // the cross-tenant guard\n    parents: (clientId) => …,   // child → parent, for the ancestry walk\n  },\n};\n\ncreateApiDiscounts({ store, copy, logger, collections: [categories, products] });\n```\n\nThree pieces of hand-written host code collapse into it:\n\n| host code | becomes |\n|---|---|\n| a per-table cross-tenant `count` before every write | `ops.ownsAll`, run once per write for every dimension |\n| the tenant's category tree loaded, and an ancestor walk with a cycle guard | `ops.parents` + the package's own `buildTargetPath` |\n| an admin page side-loading two catalogs at `?pageSize=500` to feed a picker | `GET /discounts/targets`, one round trip |\n\nTwo things it fixes as a side effect. The ownership check now covers the ids\ninside a **combo's slots** — a combo scope drops the top-level target pair and\ncarries its targets in the slots, so a host checking only the pair let a crafted\ncombo name another store's products. And the ancestry walk stops being a second\nimplementation: the host builds the cart lines this package prices, so both\nsides now read the same `buildTargetPath`.\n\nThe check runs OUTSIDE the store's transaction, and that is enough for the leak\nthat matters: a catalog row's tenant never changes, so an id owned by this\ntenant now cannot belong to another one by the time the write lands. The only\nrace left is a target deleted in between, whose outcome is a discount pointing\nat a row that no longer exists — which the evaluator already tolerates.\n\n`collections` is **optional**, and the omission is a real configuration: a host\nthat sells one undifferentiated thing has no dimension to register. What it\ncosts is stated rather than defaulted around — with nothing registered,\n`GET /discounts/targets` answers `[]` and the cross-tenant check stays the\nstore's job.\n\n### Why this is not a wiring capability yet\n\n`@12-apps/wiring`'s contract folder holds http, web, mcp, db, env, jobs, email,\nnotifications and permissions — there is no registration or extension concept,\nso promoting this would mint a new capability on two instances that are less\nalike than they look: `@12-apps/entity-lifecycle` registers a collection it\nDRIVES, with write ops, while this registers a catalog dimension it only\nQUERIES. Abstracting one contract over both risks fitting neither. It ships\nhere, shaped deliberately like its sibling so the family resemblance is visible,\nand a third consumer is what would earn it a place in the contract.\n\n`DISCOUNT_TARGET_TYPES` is closed at two today. That is a boundary, not a\nprinciple: a third dimension is expressible the moment targets are stored **by\nvalue** (`(target_type, target_id)`) instead of as a join table per member, and\nthe registration is shaped so that widening the array plus registering one more\ncollection is the whole change.\n\n## The schema travels too\n\nThe package ships a **composed Prisma partial** and its migrations\n(`prisma/discounts.prisma`, `wiring.db`). A host syncs the partial into its\nschema folder and discovers the migration structurally:\n\n```bash\npnpm --filter @12-apps/discounts prisma:sync         # copy the partial\npnpm --filter @12-apps/discounts prisma:sync:check   # drift gate, for CI\n```\n\nThree tables — `discounts`, `discount_combo_slots`, `discount_targets` — and\nwhat makes them shippable at all is that **nothing in them names a host table**:\n\n| the origin had | the partial has | why |\n|---|---|---|\n| `client Client @relation(...)` | a scalar `client_id` | this package cannot know a host's tenant model's name |\n| `discount_categories` + `discount_items`, one FK each | one `discount_targets`, keyed `(target_type, target_id)` | a partial naming `product_categories` only compiles inside a host that has that table |\n| the scope's targets and a combo slot's, separately | one table, told apart by a nullable `slot_id` | they are the same fact; two tables need every reverse read, ownership check and cascade written twice |\n\nWhich collections are discountable is answered at RUNTIME by a\n`DiscountableCollection` registration, not by the schema. That is the whole\nmove, and it is `@12-apps/entity-lifecycle`'s, applied here.\n\n**The redemption snapshot stays with the host.** `order_discounts` is a child of\nthe host's own order, with a cascade, and its purpose is to freeze what a buyer\nreceived on an order the host owns. Shipping the rule and leaving the receipt is\nthe clean cut.\n\n**What the by-value target costs**, stated plainly: the schema no longer\nguarantees that deleting a category cannot leave a rule pointing at a ghost.\nThree things carry that instead — the evaluator already tolerates an unmatched\ntarget (it covers nothing), `DiscountableOps.ownsAll` refuses a foreign or\nmissing id on every write, and a host that wants the constraint back may add it\nin its own migration.\n\n### The migration adopts an existing table\n\nEvery statement is idempotent, because the first host to adopt this package\nalready HAS a `discounts` table from its own, earlier migration — and a package\nmigration sorts AFTER the host's by name, so a bare `CREATE TABLE` would fail\n`migrate deploy` on every database that already has one. A fresh host gets\neverything; an existing host gets only what it is missing (the three combo\ncolumns, the two new tables, and the widened `type`/`scope`/value CHECKs);\nreplaying the whole folder is a no-op.\n\nIt deliberately does NOT move a host's existing target rows into\n`discount_targets`. Only that host knows what its catalog tables are called, so\nbackfilling and dropping the old join tables is one migration in the host,\nwritten once, sorting after this one.\n\n`src/prisma/__tests__/migration.test.ts` applies the SQL to real Postgres\n(PGlite) in all three situations — 17 cases, including the partial uniques, both\ncascades, and the CHECK widening a schema diff cannot see.\n\n## The admin screens travel too — `@12-apps/discounts/react`\n\n```ts\n// ONCE, at module scope. The members are component TYPES.\nexport const discounts = createWebDiscounts({\n  apiBase: `/api/admin/${slug}`,\n  copy: PT_BR_DISCOUNTS_WEB_COPY,   // required, no defaults\n  locale: \"pt-BR\",\n  currency: \"BRL\",\n  currencyField: MyCurrencyField,   // currency entry is a host decision\n  onError: reportToSentry,          // required — see below\n  breadcrumb: [{ label: \"Início\", href: \"/admin\" }, { label: \"Descontos\" }],\n});\n\n// <discounts.Screen /> is the whole promotions admin.\n```\n\nWhat that one call replaces: a server-driven grid with eight columns, five\nfilter pills and a CSV/JSON export; a create/edit form whose value input and\ntarget block both follow the toggles above them; the target pickers and the\ncombo builder; two card layouts; four confirmation popups; and every wire call\nbetween them.\n\n### Authoring a combo\n\nThe four discount types and the four scopes are not independent, and the form\nis where that stops being a table in a README. It asks ONE question — what KIND\nof promotion is this — and derives the pair (`src/react/form-kind.ts`):\n\n| the operator picks | type | scope | and is then asked |\n|---|---|---|---|\n| Porcentagem | `PERCENTAGE` | ORDER / CATEGORY / ITEM | the rate, and what it covers |\n| Valor fixo | `FIXED_AMOUNT` | ORDER / CATEGORY / ITEM | the amount, and what it covers |\n| Combo | `PERCENTAGE` or `FIXED_AMOUNT` | `COMBO` | the groups, and which of the two rewards |\n| Itens grátis | `FREE_UNITS` | `COMBO` | the products, how many, how many free |\n\nSo the offers a merchant describes map one to one onto what they fill in:\n\n| the offer | kind | what the operator fills in |\n|---|---|---|\n| \"leve 3, pague 2\" | Itens grátis | 3 hambúrgueres, 1 free |\n| \"2 refrigerantes, 2 hambúrgueres e 2 batatas com 20% de desconto\" | Combo | three groups of 2, and the rate |\n| the same six items at R$ 5,00 off | Combo | three groups of 2, and the amount |\n\nTwo consequences of asking the kind instead of the pair:\n\n- **`COMBO` is not a scope anybody picks.** A combo covers the groups it is\n  made of, so the scope toggle offers the three that are genuinely a choice.\n  Offering the fourth is what let an operator build \"preço de combo,\n  abrangência: pedido\" and have the write path refuse it.\n- **`BUNDLE_PRICE` is no longer offered.** A flat price for the group reprices\n  it, and goes silently wrong the first time one of its items changes price —\n  so a combo gives a percentage or an amount OFF what its items add up to. The\n  type is still legal in the engine and in the database, and the kind toggle\n  grows a fifth option on the form of a rule that already carries it, so an\n  older promotion opens, edits and saves as what it is.\n\nPicking Combo swaps the single target picker for the **group builder**: each\ngroup is a quantity plus the rows that can fill it, and a group may name\ncategories AND products at once, because a real menu mixes them — \"any drink,\nor specifically the large fries\" is one group. Every registered\n`DiscountableCollection` gets a picker inside every group, so a host that\nregisters a third dimension gets it here with no change to this package.\n\n\"Itens grátis\" gets a builder of its own instead (`free-units-builder.tsx`):\none group, PRODUCTS only, and the two numbers side by side — \"o cliente leva 3,\n1 sai de graça\". It is the same stored shape as a one-group combo, which is\nwhat lets the two kinds share a validator, a write path and a row; nobody\ndescribing \"leve 3, pague 2\" is building a combo, so nobody is asked to.\n\nThree consequences worth stating, because each is a bug this shape prevents:\n\n- **The reward input is MOUNTED, not hidden.** The four value columns are\n  mutually exclusive at the database, so a leftover number from a branch the\n  operator flipped away from is a 500 on a form they filled in correctly.\n- **`freeUnits` is bounded by the groups.** \"Take 3, three free\" is a giveaway,\n  so the form compares it against what one application takes out of the cart\n  and names the ceiling when it refuses.\n- **Position is the array index.** The list an operator builds is the list a\n  card reads back; two groups of the same size are otherwise indistinguishable\n  once stored, which is why `toTargetRows` stamps the index as `position`.\n\n`maxComboApplications` appears at `COMBO` scope and nowhere else — it is the\nonly scope where \"how many times may one cart claim this\" means anything — and\nblank means as often as it fits.\n\n**Build it once, at module scope.** The members are component TYPES, so\nrebuilding per render gives React new types every time and remounts the whole\ntree below — a form then cannot be typed into, and nothing says why.\n`useWebDiscounts` is the memoised form for a genuinely dynamic mount path.\n\n### `onError` is required, and it is the browser twin of the server's logger\n\nThese screens already tell the OPERATOR what went wrong. That is not the same as\nanybody knowing. A refused write, a catalog that would not load, a page read\nthat failed — each reaches a person only if a host routes it somewhere, and a\nno-op default would make *nothing is broken* and *nothing is watching* look\nidentical. `context` is a stable dotted token (`discounts.list`,\n`discounts.create`, `discounts.targets`) so a reporter can group on it.\n\n### One vocabulary, in English, on the wire\n\n`DISCOUNT_WINDOW_STATES` are `RUNNING` / `SCHEDULED` / `ENDED`, and the words on\nthe pill come from the copy pack. They are read in three places that must agree\n— the filter pill, the query the backend receives, and the badge on a row — and\nthe moment one of those is a word in a language, the set stops being a set. The\norigin filtered on `Vigente`, so its wire protocol *was* its language.\n\n`discountWindowState()` lives in the engine rather than in this surface for the\nsame reason: the badge and the backend filter must answer \"has it ended\" the\nsame way, and two implementations is how a grid shows a rule as running on a row\nthe filter excludes.\n\n### What a host still supplies, and why each one\n\n| config | why it cannot be defaulted |\n|---|---|\n| `copy` | a default in one language reads as finished to the next host until a user sees it |\n| `locale` + `currency` | \"12,5\" is not a translation of \"12.5\" — it decides PARSING as much as rendering |\n| `currencyField` | masking, which side the symbol sits on, whether cents are typed — no neutral answer exists |\n| `onError` | above |\n| `breadcrumb` | the host owns its own information hierarchy |\n\n### Storybook\n\n`pnpm --filter @12-apps/discounts dev` (port 6009). Every ported component has\nstories, including the states nobody can produce on demand in a real\nenvironment: a refused write, a backend that will not answer, a catalog that\nfailed while the grid loaded fine. The stories substitute a **transport**, not a\nstubbed client, so they exercise the real path building and envelope unwrapping\nand only the bytes are pretend.\n\n## What the host still owns\n\n**Persistence itself.** The package owns the schema; the host owns the client,\nthe tenant scoping, the transactions and the uniqueness conflicts, and answers\nthe `DiscountStore` port. `toTargetRows` / `fromTargetRows` do the fold between\nthe id arrays this package speaks and the by-value rows the schema stores, so a\nstore stays persistence and nothing else — that mapping is exactly where a\ndropped `slotId` produces a combo whose slots have silently merged.\n\n**Every sentence a human reads.** `DiscountRejectionCopy` (buyer-facing) and\n`DiscountsServerCopy` (operator-facing) are required config with **no\ndefaults** — `createApiDiscounts` throws at construction naming every missing\nkey. pt-BR packs ship as named exports a host passes by hand, so choosing a\nlanguage is a line in the host's diff rather than a silence.\n\n**Who is calling, and whether the plan allows it.** The actor arrives already\nresolved (`{ clientId }`); authentication, tenant resolution, RBAC tiers and\nplan gates stay where they belong.\n\n**Who is buying for the first time.** A rule saved with `firstOrderOnly`\n(FUT-2825) applies only when the host sets `buyerIsFirstTime: true` on it —\nthis package never reads an order, so \"has this buyer bought here before\" is\nthe host's answer, the way `buyerUsageCount` already is. Unknown (`null` or\nomitted: an anonymous cart, a cached menu) is NOT first-time, on purpose: a\ndiscount the cart shows and checkout then takes back is worse than one that\nappears once the buyer is known. So a first-purchase rule is never badged or\nteased on a catalog card, and two concurrent first orders by one buyer can both\npass, as with `perBuyerLimit`. Combos keep their own rules and never carry it.\n\n**The words on a pill.** The validity filter a promotions grid usually wants —\nrunning / scheduled / ended — compares two nullable columns against \"now\",\nwhich no `filterableField` expresses, and its values are words a host chose. So\na host extends the advertised list query and reads its own key back out in its\nstore. See `DiscountListInput`.\n","readmeFilename":"README.md"}