prism0x2a 0.6.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +4 -0
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +24 -0
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/routes-manifest.json +24 -0
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/amber/capabilities/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/amber/capabilities/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/amber/capabilities.html +1 -1
- package/.next/standalone/.next/server/app/amber/capabilities.rsc +3 -3
- package/.next/standalone/.next/server/app/amber/capabilities.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/amber/capabilities.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/amber/capabilities.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/amber/capabilities.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/amber/capabilities.segments/amber/capabilities/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/amber/capabilities.segments/amber/capabilities.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/amber/capabilities.segments/amber.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/amber/coverage/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/amber/coverage.html +1 -1
- package/.next/standalone/.next/server/app/amber/coverage.rsc +3 -3
- package/.next/standalone/.next/server/app/amber/coverage.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/amber/coverage.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/amber/coverage.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/amber/coverage.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/amber/coverage.segments/amber/coverage/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/amber/coverage.segments/amber/coverage.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/amber/coverage.segments/amber.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/amber/files/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/amber/files.html +1 -1
- package/.next/standalone/.next/server/app/amber/files.rsc +3 -3
- package/.next/standalone/.next/server/app/amber/files.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/amber/files.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/amber/files.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/amber/files.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/amber/files.segments/amber/files/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/amber/files.segments/amber/files.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/amber/files.segments/amber.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/amber/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/amber.html +1 -1
- package/.next/standalone/.next/server/app/amber.rsc +3 -3
- package/.next/standalone/.next/server/app/amber.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/amber.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/amber.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/amber.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/amber.segments/amber/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/amber.segments/amber.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/amber/capabilities/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/amber/capabilities/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/amber/capabilities/route.js +1 -1
- package/.next/standalone/.next/server/app/api/amber/capabilities/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/amber/files/route.js +1 -1
- package/.next/standalone/.next/server/app/api/amber/files/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/amber/registry/route.js +1 -1
- package/.next/standalone/.next/server/app/api/amber/registry/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/amber/scan/route.js +1 -1
- package/.next/standalone/.next/server/app/api/amber/scan/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/blue/recommendations/route.js +1 -1
- package/.next/standalone/.next/server/app/api/blue/recommendations/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/blue/scan/route.js +4 -4
- package/.next/standalone/.next/server/app/api/blue/scan/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/blue/sessions/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/blue/sessions/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/config/test/route.js +1 -1
- package/.next/standalone/.next/server/app/api/config/test/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/green/cross-layer/route.js +1 -1
- package/.next/standalone/.next/server/app/api/green/cross-layer/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/green/insights/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/green/insights/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/green/insights/route.js +1 -1
- package/.next/standalone/.next/server/app/api/green/insights/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/green/queries/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/green/queries/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/green/queries/[id]/run/route.js +1 -1
- package/.next/standalone/.next/server/app/api/green/queries/[id]/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/green/queries/route.js +1 -1
- package/.next/standalone/.next/server/app/api/green/queries/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/license/activate/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/license/activate/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/license/activate/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/license/activate/route.js +6 -0
- package/.next/standalone/.next/server/app/api/license/activate/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/license/activate/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/license/activate/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/license/deactivate/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/license/deactivate/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/license/deactivate/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/license/deactivate/route.js +6 -0
- package/.next/standalone/.next/server/app/api/license/deactivate/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/license/deactivate/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/license/deactivate/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/license/route.js +1 -1
- package/.next/standalone/.next/server/app/api/license/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/license/validate/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/license/validate/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/license/validate/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/license/validate/route.js +6 -0
- package/.next/standalone/.next/server/app/api/license/validate/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/license/validate/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/license/validate/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/monorepo/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/scan/route.js +1 -1
- package/.next/standalone/.next/server/app/api/scan/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/blue/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/blue/recommendations/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/blue/recommendations/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/blue/recommendations.html +1 -1
- package/.next/standalone/.next/server/app/blue/recommendations.rsc +3 -3
- package/.next/standalone/.next/server/app/blue/recommendations.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/blue/recommendations.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/blue/recommendations.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/blue/recommendations.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/blue/recommendations.segments/blue/recommendations/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/blue/recommendations.segments/blue/recommendations.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/blue/recommendations.segments/blue.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/blue/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/blue/sessions/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/blue/sessions.html +1 -1
- package/.next/standalone/.next/server/app/blue/sessions.rsc +3 -3
- package/.next/standalone/.next/server/app/blue/sessions.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/blue/sessions.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/blue/sessions.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/blue/sessions.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/blue/sessions.segments/blue/sessions/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/blue/sessions.segments/blue/sessions.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/blue/sessions.segments/blue.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/blue.html +1 -1
- package/.next/standalone/.next/server/app/blue.rsc +3 -3
- package/.next/standalone/.next/server/app/blue.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/blue.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/blue.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/blue.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/blue.segments/blue/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/blue.segments/blue.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/green/cross-layer/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/green/cross-layer.html +1 -1
- package/.next/standalone/.next/server/app/green/cross-layer.rsc +3 -3
- package/.next/standalone/.next/server/app/green/cross-layer.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/green/cross-layer.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/green/cross-layer.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/green/cross-layer.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/green/cross-layer.segments/green/cross-layer/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/green/cross-layer.segments/green/cross-layer.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/green/cross-layer.segments/green.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/green/insights/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/green/insights/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/green/insights.html +1 -1
- package/.next/standalone/.next/server/app/green/insights.rsc +3 -3
- package/.next/standalone/.next/server/app/green/insights.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/green/insights.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/green/insights.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/green/insights.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/green/insights.segments/green/insights/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/green/insights.segments/green/insights.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/green/insights.segments/green.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/green/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/green/queries/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/green/queries/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/green/queries.html +1 -1
- package/.next/standalone/.next/server/app/green/queries.rsc +3 -3
- package/.next/standalone/.next/server/app/green/queries.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/green/queries.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/green/queries.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/green/queries.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/green/queries.segments/green/queries/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/green/queries.segments/green/queries.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/green/queries.segments/green.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/green.html +1 -1
- package/.next/standalone/.next/server/app/green.rsc +3 -3
- package/.next/standalone/.next/server/app/green.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/green.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/green.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/green.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/green.segments/green/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/green.segments/green.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/pricing/page/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/pricing/page/build-manifest.json +16 -0
- package/.next/standalone/.next/server/app/pricing/page/next-font-manifest.json +10 -0
- package/.next/standalone/.next/server/app/pricing/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/.next/server/app/pricing/page/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/pricing/page.js +14 -0
- package/.next/standalone/.next/server/app/pricing/page.js.map +5 -0
- package/.next/standalone/.next/server/app/pricing/page.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/pricing/page_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/pricing.html +1 -0
- package/.next/standalone/.next/server/app/pricing.meta +15 -0
- package/.next/standalone/.next/server/app/pricing.rsc +21 -0
- package/.next/standalone/.next/server/app/pricing.segments/_full.segment.rsc +21 -0
- package/.next/standalone/.next/server/app/pricing.segments/_head.segment.rsc +6 -0
- package/.next/standalone/.next/server/app/pricing.segments/_index.segment.rsc +5 -0
- package/.next/standalone/.next/server/app/pricing.segments/_tree.segment.rsc +3 -0
- package/.next/standalone/.next/server/app/pricing.segments/pricing/__PAGE__.segment.rsc +9 -0
- package/.next/standalone/.next/server/app/pricing.segments/pricing.segment.rsc +5 -0
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings.html +2 -2
- package/.next/standalone/.next/server/app/settings.rsc +3 -3
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/settings.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +4 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0xv4rrt._.js → [root-of-the-server]__04187lj._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0o~b3pl._.js → [root-of-the-server]__057dm7o._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__08rxe0c._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__09l4tge._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-m3qqb._.js → [root-of-the-server]__09snvkf._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0m54e40._.js → [root-of-the-server]__0ae2a50._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d_1-q9._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0sqgu0x._.js → [root-of-the-server]__0e-cuw5._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0z3su._._.js → [root-of-the-server]__0ei452y._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0j4r.sn._.js → [root-of-the-server]__0fxxbt1._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0~pq-2q._.js → [root-of-the-server]__0jwdt56._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0idrx7n._.js → [root-of-the-server]__0kgqm5d._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0lut4-l._.js → [root-of-the-server]__0m_11-d._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__13u~wki._.js → [root-of-the-server]__0mh73gz._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0p0b_67._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0i7owf4._.js → [root-of-the-server]__0s130_c._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0skt4tx._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0xbagnl._.js → [root-of-the-server]__0v3_gr6._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0~f~0tr._.js → [root-of-the-server]__119q6r9._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-61.35._.js → [root-of-the-server]__12glosw._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__13.5op0._.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_license_activate_route_actions_0jh6t~s.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_license_deactivate_route_actions_0tmpd4n.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_license_validate_route_actions_0lhsct4.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0es1n-0._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0a2egyj._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_pricing_page_actions_0gvv0qw.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_amber_capabilities_[id]_page_tsx_12~.miw._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_amber_capabilities_page_tsx_0.t9mpr._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_amber_coverage_page_tsx_01yia_n._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_amber_files_page_tsx_00pdkc~._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_amber_page_tsx_0ihx6zl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_blue_page_tsx_0zg5eg-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_blue_recommendations_[id]_page_tsx_10zzlnx._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_blue_recommendations_page_tsx_08w-50l._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_blue_sessions_[id]_page_tsx_0_~w1xj._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_blue_sessions_page_tsx_0kbuf.c._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_green_cross-layer_page_tsx_0a80o-~._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_green_insights_[id]_page_tsx_0se6uk1._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_green_insights_page_tsx_04g8_lf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_green_page_tsx_10g2g6w._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_green_queries_[id]_page_tsx_0hg.qma._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_green_queries_page_tsx_0.p0r7z._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_pricing_page_tsx_0uxncj9._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_settings_page_tsx_0~q~t-v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_09d0h78.js +4 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/next-font-manifest.js +1 -1
- package/.next/standalone/.next/server/next-font-manifest.json +3 -0
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/static/chunks/0-nogwaahf00r.js +1 -0
- package/.next/standalone/.next/static/chunks/0-ozct-0~_~y0.js +1 -0
- package/.next/standalone/.next/static/chunks/0043va.q9c120.js +1 -0
- package/.next/standalone/.next/static/chunks/0062k_6y6ie8k.js +1 -0
- package/.next/standalone/.next/static/chunks/01x6uo5zvt23j.js +1 -0
- package/.next/standalone/.next/static/chunks/03f4g06bhu.q-.js +1 -0
- package/.next/standalone/.next/static/chunks/06ciexs8mfsoh.js +1 -0
- package/.next/standalone/.next/static/chunks/06lbhw~p794rn.js +1 -0
- package/.next/standalone/.next/static/chunks/0921_~z_rxr9h.js +1 -0
- package/.next/standalone/.next/static/chunks/0_uy4bqezjrpu.js +1 -0
- package/.next/standalone/.next/static/chunks/0aqr1e8db8tr2.js +1 -0
- package/.next/standalone/.next/static/chunks/0gtjarbzj_b0s.js +1 -0
- package/.next/standalone/.next/static/chunks/0i826dpus7xfj.js +1 -0
- package/.next/standalone/.next/static/chunks/0l.f7bl92nc~2.css +2 -0
- package/.next/standalone/.next/static/chunks/0mtsra7dpsxy_.js +1 -0
- package/.next/standalone/.next/static/chunks/0nc15zt~yfrj3.js +1 -0
- package/.next/standalone/.next/static/chunks/0ptkiyk6p_hgq.js +1 -0
- package/.next/standalone/.next/static/chunks/0vsoyv8nkj.rx.js +1 -0
- package/.next/standalone/.next/static/chunks/0zpu7b02c6huw.js +1 -0
- package/.next/standalone/.next/static/chunks/10ibvf69namr1.js +1 -0
- package/.next/standalone/.next/static/chunks/1655x4_8fq5li.js +1 -0
- package/.next/standalone/.next/static/static/chunks/0-nogwaahf00r.js +1 -0
- package/.next/standalone/.next/static/static/chunks/0-ozct-0~_~y0.js +1 -0
- package/.next/standalone/.next/static/static/chunks/0043va.q9c120.js +1 -0
- package/.next/standalone/.next/static/static/chunks/0062k_6y6ie8k.js +1 -0
- package/.next/standalone/.next/static/static/chunks/01x6uo5zvt23j.js +1 -0
- package/.next/standalone/.next/static/static/chunks/03f4g06bhu.q-.js +1 -0
- package/.next/standalone/.next/static/static/chunks/06ciexs8mfsoh.js +1 -0
- package/.next/standalone/.next/static/static/chunks/06lbhw~p794rn.js +1 -0
- package/.next/standalone/.next/static/static/chunks/0921_~z_rxr9h.js +1 -0
- package/.next/standalone/.next/static/static/chunks/0_uy4bqezjrpu.js +1 -0
- package/.next/standalone/.next/static/static/chunks/0aqr1e8db8tr2.js +1 -0
- package/.next/standalone/.next/static/static/chunks/0gtjarbzj_b0s.js +1 -0
- package/.next/standalone/.next/static/static/chunks/0i826dpus7xfj.js +1 -0
- package/.next/standalone/.next/static/static/chunks/0l.f7bl92nc~2.css +2 -0
- package/.next/standalone/.next/static/static/chunks/0mtsra7dpsxy_.js +1 -0
- package/.next/standalone/.next/static/static/chunks/0nc15zt~yfrj3.js +1 -0
- package/.next/standalone/.next/static/static/chunks/0ptkiyk6p_hgq.js +1 -0
- package/.next/standalone/.next/static/static/chunks/0vsoyv8nkj.rx.js +1 -0
- package/.next/standalone/.next/static/static/chunks/0zpu7b02c6huw.js +1 -0
- package/.next/standalone/.next/static/static/chunks/10ibvf69namr1.js +1 -0
- package/.next/standalone/.next/static/static/chunks/1655x4_8fq5li.js +1 -0
- package/.next/standalone/CHANGELOG.md +78 -0
- package/.next/standalone/app/api/blue/scan/route.ts +12 -0
- package/.next/standalone/app/api/license/activate/route.ts +41 -0
- package/.next/standalone/app/api/license/deactivate/route.ts +22 -0
- package/.next/standalone/app/api/license/route.ts +23 -3
- package/.next/standalone/app/api/license/validate/route.ts +23 -0
- package/.next/standalone/app/pricing/page.tsx +289 -0
- package/.next/standalone/app/settings/page.tsx +180 -59
- package/.next/standalone/components/PrismDock.tsx +1 -0
- package/.next/standalone/components/pricing/BillingToggle.tsx +53 -0
- package/.next/standalone/components/pricing/FeatureList.tsx +19 -0
- package/.next/standalone/components/pricing/PricingCard.tsx +120 -0
- package/.next/standalone/docs/strategy/monetization-thinking.md +164 -0
- package/.next/standalone/lib/prism/features.shared.ts +15 -5
- package/.next/standalone/lib/prism/features.ts +9 -5
- package/.next/standalone/lib/prism/hooks/useLicense.ts +26 -2
- package/.next/standalone/lib/prism/lemonsqueezy/client.ts +162 -0
- package/.next/standalone/lib/prism/lemonsqueezy/tierMapping.ts +26 -0
- package/.next/standalone/lib/prism/license/lemonsqueezy.ts +142 -0
- package/.next/standalone/lib/prism/license/store.ts +17 -8
- package/.next/standalone/lib/prism/license/types.ts +35 -11
- package/.next/standalone/lib/prism/license/validate.ts +40 -11
- package/.next/standalone/package.json +2 -2
- package/.next/standalone/server.js +1 -1
- package/.next/standalone/tsconfig.tsbuildinfo +1 -1
- package/.next/static/chunks/0-nogwaahf00r.js +1 -0
- package/.next/static/chunks/0-ozct-0~_~y0.js +1 -0
- package/.next/static/chunks/0043va.q9c120.js +1 -0
- package/.next/static/chunks/0062k_6y6ie8k.js +1 -0
- package/.next/static/chunks/01x6uo5zvt23j.js +1 -0
- package/.next/static/chunks/03f4g06bhu.q-.js +1 -0
- package/.next/static/chunks/06ciexs8mfsoh.js +1 -0
- package/.next/static/chunks/06lbhw~p794rn.js +1 -0
- package/.next/static/chunks/0921_~z_rxr9h.js +1 -0
- package/.next/static/chunks/0_uy4bqezjrpu.js +1 -0
- package/.next/static/chunks/0aqr1e8db8tr2.js +1 -0
- package/.next/static/chunks/0gtjarbzj_b0s.js +1 -0
- package/.next/static/chunks/0i826dpus7xfj.js +1 -0
- package/.next/static/chunks/0l.f7bl92nc~2.css +2 -0
- package/.next/static/chunks/0mtsra7dpsxy_.js +1 -0
- package/.next/static/chunks/0nc15zt~yfrj3.js +1 -0
- package/.next/static/chunks/0ptkiyk6p_hgq.js +1 -0
- package/.next/static/chunks/0vsoyv8nkj.rx.js +1 -0
- package/.next/static/chunks/0zpu7b02c6huw.js +1 -0
- package/.next/static/chunks/10ibvf69namr1.js +1 -0
- package/.next/static/chunks/1655x4_8fq5li.js +1 -0
- package/CHANGELOG.md +78 -0
- package/package.json +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0c4z._.._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0tsv3gt._.js +0 -3
- package/.next/standalone/.next/static/chunks/06w_r5ib1et20.css +0 -2
- package/.next/standalone/.next/static/chunks/07c3wweseos.8.js +0 -1
- package/.next/standalone/.next/static/chunks/0_mxo3sdn-obc.js +0 -1
- package/.next/standalone/.next/static/chunks/0_r6ls5m8p~ft.js +0 -1
- package/.next/standalone/.next/static/chunks/0amsn1mknilcs.js +0 -1
- package/.next/standalone/.next/static/chunks/0ca9m7vrn2t7n.js +0 -1
- package/.next/standalone/.next/static/chunks/0c~l5bjb.rw_-.js +0 -1
- package/.next/standalone/.next/static/chunks/0dpf4si7n7imd.js +0 -1
- package/.next/standalone/.next/static/chunks/0du4cx1~k7d83.js +0 -1
- package/.next/standalone/.next/static/chunks/0g6lo0qyyyd8q.js +0 -1
- package/.next/standalone/.next/static/chunks/0lmcd5yqytd7q.js +0 -1
- package/.next/standalone/.next/static/chunks/0mlj0bhfz2s6h.js +0 -1
- package/.next/standalone/.next/static/chunks/0nu3srr5njzka.js +0 -1
- package/.next/standalone/.next/static/chunks/0qsy_4~44nzw1.js +0 -1
- package/.next/standalone/.next/static/chunks/1006uokkrcoe4.js +0 -1
- package/.next/standalone/.next/static/chunks/105mo4ttns4ro.js +0 -1
- package/.next/standalone/.next/static/chunks/10q-0rneqwysg.js +0 -1
- package/.next/standalone/.next/static/chunks/12kh0-iq.4m3c.js +0 -1
- package/.next/standalone/.next/static/chunks/13_6yxs82pu_4.js +0 -1
- package/.next/standalone/.next/static/chunks/16btub.0e2sr1.js +0 -1
- package/.next/standalone/.next/static/static/chunks/06w_r5ib1et20.css +0 -2
- package/.next/standalone/.next/static/static/chunks/07c3wweseos.8.js +0 -1
- package/.next/standalone/.next/static/static/chunks/0_mxo3sdn-obc.js +0 -1
- package/.next/standalone/.next/static/static/chunks/0_r6ls5m8p~ft.js +0 -1
- package/.next/standalone/.next/static/static/chunks/0amsn1mknilcs.js +0 -1
- package/.next/standalone/.next/static/static/chunks/0ca9m7vrn2t7n.js +0 -1
- package/.next/standalone/.next/static/static/chunks/0c~l5bjb.rw_-.js +0 -1
- package/.next/standalone/.next/static/static/chunks/0dpf4si7n7imd.js +0 -1
- package/.next/standalone/.next/static/static/chunks/0du4cx1~k7d83.js +0 -1
- package/.next/standalone/.next/static/static/chunks/0g6lo0qyyyd8q.js +0 -1
- package/.next/standalone/.next/static/static/chunks/0lmcd5yqytd7q.js +0 -1
- package/.next/standalone/.next/static/static/chunks/0mlj0bhfz2s6h.js +0 -1
- package/.next/standalone/.next/static/static/chunks/0nu3srr5njzka.js +0 -1
- package/.next/standalone/.next/static/static/chunks/0qsy_4~44nzw1.js +0 -1
- package/.next/standalone/.next/static/static/chunks/1006uokkrcoe4.js +0 -1
- package/.next/standalone/.next/static/static/chunks/105mo4ttns4ro.js +0 -1
- package/.next/standalone/.next/static/static/chunks/10q-0rneqwysg.js +0 -1
- package/.next/standalone/.next/static/static/chunks/12kh0-iq.4m3c.js +0 -1
- package/.next/standalone/.next/static/static/chunks/13_6yxs82pu_4.js +0 -1
- package/.next/standalone/.next/static/static/chunks/16btub.0e2sr1.js +0 -1
- package/.next/static/chunks/06w_r5ib1et20.css +0 -2
- package/.next/static/chunks/07c3wweseos.8.js +0 -1
- package/.next/static/chunks/0_mxo3sdn-obc.js +0 -1
- package/.next/static/chunks/0_r6ls5m8p~ft.js +0 -1
- package/.next/static/chunks/0amsn1mknilcs.js +0 -1
- package/.next/static/chunks/0ca9m7vrn2t7n.js +0 -1
- package/.next/static/chunks/0c~l5bjb.rw_-.js +0 -1
- package/.next/static/chunks/0dpf4si7n7imd.js +0 -1
- package/.next/static/chunks/0du4cx1~k7d83.js +0 -1
- package/.next/static/chunks/0g6lo0qyyyd8q.js +0 -1
- package/.next/static/chunks/0lmcd5yqytd7q.js +0 -1
- package/.next/static/chunks/0mlj0bhfz2s6h.js +0 -1
- package/.next/static/chunks/0nu3srr5njzka.js +0 -1
- package/.next/static/chunks/0qsy_4~44nzw1.js +0 -1
- package/.next/static/chunks/1006uokkrcoe4.js +0 -1
- package/.next/static/chunks/105mo4ttns4ro.js +0 -1
- package/.next/static/chunks/10q-0rneqwysg.js +0 -1
- package/.next/static/chunks/12kh0-iq.4m3c.js +0 -1
- package/.next/static/chunks/13_6yxs82pu_4.js +0 -1
- package/.next/static/chunks/16btub.0e2sr1.js +0 -1
- /package/.next/standalone/.next/static/{pxEf_JFJYiGcbQWz1fBY7 → W2AhxwlOVhsTt-b4T3Mjc}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{pxEf_JFJYiGcbQWz1fBY7 → W2AhxwlOVhsTt-b4T3Mjc}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{pxEf_JFJYiGcbQWz1fBY7 → W2AhxwlOVhsTt-b4T3Mjc}/_ssgManifest.js +0 -0
- /package/.next/standalone/.next/static/static/{pxEf_JFJYiGcbQWz1fBY7 → W2AhxwlOVhsTt-b4T3Mjc}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/static/{pxEf_JFJYiGcbQWz1fBY7 → W2AhxwlOVhsTt-b4T3Mjc}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/static/{pxEf_JFJYiGcbQWz1fBY7 → W2AhxwlOVhsTt-b4T3Mjc}/_ssgManifest.js +0 -0
- /package/.next/static/{pxEf_JFJYiGcbQWz1fBY7 → W2AhxwlOVhsTt-b4T3Mjc}/_buildManifest.js +0 -0
- /package/.next/static/{pxEf_JFJYiGcbQWz1fBY7 → W2AhxwlOVhsTt-b4T3Mjc}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{pxEf_JFJYiGcbQWz1fBY7 → W2AhxwlOVhsTt-b4T3Mjc}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
interface BillingToggleProps {
|
|
4
|
+
yearly: boolean;
|
|
5
|
+
onChange: (yearly: boolean) => void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function BillingToggle({ yearly, onChange }: BillingToggleProps) {
|
|
9
|
+
return (
|
|
10
|
+
<div className="flex items-center gap-3">
|
|
11
|
+
<button
|
|
12
|
+
onClick={() => onChange(false)}
|
|
13
|
+
className="text-sm font-medium transition-colors"
|
|
14
|
+
style={{ color: yearly ? "rgba(148,163,184,0.6)" : "rgb(226,232,240)" }}
|
|
15
|
+
>
|
|
16
|
+
Monthly
|
|
17
|
+
</button>
|
|
18
|
+
|
|
19
|
+
<button
|
|
20
|
+
onClick={() => onChange(!yearly)}
|
|
21
|
+
className="relative w-11 h-6 rounded-full transition-colors"
|
|
22
|
+
style={{
|
|
23
|
+
background: yearly ? "rgb(99,102,241)" : "rgba(51,65,85,0.6)",
|
|
24
|
+
border: "1px solid rgba(71,85,105,0.5)",
|
|
25
|
+
}}
|
|
26
|
+
aria-label="Toggle billing period"
|
|
27
|
+
>
|
|
28
|
+
<span
|
|
29
|
+
className="absolute top-0.5 w-5 h-5 rounded-full transition-transform"
|
|
30
|
+
style={{
|
|
31
|
+
left: "2px",
|
|
32
|
+
background: "rgb(226,232,240)",
|
|
33
|
+
transform: yearly ? "translateX(20px)" : "translateX(0)",
|
|
34
|
+
}}
|
|
35
|
+
/>
|
|
36
|
+
</button>
|
|
37
|
+
|
|
38
|
+
<button
|
|
39
|
+
onClick={() => onChange(true)}
|
|
40
|
+
className="text-sm font-medium transition-colors flex items-center gap-1.5"
|
|
41
|
+
style={{ color: yearly ? "rgb(226,232,240)" : "rgba(148,163,184,0.6)" }}
|
|
42
|
+
>
|
|
43
|
+
Yearly
|
|
44
|
+
<span
|
|
45
|
+
className="text-xs px-1.5 py-0.5 rounded-full font-semibold"
|
|
46
|
+
style={{ background: "rgba(52,211,153,0.15)", color: "rgb(52,211,153)" }}
|
|
47
|
+
>
|
|
48
|
+
−17%
|
|
49
|
+
</span>
|
|
50
|
+
</button>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface FeatureListProps {
|
|
2
|
+
features: string[];
|
|
3
|
+
dimmed?: boolean;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function FeatureList({ features, dimmed }: FeatureListProps) {
|
|
7
|
+
return (
|
|
8
|
+
<ul className="flex flex-col gap-2">
|
|
9
|
+
{features.map((f) => (
|
|
10
|
+
<li key={f} className="flex items-start gap-2 text-sm" style={{ color: dimmed ? "rgba(148,163,184,0.5)" : "rgb(148,163,184)" }}>
|
|
11
|
+
<span className="mt-0.5 flex-shrink-0" style={{ color: dimmed ? "rgba(71,85,105,0.5)" : "rgb(71,85,105)" }}>
|
|
12
|
+
✦
|
|
13
|
+
</span>
|
|
14
|
+
{f}
|
|
15
|
+
</li>
|
|
16
|
+
))}
|
|
17
|
+
</ul>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
import { FeatureList } from "./FeatureList";
|
|
3
|
+
import type { LicenseTier } from "@/lib/prism/license/types";
|
|
4
|
+
|
|
5
|
+
export interface PricingCardProps {
|
|
6
|
+
tier: LicenseTier;
|
|
7
|
+
name: string;
|
|
8
|
+
tagline: string;
|
|
9
|
+
monthlyPrice: number;
|
|
10
|
+
yearlyMonthlyPrice: number;
|
|
11
|
+
yearly: boolean;
|
|
12
|
+
unit?: string;
|
|
13
|
+
checkoutUrl: string | null;
|
|
14
|
+
features: string[];
|
|
15
|
+
highlighted?: boolean;
|
|
16
|
+
isCurrent?: boolean;
|
|
17
|
+
accentColor: string;
|
|
18
|
+
accentBg: string;
|
|
19
|
+
accentBorder: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function PricingCard({
|
|
23
|
+
name,
|
|
24
|
+
tagline,
|
|
25
|
+
monthlyPrice,
|
|
26
|
+
yearlyMonthlyPrice,
|
|
27
|
+
yearly,
|
|
28
|
+
unit,
|
|
29
|
+
checkoutUrl,
|
|
30
|
+
features,
|
|
31
|
+
highlighted,
|
|
32
|
+
isCurrent,
|
|
33
|
+
accentColor,
|
|
34
|
+
accentBg,
|
|
35
|
+
accentBorder,
|
|
36
|
+
}: PricingCardProps) {
|
|
37
|
+
const displayPrice = yearly ? yearlyMonthlyPrice : monthlyPrice;
|
|
38
|
+
const isFree = monthlyPrice === 0;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div
|
|
42
|
+
className="relative flex flex-col rounded-2xl p-6 gap-5"
|
|
43
|
+
style={{
|
|
44
|
+
background: highlighted ? "var(--surface-elevated)" : "var(--surface-panel)",
|
|
45
|
+
border: highlighted ? `1px solid ${accentBorder}` : "1px solid var(--border-default)",
|
|
46
|
+
boxShadow: highlighted
|
|
47
|
+
? `0 0 0 1px ${accentBorder}, 0 4px 24px rgba(15,23,42,.2), inset 0 1px 0 rgba(255,255,255,.06)`
|
|
48
|
+
: "0 1px 2px rgba(15,23,42,.05), 0 4px 16px rgba(15,23,42,.08), inset 0 1px 0 rgba(255,255,255,.04)",
|
|
49
|
+
}}
|
|
50
|
+
>
|
|
51
|
+
{highlighted && (
|
|
52
|
+
<div
|
|
53
|
+
className="absolute -top-px left-6 right-6 h-px rounded-full"
|
|
54
|
+
style={{ background: `linear-gradient(90deg, transparent, ${accentColor}, transparent)` }}
|
|
55
|
+
/>
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
{isCurrent && (
|
|
59
|
+
<span
|
|
60
|
+
className="absolute top-4 right-4 text-xs px-2 py-0.5 rounded-full font-semibold"
|
|
61
|
+
style={{ background: accentBg, color: accentColor, border: `1px solid ${accentBorder}` }}
|
|
62
|
+
>
|
|
63
|
+
Current plan
|
|
64
|
+
</span>
|
|
65
|
+
)}
|
|
66
|
+
|
|
67
|
+
{/* Header */}
|
|
68
|
+
<div className="flex flex-col gap-1">
|
|
69
|
+
<h3 className="text-base font-semibold" style={{ color: accentColor }}>{name}</h3>
|
|
70
|
+
<p className="text-xs text-slate-500">{tagline}</p>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
{/* Price */}
|
|
74
|
+
<div className="flex items-end gap-1.5">
|
|
75
|
+
{isFree ? (
|
|
76
|
+
<span className="text-3xl font-bold text-slate-100">Free</span>
|
|
77
|
+
) : (
|
|
78
|
+
<>
|
|
79
|
+
<span className="text-3xl font-bold text-slate-100">€{displayPrice}</span>
|
|
80
|
+
<span className="text-sm text-slate-500 mb-1">/mo{unit ? ` /${unit}` : ""}</span>
|
|
81
|
+
</>
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
{!isFree && yearly && (
|
|
85
|
+
<p className="text-xs text-slate-500 -mt-3">
|
|
86
|
+
billed €{displayPrice * 12}/yr — save €{(monthlyPrice - yearlyMonthlyPrice) * 12}/yr
|
|
87
|
+
</p>
|
|
88
|
+
)}
|
|
89
|
+
|
|
90
|
+
{/* CTA */}
|
|
91
|
+
{isFree ? (
|
|
92
|
+
<div
|
|
93
|
+
className="text-center py-2 rounded-lg text-sm font-semibold text-slate-500"
|
|
94
|
+
style={{ background: "rgba(51,65,85,0.2)", border: "1px solid rgba(51,65,85,0.3)" }}
|
|
95
|
+
>
|
|
96
|
+
{isCurrent ? "Your current plan" : "No payment needed"}
|
|
97
|
+
</div>
|
|
98
|
+
) : checkoutUrl ? (
|
|
99
|
+
<Link
|
|
100
|
+
href={checkoutUrl}
|
|
101
|
+
target="_blank"
|
|
102
|
+
rel="noopener noreferrer"
|
|
103
|
+
className="text-center py-2.5 rounded-lg text-sm font-semibold transition-all"
|
|
104
|
+
style={{
|
|
105
|
+
background: isCurrent ? "transparent" : accentBg,
|
|
106
|
+
color: isCurrent ? accentColor : accentColor,
|
|
107
|
+
border: `1px solid ${accentBorder}`,
|
|
108
|
+
}}
|
|
109
|
+
>
|
|
110
|
+
{isCurrent ? "Manage subscription →" : "Get started →"}
|
|
111
|
+
</Link>
|
|
112
|
+
) : null}
|
|
113
|
+
|
|
114
|
+
{/* Features */}
|
|
115
|
+
<div className="pt-1 border-t" style={{ borderColor: "var(--border-default)" }}>
|
|
116
|
+
<FeatureList features={features} />
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
@@ -593,3 +593,167 @@ Free-trial-limit is per-installation but easily reset by uninstalling. Mitigatio
|
|
|
593
593
|
- Pro tier removes watermark
|
|
594
594
|
|
|
595
595
|
Watermark approach is friction-light, doesn't disable feature, drives conversion via embarrassment-factor.
|
|
596
|
+
|
|
597
|
+
---
|
|
598
|
+
|
|
599
|
+
## FINAL LOCK 2026-05-18
|
|
600
|
+
|
|
601
|
+
After multiple iterations, the monetization decisions are locked for v0.7.0 implementation.
|
|
602
|
+
|
|
603
|
+
### Pricing — LOCKED
|
|
604
|
+
|
|
605
|
+
| Tier | Price | Annual | Includes |
|
|
606
|
+
|---|---|---|---|
|
|
607
|
+
| **Free** | $0 | $0 | Up to 50k LOC/project, static analysis only, no LLM, watermark, 60-day update delay |
|
|
608
|
+
| **Pro (prism0x2A)** | $39/month | $390/year (save 17%) | Unlimited LOC, monorepo, LLM, no watermark, immediate updates |
|
|
609
|
+
| **Team (prism0x2A)** | $99/dev/month | $990/dev/year (save 17%) | Pro + shared state, CI, team dashboard |
|
|
610
|
+
| **Pro (forge0x2B)** | $29/month | $290/year (save 17%) | Unlimited generations, all asset types |
|
|
611
|
+
| **Spectrum 0x55 Bundle** | $49/month | $490/year (save 17%) | prism0x2A Pro + forge0x2B Pro (save $19/m vs separate) |
|
|
612
|
+
| **Enterprise** | Custom | — | Self-hosted, SLA, revenue-share for $10M+ ARR |
|
|
613
|
+
|
|
614
|
+
**Free trial for forge0x2B:** 3 free generations per installation.
|
|
615
|
+
|
|
616
|
+
### Value Justification
|
|
617
|
+
|
|
618
|
+
- Pro tier: 1:25 to 1:50 value-to-price ratio (saves 10-19h/m at $100/h dev rate)
|
|
619
|
+
- Team tier: 1:6 to 1:12 ratio
|
|
620
|
+
- forge0x2B: 1:22 ratio
|
|
621
|
+
- Bundle: $228/year savings, anchors as "Save 28%"
|
|
622
|
+
|
|
623
|
+
### Bundle Brand — Spectrum 0x55
|
|
624
|
+
|
|
625
|
+
**Brand Name:** Spectrum
|
|
626
|
+
**Hex Identifier:** 0x55 (`0x2A + 0x2B = 0x55`)
|
|
627
|
+
**Tagline:** "0x55 — for U." (0x55 = ASCII 'U')
|
|
628
|
+
**Sub-Tagline:** "The full spectrum: prism0x2A + forge0x2B."
|
|
629
|
+
|
|
630
|
+
**Easter Egg Layer:**
|
|
631
|
+
- 0x55 binary: 01010101 — alternating bit pattern (yin-yang aesthetic)
|
|
632
|
+
- 0x55 ASCII: 'U' — "for you, the developer"
|
|
633
|
+
- Math: prism0x2A (42) + forge0x2B (43) = 85 = 0x55
|
|
634
|
+
|
|
635
|
+
**Logo Direction (when designed):**
|
|
636
|
+
- Could feature 'U' or '0x55' subtly
|
|
637
|
+
- Binary 01010101 pattern as design element
|
|
638
|
+
- Docs/marketing: "0x2A + 0x2B = 0x55"
|
|
639
|
+
|
|
640
|
+
### Payment Provider — LOCKED: Lemonsqueezy
|
|
641
|
+
|
|
642
|
+
**Reasoning:**
|
|
643
|
+
- Merchant-of-Record handles all EU VAT + US Sales Tax compliance
|
|
644
|
+
- Built-in License Keys feature (no DIY server needed)
|
|
645
|
+
- 5% + €0.50 fee vs 2.9% + €0.30 for raw Stripe
|
|
646
|
+
- 2.1% premium = trivial cost for full tax-compliance peace-of-mind
|
|
647
|
+
- Built-in customer portal, subscriptions UI, webhook system
|
|
648
|
+
|
|
649
|
+
**Lemonsqueezy products to configure:**
|
|
650
|
+
|
|
651
|
+
1. `prism0x2A Pro` (subscription, variants: monthly $39, yearly $390)
|
|
652
|
+
2. `prism0x2A Team` (subscription, per-seat, variants: monthly $99/seat, yearly $990/seat)
|
|
653
|
+
3. `forge0x2B Pro` (subscription, variants: monthly $29, yearly $290)
|
|
654
|
+
4. `Spectrum 0x55 Bundle` (subscription, variants: monthly $49, yearly $490)
|
|
655
|
+
|
|
656
|
+
All products have License-Keys enabled. License-Key API validates on activation.
|
|
657
|
+
|
|
658
|
+
### Implementation Sequence
|
|
659
|
+
|
|
660
|
+
```
|
|
661
|
+
v0.7.0 — Lemonsqueezy Integration (BIG wave)
|
|
662
|
+
- Lemonsqueezy account setup (user)
|
|
663
|
+
- Products configured (user)
|
|
664
|
+
- License-Key validation in prism0x2A Settings
|
|
665
|
+
- Online-validation + offline-cache (30 days)
|
|
666
|
+
- Customer portal integration
|
|
667
|
+
- Webhook handling for subscription events
|
|
668
|
+
|
|
669
|
+
v0.7.1 — Pricing-Page (small wave)
|
|
670
|
+
- /pricing route
|
|
671
|
+
- Tier comparison table
|
|
672
|
+
- "Subscribe" buttons → Lemonsqueezy checkout
|
|
673
|
+
- Free-trial flow for forge0x2B
|
|
674
|
+
|
|
675
|
+
v0.7.2 — Guided Tour (Phase 2.5 backlog)
|
|
676
|
+
|
|
677
|
+
v0.8.0 — Marketing Landing
|
|
678
|
+
- Hero animation (prism-spectrum metaphor)
|
|
679
|
+
- prism0x2a.dev setup
|
|
680
|
+
- References pricing-page
|
|
681
|
+
|
|
682
|
+
🚀 HN-Launch (with paid tier active)
|
|
683
|
+
|
|
684
|
+
Post-launch:
|
|
685
|
+
- forgesmith engine v0.1.0
|
|
686
|
+
- forge0x2B dashboard v0.1.0
|
|
687
|
+
- Spectrum 0x55 Bundle marketing push
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
### Closed Open-Questions (final)
|
|
691
|
+
|
|
692
|
+
| # | Question | Decision |
|
|
693
|
+
|---|---|---|
|
|
694
|
+
| 1 | Payment provider | Lemonsqueezy (MoR) |
|
|
695
|
+
| 2 | Pricing | $39/$99/$29/$49 confirmed |
|
|
696
|
+
| 3 | Bundle name | Spectrum 0x55 |
|
|
697
|
+
| 4 | Annual discount | 17% (2 months free) |
|
|
698
|
+
| 5 | Free tier limit | 50k LOC/project |
|
|
699
|
+
| 6 | Free trial (forge0x2B) | 3 generations |
|
|
700
|
+
| 7 | License validation | Lemonsqueezy License Keys API + offline cache |
|
|
701
|
+
| 8 | Customer portal | Lemonsqueezy built-in |
|
|
702
|
+
| 9 | Revenue share | Enterprise-only, $10M+ ARR negotiation |
|
|
703
|
+
|
|
704
|
+
### Anti-Patterns Avoided (final)
|
|
705
|
+
|
|
706
|
+
- ❌ Free for revenue < $X (charity model) → REJECTED
|
|
707
|
+
- ❌ Pure FOSS (no revenue capture) → REJECTED
|
|
708
|
+
- ❌ Phone-home telemetry → REJECTED
|
|
709
|
+
- ❌ Hard-disable at limits → REJECTED (degraded mode + CTA instead)
|
|
710
|
+
- ❌ Raw Stripe (tax compliance nightmare) → REJECTED in favor of Lemonsqueezy
|
|
711
|
+
|
|
712
|
+
### Status: LOCKED — Ready for v0.7.0 Implementation
|
|
713
|
+
|
|
714
|
+
---
|
|
715
|
+
|
|
716
|
+
## EUR Pricing Adjustment 2026-05-18
|
|
717
|
+
|
|
718
|
+
### Context
|
|
719
|
+
|
|
720
|
+
Lemonsqueezy store was configured with EUR as default currency (German legal entity). All pricing now expressed in EUR with same numeric values as original USD spec.
|
|
721
|
+
|
|
722
|
+
### Rationale: Same numbers, different currency
|
|
723
|
+
|
|
724
|
+
- Clean numbers (€39 / €99 / €29 / €49)
|
|
725
|
+
- Slight premium for EU market vs USD ($39 ≈ €36 at current FX)
|
|
726
|
+
- Lemonsqueezy auto-converts for non-EU customers
|
|
727
|
+
- US customers see ~$42 (FX-converted by Lemonsqueezy) — fair premium for buying from EU vendor
|
|
728
|
+
|
|
729
|
+
### Final EUR Pricing (LOCKED)
|
|
730
|
+
|
|
731
|
+
| Tier | Monthly | Yearly (17% off) |
|
|
732
|
+
|---|---|---|
|
|
733
|
+
| **Free** | €0 | — |
|
|
734
|
+
| **Pro (prism0x2A)** | €39 | €390 |
|
|
735
|
+
| **Team (prism0x2A)** | €99/dev | €990/dev |
|
|
736
|
+
| **Pro (forge0x2B)** | €29 | €290 |
|
|
737
|
+
| **Spectrum 0x55 Bundle** | €49 | €490 |
|
|
738
|
+
|
|
739
|
+
### Lemonsqueezy Product IDs (Reference)
|
|
740
|
+
|
|
741
|
+
```
|
|
742
|
+
prism0x2A Pro: 1066633
|
|
743
|
+
prism0x2A Team: 1066653
|
|
744
|
+
forge0x2B Pro: 1066668
|
|
745
|
+
Spectrum 0x55 Bundle: 1066695
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
Checkout-Variant-UUIDs:
|
|
749
|
+
```
|
|
750
|
+
prism0x2A Pro: a9097951-5d54-438c-89ef-3e093d88f6c6
|
|
751
|
+
prism0x2A Team: ddcff26c-303a-4993-a7a7-73129b150a97
|
|
752
|
+
forge0x2B Pro: 21c1146e-9fea-4c32-a4fc-c5a5e64215e7
|
|
753
|
+
Spectrum 0x55 Bundle: 64d25b45-7c4a-410d-b4aa-7504f98ab5f7
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
Marketing-copy adapts:
|
|
757
|
+
- "Save €19/month with Spectrum 0x55 Bundle" (was: "$19")
|
|
758
|
+
- "starts at €39/month" (was: "$39")
|
|
759
|
+
- Spectrum description: "0x55 = 0x2A + 0x2B (and ASCII U)" unchanged
|
|
@@ -8,17 +8,27 @@ export interface FeatureDef {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export const FEATURES = {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
// Pro-tier features
|
|
12
|
+
monorepo: { tier: "pro" as LicenseTier, label: "Monorepo support" },
|
|
13
|
+
aggregate_scan: { tier: "pro" as LicenseTier, label: "Aggregate workspace scan" },
|
|
14
|
+
cross_workspace_insights: { tier: "pro" as LicenseTier, label: "Cross-workspace insights" },
|
|
15
|
+
llm_insights: { tier: "pro" as LicenseTier, label: "AI-powered insights" },
|
|
16
|
+
custom_queries: { tier: "pro" as LicenseTier, label: "Custom GREEN queries" },
|
|
17
|
+
cross_layer_context: { tier: "pro" as LicenseTier, label: "Cross-layer context" },
|
|
18
|
+
auto_insights: { tier: "pro" as LicenseTier, label: "Auto-generated insights" },
|
|
19
|
+
// Team-tier features
|
|
20
|
+
team_sync: { tier: "team" as LicenseTier, label: "Team sync" },
|
|
21
|
+
ci_integration: { tier: "team" as LicenseTier, label: "CI integration" },
|
|
22
|
+
// forge-pro / Spectrum features
|
|
23
|
+
forge_unlimited: { tier: "forge-pro" as LicenseTier, label: "forge0x2B unlimited" },
|
|
24
|
+
forge_unlimited_generation: { tier: "forge-pro" as LicenseTier, label: "forge0x2B unlimited generation" },
|
|
15
25
|
} satisfies Record<string, FeatureDef>;
|
|
16
26
|
|
|
17
27
|
export type FeatureName = keyof typeof FEATURES;
|
|
18
28
|
|
|
19
29
|
export const FREE_TIER_LOC_LIMIT = 50_000;
|
|
20
30
|
|
|
21
|
-
/** Returns true if the feature should show a Pro badge in the UI (static, no license check). */
|
|
31
|
+
/** Returns true if the feature should show a Pro/upgrade badge in the UI (static, no license check). */
|
|
22
32
|
export function showProBadge(name: FeatureName): boolean {
|
|
23
33
|
return FEATURES[name].tier !== "free";
|
|
24
34
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { loadLicense } from "./license/store";
|
|
2
|
-
import { effectiveTier, hasFeatureTier } from "./license/validate";
|
|
3
|
-
import { FEATURES } from "./features.shared";
|
|
4
|
-
import type { LicenseTier, FeatureName } from "./features.shared";
|
|
5
|
-
|
|
2
|
+
import { effectiveTier, hasFeatureTier, hasForge } from "./license/validate";
|
|
6
3
|
export { FEATURES, FREE_TIER_LOC_LIMIT, showProBadge } from "./features.shared";
|
|
7
4
|
export type { LicenseTier, FeatureDef, FeatureName } from "./features.shared";
|
|
5
|
+
import { FEATURES } from "./features.shared";
|
|
6
|
+
import type { LicenseTier, FeatureName } from "./features.shared";
|
|
8
7
|
|
|
9
8
|
/** Returns the effective tier for the given target project. */
|
|
10
9
|
export function getCurrentTier(targetPath: string): LicenseTier {
|
|
@@ -12,9 +11,14 @@ export function getCurrentTier(targetPath: string): LicenseTier {
|
|
|
12
11
|
return effectiveTier(license);
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
/** Returns true if the license
|
|
14
|
+
/** Returns true if the license grants access to the named feature. */
|
|
16
15
|
export function isFeatureAvailable(targetPath: string, name: FeatureName): boolean {
|
|
17
16
|
const license = loadLicense(targetPath);
|
|
18
17
|
const required = FEATURES[name].tier;
|
|
19
18
|
return hasFeatureTier(license, required);
|
|
20
19
|
}
|
|
20
|
+
|
|
21
|
+
/** Returns true if forge0x2B features are enabled for this target. */
|
|
22
|
+
export function isForgeAvailable(targetPath: string): boolean {
|
|
23
|
+
return hasForge(loadLicense(targetPath));
|
|
24
|
+
}
|
|
@@ -11,13 +11,25 @@ export interface LicenseInfo {
|
|
|
11
11
|
features: string[];
|
|
12
12
|
isValid: boolean;
|
|
13
13
|
isExpired: boolean;
|
|
14
|
+
// Lemonsqueezy fields
|
|
15
|
+
licenseKey: string | null; // masked (e.g. "LS-A****-B")
|
|
16
|
+
instanceId: string | null;
|
|
17
|
+
productName: string | null;
|
|
18
|
+
subscriptionStatus: "active" | "cancelled" | "expired" | "paused" | null;
|
|
19
|
+
validatedAt: number | null;
|
|
20
|
+
revalidationDue: number | null;
|
|
21
|
+
gracePeriodEnd: number | null;
|
|
22
|
+
activationLimit: number | null;
|
|
23
|
+
activationUsage: number | null;
|
|
24
|
+
cacheStatus: "valid" | "grace" | "expired" | "no-cache";
|
|
25
|
+
daysSinceValidation: number | null;
|
|
14
26
|
}
|
|
15
27
|
|
|
16
28
|
export interface UseLicenseResult {
|
|
17
29
|
license: LicenseInfo | null;
|
|
18
30
|
ready: boolean;
|
|
19
31
|
refresh: () => void;
|
|
20
|
-
/** True when tier is pro/team/enterprise and license is valid. */
|
|
32
|
+
/** True when tier is pro/spectrum/team/enterprise and license is valid. */
|
|
21
33
|
isPro: boolean;
|
|
22
34
|
}
|
|
23
35
|
|
|
@@ -29,6 +41,17 @@ const FREE_INFO: LicenseInfo = {
|
|
|
29
41
|
features: [],
|
|
30
42
|
isValid: false,
|
|
31
43
|
isExpired: false,
|
|
44
|
+
licenseKey: null,
|
|
45
|
+
instanceId: null,
|
|
46
|
+
productName: null,
|
|
47
|
+
subscriptionStatus: null,
|
|
48
|
+
validatedAt: null,
|
|
49
|
+
revalidationDue: null,
|
|
50
|
+
gracePeriodEnd: null,
|
|
51
|
+
activationLimit: null,
|
|
52
|
+
activationUsage: null,
|
|
53
|
+
cacheStatus: "no-cache",
|
|
54
|
+
daysSinceValidation: null,
|
|
32
55
|
};
|
|
33
56
|
|
|
34
57
|
export function useLicense(tq: string): UseLicenseResult {
|
|
@@ -46,7 +69,8 @@ export function useLicense(tq: string): UseLicenseResult {
|
|
|
46
69
|
|
|
47
70
|
const refresh = useCallback(() => setTick((t) => t + 1), []);
|
|
48
71
|
|
|
49
|
-
const isPro = !!license && license.tier !== "free" && license.
|
|
72
|
+
const isPro = !!license && license.tier !== "free" && license.tier !== "forge-pro"
|
|
73
|
+
&& license.isValid && !license.isExpired && license.cacheStatus !== "expired";
|
|
50
74
|
|
|
51
75
|
return { license, ready, refresh, isPro };
|
|
52
76
|
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lemonsqueezy License Keys API — raw fetch client.
|
|
3
|
+
*
|
|
4
|
+
* License key endpoints are unauthenticated (they use the key itself, not bearer auth).
|
|
5
|
+
* The public API key (LEMONSQUEEZY_API_KEY) is for store-management endpoints only,
|
|
6
|
+
* not needed here. We keep it available for future store-level calls.
|
|
7
|
+
*
|
|
8
|
+
* Docs: https://docs.lemonsqueezy.com/api/license-keys
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const LS_BASE = "https://api.lemonsqueezy.com/v1/licenses";
|
|
12
|
+
|
|
13
|
+
// ─── Typed errors ────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
export type LsErrorCode =
|
|
16
|
+
| "invalid_key" // key not found in LS (404)
|
|
17
|
+
| "activation_limit" // max instances reached (409)
|
|
18
|
+
| "not_activated" // instance not found for validate/deactivate (404)
|
|
19
|
+
| "api_error" // LS returned unexpected error (502)
|
|
20
|
+
| "network_error"; // fetch failed entirely
|
|
21
|
+
|
|
22
|
+
export class LsApiError extends Error {
|
|
23
|
+
constructor(
|
|
24
|
+
message: string,
|
|
25
|
+
public readonly code: LsErrorCode,
|
|
26
|
+
public readonly httpStatus?: number,
|
|
27
|
+
) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = "LsApiError";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Map LS error strings to typed error codes. */
|
|
34
|
+
function classifyLsError(message: string, httpStatus: number): LsErrorCode {
|
|
35
|
+
const m = message.toLowerCase();
|
|
36
|
+
if (m.includes("does not exist") || m.includes("not found") || httpStatus === 404) return "invalid_key";
|
|
37
|
+
if (m.includes("activation limit") || m.includes("reached its activation") || httpStatus === 422) return "activation_limit";
|
|
38
|
+
if (m.includes("instance") && (m.includes("not found") || m.includes("does not exist"))) return "not_activated";
|
|
39
|
+
return "api_error";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ─── Response shapes ──────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
export interface LsLicenseKey {
|
|
45
|
+
id: number;
|
|
46
|
+
status: "active" | "inactive" | "expired" | "disabled";
|
|
47
|
+
key: string;
|
|
48
|
+
activation_limit: number | null;
|
|
49
|
+
activation_usage: number;
|
|
50
|
+
created_at: string;
|
|
51
|
+
expires_at: string | null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface LsInstance {
|
|
55
|
+
id: string;
|
|
56
|
+
name: string;
|
|
57
|
+
created_at: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface LsMeta {
|
|
61
|
+
store_id: number;
|
|
62
|
+
order_id: number;
|
|
63
|
+
order_item_id: number;
|
|
64
|
+
product_id: number;
|
|
65
|
+
product_name: string;
|
|
66
|
+
variant_id: number;
|
|
67
|
+
variant_name: string;
|
|
68
|
+
customer_id: number;
|
|
69
|
+
customer_name: string;
|
|
70
|
+
customer_email: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface LsActivateResponse {
|
|
74
|
+
activated: boolean;
|
|
75
|
+
error: string | null;
|
|
76
|
+
license_key: LsLicenseKey;
|
|
77
|
+
instance: LsInstance;
|
|
78
|
+
meta: LsMeta;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface LsValidateResponse {
|
|
82
|
+
valid: boolean;
|
|
83
|
+
error: string | null;
|
|
84
|
+
license_key: LsLicenseKey;
|
|
85
|
+
instance: LsInstance | null;
|
|
86
|
+
meta: LsMeta;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface LsDeactivateResponse {
|
|
90
|
+
deactivated: boolean;
|
|
91
|
+
error: string | null;
|
|
92
|
+
license_key: LsLicenseKey;
|
|
93
|
+
meta: LsMeta;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
async function lsPost<T>(endpoint: string, params: Record<string, string>): Promise<T> {
|
|
99
|
+
const body = new URLSearchParams(params);
|
|
100
|
+
let res: Response;
|
|
101
|
+
try {
|
|
102
|
+
res = await fetch(`${LS_BASE}/${endpoint}`, {
|
|
103
|
+
method: "POST",
|
|
104
|
+
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
105
|
+
body: body.toString(),
|
|
106
|
+
});
|
|
107
|
+
} catch (cause) {
|
|
108
|
+
throw new LsApiError(
|
|
109
|
+
`Network error reaching Lemonsqueezy API: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
110
|
+
"network_error",
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
const json = await res.json() as T & { error?: string | null };
|
|
114
|
+
return { _status: res.status, ...json } as T;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ─── API calls ───────────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
export async function lsActivate(
|
|
120
|
+
licenseKey: string,
|
|
121
|
+
instanceName: string,
|
|
122
|
+
): Promise<LsActivateResponse> {
|
|
123
|
+
const json = await lsPost<LsActivateResponse & { _status: number }>("activate", {
|
|
124
|
+
license_key: licenseKey,
|
|
125
|
+
instance_name: instanceName,
|
|
126
|
+
});
|
|
127
|
+
if (!json.activated) {
|
|
128
|
+
const msg = json.error ?? "Lemonsqueezy activation failed";
|
|
129
|
+
throw new LsApiError(msg, classifyLsError(msg, json._status), json._status);
|
|
130
|
+
}
|
|
131
|
+
return json;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function lsValidate(
|
|
135
|
+
licenseKey: string,
|
|
136
|
+
instanceId: string,
|
|
137
|
+
): Promise<LsValidateResponse> {
|
|
138
|
+
const json = await lsPost<LsValidateResponse & { _status: number }>("validate", {
|
|
139
|
+
license_key: licenseKey,
|
|
140
|
+
instance_id: instanceId,
|
|
141
|
+
});
|
|
142
|
+
if (!json.valid) {
|
|
143
|
+
const msg = json.error ?? "Lemonsqueezy validation failed";
|
|
144
|
+
throw new LsApiError(msg, classifyLsError(msg, json._status), json._status);
|
|
145
|
+
}
|
|
146
|
+
return json;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export async function lsDeactivate(
|
|
150
|
+
licenseKey: string,
|
|
151
|
+
instanceId: string,
|
|
152
|
+
): Promise<LsDeactivateResponse> {
|
|
153
|
+
const json = await lsPost<LsDeactivateResponse & { _status: number }>("deactivate", {
|
|
154
|
+
license_key: licenseKey,
|
|
155
|
+
instance_id: instanceId,
|
|
156
|
+
});
|
|
157
|
+
if (!json.deactivated) {
|
|
158
|
+
const msg = json.error ?? "Lemonsqueezy deactivation failed";
|
|
159
|
+
throw new LsApiError(msg, classifyLsError(msg, json._status), json._status);
|
|
160
|
+
}
|
|
161
|
+
return json;
|
|
162
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { LicenseTier } from "../license/types";
|
|
2
|
+
|
|
3
|
+
export class ProductNotRecognizedError extends Error {
|
|
4
|
+
constructor(productName: string) {
|
|
5
|
+
super(`Unrecognized Lemonsqueezy product: "${productName}". Add it to PRODUCT_TIER_MAP.`);
|
|
6
|
+
this.name = "ProductNotRecognizedError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const PRODUCT_TIER_MAP: Record<string, LicenseTier> = {
|
|
11
|
+
"prism0x2A Pro": "pro",
|
|
12
|
+
"prism0x2A Team": "team",
|
|
13
|
+
"forge0x2B Pro": "forge-pro",
|
|
14
|
+
"Spectrum 0x55": "spectrum",
|
|
15
|
+
"Spectrum 0x55 Bundle": "spectrum",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** Maps Lemonsqueezy product_name to internal LicenseTier. Throws if unrecognized. */
|
|
19
|
+
export function mapProductNameToTier(productName: string): LicenseTier {
|
|
20
|
+
const tier = PRODUCT_TIER_MAP[productName];
|
|
21
|
+
if (!tier) throw new ProductNotRecognizedError(productName);
|
|
22
|
+
return tier;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** All recognized product names (for validation). */
|
|
26
|
+
export const RECOGNIZED_PRODUCTS = Object.keys(PRODUCT_TIER_MAP);
|