[{"data":1,"prerenderedAt":7660},["ShallowReactive",2],{"navigation-landing-en":3,"navigation-nuxt-auto-en":180,"navigation-nuxt-protokit-en":365,"\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fbilling-en":507,"\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fbilling-surround-en":7655},[4,8,13,28,41,51,64,77,94,110,134,150,157,172],{"title":5,"path":6,"stem":7},"Overview","\u002Fdocs\u002Flanding","0.docs\u002F1.landing\u002F001.index",{"title":9,"path":10,"stem":11,"badge":12},"Built-in Features","\u002Fdocs\u002Flanding\u002Fbuilt-in-features","0.docs\u002F1.landing\u002F002.built-in-features","New",{"title":14,"path":15,"stem":16,"children":17,"icon":27},"Content Foundation","\u002Fdocs\u002Flanding\u002Fcontent","0.docs\u002F1.landing\u002F02.content\u002F1.index",[18,19,23],{"title":5,"path":15,"stem":16},{"title":20,"path":21,"stem":22},"Details","\u002Fdocs\u002Flanding\u002Fcontent\u002Fdetails","0.docs\u002F1.landing\u002F02.content\u002F2.details",{"title":24,"path":25,"stem":26},"Technical","\u002Fdocs\u002Flanding\u002Fcontent\u002Ftechnical","0.docs\u002F1.landing\u002F02.content\u002F4.technical","i-heroicons-document-text",{"title":29,"path":30,"stem":31,"children":32,"icon":40},"Regional Content","\u002Fdocs\u002Flanding\u002Fregional","0.docs\u002F1.landing\u002F03.regional\u002F1.index",[33,34,37],{"title":5,"path":30,"stem":31},{"title":20,"path":35,"stem":36},"\u002Fdocs\u002Flanding\u002Fregional\u002Fdetails","0.docs\u002F1.landing\u002F03.regional\u002F2.details",{"title":24,"path":38,"stem":39},"\u002Fdocs\u002Flanding\u002Fregional\u002Ftechnical","0.docs\u002F1.landing\u002F03.regional\u002F4.technical","i-heroicons-globe-alt",{"title":42,"path":43,"stem":44,"children":45,"icon":50},"Multi-language","\u002Fdocs\u002Flanding\u002Fmultilang","0.docs\u002F1.landing\u002F04.multilang\u002F1.index",[46,47],{"title":5,"path":43,"stem":44},{"title":20,"path":48,"stem":49},"\u002Fdocs\u002Flanding\u002Fmultilang\u002Fdetails","0.docs\u002F1.landing\u002F04.multilang\u002F2.details","i-heroicons-language",{"title":52,"path":53,"stem":54,"children":55,"icon":63},"Blog","\u002Fdocs\u002Flanding\u002Fblog","0.docs\u002F1.landing\u002F05.blog\u002F1.index",[56,57,60],{"title":5,"path":53,"stem":54},{"title":20,"path":58,"stem":59},"\u002Fdocs\u002Flanding\u002Fblog\u002Fdetails","0.docs\u002F1.landing\u002F05.blog\u002F2.details",{"title":24,"path":61,"stem":62},"\u002Fdocs\u002Flanding\u002Fblog\u002Ftechnical","0.docs\u002F1.landing\u002F05.blog\u002F4.technical","i-heroicons-pencil-square",{"title":65,"path":66,"stem":67,"children":68,"icon":76},"Documentation","\u002Fdocs\u002Flanding\u002Fdocs","0.docs\u002F1.landing\u002F06.docs\u002F1.index",[69,70,73],{"title":5,"path":66,"stem":67},{"title":20,"path":71,"stem":72},"\u002Fdocs\u002Flanding\u002Fdocs\u002Fdetails","0.docs\u002F1.landing\u002F06.docs\u002F2.details",{"title":24,"path":74,"stem":75},"\u002Fdocs\u002Flanding\u002Fdocs\u002Ftechnical","0.docs\u002F1.landing\u002F06.docs\u002F4.technical","i-heroicons-book-open",{"title":78,"path":79,"stem":80,"children":81,"icon":93},"Forms","\u002Fdocs\u002Flanding\u002Fforms","0.docs\u002F1.landing\u002F07.forms\u002F1.index",[82,83,86,90],{"title":5,"path":79,"stem":80},{"title":20,"path":84,"stem":85},"\u002Fdocs\u002Flanding\u002Fforms\u002Fdetails","0.docs\u002F1.landing\u002F07.forms\u002F2.details",{"title":87,"path":88,"stem":89},"Admin","\u002Fdocs\u002Flanding\u002Fforms\u002Fadmin","0.docs\u002F1.landing\u002F07.forms\u002F3.admin",{"title":24,"path":91,"stem":92},"\u002Fdocs\u002Flanding\u002Fforms\u002Ftechnical","0.docs\u002F1.landing\u002F07.forms\u002F4.technical","i-heroicons-clipboard-document-list",{"title":95,"path":96,"stem":97,"children":98,"icon":109},"Email","\u002Fdocs\u002Flanding\u002Femail","0.docs\u002F1.landing\u002F08.email\u002F1.index",[99,100,103,106],{"title":5,"path":96,"stem":97},{"title":20,"path":101,"stem":102},"\u002Fdocs\u002Flanding\u002Femail\u002Fdetails","0.docs\u002F1.landing\u002F08.email\u002F2.details",{"title":87,"path":104,"stem":105},"\u002Fdocs\u002Flanding\u002Femail\u002Fadmin","0.docs\u002F1.landing\u002F08.email\u002F3.admin",{"title":24,"path":107,"stem":108},"\u002Fdocs\u002Flanding\u002Femail\u002Ftechnical","0.docs\u002F1.landing\u002F08.email\u002F4.technical","i-heroicons-envelope",{"title":111,"path":112,"stem":113,"children":114,"icon":133},"Feedback Platform","\u002Fdocs\u002Flanding\u002Ffeedback","0.docs\u002F1.landing\u002F09.feedback\u002F1.index",[115,116,119,122,125,129],{"title":5,"path":112,"stem":113},{"title":20,"path":117,"stem":118},"\u002Fdocs\u002Flanding\u002Ffeedback\u002Fdetails","0.docs\u002F1.landing\u002F09.feedback\u002F2.details",{"title":87,"path":120,"stem":121},"\u002Fdocs\u002Flanding\u002Ffeedback\u002Fadmin","0.docs\u002F1.landing\u002F09.feedback\u002F3.admin",{"title":24,"path":123,"stem":124},"\u002Fdocs\u002Flanding\u002Ffeedback\u002Ftechnical","0.docs\u002F1.landing\u002F09.feedback\u002F4.technical",{"title":126,"path":127,"stem":128},"Compare vs SaaS","\u002Fdocs\u002Flanding\u002Ffeedback\u002Fcompare","0.docs\u002F1.landing\u002F09.feedback\u002F5.compare",{"title":130,"path":131,"stem":132},"FAQ","\u002Fdocs\u002Flanding\u002Ffeedback\u002Ffaq","0.docs\u002F1.landing\u002F09.feedback\u002F6.faq","i-heroicons-chat-bubble-left-right",{"title":135,"path":136,"stem":137,"children":138,"icon":149},"Storage","\u002Fdocs\u002Flanding\u002Fstorage","0.docs\u002F1.landing\u002F10.storage\u002F1.index",[139,140,143,146],{"title":5,"path":136,"stem":137},{"title":20,"path":141,"stem":142},"\u002Fdocs\u002Flanding\u002Fstorage\u002Fdetails","0.docs\u002F1.landing\u002F10.storage\u002F2.details",{"title":87,"path":144,"stem":145},"\u002Fdocs\u002Flanding\u002Fstorage\u002Fadmin","0.docs\u002F1.landing\u002F10.storage\u002F3.admin",{"title":24,"path":147,"stem":148},"\u002Fdocs\u002Flanding\u002Fstorage\u002Ftechnical","0.docs\u002F1.landing\u002F10.storage\u002F4.technical","i-heroicons-circle-stack",{"title":151,"path":152,"stem":153,"children":154,"icon":156},"Offline First","\u002Fdocs\u002Flanding\u002Foffline-first","0.docs\u002F1.landing\u002F11.offline-first\u002F1.index",[155],{"title":151,"path":152,"stem":153},"i-heroicons-users",{"title":158,"path":159,"stem":160,"children":161,"icon":156},"Yjs Sync","\u002Fdocs\u002Flanding\u002Fyjs-sync","0.docs\u002F1.landing\u002F12.yjs-sync\u002F1.index",[162,163,166,169],{"title":5,"path":159,"stem":160},{"title":20,"path":164,"stem":165},"\u002Fdocs\u002Flanding\u002Fyjs-sync\u002Fdetails","0.docs\u002F1.landing\u002F12.yjs-sync\u002F2.details",{"title":87,"path":167,"stem":168},"\u002Fdocs\u002Flanding\u002Fyjs-sync\u002Fadmin","0.docs\u002F1.landing\u002F12.yjs-sync\u002F3.admin",{"title":24,"path":170,"stem":171},"\u002Fdocs\u002Flanding\u002Fyjs-sync\u002Ftechnical","0.docs\u002F1.landing\u002F12.yjs-sync\u002F4.technical",{"title":173,"path":174,"stem":175,"children":176,"badge":178,"icon":179},"Newsletter","\u002Fdocs\u002Flanding\u002Fnewsletter","0.docs\u002F1.landing\u002F13.newsletter\u002Findex",[177],{"title":173,"path":174,"stem":175,"badge":178},"Coming Soon","i-lucide-send",[181,184,202,318,358],{"title":5,"path":182,"stem":183},"\u002Fdocs\u002Fnuxt-auto","0.docs\u002F3.nuxt-auto\u002Findex",{"title":185,"path":186,"stem":187,"children":188,"page":201},"Getting Started","\u002Fdocs\u002Fnuxt-auto\u002Fgetting-started","0.docs\u002F3.nuxt-auto\u002F1.getting-started",[189,193,197],{"title":190,"path":191,"stem":192},"Introduction","\u002Fdocs\u002Fnuxt-auto\u002Fgetting-started\u002Fintroduction","0.docs\u002F3.nuxt-auto\u002F1.getting-started\u002F1.introduction",{"title":194,"path":195,"stem":196},"Installation","\u002Fdocs\u002Fnuxt-auto\u002Fgetting-started\u002Finstallation","0.docs\u002F3.nuxt-auto\u002F1.getting-started\u002F2.installation",{"title":198,"path":199,"stem":200},"Quick Start","\u002Fdocs\u002Fnuxt-auto\u002Fgetting-started\u002Fquick-start","0.docs\u002F3.nuxt-auto\u002F1.getting-started\u002F3.quick-start",false,{"title":203,"path":204,"stem":205,"children":206,"page":201},"Auto API","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api","0.docs\u002F3.nuxt-auto\u002F2.auto-api",[207,210,214,218,222,226,230,234,238,242,246,250,254,258,262,266,270,274,278,282,286,290,294,298,302,306,310,314],{"title":185,"path":208,"stem":209},"\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fgetting-started","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F1.getting-started",{"title":211,"path":212,"stem":213},"Aggregations","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Faggregations","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F10.aggregations",{"title":215,"path":216,"stem":217},"Lifecycle Hooks","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Flifecycle-hooks","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F11.lifecycle-hooks",{"title":219,"path":220,"stem":221},"Many-to-Many (M2M) Relationships","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fm2m-relationships","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F12.m2m-relationships",{"title":223,"path":224,"stem":225},"Plugin System","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fplugin-system","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F13.plugin-system",{"title":227,"path":228,"stem":229},"Database Adapters","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fdatabase-adapters","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F14.database-adapters",{"title":231,"path":232,"stem":233},"Custom Endpoints","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fcustom-endpoints","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F15.custom-endpoints",{"title":235,"path":236,"stem":237},"Multi-Tenancy","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fmulti-tenancy","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F16.multi-tenancy",{"title":239,"path":240,"stem":241},"Schema Presets (Cross-engine)","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fschema-presets","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F17.schema-presets",{"title":243,"path":244,"stem":245},"Permissions Cookbook","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fpermissions-cookbook","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F18.permissions-cookbook",{"title":247,"path":248,"stem":249},"Billing & Plan Gating","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fbilling","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F19.billing",{"title":251,"path":252,"stem":253},"Validation","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fvalidation","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F2.validation",{"title":255,"path":256,"stem":257},"Rate Limiting","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Frate-limiting","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F20.rate-limiting",{"title":259,"path":260,"stem":261},"Request Metadata Plugin","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Frequest-metadata","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F21.request-metadata",{"title":263,"path":264,"stem":265},"Plugin Catalog","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fplugin-catalog","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F22.plugin-catalog",{"title":267,"path":268,"stem":269},"Configuration Reference","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fconfiguration","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F23.configuration",{"title":271,"path":272,"stem":273},"Handler Overrides","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fhandler-overrides","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F3.handler-overrides",{"title":275,"path":276,"stem":277},"Cloudflare D1","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fcloudflare-d1","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F30.cloudflare-d1",{"title":279,"path":280,"stem":281},"SQLite to D1 Migration","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fmigration-sqlite-d1","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F31.migration-sqlite-d1",{"title":283,"path":284,"stem":285},"Frontend Composables","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Ffrontend-composables","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F32.frontend-composables",{"title":287,"path":288,"stem":289},"Testing","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Ftesting","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F33.testing",{"title":291,"path":292,"stem":293},"Pagination","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fpagination","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F4.pagination",{"title":295,"path":296,"stem":297},"Upgrading","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fupgrading","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F40.upgrading",{"title":299,"path":300,"stem":301},"Soft Deletes","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fsoft-deletes","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F5.soft-deletes",{"title":303,"path":304,"stem":305},"Authentication & Authorization","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fauthentication-authorization","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F6.authentication-authorization",{"title":307,"path":308,"stem":309},"Better-Auth Integration","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fbetter-auth","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F7.better-auth",{"title":311,"path":312,"stem":313},"Nested Relations","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fnested-relationships","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F8.nested-relationships",{"title":315,"path":316,"stem":317},"Bulk Operations","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fbulk-operations","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F9.bulk-operations",{"title":319,"path":320,"stem":321,"children":322,"page":201},"Auto Admin","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin","0.docs\u002F3.nuxt-auto\u002F3.auto-admin",[323,326,330,334,338,342,346,350,354],{"title":185,"path":324,"stem":325},"\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fgetting-started","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F1.getting-started",{"title":327,"path":328,"stem":329},"Configuration & Theming","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fconfiguration-theming","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F2.configuration-theming",{"title":331,"path":332,"stem":333},"Resource Configuration","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fresource-configuration","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F3.resource-configuration",{"title":335,"path":336,"stem":337},"Form Fields & Widgets","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fform-fields-widgets","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F4.form-fields-widgets",{"title":339,"path":340,"stem":341},"Permissions","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fpermissions","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F5.permissions",{"title":343,"path":344,"stem":345},"Custom Pages","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fcustom-pages","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F6.custom-pages",{"title":347,"path":348,"stem":349},"M2M Relationships","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fm2m-relationships","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F7.m2m-relationships",{"title":351,"path":352,"stem":353},"Custom Actions","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fcustom-actions","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F8.custom-actions",{"title":355,"path":356,"stem":357},"Composables","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fcomposables","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F9.composables",{"title":359,"path":360,"stem":361,"children":362,"icon":364},"Claude Code Skills","\u002Fdocs\u002Fnuxt-auto\u002Fclaude-code-skills","0.docs\u002F3.nuxt-auto\u002F4.claude-code-skills\u002F1.index",[363],{"title":5,"path":360,"stem":361},"i-lucide-bot",[366,369,382,409,427,450,472,502],{"title":5,"path":367,"stem":368},"\u002Fdocs\u002Fnuxt-protokit","0.docs\u002F4.nuxt-protokit\u002Findex",{"title":185,"path":370,"stem":371,"children":372,"icon":381},"\u002Fdocs\u002Fnuxt-protokit\u002Fgetting-started","0.docs\u002F4.nuxt-protokit\u002F1.getting-started\u002F1.index",[373,374,378],{"title":190,"path":370,"stem":371},{"title":375,"path":376,"stem":377},"Core Concepts","\u002Fdocs\u002Fnuxt-protokit\u002Fgetting-started\u002Fconcepts","0.docs\u002F4.nuxt-protokit\u002F1.getting-started\u002F2.concepts",{"title":198,"path":379,"stem":380},"\u002Fdocs\u002Fnuxt-protokit\u002Fgetting-started\u002Fquick-start","0.docs\u002F4.nuxt-protokit\u002F1.getting-started\u002F3.quick-start","i-lucide-rocket",{"title":383,"path":384,"stem":385,"children":386,"icon":408},"Schemas","\u002Fdocs\u002Fnuxt-protokit\u002Fschemas","0.docs\u002F4.nuxt-protokit\u002F2.schemas\u002F1.index",[387,388,392,396,400,404],{"title":5,"path":384,"stem":385},{"title":389,"path":390,"stem":391},"Field Types","\u002Fdocs\u002Fnuxt-protokit\u002Fschemas\u002Ffields","0.docs\u002F4.nuxt-protokit\u002F2.schemas\u002F2.fields",{"title":393,"path":394,"stem":395},"Collections","\u002Fdocs\u002Fnuxt-protokit\u002Fschemas\u002Fcollections","0.docs\u002F4.nuxt-protokit\u002F2.schemas\u002F3.collections",{"title":397,"path":398,"stem":399},"Derived & Computed","\u002Fdocs\u002Fnuxt-protokit\u002Fschemas\u002Fderived-computed","0.docs\u002F4.nuxt-protokit\u002F2.schemas\u002F4.derived-computed",{"title":401,"path":402,"stem":403},"Connections","\u002Fdocs\u002Fnuxt-protokit\u002Fschemas\u002Fconnections","0.docs\u002F4.nuxt-protokit\u002F2.schemas\u002F5.connections",{"title":405,"path":406,"stem":407},"Visualizations & Layouts","\u002Fdocs\u002Fnuxt-protokit\u002Fschemas\u002Fvisualizations","0.docs\u002F4.nuxt-protokit\u002F2.schemas\u002F6.visualizations","i-lucide-file-code",{"title":355,"path":410,"stem":411,"children":412,"icon":426},"\u002Fdocs\u002Fnuxt-protokit\u002Fcomposables","0.docs\u002F4.nuxt-protokit\u002F3.composables\u002F1.index",[413,414,418,422],{"title":5,"path":410,"stem":411},{"title":415,"path":416,"stem":417},"usePrototype","\u002Fdocs\u002Fnuxt-protokit\u002Fcomposables\u002Fuse-prototype","0.docs\u002F4.nuxt-protokit\u002F3.composables\u002F2.use-prototype",{"title":419,"path":420,"stem":421},"useProtoDoc","\u002Fdocs\u002Fnuxt-protokit\u002Fcomposables\u002Fuse-proto-doc","0.docs\u002F4.nuxt-protokit\u002F3.composables\u002F3.use-proto-doc",{"title":423,"path":424,"stem":425},"useProtoCollection","\u002Fdocs\u002Fnuxt-protokit\u002Fcomposables\u002Fuse-proto-collection","0.docs\u002F4.nuxt-protokit\u002F3.composables\u002F4.use-proto-collection","i-lucide-layers",{"title":428,"path":429,"stem":430,"children":431,"icon":449},"Components","\u002Fdocs\u002Fnuxt-protokit\u002Fcomponents","0.docs\u002F4.nuxt-protokit\u002F4.components\u002F1.index",[432,433,437,441,445],{"title":5,"path":429,"stem":430},{"title":434,"path":435,"stem":436},"ProtoTool","\u002Fdocs\u002Fnuxt-protokit\u002Fcomponents\u002Fproto-tool","0.docs\u002F4.nuxt-protokit\u002F4.components\u002F2.proto-tool",{"title":438,"path":439,"stem":440},"ProtoCrudModal","\u002Fdocs\u002Fnuxt-protokit\u002Fcomponents\u002Fproto-crud-modal","0.docs\u002F4.nuxt-protokit\u002F4.components\u002F3.proto-crud-modal",{"title":442,"path":443,"stem":444},"ProtoCalendar","\u002Fdocs\u002Fnuxt-protokit\u002Fcomponents\u002Fproto-calendar","0.docs\u002F4.nuxt-protokit\u002F4.components\u002F4.proto-calendar",{"title":446,"path":447,"stem":448},"ProtoItemPanel variants","\u002Fdocs\u002Fnuxt-protokit\u002Fcomponents\u002Fproto-item-panels","0.docs\u002F4.nuxt-protokit\u002F4.components\u002F5.proto-item-panels","i-lucide-puzzle",{"title":151,"path":451,"stem":452,"children":453,"icon":471},"\u002Fdocs\u002Fnuxt-protokit\u002Foffline-first","0.docs\u002F4.nuxt-protokit\u002F5.offline-first\u002F1.index",[454,455,459,463,467],{"title":151,"path":451,"stem":452},{"title":456,"path":457,"stem":458},"Corruption Recovery","\u002Fdocs\u002Fnuxt-protokit\u002Foffline-first\u002Fcorruption-recovery","0.docs\u002F4.nuxt-protokit\u002F5.offline-first\u002F2.corruption-recovery",{"title":460,"path":461,"stem":462},"Size & GC","\u002Fdocs\u002Fnuxt-protokit\u002Foffline-first\u002Fdocument-size-and-gc","0.docs\u002F4.nuxt-protokit\u002F5.offline-first\u002F3.document-size-and-gc",{"title":464,"path":465,"stem":466},"Multi-tenant Isolation","\u002Fdocs\u002Fnuxt-protokit\u002Foffline-first\u002Fmulti-tenant-isolation","0.docs\u002F4.nuxt-protokit\u002F5.offline-first\u002F4.multi-tenant-isolation",{"title":468,"path":469,"stem":470},"Encryption at Rest","\u002Fdocs\u002Fnuxt-protokit\u002Foffline-first\u002Fencryption","0.docs\u002F4.nuxt-protokit\u002F5.offline-first\u002F5.encryption","i-lucide-wifi-off",{"title":473,"icon":474,"path":475,"stem":476,"children":477},"Advanced","i-lucide-graduation-cap","\u002Fdocs\u002Fnuxt-protokit\u002Fadvanced","0.docs\u002F4.nuxt-protokit\u002F6.advanced\u002F1.index",[478,479,483,487,491,494,498],{"title":5,"path":475,"stem":476},{"title":480,"path":481,"stem":482},"Multi-Tool Apps","\u002Fdocs\u002Fnuxt-protokit\u002Fadvanced\u002Fbuilding-a-toolkit","0.docs\u002F4.nuxt-protokit\u002F6.advanced\u002F1.building-a-toolkit",{"title":484,"path":485,"stem":486},"Schema Patterns","\u002Fdocs\u002Fnuxt-protokit\u002Fadvanced\u002Fcustom-schema-patterns","0.docs\u002F4.nuxt-protokit\u002F6.advanced\u002F2.custom-schema-patterns",{"title":488,"path":489,"stem":490},"Custom Fields & Viz","\u002Fdocs\u002Fnuxt-protokit\u002Fadvanced\u002Fextensibility","0.docs\u002F4.nuxt-protokit\u002F6.advanced\u002F3.extensibility",{"title":339,"path":492,"stem":493},"\u002Fdocs\u002Fnuxt-protokit\u002Fadvanced\u002Fpermissions","0.docs\u002F4.nuxt-protokit\u002F6.advanced\u002F4.permissions",{"title":495,"path":496,"stem":497},"Business Logic Guards","\u002Fdocs\u002Fnuxt-protokit\u002Fadvanced\u002Fpermissions-business-logic","0.docs\u002F4.nuxt-protokit\u002F6.advanced\u002F5.permissions-business-logic",{"title":499,"path":500,"stem":501},"Headless Mode","\u002Fdocs\u002Fnuxt-protokit\u002Fadvanced\u002Fheadless-mode","0.docs\u002F4.nuxt-protokit\u002F6.advanced\u002F6.headless-mode",{"title":359,"path":503,"stem":504,"children":505,"icon":364},"\u002Fdocs\u002Fnuxt-protokit\u002Fclaude-code-skills","0.docs\u002F4.nuxt-protokit\u002F7.claude-code-skills\u002F1.index",[506],{"title":5,"path":503,"stem":504},{"page":508,"fallbackPage":7651},{"id":509,"title":247,"body":510,"description":7649,"extension":7650,"links":7651,"meta":7652,"navigation":7651,"ogImage":7651,"path":248,"seo":7653,"stem":249,"__hash__":7654},"nuxt_auto\u002F0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F19.billing.md",{"type":511,"value":512,"toc":7632},"minimark",[513,517,526,542,552,557,560,1022,1026,1601,1608,1895,2047,2053,2057,2063,2224,2704,2866,2872,2876,2881,3935,3946,3950,3953,4483,4486,4490,4493,4703,4726,4730,4737,5115,5125,5129,5134,5509,5516,6882,6886,7252,7300,7304,7557,7561,7628],[514,515,247],"h1",{"id":516},"billing-plan-gating",[518,519,520,521,525],"p",{},"Subscriptions, plan-gated features and usage quotas on top of auto-api: the payment provider owns the money, a\nwebhook keeps a ",[522,523,524],"code",{},"subscriptions"," table in sync, a context extender puts the caller's plan on every request, and\nresources gate on it like on any other permission. The UI uses Nuxt UI's pricing components.",[518,527,528,529,536,537,541],{},"The examples use ",[530,531,535],"a",{"href":532,"rel":533},"https:\u002F\u002Fpolar.sh",[534],"nofollow","Polar"," with ",[538,539,540],"strong",{},"organization-level billing"," (one subscription per\nworkspace); the same shape works for Stripe or Paddle — only the checkout call and the webhook payload differ.",[543,544,549],"pre",{"className":545,"code":547,"language":548},[546],"language-text","checkout (createEndpoint) ──► provider ──► webhook ──► subscriptions table\n                                                            │\nrequest ──► context extender: ctx.requestMeta.billing ◄─────┘\n               │\n               ├─► permissions: create: ctx => billing.features.includes('projects')\n               ├─► quotas: beforeCreate hook counts rows\n               └─► GET \u002Fapi\u002Fpermissions ──► usePermissions() ──► UI\n","text",[522,550,547],{"__ignoreMap":551},"",[553,554,556],"h2",{"id":555},"_1-plans","1. Plans",[518,558,559],{},"Keep the catalog in config so server and client read the same thing:",[543,561,565],{"className":562,"code":563,"language":564,"meta":551,"style":551},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  runtimeConfig: {\n    polar: { accessToken: '', webhookSecret: '', server: 'sandbox' }, \u002F\u002F NUXT_POLAR_* in production\n    public: {\n      plans: {\n        free: { label: 'Free', price: '$0', features: ['projects'], limits: { projects: 3 } },\n        pro: {\n          label: 'Pro', price: '$19', productId: 'prod_…', features: ['projects', 'reports', 'api'],\n          limits: { projects: 50 },\n        },\n        business: {\n          label: 'Business', price: '$49', productId: 'prod_…', features: ['projects', 'reports', 'api', 'sso'],\n          limits: { projects: null }, \u002F\u002F null = unlimited\n        },\n      },\n    },\n  },\n})\n","ts",[522,566,567,576,598,611,663,673,683,763,773,852,871,877,887,969,990,995,1001,1007,1013],{"__ignoreMap":551},[568,569,572],"span",{"class":570,"line":571},"line",1,[568,573,575],{"class":574},"sHwdD","\u002F\u002F nuxt.config.ts\n",[568,577,579,583,586,590,594],{"class":570,"line":578},2,[568,580,582],{"class":581},"s7zQu","export",[568,584,585],{"class":581}," default",[568,587,589],{"class":588},"s2Zo4"," defineNuxtConfig",[568,591,593],{"class":592},"sTEyZ","(",[568,595,597],{"class":596},"sMK4o","{\n",[568,599,601,605,608],{"class":570,"line":600},3,[568,602,604],{"class":603},"swJcz","  runtimeConfig",[568,606,607],{"class":596},":",[568,609,610],{"class":596}," {\n",[568,612,614,617,619,622,625,627,630,633,636,638,640,642,645,647,650,654,657,660],{"class":570,"line":613},4,[568,615,616],{"class":603},"    polar",[568,618,607],{"class":596},[568,620,621],{"class":596}," {",[568,623,624],{"class":603}," accessToken",[568,626,607],{"class":596},[568,628,629],{"class":596}," ''",[568,631,632],{"class":596},",",[568,634,635],{"class":603}," webhookSecret",[568,637,607],{"class":596},[568,639,629],{"class":596},[568,641,632],{"class":596},[568,643,644],{"class":603}," server",[568,646,607],{"class":596},[568,648,649],{"class":596}," '",[568,651,653],{"class":652},"sfazB","sandbox",[568,655,656],{"class":596},"'",[568,658,659],{"class":596}," },",[568,661,662],{"class":574}," \u002F\u002F NUXT_POLAR_* in production\n",[568,664,666,669,671],{"class":570,"line":665},5,[568,667,668],{"class":603},"    public",[568,670,607],{"class":596},[568,672,610],{"class":596},[568,674,676,679,681],{"class":570,"line":675},6,[568,677,678],{"class":603},"      plans",[568,680,607],{"class":596},[568,682,610],{"class":596},[568,684,686,689,691,693,696,698,700,703,705,707,710,712,714,717,719,721,724,726,729,731,734,736,739,741,744,746,748,751,753,757,760],{"class":570,"line":685},7,[568,687,688],{"class":603},"        free",[568,690,607],{"class":596},[568,692,621],{"class":596},[568,694,695],{"class":603}," label",[568,697,607],{"class":596},[568,699,649],{"class":596},[568,701,702],{"class":652},"Free",[568,704,656],{"class":596},[568,706,632],{"class":596},[568,708,709],{"class":603}," price",[568,711,607],{"class":596},[568,713,649],{"class":596},[568,715,716],{"class":652},"$0",[568,718,656],{"class":596},[568,720,632],{"class":596},[568,722,723],{"class":603}," features",[568,725,607],{"class":596},[568,727,728],{"class":592}," [",[568,730,656],{"class":596},[568,732,733],{"class":652},"projects",[568,735,656],{"class":596},[568,737,738],{"class":592},"]",[568,740,632],{"class":596},[568,742,743],{"class":603}," limits",[568,745,607],{"class":596},[568,747,621],{"class":596},[568,749,750],{"class":603}," projects",[568,752,607],{"class":596},[568,754,756],{"class":755},"sbssI"," 3",[568,758,759],{"class":596}," }",[568,761,762],{"class":596}," },\n",[568,764,766,769,771],{"class":570,"line":765},8,[568,767,768],{"class":603},"        pro",[568,770,607],{"class":596},[568,772,610],{"class":596},[568,774,776,779,781,783,786,788,790,792,794,796,799,801,803,806,808,810,813,815,817,819,821,823,825,827,829,831,833,836,838,840,842,845,847,849],{"class":570,"line":775},9,[568,777,778],{"class":603},"          label",[568,780,607],{"class":596},[568,782,649],{"class":596},[568,784,785],{"class":652},"Pro",[568,787,656],{"class":596},[568,789,632],{"class":596},[568,791,709],{"class":603},[568,793,607],{"class":596},[568,795,649],{"class":596},[568,797,798],{"class":652},"$19",[568,800,656],{"class":596},[568,802,632],{"class":596},[568,804,805],{"class":603}," productId",[568,807,607],{"class":596},[568,809,649],{"class":596},[568,811,812],{"class":652},"prod_…",[568,814,656],{"class":596},[568,816,632],{"class":596},[568,818,723],{"class":603},[568,820,607],{"class":596},[568,822,728],{"class":592},[568,824,656],{"class":596},[568,826,733],{"class":652},[568,828,656],{"class":596},[568,830,632],{"class":596},[568,832,649],{"class":596},[568,834,835],{"class":652},"reports",[568,837,656],{"class":596},[568,839,632],{"class":596},[568,841,649],{"class":596},[568,843,844],{"class":652},"api",[568,846,656],{"class":596},[568,848,738],{"class":592},[568,850,851],{"class":596},",\n",[568,853,855,858,860,862,864,866,869],{"class":570,"line":854},10,[568,856,857],{"class":603},"          limits",[568,859,607],{"class":596},[568,861,621],{"class":596},[568,863,750],{"class":603},[568,865,607],{"class":596},[568,867,868],{"class":755}," 50",[568,870,762],{"class":596},[568,872,874],{"class":570,"line":873},11,[568,875,876],{"class":596},"        },\n",[568,878,880,883,885],{"class":570,"line":879},12,[568,881,882],{"class":603},"        business",[568,884,607],{"class":596},[568,886,610],{"class":596},[568,888,890,892,894,896,899,901,903,905,907,909,912,914,916,918,920,922,924,926,928,930,932,934,936,938,940,942,944,946,948,950,952,954,956,958,960,963,965,967],{"class":570,"line":889},13,[568,891,778],{"class":603},[568,893,607],{"class":596},[568,895,649],{"class":596},[568,897,898],{"class":652},"Business",[568,900,656],{"class":596},[568,902,632],{"class":596},[568,904,709],{"class":603},[568,906,607],{"class":596},[568,908,649],{"class":596},[568,910,911],{"class":652},"$49",[568,913,656],{"class":596},[568,915,632],{"class":596},[568,917,805],{"class":603},[568,919,607],{"class":596},[568,921,649],{"class":596},[568,923,812],{"class":652},[568,925,656],{"class":596},[568,927,632],{"class":596},[568,929,723],{"class":603},[568,931,607],{"class":596},[568,933,728],{"class":592},[568,935,656],{"class":596},[568,937,733],{"class":652},[568,939,656],{"class":596},[568,941,632],{"class":596},[568,943,649],{"class":596},[568,945,835],{"class":652},[568,947,656],{"class":596},[568,949,632],{"class":596},[568,951,649],{"class":596},[568,953,844],{"class":652},[568,955,656],{"class":596},[568,957,632],{"class":596},[568,959,649],{"class":596},[568,961,962],{"class":652},"sso",[568,964,656],{"class":596},[568,966,738],{"class":592},[568,968,851],{"class":596},[568,970,972,974,976,978,980,982,985,987],{"class":570,"line":971},14,[568,973,857],{"class":603},[568,975,607],{"class":596},[568,977,621],{"class":596},[568,979,750],{"class":603},[568,981,607],{"class":596},[568,983,984],{"class":596}," null",[568,986,659],{"class":596},[568,988,989],{"class":574}," \u002F\u002F null = unlimited\n",[568,991,993],{"class":570,"line":992},15,[568,994,876],{"class":596},[568,996,998],{"class":570,"line":997},16,[568,999,1000],{"class":596},"      },\n",[568,1002,1004],{"class":570,"line":1003},17,[568,1005,1006],{"class":596},"    },\n",[568,1008,1010],{"class":570,"line":1009},18,[568,1011,1012],{"class":596},"  },\n",[568,1014,1016,1019],{"class":570,"line":1015},19,[568,1017,1018],{"class":596},"}",[568,1020,1021],{"class":592},")\n",[553,1023,1025],{"id":1024},"_2-the-subscriptions-table","2. The subscriptions table",[543,1027,1029],{"className":562,"code":1028,"language":564,"meta":551,"style":551},"\u002F\u002F server\u002Fdatabase\u002Fschema.ts\nexport const subscriptions = sqliteTable('subscriptions', {\n  id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),\n  organizationId: text('organization_id').notNull().unique(), \u002F\u002F tenant column — one per workspace\n  providerSubscriptionId: text('provider_subscription_id'),\n  planId: text('plan_id').notNull().default('free'),\n  status: text('status', { enum: ['active', 'trialing', 'past_due', 'canceled', 'none'] }).notNull().default('none'),\n  currentPeriodEnd: integer('current_period_end', { mode: 'timestamp' }),\n  updatedAt: integer('updated_at', { mode: 'timestamp' }).$defaultFn(() => new Date()).$onUpdate(() => new Date()),\n})\n\n\u002F\u002F Webhook idempotency: providers retry, so remember what was processed.\nexport const billingEvents = sqliteTable('billing_events', {\n  id: text('id').primaryKey(), \u002F\u002F the provider's event id\n  type: text('type').notNull(),\n  receivedAt: integer('received_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),\n})\n",[522,1030,1031,1036,1065,1121,1160,1182,1226,1327,1368,1441,1447,1453,1458,1484,1513,1541,1595],{"__ignoreMap":551},[568,1032,1033],{"class":570,"line":571},[568,1034,1035],{"class":574},"\u002F\u002F server\u002Fdatabase\u002Fschema.ts\n",[568,1037,1038,1040,1044,1047,1050,1053,1055,1057,1059,1061,1063],{"class":570,"line":578},[568,1039,582],{"class":581},[568,1041,1043],{"class":1042},"spNyl"," const",[568,1045,1046],{"class":592}," subscriptions ",[568,1048,1049],{"class":596},"=",[568,1051,1052],{"class":588}," sqliteTable",[568,1054,593],{"class":592},[568,1056,656],{"class":596},[568,1058,524],{"class":652},[568,1060,656],{"class":596},[568,1062,632],{"class":596},[568,1064,610],{"class":596},[568,1066,1067,1070,1072,1075,1077,1079,1082,1084,1087,1090,1093,1096,1098,1101,1103,1105,1108,1111,1113,1116,1119],{"class":570,"line":600},[568,1068,1069],{"class":603},"  id",[568,1071,607],{"class":596},[568,1073,1074],{"class":588}," text",[568,1076,593],{"class":592},[568,1078,656],{"class":596},[568,1080,1081],{"class":652},"id",[568,1083,656],{"class":596},[568,1085,1086],{"class":592},")",[568,1088,1089],{"class":596},".",[568,1091,1092],{"class":588},"primaryKey",[568,1094,1095],{"class":592},"()",[568,1097,1089],{"class":596},[568,1099,1100],{"class":588},"$defaultFn",[568,1102,593],{"class":592},[568,1104,1095],{"class":596},[568,1106,1107],{"class":1042}," =>",[568,1109,1110],{"class":592}," crypto",[568,1112,1089],{"class":596},[568,1114,1115],{"class":588},"randomUUID",[568,1117,1118],{"class":592},"())",[568,1120,851],{"class":596},[568,1122,1123,1126,1128,1130,1132,1134,1137,1139,1141,1143,1146,1148,1150,1153,1155,1157],{"class":570,"line":613},[568,1124,1125],{"class":603},"  organizationId",[568,1127,607],{"class":596},[568,1129,1074],{"class":588},[568,1131,593],{"class":592},[568,1133,656],{"class":596},[568,1135,1136],{"class":652},"organization_id",[568,1138,656],{"class":596},[568,1140,1086],{"class":592},[568,1142,1089],{"class":596},[568,1144,1145],{"class":588},"notNull",[568,1147,1095],{"class":592},[568,1149,1089],{"class":596},[568,1151,1152],{"class":588},"unique",[568,1154,1095],{"class":592},[568,1156,632],{"class":596},[568,1158,1159],{"class":574}," \u002F\u002F tenant column — one per workspace\n",[568,1161,1162,1165,1167,1169,1171,1173,1176,1178,1180],{"class":570,"line":665},[568,1163,1164],{"class":603},"  providerSubscriptionId",[568,1166,607],{"class":596},[568,1168,1074],{"class":588},[568,1170,593],{"class":592},[568,1172,656],{"class":596},[568,1174,1175],{"class":652},"provider_subscription_id",[568,1177,656],{"class":596},[568,1179,1086],{"class":592},[568,1181,851],{"class":596},[568,1183,1184,1187,1189,1191,1193,1195,1198,1200,1202,1204,1206,1208,1210,1213,1215,1217,1220,1222,1224],{"class":570,"line":675},[568,1185,1186],{"class":603},"  planId",[568,1188,607],{"class":596},[568,1190,1074],{"class":588},[568,1192,593],{"class":592},[568,1194,656],{"class":596},[568,1196,1197],{"class":652},"plan_id",[568,1199,656],{"class":596},[568,1201,1086],{"class":592},[568,1203,1089],{"class":596},[568,1205,1145],{"class":588},[568,1207,1095],{"class":592},[568,1209,1089],{"class":596},[568,1211,1212],{"class":588},"default",[568,1214,593],{"class":592},[568,1216,656],{"class":596},[568,1218,1219],{"class":652},"free",[568,1221,656],{"class":596},[568,1223,1086],{"class":592},[568,1225,851],{"class":596},[568,1227,1228,1231,1233,1235,1237,1239,1242,1244,1246,1248,1251,1253,1255,1257,1260,1262,1264,1266,1269,1271,1273,1275,1278,1280,1282,1284,1287,1289,1291,1293,1296,1298,1301,1303,1305,1307,1309,1311,1313,1315,1317,1319,1321,1323,1325],{"class":570,"line":685},[568,1229,1230],{"class":603},"  status",[568,1232,607],{"class":596},[568,1234,1074],{"class":588},[568,1236,593],{"class":592},[568,1238,656],{"class":596},[568,1240,1241],{"class":652},"status",[568,1243,656],{"class":596},[568,1245,632],{"class":596},[568,1247,621],{"class":596},[568,1249,1250],{"class":603}," enum",[568,1252,607],{"class":596},[568,1254,728],{"class":592},[568,1256,656],{"class":596},[568,1258,1259],{"class":652},"active",[568,1261,656],{"class":596},[568,1263,632],{"class":596},[568,1265,649],{"class":596},[568,1267,1268],{"class":652},"trialing",[568,1270,656],{"class":596},[568,1272,632],{"class":596},[568,1274,649],{"class":596},[568,1276,1277],{"class":652},"past_due",[568,1279,656],{"class":596},[568,1281,632],{"class":596},[568,1283,649],{"class":596},[568,1285,1286],{"class":652},"canceled",[568,1288,656],{"class":596},[568,1290,632],{"class":596},[568,1292,649],{"class":596},[568,1294,1295],{"class":652},"none",[568,1297,656],{"class":596},[568,1299,1300],{"class":592},"] ",[568,1302,1018],{"class":596},[568,1304,1086],{"class":592},[568,1306,1089],{"class":596},[568,1308,1145],{"class":588},[568,1310,1095],{"class":592},[568,1312,1089],{"class":596},[568,1314,1212],{"class":588},[568,1316,593],{"class":592},[568,1318,656],{"class":596},[568,1320,1295],{"class":652},[568,1322,656],{"class":596},[568,1324,1086],{"class":592},[568,1326,851],{"class":596},[568,1328,1329,1332,1334,1337,1339,1341,1344,1346,1348,1350,1353,1355,1357,1360,1362,1364,1366],{"class":570,"line":765},[568,1330,1331],{"class":603},"  currentPeriodEnd",[568,1333,607],{"class":596},[568,1335,1336],{"class":588}," integer",[568,1338,593],{"class":592},[568,1340,656],{"class":596},[568,1342,1343],{"class":652},"current_period_end",[568,1345,656],{"class":596},[568,1347,632],{"class":596},[568,1349,621],{"class":596},[568,1351,1352],{"class":603}," mode",[568,1354,607],{"class":596},[568,1356,649],{"class":596},[568,1358,1359],{"class":652},"timestamp",[568,1361,656],{"class":596},[568,1363,759],{"class":596},[568,1365,1086],{"class":592},[568,1367,851],{"class":596},[568,1369,1370,1373,1375,1377,1379,1381,1384,1386,1388,1390,1392,1394,1396,1398,1400,1402,1404,1406,1408,1410,1412,1414,1417,1420,1422,1424,1427,1429,1431,1433,1435,1437,1439],{"class":570,"line":775},[568,1371,1372],{"class":603},"  updatedAt",[568,1374,607],{"class":596},[568,1376,1336],{"class":588},[568,1378,593],{"class":592},[568,1380,656],{"class":596},[568,1382,1383],{"class":652},"updated_at",[568,1385,656],{"class":596},[568,1387,632],{"class":596},[568,1389,621],{"class":596},[568,1391,1352],{"class":603},[568,1393,607],{"class":596},[568,1395,649],{"class":596},[568,1397,1359],{"class":652},[568,1399,656],{"class":596},[568,1401,759],{"class":596},[568,1403,1086],{"class":592},[568,1405,1089],{"class":596},[568,1407,1100],{"class":588},[568,1409,593],{"class":592},[568,1411,1095],{"class":596},[568,1413,1107],{"class":1042},[568,1415,1416],{"class":596}," new",[568,1418,1419],{"class":588}," Date",[568,1421,1118],{"class":592},[568,1423,1089],{"class":596},[568,1425,1426],{"class":588},"$onUpdate",[568,1428,593],{"class":592},[568,1430,1095],{"class":596},[568,1432,1107],{"class":1042},[568,1434,1416],{"class":596},[568,1436,1419],{"class":588},[568,1438,1118],{"class":592},[568,1440,851],{"class":596},[568,1442,1443,1445],{"class":570,"line":854},[568,1444,1018],{"class":596},[568,1446,1021],{"class":592},[568,1448,1449],{"class":570,"line":873},[568,1450,1452],{"emptyLinePlaceholder":1451},true,"\n",[568,1454,1455],{"class":570,"line":879},[568,1456,1457],{"class":574},"\u002F\u002F Webhook idempotency: providers retry, so remember what was processed.\n",[568,1459,1460,1462,1464,1467,1469,1471,1473,1475,1478,1480,1482],{"class":570,"line":889},[568,1461,582],{"class":581},[568,1463,1043],{"class":1042},[568,1465,1466],{"class":592}," billingEvents ",[568,1468,1049],{"class":596},[568,1470,1052],{"class":588},[568,1472,593],{"class":592},[568,1474,656],{"class":596},[568,1476,1477],{"class":652},"billing_events",[568,1479,656],{"class":596},[568,1481,632],{"class":596},[568,1483,610],{"class":596},[568,1485,1486,1488,1490,1492,1494,1496,1498,1500,1502,1504,1506,1508,1510],{"class":570,"line":971},[568,1487,1069],{"class":603},[568,1489,607],{"class":596},[568,1491,1074],{"class":588},[568,1493,593],{"class":592},[568,1495,656],{"class":596},[568,1497,1081],{"class":652},[568,1499,656],{"class":596},[568,1501,1086],{"class":592},[568,1503,1089],{"class":596},[568,1505,1092],{"class":588},[568,1507,1095],{"class":592},[568,1509,632],{"class":596},[568,1511,1512],{"class":574}," \u002F\u002F the provider's event id\n",[568,1514,1515,1518,1520,1522,1524,1526,1529,1531,1533,1535,1537,1539],{"class":570,"line":992},[568,1516,1517],{"class":603},"  type",[568,1519,607],{"class":596},[568,1521,1074],{"class":588},[568,1523,593],{"class":592},[568,1525,656],{"class":596},[568,1527,1528],{"class":652},"type",[568,1530,656],{"class":596},[568,1532,1086],{"class":592},[568,1534,1089],{"class":596},[568,1536,1145],{"class":588},[568,1538,1095],{"class":592},[568,1540,851],{"class":596},[568,1542,1543,1546,1548,1550,1552,1554,1557,1559,1561,1563,1565,1567,1569,1571,1573,1575,1577,1579,1581,1583,1585,1587,1589,1591,1593],{"class":570,"line":997},[568,1544,1545],{"class":603},"  receivedAt",[568,1547,607],{"class":596},[568,1549,1336],{"class":588},[568,1551,593],{"class":592},[568,1553,656],{"class":596},[568,1555,1556],{"class":652},"received_at",[568,1558,656],{"class":596},[568,1560,632],{"class":596},[568,1562,621],{"class":596},[568,1564,1352],{"class":603},[568,1566,607],{"class":596},[568,1568,649],{"class":596},[568,1570,1359],{"class":652},[568,1572,656],{"class":596},[568,1574,759],{"class":596},[568,1576,1086],{"class":592},[568,1578,1089],{"class":596},[568,1580,1100],{"class":588},[568,1582,593],{"class":592},[568,1584,1095],{"class":596},[568,1586,1107],{"class":1042},[568,1588,1416],{"class":596},[568,1590,1419],{"class":588},[568,1592,1118],{"class":592},[568,1594,851],{"class":596},[568,1596,1597,1599],{"class":570,"line":1003},[568,1598,1018],{"class":596},[568,1600,1021],{"class":592},[518,1602,1603,1604,1607],{},"Register the subscription ",[538,1605,1606],{},"read-only",". Writes come from the webhook only, so no request can grant itself a plan:",[543,1609,1611],{"className":562,"code":1610,"language":564,"meta":551,"style":551},"\u002F\u002F modules\u002Fbilling\u002Fauth.ts\nimport type { HandlerContext, ResourceAuthConfig } from '@websideproject\u002Fnuxt-auto-api'\n\nconst isOrgAdmin = (ctx: HandlerContext) => ['owner', 'admin'].includes(ctx.user?.orgRole ?? '')\n\nexport const subscriptionsAuth: ResourceAuthConfig = {\n  permissions: {\n    read: ctx => !!ctx.user, \u002F\u002F tenancy limits it to the active workspace's row\n    create: false,\n    update: false,\n    delete: false,\n  },\n  custom: {\n    checkout: { permissions: { create: isOrgAdmin } }, \u002F\u002F only workspace admins can buy\n    portal: { permissions: { read: isOrgAdmin } },\n  },\n}\n",[522,1612,1613,1618,1649,1653,1725,1729,1747,1756,1782,1795,1806,1817,1821,1830,1860,1886,1890],{"__ignoreMap":551},[568,1614,1615],{"class":570,"line":571},[568,1616,1617],{"class":574},"\u002F\u002F modules\u002Fbilling\u002Fauth.ts\n",[568,1619,1620,1623,1626,1628,1631,1633,1636,1638,1641,1643,1646],{"class":570,"line":578},[568,1621,1622],{"class":581},"import",[568,1624,1625],{"class":581}," type",[568,1627,621],{"class":596},[568,1629,1630],{"class":592}," HandlerContext",[568,1632,632],{"class":596},[568,1634,1635],{"class":592}," ResourceAuthConfig",[568,1637,759],{"class":596},[568,1639,1640],{"class":581}," from",[568,1642,649],{"class":596},[568,1644,1645],{"class":652},"@websideproject\u002Fnuxt-auto-api",[568,1647,1648],{"class":596},"'\n",[568,1650,1651],{"class":570,"line":600},[568,1652,1452],{"emptyLinePlaceholder":1451},[568,1654,1655,1658,1661,1663,1666,1670,1672,1675,1677,1679,1681,1683,1686,1688,1690,1692,1695,1697,1699,1701,1704,1707,1709,1712,1715,1718,1721,1723],{"class":570,"line":613},[568,1656,1657],{"class":1042},"const",[568,1659,1660],{"class":592}," isOrgAdmin ",[568,1662,1049],{"class":596},[568,1664,1665],{"class":596}," (",[568,1667,1669],{"class":1668},"sHdIc","ctx",[568,1671,607],{"class":596},[568,1673,1630],{"class":1674},"sBMFI",[568,1676,1086],{"class":596},[568,1678,1107],{"class":1042},[568,1680,728],{"class":592},[568,1682,656],{"class":596},[568,1684,1685],{"class":652},"owner",[568,1687,656],{"class":596},[568,1689,632],{"class":596},[568,1691,649],{"class":596},[568,1693,1694],{"class":652},"admin",[568,1696,656],{"class":596},[568,1698,738],{"class":592},[568,1700,1089],{"class":596},[568,1702,1703],{"class":588},"includes",[568,1705,1706],{"class":592},"(ctx",[568,1708,1089],{"class":596},[568,1710,1711],{"class":592},"user",[568,1713,1714],{"class":596},"?.",[568,1716,1717],{"class":592},"orgRole ",[568,1719,1720],{"class":596},"??",[568,1722,629],{"class":596},[568,1724,1021],{"class":592},[568,1726,1727],{"class":570,"line":665},[568,1728,1452],{"emptyLinePlaceholder":1451},[568,1730,1731,1733,1735,1738,1740,1742,1745],{"class":570,"line":675},[568,1732,582],{"class":581},[568,1734,1043],{"class":1042},[568,1736,1737],{"class":592}," subscriptionsAuth",[568,1739,607],{"class":596},[568,1741,1635],{"class":1674},[568,1743,1744],{"class":596}," =",[568,1746,610],{"class":596},[568,1748,1749,1752,1754],{"class":570,"line":685},[568,1750,1751],{"class":603},"  permissions",[568,1753,607],{"class":596},[568,1755,610],{"class":596},[568,1757,1758,1761,1763,1766,1768,1771,1773,1775,1777,1779],{"class":570,"line":765},[568,1759,1760],{"class":588},"    read",[568,1762,607],{"class":596},[568,1764,1765],{"class":1668}," ctx",[568,1767,1107],{"class":1042},[568,1769,1770],{"class":596}," !!",[568,1772,1669],{"class":592},[568,1774,1089],{"class":596},[568,1776,1711],{"class":592},[568,1778,632],{"class":596},[568,1780,1781],{"class":574}," \u002F\u002F tenancy limits it to the active workspace's row\n",[568,1783,1784,1787,1789,1793],{"class":570,"line":775},[568,1785,1786],{"class":603},"    create",[568,1788,607],{"class":596},[568,1790,1792],{"class":1791},"sfNiH"," false",[568,1794,851],{"class":596},[568,1796,1797,1800,1802,1804],{"class":570,"line":854},[568,1798,1799],{"class":603},"    update",[568,1801,607],{"class":596},[568,1803,1792],{"class":1791},[568,1805,851],{"class":596},[568,1807,1808,1811,1813,1815],{"class":570,"line":873},[568,1809,1810],{"class":603},"    delete",[568,1812,607],{"class":596},[568,1814,1792],{"class":1791},[568,1816,851],{"class":596},[568,1818,1819],{"class":570,"line":879},[568,1820,1012],{"class":596},[568,1822,1823,1826,1828],{"class":570,"line":889},[568,1824,1825],{"class":603},"  custom",[568,1827,607],{"class":596},[568,1829,610],{"class":596},[568,1831,1832,1835,1837,1839,1842,1844,1846,1849,1851,1853,1855,1857],{"class":570,"line":971},[568,1833,1834],{"class":603},"    checkout",[568,1836,607],{"class":596},[568,1838,621],{"class":596},[568,1840,1841],{"class":603}," permissions",[568,1843,607],{"class":596},[568,1845,621],{"class":596},[568,1847,1848],{"class":603}," create",[568,1850,607],{"class":596},[568,1852,1660],{"class":592},[568,1854,1018],{"class":596},[568,1856,659],{"class":596},[568,1858,1859],{"class":574}," \u002F\u002F only workspace admins can buy\n",[568,1861,1862,1865,1867,1869,1871,1873,1875,1878,1880,1882,1884],{"class":570,"line":992},[568,1863,1864],{"class":603},"    portal",[568,1866,607],{"class":596},[568,1868,621],{"class":596},[568,1870,1841],{"class":603},[568,1872,607],{"class":596},[568,1874,621],{"class":596},[568,1876,1877],{"class":603}," read",[568,1879,607],{"class":596},[568,1881,1660],{"class":592},[568,1883,1018],{"class":596},[568,1885,762],{"class":596},[568,1887,1888],{"class":570,"line":997},[568,1889,1012],{"class":596},[568,1891,1892],{"class":570,"line":1003},[568,1893,1894],{"class":596},"}\n",[543,1896,1898],{"className":562,"code":1897,"language":564,"meta":551,"style":551},"\u002F\u002F modules\u002Fbilling\u002Findex.ts\nnuxt.hook('autoApi:registerSchema', (registry) => {\n  registry.register('subscriptions', {\n    schema: createModuleImport(resolve('..\u002F..\u002Fserver\u002Fdatabase\u002Fschema'), 'subscriptions'),\n    authorization: createModuleImport(resolve('.\u002Fauth'), 'subscriptionsAuth'),\n  })\n})\n",[522,1899,1900,1905,1937,1959,1997,2034,2041],{"__ignoreMap":551},[568,1901,1902],{"class":570,"line":571},[568,1903,1904],{"class":574},"\u002F\u002F modules\u002Fbilling\u002Findex.ts\n",[568,1906,1907,1910,1912,1915,1917,1919,1922,1924,1926,1928,1931,1933,1935],{"class":570,"line":578},[568,1908,1909],{"class":592},"nuxt",[568,1911,1089],{"class":596},[568,1913,1914],{"class":588},"hook",[568,1916,593],{"class":592},[568,1918,656],{"class":596},[568,1920,1921],{"class":652},"autoApi:registerSchema",[568,1923,656],{"class":596},[568,1925,632],{"class":596},[568,1927,1665],{"class":596},[568,1929,1930],{"class":1668},"registry",[568,1932,1086],{"class":596},[568,1934,1107],{"class":1042},[568,1936,610],{"class":596},[568,1938,1939,1942,1944,1947,1949,1951,1953,1955,1957],{"class":570,"line":600},[568,1940,1941],{"class":592},"  registry",[568,1943,1089],{"class":596},[568,1945,1946],{"class":588},"register",[568,1948,593],{"class":603},[568,1950,656],{"class":596},[568,1952,524],{"class":652},[568,1954,656],{"class":596},[568,1956,632],{"class":596},[568,1958,610],{"class":596},[568,1960,1961,1964,1966,1969,1971,1974,1976,1978,1981,1983,1985,1987,1989,1991,1993,1995],{"class":570,"line":613},[568,1962,1963],{"class":603},"    schema",[568,1965,607],{"class":596},[568,1967,1968],{"class":588}," createModuleImport",[568,1970,593],{"class":603},[568,1972,1973],{"class":588},"resolve",[568,1975,593],{"class":603},[568,1977,656],{"class":596},[568,1979,1980],{"class":652},"..\u002F..\u002Fserver\u002Fdatabase\u002Fschema",[568,1982,656],{"class":596},[568,1984,1086],{"class":603},[568,1986,632],{"class":596},[568,1988,649],{"class":596},[568,1990,524],{"class":652},[568,1992,656],{"class":596},[568,1994,1086],{"class":603},[568,1996,851],{"class":596},[568,1998,1999,2002,2004,2006,2008,2010,2012,2014,2017,2019,2021,2023,2025,2028,2030,2032],{"class":570,"line":665},[568,2000,2001],{"class":603},"    authorization",[568,2003,607],{"class":596},[568,2005,1968],{"class":588},[568,2007,593],{"class":603},[568,2009,1973],{"class":588},[568,2011,593],{"class":603},[568,2013,656],{"class":596},[568,2015,2016],{"class":652},".\u002Fauth",[568,2018,656],{"class":596},[568,2020,1086],{"class":603},[568,2022,632],{"class":596},[568,2024,649],{"class":596},[568,2026,2027],{"class":652},"subscriptionsAuth",[568,2029,656],{"class":596},[568,2031,1086],{"class":603},[568,2033,851],{"class":596},[568,2035,2036,2039],{"class":570,"line":675},[568,2037,2038],{"class":596},"  }",[568,2040,1021],{"class":603},[568,2042,2043,2045],{"class":570,"line":685},[568,2044,1018],{"class":596},[568,2046,1021],{"class":592},[518,2048,2049,2052],{},[522,2050,2051],{},"billingEvents"," is not registered — it is internal.",[553,2054,2056],{"id":2055},"_3-checkout-and-the-customer-portal","3. Checkout and the customer portal",[518,2058,2059,2060,2062],{},"Both are custom endpoints on the ",[522,2061,524],{}," resource, opened by the named gates above:",[543,2064,2066],{"className":562,"code":2065,"language":564,"meta":551,"style":551},"\u002F\u002F server\u002Futils\u002Fpolar.ts\nimport { Polar } from '@polar-sh\u002Fsdk'\n\nlet _polar: Polar | undefined\nexport function usePolar() {\n  const { polar } = useRuntimeConfig()\n  _polar ??= new Polar({ accessToken: polar.accessToken, server: polar.server as 'sandbox' | 'production' })\n  return _polar\n}\n",[522,2067,2068,2073,2093,2097,2115,2129,2149,2212,2220],{"__ignoreMap":551},[568,2069,2070],{"class":570,"line":571},[568,2071,2072],{"class":574},"\u002F\u002F server\u002Futils\u002Fpolar.ts\n",[568,2074,2075,2077,2079,2082,2084,2086,2088,2091],{"class":570,"line":578},[568,2076,1622],{"class":581},[568,2078,621],{"class":596},[568,2080,2081],{"class":592}," Polar",[568,2083,759],{"class":596},[568,2085,1640],{"class":581},[568,2087,649],{"class":596},[568,2089,2090],{"class":652},"@polar-sh\u002Fsdk",[568,2092,1648],{"class":596},[568,2094,2095],{"class":570,"line":600},[568,2096,1452],{"emptyLinePlaceholder":1451},[568,2098,2099,2102,2105,2107,2109,2112],{"class":570,"line":613},[568,2100,2101],{"class":1042},"let",[568,2103,2104],{"class":592}," _polar",[568,2106,607],{"class":596},[568,2108,2081],{"class":1674},[568,2110,2111],{"class":596}," |",[568,2113,2114],{"class":1674}," undefined\n",[568,2116,2117,2119,2122,2125,2127],{"class":570,"line":665},[568,2118,582],{"class":581},[568,2120,2121],{"class":1042}," function",[568,2123,2124],{"class":588}," usePolar",[568,2126,1095],{"class":596},[568,2128,610],{"class":596},[568,2130,2131,2134,2136,2139,2141,2143,2146],{"class":570,"line":675},[568,2132,2133],{"class":1042},"  const",[568,2135,621],{"class":596},[568,2137,2138],{"class":592}," polar",[568,2140,759],{"class":596},[568,2142,1744],{"class":596},[568,2144,2145],{"class":588}," useRuntimeConfig",[568,2147,2148],{"class":603},"()\n",[568,2150,2151,2154,2157,2159,2161,2163,2166,2168,2170,2172,2174,2177,2179,2181,2183,2185,2187,2190,2193,2195,2197,2199,2201,2203,2206,2208,2210],{"class":570,"line":685},[568,2152,2153],{"class":592},"  _polar",[568,2155,2156],{"class":596}," ??=",[568,2158,1416],{"class":596},[568,2160,2081],{"class":588},[568,2162,593],{"class":603},[568,2164,2165],{"class":596},"{",[568,2167,624],{"class":603},[568,2169,607],{"class":596},[568,2171,2138],{"class":592},[568,2173,1089],{"class":596},[568,2175,2176],{"class":592},"accessToken",[568,2178,632],{"class":596},[568,2180,644],{"class":603},[568,2182,607],{"class":596},[568,2184,2138],{"class":592},[568,2186,1089],{"class":596},[568,2188,2189],{"class":592},"server",[568,2191,2192],{"class":581}," as",[568,2194,649],{"class":596},[568,2196,653],{"class":652},[568,2198,656],{"class":596},[568,2200,2111],{"class":596},[568,2202,649],{"class":596},[568,2204,2205],{"class":652},"production",[568,2207,656],{"class":596},[568,2209,759],{"class":596},[568,2211,1021],{"class":603},[568,2213,2214,2217],{"class":570,"line":765},[568,2215,2216],{"class":581},"  return",[568,2218,2219],{"class":592}," _polar\n",[568,2221,2222],{"class":570,"line":775},[568,2223,1894],{"class":596},[543,2225,2227],{"className":562,"code":2226,"language":564,"meta":551,"style":551},"\u002F\u002F server\u002Fapi\u002Fbilling\u002Fcheckout.post.ts\nimport { z } from 'zod'\n\nexport default createEndpoint({\n  resource: 'subscriptions',\n  operation: 'create',\n  endpointName: 'checkout', \u002F\u002F → custom.checkout: workspace admins only\n  skipValidation: true, \u002F\u002F the Zod body below is the contract, not the table's create schema\n  body: z.object({ planId: z.string() }),\n  async handler(ctx, event) {\n    const plan = useRuntimeConfig().public.plans[ctx.body.planId]\n    if (!plan?.productId) throw createError({ statusCode: 400, message: 'Unknown or free plan' })\n\n    const checkout = await usePolar().checkouts.create({\n      products: [plan.productId],\n      externalCustomerId: ctx.tenant!.id as string, \u002F\u002F the WORKSPACE is the customer\n      customerEmail: ctx.user!.email,\n      metadata: { organizationId: String(ctx.tenant!.id) },\n      successUrl: `${getRequestURL(event).origin}\u002Fapp\u002Fbilling?success=1`,\n    })\n    return { url: checkout.url }\n  },\n})\n",[522,2228,2229,2234,2254,2258,2271,2286,2302,2321,2336,2375,2396,2438,2495,2499,2528,2547,2576,2596,2630,2661,2669,2692,2697],{"__ignoreMap":551},[568,2230,2231],{"class":570,"line":571},[568,2232,2233],{"class":574},"\u002F\u002F server\u002Fapi\u002Fbilling\u002Fcheckout.post.ts\n",[568,2235,2236,2238,2240,2243,2245,2247,2249,2252],{"class":570,"line":578},[568,2237,1622],{"class":581},[568,2239,621],{"class":596},[568,2241,2242],{"class":592}," z",[568,2244,759],{"class":596},[568,2246,1640],{"class":581},[568,2248,649],{"class":596},[568,2250,2251],{"class":652},"zod",[568,2253,1648],{"class":596},[568,2255,2256],{"class":570,"line":600},[568,2257,1452],{"emptyLinePlaceholder":1451},[568,2259,2260,2262,2264,2267,2269],{"class":570,"line":613},[568,2261,582],{"class":581},[568,2263,585],{"class":581},[568,2265,2266],{"class":588}," createEndpoint",[568,2268,593],{"class":592},[568,2270,597],{"class":596},[568,2272,2273,2276,2278,2280,2282,2284],{"class":570,"line":665},[568,2274,2275],{"class":603},"  resource",[568,2277,607],{"class":596},[568,2279,649],{"class":596},[568,2281,524],{"class":652},[568,2283,656],{"class":596},[568,2285,851],{"class":596},[568,2287,2288,2291,2293,2295,2298,2300],{"class":570,"line":675},[568,2289,2290],{"class":603},"  operation",[568,2292,607],{"class":596},[568,2294,649],{"class":596},[568,2296,2297],{"class":652},"create",[568,2299,656],{"class":596},[568,2301,851],{"class":596},[568,2303,2304,2307,2309,2311,2314,2316,2318],{"class":570,"line":685},[568,2305,2306],{"class":603},"  endpointName",[568,2308,607],{"class":596},[568,2310,649],{"class":596},[568,2312,2313],{"class":652},"checkout",[568,2315,656],{"class":596},[568,2317,632],{"class":596},[568,2319,2320],{"class":574}," \u002F\u002F → custom.checkout: workspace admins only\n",[568,2322,2323,2326,2328,2331,2333],{"class":570,"line":765},[568,2324,2325],{"class":603},"  skipValidation",[568,2327,607],{"class":596},[568,2329,2330],{"class":1791}," true",[568,2332,632],{"class":596},[568,2334,2335],{"class":574}," \u002F\u002F the Zod body below is the contract, not the table's create schema\n",[568,2337,2338,2341,2343,2345,2347,2350,2352,2354,2357,2359,2361,2363,2366,2369,2371,2373],{"class":570,"line":775},[568,2339,2340],{"class":603},"  body",[568,2342,607],{"class":596},[568,2344,2242],{"class":592},[568,2346,1089],{"class":596},[568,2348,2349],{"class":588},"object",[568,2351,593],{"class":592},[568,2353,2165],{"class":596},[568,2355,2356],{"class":603}," planId",[568,2358,607],{"class":596},[568,2360,2242],{"class":592},[568,2362,1089],{"class":596},[568,2364,2365],{"class":588},"string",[568,2367,2368],{"class":592},"() ",[568,2370,1018],{"class":596},[568,2372,1086],{"class":592},[568,2374,851],{"class":596},[568,2376,2377,2380,2383,2385,2387,2389,2392,2394],{"class":570,"line":854},[568,2378,2379],{"class":1042},"  async",[568,2381,2382],{"class":603}," handler",[568,2384,593],{"class":596},[568,2386,1669],{"class":1668},[568,2388,632],{"class":596},[568,2390,2391],{"class":1668}," event",[568,2393,1086],{"class":596},[568,2395,610],{"class":596},[568,2397,2398,2401,2404,2406,2408,2410,2412,2415,2417,2420,2423,2425,2427,2430,2432,2435],{"class":570,"line":873},[568,2399,2400],{"class":1042},"    const",[568,2402,2403],{"class":592}," plan",[568,2405,1744],{"class":596},[568,2407,2145],{"class":588},[568,2409,1095],{"class":603},[568,2411,1089],{"class":596},[568,2413,2414],{"class":592},"public",[568,2416,1089],{"class":596},[568,2418,2419],{"class":592},"plans",[568,2421,2422],{"class":603},"[",[568,2424,1669],{"class":592},[568,2426,1089],{"class":596},[568,2428,2429],{"class":592},"body",[568,2431,1089],{"class":596},[568,2433,2434],{"class":592},"planId",[568,2436,2437],{"class":603},"]\n",[568,2439,2440,2443,2445,2448,2451,2453,2456,2459,2462,2465,2467,2469,2472,2474,2477,2479,2482,2484,2486,2489,2491,2493],{"class":570,"line":879},[568,2441,2442],{"class":581},"    if",[568,2444,1665],{"class":603},[568,2446,2447],{"class":596},"!",[568,2449,2450],{"class":592},"plan",[568,2452,1714],{"class":596},[568,2454,2455],{"class":592},"productId",[568,2457,2458],{"class":603},") ",[568,2460,2461],{"class":581},"throw",[568,2463,2464],{"class":588}," createError",[568,2466,593],{"class":603},[568,2468,2165],{"class":596},[568,2470,2471],{"class":603}," statusCode",[568,2473,607],{"class":596},[568,2475,2476],{"class":755}," 400",[568,2478,632],{"class":596},[568,2480,2481],{"class":603}," message",[568,2483,607],{"class":596},[568,2485,649],{"class":596},[568,2487,2488],{"class":652},"Unknown or free plan",[568,2490,656],{"class":596},[568,2492,759],{"class":596},[568,2494,1021],{"class":603},[568,2496,2497],{"class":570,"line":889},[568,2498,1452],{"emptyLinePlaceholder":1451},[568,2500,2501,2503,2506,2508,2511,2513,2515,2517,2520,2522,2524,2526],{"class":570,"line":971},[568,2502,2400],{"class":1042},[568,2504,2505],{"class":592}," checkout",[568,2507,1744],{"class":596},[568,2509,2510],{"class":581}," await",[568,2512,2124],{"class":588},[568,2514,1095],{"class":603},[568,2516,1089],{"class":596},[568,2518,2519],{"class":592},"checkouts",[568,2521,1089],{"class":596},[568,2523,2297],{"class":588},[568,2525,593],{"class":603},[568,2527,597],{"class":596},[568,2529,2530,2533,2535,2537,2539,2541,2543,2545],{"class":570,"line":992},[568,2531,2532],{"class":603},"      products",[568,2534,607],{"class":596},[568,2536,728],{"class":603},[568,2538,2450],{"class":592},[568,2540,1089],{"class":596},[568,2542,2455],{"class":592},[568,2544,738],{"class":603},[568,2546,851],{"class":596},[568,2548,2549,2552,2554,2556,2558,2561,2564,2566,2568,2571,2573],{"class":570,"line":997},[568,2550,2551],{"class":603},"      externalCustomerId",[568,2553,607],{"class":596},[568,2555,1765],{"class":592},[568,2557,1089],{"class":596},[568,2559,2560],{"class":592},"tenant",[568,2562,2563],{"class":596},"!.",[568,2565,1081],{"class":592},[568,2567,2192],{"class":581},[568,2569,2570],{"class":1674}," string",[568,2572,632],{"class":596},[568,2574,2575],{"class":574}," \u002F\u002F the WORKSPACE is the customer\n",[568,2577,2578,2581,2583,2585,2587,2589,2591,2594],{"class":570,"line":1003},[568,2579,2580],{"class":603},"      customerEmail",[568,2582,607],{"class":596},[568,2584,1765],{"class":592},[568,2586,1089],{"class":596},[568,2588,1711],{"class":592},[568,2590,2563],{"class":596},[568,2592,2593],{"class":592},"email",[568,2595,851],{"class":596},[568,2597,2598,2601,2603,2605,2608,2610,2613,2615,2617,2619,2621,2623,2625,2627],{"class":570,"line":1009},[568,2599,2600],{"class":603},"      metadata",[568,2602,607],{"class":596},[568,2604,621],{"class":596},[568,2606,2607],{"class":603}," organizationId",[568,2609,607],{"class":596},[568,2611,2612],{"class":588}," String",[568,2614,593],{"class":603},[568,2616,1669],{"class":592},[568,2618,1089],{"class":596},[568,2620,2560],{"class":592},[568,2622,2563],{"class":596},[568,2624,1081],{"class":592},[568,2626,2458],{"class":603},[568,2628,2629],{"class":596},"},\n",[568,2631,2632,2635,2637,2640,2643,2646,2648,2651,2653,2656,2659],{"class":570,"line":1015},[568,2633,2634],{"class":603},"      successUrl",[568,2636,607],{"class":596},[568,2638,2639],{"class":596}," `${",[568,2641,2642],{"class":588},"getRequestURL",[568,2644,2645],{"class":592},"(event)",[568,2647,1089],{"class":596},[568,2649,2650],{"class":592},"origin",[568,2652,1018],{"class":596},[568,2654,2655],{"class":652},"\u002Fapp\u002Fbilling?success=1",[568,2657,2658],{"class":596},"`",[568,2660,851],{"class":596},[568,2662,2664,2667],{"class":570,"line":2663},20,[568,2665,2666],{"class":596},"    }",[568,2668,1021],{"class":603},[568,2670,2672,2675,2677,2680,2682,2684,2686,2689],{"class":570,"line":2671},21,[568,2673,2674],{"class":581},"    return",[568,2676,621],{"class":596},[568,2678,2679],{"class":603}," url",[568,2681,607],{"class":596},[568,2683,2505],{"class":592},[568,2685,1089],{"class":596},[568,2687,2688],{"class":592},"url",[568,2690,2691],{"class":596}," }\n",[568,2693,2695],{"class":570,"line":2694},22,[568,2696,1012],{"class":596},[568,2698,2700,2702],{"class":570,"line":2699},23,[568,2701,1018],{"class":596},[568,2703,1021],{"class":592},[543,2705,2707],{"className":562,"code":2706,"language":564,"meta":551,"style":551},"\u002F\u002F server\u002Fapi\u002Fbilling\u002Fportal.get.ts\nexport default createEndpoint({\n  resource: 'subscriptions',\n  operation: 'get',\n  endpointName: 'portal',\n  async handler(ctx) {\n    const session = await usePolar().customerSessions.create({ externalCustomerId: String(ctx.tenant!.id) })\n    return { url: session.customerPortalUrl }\n  },\n})\n",[522,2708,2709,2714,2726,2740,2755,2770,2784,2837,2856,2860],{"__ignoreMap":551},[568,2710,2711],{"class":570,"line":571},[568,2712,2713],{"class":574},"\u002F\u002F server\u002Fapi\u002Fbilling\u002Fportal.get.ts\n",[568,2715,2716,2718,2720,2722,2724],{"class":570,"line":578},[568,2717,582],{"class":581},[568,2719,585],{"class":581},[568,2721,2266],{"class":588},[568,2723,593],{"class":592},[568,2725,597],{"class":596},[568,2727,2728,2730,2732,2734,2736,2738],{"class":570,"line":600},[568,2729,2275],{"class":603},[568,2731,607],{"class":596},[568,2733,649],{"class":596},[568,2735,524],{"class":652},[568,2737,656],{"class":596},[568,2739,851],{"class":596},[568,2741,2742,2744,2746,2748,2751,2753],{"class":570,"line":613},[568,2743,2290],{"class":603},[568,2745,607],{"class":596},[568,2747,649],{"class":596},[568,2749,2750],{"class":652},"get",[568,2752,656],{"class":596},[568,2754,851],{"class":596},[568,2756,2757,2759,2761,2763,2766,2768],{"class":570,"line":665},[568,2758,2306],{"class":603},[568,2760,607],{"class":596},[568,2762,649],{"class":596},[568,2764,2765],{"class":652},"portal",[568,2767,656],{"class":596},[568,2769,851],{"class":596},[568,2771,2772,2774,2776,2778,2780,2782],{"class":570,"line":675},[568,2773,2379],{"class":1042},[568,2775,2382],{"class":603},[568,2777,593],{"class":596},[568,2779,1669],{"class":1668},[568,2781,1086],{"class":596},[568,2783,610],{"class":596},[568,2785,2786,2788,2791,2793,2795,2797,2799,2801,2804,2806,2808,2810,2812,2815,2817,2819,2821,2823,2825,2827,2829,2831,2833,2835],{"class":570,"line":685},[568,2787,2400],{"class":1042},[568,2789,2790],{"class":592}," session",[568,2792,1744],{"class":596},[568,2794,2510],{"class":581},[568,2796,2124],{"class":588},[568,2798,1095],{"class":603},[568,2800,1089],{"class":596},[568,2802,2803],{"class":592},"customerSessions",[568,2805,1089],{"class":596},[568,2807,2297],{"class":588},[568,2809,593],{"class":603},[568,2811,2165],{"class":596},[568,2813,2814],{"class":603}," externalCustomerId",[568,2816,607],{"class":596},[568,2818,2612],{"class":588},[568,2820,593],{"class":603},[568,2822,1669],{"class":592},[568,2824,1089],{"class":596},[568,2826,2560],{"class":592},[568,2828,2563],{"class":596},[568,2830,1081],{"class":592},[568,2832,2458],{"class":603},[568,2834,1018],{"class":596},[568,2836,1021],{"class":603},[568,2838,2839,2841,2843,2845,2847,2849,2851,2854],{"class":570,"line":765},[568,2840,2674],{"class":581},[568,2842,621],{"class":596},[568,2844,2679],{"class":603},[568,2846,607],{"class":596},[568,2848,2790],{"class":592},[568,2850,1089],{"class":596},[568,2852,2853],{"class":592},"customerPortalUrl",[568,2855,2691],{"class":596},[568,2857,2858],{"class":570,"line":775},[568,2859,1012],{"class":596},[568,2861,2862,2864],{"class":570,"line":854},[568,2863,1018],{"class":596},[568,2865,1021],{"class":592},[518,2867,2868,2871],{},[522,2869,2870],{},"ctx.tenant"," is the active workspace resolved on the server — never trust an organization id from the body.",[553,2873,2875],{"id":2874},"_4-the-webhook","4. The webhook",[518,2877,2878,2879,607],{},"The webhook is a plain Nitro route (the provider is not a user), verifies the signature, is idempotent, and writes\nwith Drizzle directly — the only writer of ",[522,2880,524],{},[543,2882,2884],{"className":562,"code":2883,"language":564,"meta":551,"style":551},"\u002F\u002F server\u002Fapi\u002Fwebhooks\u002Fpolar.post.ts\nimport { validateEvent, WebhookVerificationError } from '@polar-sh\u002Fsdk\u002Fwebhooks'\nimport { eq } from 'drizzle-orm'\nimport { billingEvents, subscriptions } from '~~\u002Fserver\u002Fdatabase\u002Fschema'\n\nexport default defineEventHandler(async (event) => {\n  const body = await readRawBody(event)\n  if (!body) throw createError({ statusCode: 400 })\n\n  let payload: any\n  try {\n    payload = validateEvent(body, Object.fromEntries(event.headers.entries()), useRuntimeConfig().polar.webhookSecret)\n  }\n  catch (error) {\n    if (error instanceof WebhookVerificationError) throw createError({ statusCode: 403, message: 'Bad signature' })\n    throw error\n  }\n\n  const { db } = getDb()\n  const eventId = event.headers.get('webhook-id') ?? `${payload.type}:${payload.data?.id}`\n  const [seen] = await db.select().from(billingEvents).where(eq(billingEvents.id, eventId)).limit(1)\n  if (seen) return { ok: true } \u002F\u002F a retry\n  await db.insert(billingEvents).values({ id: eventId, type: payload.type })\n\n  if (payload.type.startsWith('subscription.')) {\n    const sub = payload.data\n    const organizationId = sub.customer?.externalId ?? sub.metadata?.organizationId\n    if (!organizationId) return { ok: true }\n\n    const plans = useRuntimeConfig().public.plans\n    const planId = Object.entries(plans).find(([, p]: any) => p.productId === sub.productId)?.[0] ?? 'free'\n    const status = ['active', 'trialing', 'past_due'].includes(sub.status) ? sub.status : 'canceled'\n    const values = {\n      organizationId,\n      providerSubscriptionId: sub.id,\n      planId: status === 'canceled' ? 'free' : planId,\n      status,\n      currentPeriodEnd: sub.currentPeriodEnd ? new Date(sub.currentPeriodEnd) : null,\n    }\n    await db.insert(subscriptions).values(values)\n      .onConflictDoUpdate({ target: subscriptions.organizationId, set: values })\n  }\n  return { ok: true }\n})\n",[522,2885,2886,2891,2916,2936,2961,2965,2990,3010,3041,3045,3058,3065,3122,3127,3141,3188,3196,3200,3204,3222,3284,3356,3383,3433,3438,3470,3487,3523,3549,3554,3577,3655,3724,3736,3744,3760,3793,3801,3837,3843,3871,3908,3913,3928],{"__ignoreMap":551},[568,2887,2888],{"class":570,"line":571},[568,2889,2890],{"class":574},"\u002F\u002F server\u002Fapi\u002Fwebhooks\u002Fpolar.post.ts\n",[568,2892,2893,2895,2897,2900,2902,2905,2907,2909,2911,2914],{"class":570,"line":578},[568,2894,1622],{"class":581},[568,2896,621],{"class":596},[568,2898,2899],{"class":592}," validateEvent",[568,2901,632],{"class":596},[568,2903,2904],{"class":592}," WebhookVerificationError",[568,2906,759],{"class":596},[568,2908,1640],{"class":581},[568,2910,649],{"class":596},[568,2912,2913],{"class":652},"@polar-sh\u002Fsdk\u002Fwebhooks",[568,2915,1648],{"class":596},[568,2917,2918,2920,2922,2925,2927,2929,2931,2934],{"class":570,"line":600},[568,2919,1622],{"class":581},[568,2921,621],{"class":596},[568,2923,2924],{"class":592}," eq",[568,2926,759],{"class":596},[568,2928,1640],{"class":581},[568,2930,649],{"class":596},[568,2932,2933],{"class":652},"drizzle-orm",[568,2935,1648],{"class":596},[568,2937,2938,2940,2942,2945,2947,2950,2952,2954,2956,2959],{"class":570,"line":613},[568,2939,1622],{"class":581},[568,2941,621],{"class":596},[568,2943,2944],{"class":592}," billingEvents",[568,2946,632],{"class":596},[568,2948,2949],{"class":592}," subscriptions",[568,2951,759],{"class":596},[568,2953,1640],{"class":581},[568,2955,649],{"class":596},[568,2957,2958],{"class":652},"~~\u002Fserver\u002Fdatabase\u002Fschema",[568,2960,1648],{"class":596},[568,2962,2963],{"class":570,"line":665},[568,2964,1452],{"emptyLinePlaceholder":1451},[568,2966,2967,2969,2971,2974,2976,2979,2981,2984,2986,2988],{"class":570,"line":675},[568,2968,582],{"class":581},[568,2970,585],{"class":581},[568,2972,2973],{"class":588}," defineEventHandler",[568,2975,593],{"class":592},[568,2977,2978],{"class":1042},"async",[568,2980,1665],{"class":596},[568,2982,2983],{"class":1668},"event",[568,2985,1086],{"class":596},[568,2987,1107],{"class":1042},[568,2989,610],{"class":596},[568,2991,2992,2994,2997,2999,3001,3004,3006,3008],{"class":570,"line":685},[568,2993,2133],{"class":1042},[568,2995,2996],{"class":592}," body",[568,2998,1744],{"class":596},[568,3000,2510],{"class":581},[568,3002,3003],{"class":588}," readRawBody",[568,3005,593],{"class":603},[568,3007,2983],{"class":592},[568,3009,1021],{"class":603},[568,3011,3012,3015,3017,3019,3021,3023,3025,3027,3029,3031,3033,3035,3037,3039],{"class":570,"line":765},[568,3013,3014],{"class":581},"  if",[568,3016,1665],{"class":603},[568,3018,2447],{"class":596},[568,3020,2429],{"class":592},[568,3022,2458],{"class":603},[568,3024,2461],{"class":581},[568,3026,2464],{"class":588},[568,3028,593],{"class":603},[568,3030,2165],{"class":596},[568,3032,2471],{"class":603},[568,3034,607],{"class":596},[568,3036,2476],{"class":755},[568,3038,759],{"class":596},[568,3040,1021],{"class":603},[568,3042,3043],{"class":570,"line":775},[568,3044,1452],{"emptyLinePlaceholder":1451},[568,3046,3047,3050,3053,3055],{"class":570,"line":854},[568,3048,3049],{"class":1042},"  let",[568,3051,3052],{"class":592}," payload",[568,3054,607],{"class":596},[568,3056,3057],{"class":1674}," any\n",[568,3059,3060,3063],{"class":570,"line":873},[568,3061,3062],{"class":581},"  try",[568,3064,610],{"class":596},[568,3066,3067,3070,3072,3074,3076,3078,3080,3083,3085,3088,3090,3092,3094,3097,3099,3102,3104,3106,3108,3110,3112,3115,3117,3120],{"class":570,"line":879},[568,3068,3069],{"class":592},"    payload",[568,3071,1744],{"class":596},[568,3073,2899],{"class":588},[568,3075,593],{"class":603},[568,3077,2429],{"class":592},[568,3079,632],{"class":596},[568,3081,3082],{"class":592}," Object",[568,3084,1089],{"class":596},[568,3086,3087],{"class":588},"fromEntries",[568,3089,593],{"class":603},[568,3091,2983],{"class":592},[568,3093,1089],{"class":596},[568,3095,3096],{"class":592},"headers",[568,3098,1089],{"class":596},[568,3100,3101],{"class":588},"entries",[568,3103,1118],{"class":603},[568,3105,632],{"class":596},[568,3107,2145],{"class":588},[568,3109,1095],{"class":603},[568,3111,1089],{"class":596},[568,3113,3114],{"class":592},"polar",[568,3116,1089],{"class":596},[568,3118,3119],{"class":592},"webhookSecret",[568,3121,1021],{"class":603},[568,3123,3124],{"class":570,"line":889},[568,3125,3126],{"class":596},"  }\n",[568,3128,3129,3132,3134,3137,3139],{"class":570,"line":971},[568,3130,3131],{"class":581},"  catch",[568,3133,1665],{"class":603},[568,3135,3136],{"class":592},"error",[568,3138,2458],{"class":603},[568,3140,597],{"class":596},[568,3142,3143,3145,3147,3149,3152,3154,3156,3158,3160,3162,3164,3166,3168,3171,3173,3175,3177,3179,3182,3184,3186],{"class":570,"line":992},[568,3144,2442],{"class":581},[568,3146,1665],{"class":603},[568,3148,3136],{"class":592},[568,3150,3151],{"class":596}," instanceof",[568,3153,2904],{"class":1674},[568,3155,2458],{"class":603},[568,3157,2461],{"class":581},[568,3159,2464],{"class":588},[568,3161,593],{"class":603},[568,3163,2165],{"class":596},[568,3165,2471],{"class":603},[568,3167,607],{"class":596},[568,3169,3170],{"class":755}," 403",[568,3172,632],{"class":596},[568,3174,2481],{"class":603},[568,3176,607],{"class":596},[568,3178,649],{"class":596},[568,3180,3181],{"class":652},"Bad signature",[568,3183,656],{"class":596},[568,3185,759],{"class":596},[568,3187,1021],{"class":603},[568,3189,3190,3193],{"class":570,"line":997},[568,3191,3192],{"class":581},"    throw",[568,3194,3195],{"class":592}," error\n",[568,3197,3198],{"class":570,"line":1003},[568,3199,3126],{"class":596},[568,3201,3202],{"class":570,"line":1009},[568,3203,1452],{"emptyLinePlaceholder":1451},[568,3205,3206,3208,3210,3213,3215,3217,3220],{"class":570,"line":1015},[568,3207,2133],{"class":1042},[568,3209,621],{"class":596},[568,3211,3212],{"class":592}," db",[568,3214,759],{"class":596},[568,3216,1744],{"class":596},[568,3218,3219],{"class":588}," getDb",[568,3221,2148],{"class":603},[568,3223,3224,3226,3229,3231,3233,3235,3237,3239,3241,3243,3245,3248,3250,3252,3254,3256,3259,3261,3263,3265,3267,3270,3272,3274,3277,3279,3281],{"class":570,"line":2663},[568,3225,2133],{"class":1042},[568,3227,3228],{"class":592}," eventId",[568,3230,1744],{"class":596},[568,3232,2391],{"class":592},[568,3234,1089],{"class":596},[568,3236,3096],{"class":592},[568,3238,1089],{"class":596},[568,3240,2750],{"class":588},[568,3242,593],{"class":603},[568,3244,656],{"class":596},[568,3246,3247],{"class":652},"webhook-id",[568,3249,656],{"class":596},[568,3251,2458],{"class":603},[568,3253,1720],{"class":596},[568,3255,2639],{"class":596},[568,3257,3258],{"class":592},"payload",[568,3260,1089],{"class":596},[568,3262,1528],{"class":592},[568,3264,1018],{"class":596},[568,3266,607],{"class":652},[568,3268,3269],{"class":596},"${",[568,3271,3258],{"class":592},[568,3273,1089],{"class":596},[568,3275,3276],{"class":592},"data",[568,3278,1714],{"class":596},[568,3280,1081],{"class":592},[568,3282,3283],{"class":596},"}`\n",[568,3285,3286,3288,3290,3293,3295,3297,3299,3301,3303,3306,3308,3310,3313,3315,3317,3319,3321,3324,3326,3329,3331,3333,3335,3337,3339,3341,3344,3346,3349,3351,3354],{"class":570,"line":2671},[568,3287,2133],{"class":1042},[568,3289,728],{"class":596},[568,3291,3292],{"class":592},"seen",[568,3294,738],{"class":596},[568,3296,1744],{"class":596},[568,3298,2510],{"class":581},[568,3300,3212],{"class":592},[568,3302,1089],{"class":596},[568,3304,3305],{"class":588},"select",[568,3307,1095],{"class":603},[568,3309,1089],{"class":596},[568,3311,3312],{"class":588},"from",[568,3314,593],{"class":603},[568,3316,2051],{"class":592},[568,3318,1086],{"class":603},[568,3320,1089],{"class":596},[568,3322,3323],{"class":588},"where",[568,3325,593],{"class":603},[568,3327,3328],{"class":588},"eq",[568,3330,593],{"class":603},[568,3332,2051],{"class":592},[568,3334,1089],{"class":596},[568,3336,1081],{"class":592},[568,3338,632],{"class":596},[568,3340,3228],{"class":592},[568,3342,3343],{"class":603},"))",[568,3345,1089],{"class":596},[568,3347,3348],{"class":588},"limit",[568,3350,593],{"class":603},[568,3352,3353],{"class":755},"1",[568,3355,1021],{"class":603},[568,3357,3358,3360,3362,3364,3366,3369,3371,3374,3376,3378,3380],{"class":570,"line":2694},[568,3359,3014],{"class":581},[568,3361,1665],{"class":603},[568,3363,3292],{"class":592},[568,3365,2458],{"class":603},[568,3367,3368],{"class":581},"return",[568,3370,621],{"class":596},[568,3372,3373],{"class":603}," ok",[568,3375,607],{"class":596},[568,3377,2330],{"class":1791},[568,3379,759],{"class":596},[568,3381,3382],{"class":574}," \u002F\u002F a retry\n",[568,3384,3385,3388,3390,3392,3395,3397,3399,3401,3403,3406,3408,3410,3413,3415,3417,3419,3421,3423,3425,3427,3429,3431],{"class":570,"line":2699},[568,3386,3387],{"class":581},"  await",[568,3389,3212],{"class":592},[568,3391,1089],{"class":596},[568,3393,3394],{"class":588},"insert",[568,3396,593],{"class":603},[568,3398,2051],{"class":592},[568,3400,1086],{"class":603},[568,3402,1089],{"class":596},[568,3404,3405],{"class":588},"values",[568,3407,593],{"class":603},[568,3409,2165],{"class":596},[568,3411,3412],{"class":603}," id",[568,3414,607],{"class":596},[568,3416,3228],{"class":592},[568,3418,632],{"class":596},[568,3420,1625],{"class":603},[568,3422,607],{"class":596},[568,3424,3052],{"class":592},[568,3426,1089],{"class":596},[568,3428,1528],{"class":592},[568,3430,759],{"class":596},[568,3432,1021],{"class":603},[568,3434,3436],{"class":570,"line":3435},24,[568,3437,1452],{"emptyLinePlaceholder":1451},[568,3439,3441,3443,3445,3447,3449,3451,3453,3456,3458,3460,3463,3465,3468],{"class":570,"line":3440},25,[568,3442,3014],{"class":581},[568,3444,1665],{"class":603},[568,3446,3258],{"class":592},[568,3448,1089],{"class":596},[568,3450,1528],{"class":592},[568,3452,1089],{"class":596},[568,3454,3455],{"class":588},"startsWith",[568,3457,593],{"class":603},[568,3459,656],{"class":596},[568,3461,3462],{"class":652},"subscription.",[568,3464,656],{"class":596},[568,3466,3467],{"class":603},")) ",[568,3469,597],{"class":596},[568,3471,3473,3475,3478,3480,3482,3484],{"class":570,"line":3472},26,[568,3474,2400],{"class":1042},[568,3476,3477],{"class":592}," sub",[568,3479,1744],{"class":596},[568,3481,3052],{"class":592},[568,3483,1089],{"class":596},[568,3485,3486],{"class":592},"data\n",[568,3488,3490,3492,3494,3496,3498,3500,3503,3505,3508,3511,3513,3515,3518,3520],{"class":570,"line":3489},27,[568,3491,2400],{"class":1042},[568,3493,2607],{"class":592},[568,3495,1744],{"class":596},[568,3497,3477],{"class":592},[568,3499,1089],{"class":596},[568,3501,3502],{"class":592},"customer",[568,3504,1714],{"class":596},[568,3506,3507],{"class":592},"externalId",[568,3509,3510],{"class":596}," ??",[568,3512,3477],{"class":592},[568,3514,1089],{"class":596},[568,3516,3517],{"class":592},"metadata",[568,3519,1714],{"class":596},[568,3521,3522],{"class":592},"organizationId\n",[568,3524,3526,3528,3530,3532,3535,3537,3539,3541,3543,3545,3547],{"class":570,"line":3525},28,[568,3527,2442],{"class":581},[568,3529,1665],{"class":603},[568,3531,2447],{"class":596},[568,3533,3534],{"class":592},"organizationId",[568,3536,2458],{"class":603},[568,3538,3368],{"class":581},[568,3540,621],{"class":596},[568,3542,3373],{"class":603},[568,3544,607],{"class":596},[568,3546,2330],{"class":1791},[568,3548,2691],{"class":596},[568,3550,3552],{"class":570,"line":3551},29,[568,3553,1452],{"emptyLinePlaceholder":1451},[568,3555,3557,3559,3562,3564,3566,3568,3570,3572,3574],{"class":570,"line":3556},30,[568,3558,2400],{"class":1042},[568,3560,3561],{"class":592}," plans",[568,3563,1744],{"class":596},[568,3565,2145],{"class":588},[568,3567,1095],{"class":603},[568,3569,1089],{"class":596},[568,3571,2414],{"class":592},[568,3573,1089],{"class":596},[568,3575,3576],{"class":592},"plans\n",[568,3578,3580,3582,3584,3586,3588,3590,3592,3594,3596,3598,3600,3603,3605,3608,3611,3614,3617,3619,3621,3623,3625,3627,3630,3632,3634,3636,3638,3640,3642,3645,3647,3649,3651,3653],{"class":570,"line":3579},31,[568,3581,2400],{"class":1042},[568,3583,2356],{"class":592},[568,3585,1744],{"class":596},[568,3587,3082],{"class":592},[568,3589,1089],{"class":596},[568,3591,3101],{"class":588},[568,3593,593],{"class":603},[568,3595,2419],{"class":592},[568,3597,1086],{"class":603},[568,3599,1089],{"class":596},[568,3601,3602],{"class":588},"find",[568,3604,593],{"class":603},[568,3606,3607],{"class":596},"([,",[568,3609,3610],{"class":1668}," p",[568,3612,3613],{"class":596},"]:",[568,3615,3616],{"class":1674}," any",[568,3618,1086],{"class":596},[568,3620,1107],{"class":1042},[568,3622,3610],{"class":592},[568,3624,1089],{"class":596},[568,3626,2455],{"class":592},[568,3628,3629],{"class":596}," ===",[568,3631,3477],{"class":592},[568,3633,1089],{"class":596},[568,3635,2455],{"class":592},[568,3637,1086],{"class":603},[568,3639,1714],{"class":596},[568,3641,2422],{"class":603},[568,3643,3644],{"class":755},"0",[568,3646,1300],{"class":603},[568,3648,1720],{"class":596},[568,3650,649],{"class":596},[568,3652,1219],{"class":652},[568,3654,1648],{"class":596},[568,3656,3658,3660,3663,3665,3667,3669,3671,3673,3675,3677,3679,3681,3683,3685,3687,3689,3691,3693,3695,3697,3700,3702,3704,3706,3709,3711,3713,3715,3718,3720,3722],{"class":570,"line":3657},32,[568,3659,2400],{"class":1042},[568,3661,3662],{"class":592}," status",[568,3664,1744],{"class":596},[568,3666,728],{"class":603},[568,3668,656],{"class":596},[568,3670,1259],{"class":652},[568,3672,656],{"class":596},[568,3674,632],{"class":596},[568,3676,649],{"class":596},[568,3678,1268],{"class":652},[568,3680,656],{"class":596},[568,3682,632],{"class":596},[568,3684,649],{"class":596},[568,3686,1277],{"class":652},[568,3688,656],{"class":596},[568,3690,738],{"class":603},[568,3692,1089],{"class":596},[568,3694,1703],{"class":588},[568,3696,593],{"class":603},[568,3698,3699],{"class":592},"sub",[568,3701,1089],{"class":596},[568,3703,1241],{"class":592},[568,3705,2458],{"class":603},[568,3707,3708],{"class":596},"?",[568,3710,3477],{"class":592},[568,3712,1089],{"class":596},[568,3714,1241],{"class":592},[568,3716,3717],{"class":596}," :",[568,3719,649],{"class":596},[568,3721,1286],{"class":652},[568,3723,1648],{"class":596},[568,3725,3727,3729,3732,3734],{"class":570,"line":3726},33,[568,3728,2400],{"class":1042},[568,3730,3731],{"class":592}," values",[568,3733,1744],{"class":596},[568,3735,610],{"class":596},[568,3737,3739,3742],{"class":570,"line":3738},34,[568,3740,3741],{"class":592},"      organizationId",[568,3743,851],{"class":596},[568,3745,3747,3750,3752,3754,3756,3758],{"class":570,"line":3746},35,[568,3748,3749],{"class":603},"      providerSubscriptionId",[568,3751,607],{"class":596},[568,3753,3477],{"class":592},[568,3755,1089],{"class":596},[568,3757,1081],{"class":592},[568,3759,851],{"class":596},[568,3761,3763,3766,3768,3770,3772,3774,3776,3778,3781,3783,3785,3787,3789,3791],{"class":570,"line":3762},36,[568,3764,3765],{"class":603},"      planId",[568,3767,607],{"class":596},[568,3769,3662],{"class":592},[568,3771,3629],{"class":596},[568,3773,649],{"class":596},[568,3775,1286],{"class":652},[568,3777,656],{"class":596},[568,3779,3780],{"class":596}," ?",[568,3782,649],{"class":596},[568,3784,1219],{"class":652},[568,3786,656],{"class":596},[568,3788,3717],{"class":596},[568,3790,2356],{"class":592},[568,3792,851],{"class":596},[568,3794,3796,3799],{"class":570,"line":3795},37,[568,3797,3798],{"class":592},"      status",[568,3800,851],{"class":596},[568,3802,3804,3807,3809,3811,3813,3816,3818,3820,3822,3824,3826,3828,3830,3832,3834],{"class":570,"line":3803},38,[568,3805,3806],{"class":603},"      currentPeriodEnd",[568,3808,607],{"class":596},[568,3810,3477],{"class":592},[568,3812,1089],{"class":596},[568,3814,3815],{"class":592},"currentPeriodEnd",[568,3817,3780],{"class":596},[568,3819,1416],{"class":596},[568,3821,1419],{"class":588},[568,3823,593],{"class":603},[568,3825,3699],{"class":592},[568,3827,1089],{"class":596},[568,3829,3815],{"class":592},[568,3831,2458],{"class":603},[568,3833,607],{"class":596},[568,3835,3836],{"class":596}," null,\n",[568,3838,3840],{"class":570,"line":3839},39,[568,3841,3842],{"class":596},"    }\n",[568,3844,3846,3849,3851,3853,3855,3857,3859,3861,3863,3865,3867,3869],{"class":570,"line":3845},40,[568,3847,3848],{"class":581},"    await",[568,3850,3212],{"class":592},[568,3852,1089],{"class":596},[568,3854,3394],{"class":588},[568,3856,593],{"class":603},[568,3858,524],{"class":592},[568,3860,1086],{"class":603},[568,3862,1089],{"class":596},[568,3864,3405],{"class":588},[568,3866,593],{"class":603},[568,3868,3405],{"class":592},[568,3870,1021],{"class":603},[568,3872,3874,3877,3880,3882,3884,3887,3889,3891,3893,3895,3897,3900,3902,3904,3906],{"class":570,"line":3873},41,[568,3875,3876],{"class":596},"      .",[568,3878,3879],{"class":588},"onConflictDoUpdate",[568,3881,593],{"class":603},[568,3883,2165],{"class":596},[568,3885,3886],{"class":603}," target",[568,3888,607],{"class":596},[568,3890,2949],{"class":592},[568,3892,1089],{"class":596},[568,3894,3534],{"class":592},[568,3896,632],{"class":596},[568,3898,3899],{"class":603}," set",[568,3901,607],{"class":596},[568,3903,3731],{"class":592},[568,3905,759],{"class":596},[568,3907,1021],{"class":603},[568,3909,3911],{"class":570,"line":3910},42,[568,3912,3126],{"class":596},[568,3914,3916,3918,3920,3922,3924,3926],{"class":570,"line":3915},43,[568,3917,2216],{"class":581},[568,3919,621],{"class":596},[568,3921,3373],{"class":603},[568,3923,607],{"class":596},[568,3925,2330],{"class":1791},[568,3927,2691],{"class":596},[568,3929,3931,3933],{"class":570,"line":3930},44,[568,3932,1018],{"class":596},[568,3934,1021],{"class":592},[3936,3937,3939],"callout",{"icon":3938},"i-lucide-triangle-alert",[518,3940,3941,3942,3945],{},"Answer within the provider's timeout (Polar: a few seconds). If processing grows, acknowledge first and finish in\nthe background (",[522,3943,3944],{},"event.waitUntil(...)"," on Cloudflare, a queue elsewhere).",[553,3947,3949],{"id":3948},"_5-put-the-plan-on-every-request","5. Put the plan on every request",[518,3951,3952],{},"A context extender runs after authentication and before authorization, so every permission function, hook and\ncustom endpoint can read the plan:",[543,3954,3956],{"className":562,"code":3955,"language":564,"meta":551,"style":551},"\u002F\u002F server\u002Fautoapi-plugins.ts\nimport { createBetterAuthPlugin, defineAutoApiPlugin } from '@websideproject\u002Fnuxt-auto-api\u002Fplugins'\nimport { eq } from 'drizzle-orm'\nimport { subscriptions } from '.\u002Fdatabase\u002Fschema'\n\nconst billingContext = defineAutoApiPlugin({\n  name: 'billing-context',\n  runtimeSetup(ctx) {\n    const plans = (ctx.runtimeConfig.public as any).plans\n    ctx.extendContext(async (handlerCtx) => {\n      const orgId = handlerCtx.user?.organizationId\n      if (!orgId) return\n      const [sub] = await handlerCtx.db.select().from(subscriptions).where(eq(subscriptions.organizationId, orgId)).limit(1)\n      const planId = sub && ['active', 'trialing', 'past_due'].includes(sub.status) ? sub.planId : 'free'\n      handlerCtx.requestMeta = {\n        ...handlerCtx.requestMeta,\n        billing: {\n          planId,\n          status: sub?.status ?? 'none',\n          features: plans[planId]?.features ?? [],\n          limits: plans[planId]?.limits ?? {},\n        },\n      }\n    })\n  },\n})\n\n\u002F\u002F Order matters: the session plugin sets ctx.user first.\nexport default [createBetterAuthPlugin({ \u002F* … see Better-Auth *\u002F }), billingContext]\n",[522,3957,3958,3963,3988,4006,4025,4029,4044,4060,4073,4104,4129,4150,4167,4236,4305,4319,4332,4341,4348,4371,4398,4422,4426,4431,4437,4441,4447,4451,4456],{"__ignoreMap":551},[568,3959,3960],{"class":570,"line":571},[568,3961,3962],{"class":574},"\u002F\u002F server\u002Fautoapi-plugins.ts\n",[568,3964,3965,3967,3969,3972,3974,3977,3979,3981,3983,3986],{"class":570,"line":578},[568,3966,1622],{"class":581},[568,3968,621],{"class":596},[568,3970,3971],{"class":592}," createBetterAuthPlugin",[568,3973,632],{"class":596},[568,3975,3976],{"class":592}," defineAutoApiPlugin",[568,3978,759],{"class":596},[568,3980,1640],{"class":581},[568,3982,649],{"class":596},[568,3984,3985],{"class":652},"@websideproject\u002Fnuxt-auto-api\u002Fplugins",[568,3987,1648],{"class":596},[568,3989,3990,3992,3994,3996,3998,4000,4002,4004],{"class":570,"line":600},[568,3991,1622],{"class":581},[568,3993,621],{"class":596},[568,3995,2924],{"class":592},[568,3997,759],{"class":596},[568,3999,1640],{"class":581},[568,4001,649],{"class":596},[568,4003,2933],{"class":652},[568,4005,1648],{"class":596},[568,4007,4008,4010,4012,4014,4016,4018,4020,4023],{"class":570,"line":613},[568,4009,1622],{"class":581},[568,4011,621],{"class":596},[568,4013,2949],{"class":592},[568,4015,759],{"class":596},[568,4017,1640],{"class":581},[568,4019,649],{"class":596},[568,4021,4022],{"class":652},".\u002Fdatabase\u002Fschema",[568,4024,1648],{"class":596},[568,4026,4027],{"class":570,"line":665},[568,4028,1452],{"emptyLinePlaceholder":1451},[568,4030,4031,4033,4036,4038,4040,4042],{"class":570,"line":675},[568,4032,1657],{"class":1042},[568,4034,4035],{"class":592}," billingContext ",[568,4037,1049],{"class":596},[568,4039,3976],{"class":588},[568,4041,593],{"class":592},[568,4043,597],{"class":596},[568,4045,4046,4049,4051,4053,4056,4058],{"class":570,"line":685},[568,4047,4048],{"class":603},"  name",[568,4050,607],{"class":596},[568,4052,649],{"class":596},[568,4054,4055],{"class":652},"billing-context",[568,4057,656],{"class":596},[568,4059,851],{"class":596},[568,4061,4062,4065,4067,4069,4071],{"class":570,"line":765},[568,4063,4064],{"class":603},"  runtimeSetup",[568,4066,593],{"class":596},[568,4068,1669],{"class":1668},[568,4070,1086],{"class":596},[568,4072,610],{"class":596},[568,4074,4075,4077,4079,4081,4083,4085,4087,4090,4092,4094,4096,4098,4100,4102],{"class":570,"line":775},[568,4076,2400],{"class":1042},[568,4078,3561],{"class":592},[568,4080,1744],{"class":596},[568,4082,1665],{"class":603},[568,4084,1669],{"class":592},[568,4086,1089],{"class":596},[568,4088,4089],{"class":592},"runtimeConfig",[568,4091,1089],{"class":596},[568,4093,2414],{"class":592},[568,4095,2192],{"class":581},[568,4097,3616],{"class":1674},[568,4099,1086],{"class":603},[568,4101,1089],{"class":596},[568,4103,3576],{"class":592},[568,4105,4106,4109,4111,4114,4116,4118,4120,4123,4125,4127],{"class":570,"line":854},[568,4107,4108],{"class":592},"    ctx",[568,4110,1089],{"class":596},[568,4112,4113],{"class":588},"extendContext",[568,4115,593],{"class":603},[568,4117,2978],{"class":1042},[568,4119,1665],{"class":596},[568,4121,4122],{"class":1668},"handlerCtx",[568,4124,1086],{"class":596},[568,4126,1107],{"class":1042},[568,4128,610],{"class":596},[568,4130,4131,4134,4137,4139,4142,4144,4146,4148],{"class":570,"line":873},[568,4132,4133],{"class":1042},"      const",[568,4135,4136],{"class":592}," orgId",[568,4138,1744],{"class":596},[568,4140,4141],{"class":592}," handlerCtx",[568,4143,1089],{"class":596},[568,4145,1711],{"class":592},[568,4147,1714],{"class":596},[568,4149,3522],{"class":592},[568,4151,4152,4155,4157,4159,4162,4164],{"class":570,"line":879},[568,4153,4154],{"class":581},"      if",[568,4156,1665],{"class":603},[568,4158,2447],{"class":596},[568,4160,4161],{"class":592},"orgId",[568,4163,2458],{"class":603},[568,4165,4166],{"class":581},"return\n",[568,4168,4169,4171,4173,4175,4177,4179,4181,4183,4185,4188,4190,4192,4194,4196,4198,4200,4202,4204,4206,4208,4210,4212,4214,4216,4218,4220,4222,4224,4226,4228,4230,4232,4234],{"class":570,"line":889},[568,4170,4133],{"class":1042},[568,4172,728],{"class":596},[568,4174,3699],{"class":592},[568,4176,738],{"class":596},[568,4178,1744],{"class":596},[568,4180,2510],{"class":581},[568,4182,4141],{"class":592},[568,4184,1089],{"class":596},[568,4186,4187],{"class":592},"db",[568,4189,1089],{"class":596},[568,4191,3305],{"class":588},[568,4193,1095],{"class":603},[568,4195,1089],{"class":596},[568,4197,3312],{"class":588},[568,4199,593],{"class":603},[568,4201,524],{"class":592},[568,4203,1086],{"class":603},[568,4205,1089],{"class":596},[568,4207,3323],{"class":588},[568,4209,593],{"class":603},[568,4211,3328],{"class":588},[568,4213,593],{"class":603},[568,4215,524],{"class":592},[568,4217,1089],{"class":596},[568,4219,3534],{"class":592},[568,4221,632],{"class":596},[568,4223,4136],{"class":592},[568,4225,3343],{"class":603},[568,4227,1089],{"class":596},[568,4229,3348],{"class":588},[568,4231,593],{"class":603},[568,4233,3353],{"class":755},[568,4235,1021],{"class":603},[568,4237,4238,4240,4242,4244,4246,4249,4251,4253,4255,4257,4259,4261,4263,4265,4267,4269,4271,4273,4275,4277,4279,4281,4283,4285,4287,4289,4291,4293,4295,4297,4299,4301,4303],{"class":570,"line":971},[568,4239,4133],{"class":1042},[568,4241,2356],{"class":592},[568,4243,1744],{"class":596},[568,4245,3477],{"class":592},[568,4247,4248],{"class":596}," &&",[568,4250,728],{"class":603},[568,4252,656],{"class":596},[568,4254,1259],{"class":652},[568,4256,656],{"class":596},[568,4258,632],{"class":596},[568,4260,649],{"class":596},[568,4262,1268],{"class":652},[568,4264,656],{"class":596},[568,4266,632],{"class":596},[568,4268,649],{"class":596},[568,4270,1277],{"class":652},[568,4272,656],{"class":596},[568,4274,738],{"class":603},[568,4276,1089],{"class":596},[568,4278,1703],{"class":588},[568,4280,593],{"class":603},[568,4282,3699],{"class":592},[568,4284,1089],{"class":596},[568,4286,1241],{"class":592},[568,4288,2458],{"class":603},[568,4290,3708],{"class":596},[568,4292,3477],{"class":592},[568,4294,1089],{"class":596},[568,4296,2434],{"class":592},[568,4298,3717],{"class":596},[568,4300,649],{"class":596},[568,4302,1219],{"class":652},[568,4304,1648],{"class":596},[568,4306,4307,4310,4312,4315,4317],{"class":570,"line":992},[568,4308,4309],{"class":592},"      handlerCtx",[568,4311,1089],{"class":596},[568,4313,4314],{"class":592},"requestMeta",[568,4316,1744],{"class":596},[568,4318,610],{"class":596},[568,4320,4321,4324,4326,4328,4330],{"class":570,"line":997},[568,4322,4323],{"class":596},"        ...",[568,4325,4122],{"class":592},[568,4327,1089],{"class":596},[568,4329,4314],{"class":592},[568,4331,851],{"class":596},[568,4333,4334,4337,4339],{"class":570,"line":1003},[568,4335,4336],{"class":603},"        billing",[568,4338,607],{"class":596},[568,4340,610],{"class":596},[568,4342,4343,4346],{"class":570,"line":1009},[568,4344,4345],{"class":592},"          planId",[568,4347,851],{"class":596},[568,4349,4350,4353,4355,4357,4359,4361,4363,4365,4367,4369],{"class":570,"line":1015},[568,4351,4352],{"class":603},"          status",[568,4354,607],{"class":596},[568,4356,3477],{"class":592},[568,4358,1714],{"class":596},[568,4360,1241],{"class":592},[568,4362,3510],{"class":596},[568,4364,649],{"class":596},[568,4366,1295],{"class":652},[568,4368,656],{"class":596},[568,4370,851],{"class":596},[568,4372,4373,4376,4378,4380,4382,4384,4386,4388,4391,4393,4396],{"class":570,"line":2663},[568,4374,4375],{"class":603},"          features",[568,4377,607],{"class":596},[568,4379,3561],{"class":592},[568,4381,2422],{"class":603},[568,4383,2434],{"class":592},[568,4385,738],{"class":603},[568,4387,1714],{"class":596},[568,4389,4390],{"class":592},"features",[568,4392,3510],{"class":596},[568,4394,4395],{"class":603}," []",[568,4397,851],{"class":596},[568,4399,4400,4402,4404,4406,4408,4410,4412,4414,4417,4419],{"class":570,"line":2671},[568,4401,857],{"class":603},[568,4403,607],{"class":596},[568,4405,3561],{"class":592},[568,4407,2422],{"class":603},[568,4409,2434],{"class":592},[568,4411,738],{"class":603},[568,4413,1714],{"class":596},[568,4415,4416],{"class":592},"limits",[568,4418,3510],{"class":596},[568,4420,4421],{"class":596}," {},\n",[568,4423,4424],{"class":570,"line":2694},[568,4425,876],{"class":596},[568,4427,4428],{"class":570,"line":2699},[568,4429,4430],{"class":596},"      }\n",[568,4432,4433,4435],{"class":570,"line":3435},[568,4434,2666],{"class":596},[568,4436,1021],{"class":603},[568,4438,4439],{"class":570,"line":3440},[568,4440,1012],{"class":596},[568,4442,4443,4445],{"class":570,"line":3472},[568,4444,1018],{"class":596},[568,4446,1021],{"class":592},[568,4448,4449],{"class":570,"line":3489},[568,4450,1452],{"emptyLinePlaceholder":1451},[568,4452,4453],{"class":570,"line":3525},[568,4454,4455],{"class":574},"\u002F\u002F Order matters: the session plugin sets ctx.user first.\n",[568,4457,4458,4460,4462,4464,4467,4469,4471,4474,4476,4478,4480],{"class":570,"line":3551},[568,4459,582],{"class":581},[568,4461,585],{"class":581},[568,4463,728],{"class":592},[568,4465,4466],{"class":588},"createBetterAuthPlugin",[568,4468,593],{"class":592},[568,4470,2165],{"class":596},[568,4472,4473],{"class":574}," \u002F* … see Better-Auth *\u002F",[568,4475,759],{"class":596},[568,4477,1086],{"class":592},[568,4479,632],{"class":596},[568,4481,4482],{"class":592}," billingContext]\n",[518,4484,4485],{},"This costs one indexed query per API request. Cache it (KV, or on the session) if that matters for you.",[553,4487,4489],{"id":4488},"_6-gate-features","6. Gate features",[518,4491,4492],{},"A feature is a permission like any other:",[543,4494,4496],{"className":562,"code":4495,"language":564,"meta":551,"style":551},"\u002F\u002F modules\u002Freports\u002Fauth.ts\nconst hasFeature = (name: string) => (ctx: HandlerContext) => ctx.requestMeta?.billing?.features.includes(name) ?? false\n\nexport const reportsAuth: ResourceAuthConfig = {\n  permissions: {\n    read: hasFeature('reports'),\n    create: hasFeature('reports'),\n    update: hasFeature('reports'),\n    delete: hasFeature('reports'),\n    aggregate: hasFeature('reports'),\n  },\n}\n",[522,4497,4498,4503,4564,4568,4585,4593,4614,4634,4654,4674,4695,4699],{"__ignoreMap":551},[568,4499,4500],{"class":570,"line":571},[568,4501,4502],{"class":574},"\u002F\u002F modules\u002Freports\u002Fauth.ts\n",[568,4504,4505,4507,4510,4512,4514,4517,4519,4521,4523,4525,4527,4529,4531,4533,4535,4537,4539,4541,4543,4545,4548,4550,4552,4554,4556,4559,4561],{"class":570,"line":578},[568,4506,1657],{"class":1042},[568,4508,4509],{"class":592}," hasFeature ",[568,4511,1049],{"class":596},[568,4513,1665],{"class":596},[568,4515,4516],{"class":1668},"name",[568,4518,607],{"class":596},[568,4520,2570],{"class":1674},[568,4522,1086],{"class":596},[568,4524,1107],{"class":1042},[568,4526,1665],{"class":596},[568,4528,1669],{"class":1668},[568,4530,607],{"class":596},[568,4532,1630],{"class":1674},[568,4534,1086],{"class":596},[568,4536,1107],{"class":1042},[568,4538,1765],{"class":592},[568,4540,1089],{"class":596},[568,4542,4314],{"class":592},[568,4544,1714],{"class":596},[568,4546,4547],{"class":592},"billing",[568,4549,1714],{"class":596},[568,4551,4390],{"class":592},[568,4553,1089],{"class":596},[568,4555,1703],{"class":588},[568,4557,4558],{"class":592},"(name) ",[568,4560,1720],{"class":596},[568,4562,4563],{"class":1791}," false\n",[568,4565,4566],{"class":570,"line":600},[568,4567,1452],{"emptyLinePlaceholder":1451},[568,4569,4570,4572,4574,4577,4579,4581,4583],{"class":570,"line":613},[568,4571,582],{"class":581},[568,4573,1043],{"class":1042},[568,4575,4576],{"class":592}," reportsAuth",[568,4578,607],{"class":596},[568,4580,1635],{"class":1674},[568,4582,1744],{"class":596},[568,4584,610],{"class":596},[568,4586,4587,4589,4591],{"class":570,"line":665},[568,4588,1751],{"class":603},[568,4590,607],{"class":596},[568,4592,610],{"class":596},[568,4594,4595,4597,4599,4602,4604,4606,4608,4610,4612],{"class":570,"line":675},[568,4596,1760],{"class":603},[568,4598,607],{"class":596},[568,4600,4601],{"class":588}," hasFeature",[568,4603,593],{"class":592},[568,4605,656],{"class":596},[568,4607,835],{"class":652},[568,4609,656],{"class":596},[568,4611,1086],{"class":592},[568,4613,851],{"class":596},[568,4615,4616,4618,4620,4622,4624,4626,4628,4630,4632],{"class":570,"line":685},[568,4617,1786],{"class":603},[568,4619,607],{"class":596},[568,4621,4601],{"class":588},[568,4623,593],{"class":592},[568,4625,656],{"class":596},[568,4627,835],{"class":652},[568,4629,656],{"class":596},[568,4631,1086],{"class":592},[568,4633,851],{"class":596},[568,4635,4636,4638,4640,4642,4644,4646,4648,4650,4652],{"class":570,"line":765},[568,4637,1799],{"class":603},[568,4639,607],{"class":596},[568,4641,4601],{"class":588},[568,4643,593],{"class":592},[568,4645,656],{"class":596},[568,4647,835],{"class":652},[568,4649,656],{"class":596},[568,4651,1086],{"class":592},[568,4653,851],{"class":596},[568,4655,4656,4658,4660,4662,4664,4666,4668,4670,4672],{"class":570,"line":775},[568,4657,1810],{"class":603},[568,4659,607],{"class":596},[568,4661,4601],{"class":588},[568,4663,593],{"class":592},[568,4665,656],{"class":596},[568,4667,835],{"class":652},[568,4669,656],{"class":596},[568,4671,1086],{"class":592},[568,4673,851],{"class":596},[568,4675,4676,4679,4681,4683,4685,4687,4689,4691,4693],{"class":570,"line":854},[568,4677,4678],{"class":603},"    aggregate",[568,4680,607],{"class":596},[568,4682,4601],{"class":588},[568,4684,593],{"class":592},[568,4686,656],{"class":596},[568,4688,835],{"class":652},[568,4690,656],{"class":596},[568,4692,1086],{"class":592},[568,4694,851],{"class":596},[568,4696,4697],{"class":570,"line":873},[568,4698,1012],{"class":596},[568,4700,4701],{"class":570,"line":879},[568,4702,1894],{"class":596},[518,4704,4705,4706,4709,4710,4713,4714,4717,4718,4721,4722,1089],{},"A Free workspace gets ",[522,4707,4708],{},"403"," on ",[522,4711,4712],{},"\u002Fapi\u002Freports"," — and ",[522,4715,4716],{},"GET \u002Fapi\u002Fpermissions"," reports ",[522,4719,4720],{},"canRead: false",", so the UI can\nshow an upgrade prompt instead of an error. For policies the client can also read (why is it blocked? which plan\nunlocks it?), use permission objects and an evaluator — see\n",[530,4723,4725],{"href":4724},"\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fpermissions-cookbook#declarative-policies-with-a-permission-evaluator","Permissions Cookbook › Declarative policies",[553,4727,4729],{"id":4728},"_7-enforce-quotas","7. Enforce quotas",[518,4731,4732,4733,4736],{},"Limits are counted at write time, in a ",[522,4734,4735],{},"beforeCreate"," hook:",[543,4738,4740],{"className":562,"code":4739,"language":564,"meta":551,"style":551},"\u002F\u002F modules\u002Fprojects\u002Fhooks.ts\nimport { and, count, eq, isNull } from 'drizzle-orm'\nimport { projects } from '~~\u002Fserver\u002Fdatabase\u002Fschema'\n\nexport const projectsHooks = {\n  async beforeCreate(data: any, ctx: any) {\n    const limit = ctx.requestMeta?.billing?.limits?.projects\n    if (limit == null) return data \u002F\u002F unlimited\n    const [{ n }] = await ctx.db.select({ n: count() }).from(projects)\n      .where(and(eq(projects.organizationId, ctx.tenant.id), isNull(projects.deletedAt)))\n    if (n >= limit) {\n      throw createError({ statusCode: 402, message: `Your plan allows ${limit} projects`, data: { reason: 'quota', key: 'projects' } })\n    }\n    return data\n  },\n}\n",[522,4741,4742,4747,4780,4798,4802,4815,4842,4870,4893,4946,4999,5017,5096,5100,5107,5111],{"__ignoreMap":551},[568,4743,4744],{"class":570,"line":571},[568,4745,4746],{"class":574},"\u002F\u002F modules\u002Fprojects\u002Fhooks.ts\n",[568,4748,4749,4751,4753,4756,4758,4761,4763,4765,4767,4770,4772,4774,4776,4778],{"class":570,"line":578},[568,4750,1622],{"class":581},[568,4752,621],{"class":596},[568,4754,4755],{"class":592}," and",[568,4757,632],{"class":596},[568,4759,4760],{"class":592}," count",[568,4762,632],{"class":596},[568,4764,2924],{"class":592},[568,4766,632],{"class":596},[568,4768,4769],{"class":592}," isNull",[568,4771,759],{"class":596},[568,4773,1640],{"class":581},[568,4775,649],{"class":596},[568,4777,2933],{"class":652},[568,4779,1648],{"class":596},[568,4781,4782,4784,4786,4788,4790,4792,4794,4796],{"class":570,"line":600},[568,4783,1622],{"class":581},[568,4785,621],{"class":596},[568,4787,750],{"class":592},[568,4789,759],{"class":596},[568,4791,1640],{"class":581},[568,4793,649],{"class":596},[568,4795,2958],{"class":652},[568,4797,1648],{"class":596},[568,4799,4800],{"class":570,"line":613},[568,4801,1452],{"emptyLinePlaceholder":1451},[568,4803,4804,4806,4808,4811,4813],{"class":570,"line":665},[568,4805,582],{"class":581},[568,4807,1043],{"class":1042},[568,4809,4810],{"class":592}," projectsHooks ",[568,4812,1049],{"class":596},[568,4814,610],{"class":596},[568,4816,4817,4819,4822,4824,4826,4828,4830,4832,4834,4836,4838,4840],{"class":570,"line":675},[568,4818,2379],{"class":1042},[568,4820,4821],{"class":603}," beforeCreate",[568,4823,593],{"class":596},[568,4825,3276],{"class":1668},[568,4827,607],{"class":596},[568,4829,3616],{"class":1674},[568,4831,632],{"class":596},[568,4833,1765],{"class":1668},[568,4835,607],{"class":596},[568,4837,3616],{"class":1674},[568,4839,1086],{"class":596},[568,4841,610],{"class":596},[568,4843,4844,4846,4849,4851,4853,4855,4857,4859,4861,4863,4865,4867],{"class":570,"line":685},[568,4845,2400],{"class":1042},[568,4847,4848],{"class":592}," limit",[568,4850,1744],{"class":596},[568,4852,1765],{"class":592},[568,4854,1089],{"class":596},[568,4856,4314],{"class":592},[568,4858,1714],{"class":596},[568,4860,4547],{"class":592},[568,4862,1714],{"class":596},[568,4864,4416],{"class":592},[568,4866,1714],{"class":596},[568,4868,4869],{"class":592},"projects\n",[568,4871,4872,4874,4876,4878,4881,4883,4885,4887,4890],{"class":570,"line":765},[568,4873,2442],{"class":581},[568,4875,1665],{"class":603},[568,4877,3348],{"class":592},[568,4879,4880],{"class":596}," ==",[568,4882,984],{"class":596},[568,4884,2458],{"class":603},[568,4886,3368],{"class":581},[568,4888,4889],{"class":592}," data",[568,4891,4892],{"class":574}," \u002F\u002F unlimited\n",[568,4894,4895,4897,4900,4903,4906,4908,4910,4912,4914,4916,4918,4920,4922,4924,4926,4928,4930,4932,4934,4936,4938,4940,4942,4944],{"class":570,"line":775},[568,4896,2400],{"class":1042},[568,4898,4899],{"class":596}," [{",[568,4901,4902],{"class":592}," n",[568,4904,4905],{"class":596}," }]",[568,4907,1744],{"class":596},[568,4909,2510],{"class":581},[568,4911,1765],{"class":592},[568,4913,1089],{"class":596},[568,4915,4187],{"class":592},[568,4917,1089],{"class":596},[568,4919,3305],{"class":588},[568,4921,593],{"class":603},[568,4923,2165],{"class":596},[568,4925,4902],{"class":603},[568,4927,607],{"class":596},[568,4929,4760],{"class":588},[568,4931,2368],{"class":603},[568,4933,1018],{"class":596},[568,4935,1086],{"class":603},[568,4937,1089],{"class":596},[568,4939,3312],{"class":588},[568,4941,593],{"class":603},[568,4943,733],{"class":592},[568,4945,1021],{"class":603},[568,4947,4948,4950,4952,4954,4957,4959,4961,4963,4965,4967,4969,4971,4973,4975,4977,4979,4981,4983,4985,4987,4989,4991,4993,4996],{"class":570,"line":854},[568,4949,3876],{"class":596},[568,4951,3323],{"class":588},[568,4953,593],{"class":603},[568,4955,4956],{"class":588},"and",[568,4958,593],{"class":603},[568,4960,3328],{"class":588},[568,4962,593],{"class":603},[568,4964,733],{"class":592},[568,4966,1089],{"class":596},[568,4968,3534],{"class":592},[568,4970,632],{"class":596},[568,4972,1765],{"class":592},[568,4974,1089],{"class":596},[568,4976,2560],{"class":592},[568,4978,1089],{"class":596},[568,4980,1081],{"class":592},[568,4982,1086],{"class":603},[568,4984,632],{"class":596},[568,4986,4769],{"class":588},[568,4988,593],{"class":603},[568,4990,733],{"class":592},[568,4992,1089],{"class":596},[568,4994,4995],{"class":592},"deletedAt",[568,4997,4998],{"class":603},")))\n",[568,5000,5001,5003,5005,5008,5011,5013,5015],{"class":570,"line":873},[568,5002,2442],{"class":581},[568,5004,1665],{"class":603},[568,5006,5007],{"class":592},"n",[568,5009,5010],{"class":596}," >=",[568,5012,4848],{"class":592},[568,5014,2458],{"class":603},[568,5016,597],{"class":596},[568,5018,5019,5022,5024,5026,5028,5030,5032,5035,5037,5039,5041,5044,5047,5049,5051,5053,5055,5057,5059,5061,5063,5065,5068,5070,5072,5075,5077,5079,5082,5084,5086,5088,5090,5092,5094],{"class":570,"line":879},[568,5020,5021],{"class":581},"      throw",[568,5023,2464],{"class":588},[568,5025,593],{"class":603},[568,5027,2165],{"class":596},[568,5029,2471],{"class":603},[568,5031,607],{"class":596},[568,5033,5034],{"class":755}," 402",[568,5036,632],{"class":596},[568,5038,2481],{"class":603},[568,5040,607],{"class":596},[568,5042,5043],{"class":596}," `",[568,5045,5046],{"class":652},"Your plan allows ",[568,5048,3269],{"class":596},[568,5050,3348],{"class":592},[568,5052,1018],{"class":596},[568,5054,750],{"class":652},[568,5056,2658],{"class":596},[568,5058,632],{"class":596},[568,5060,4889],{"class":603},[568,5062,607],{"class":596},[568,5064,621],{"class":596},[568,5066,5067],{"class":603}," reason",[568,5069,607],{"class":596},[568,5071,649],{"class":596},[568,5073,5074],{"class":652},"quota",[568,5076,656],{"class":596},[568,5078,632],{"class":596},[568,5080,5081],{"class":603}," key",[568,5083,607],{"class":596},[568,5085,649],{"class":596},[568,5087,733],{"class":652},[568,5089,656],{"class":596},[568,5091,759],{"class":596},[568,5093,759],{"class":596},[568,5095,1021],{"class":603},[568,5097,5098],{"class":570,"line":889},[568,5099,3842],{"class":596},[568,5101,5102,5104],{"class":570,"line":971},[568,5103,2674],{"class":581},[568,5105,5106],{"class":592}," data\n",[568,5108,5109],{"class":570,"line":992},[568,5110,1012],{"class":596},[568,5112,5113],{"class":570,"line":997},[568,5114,1894],{"class":596},[518,5116,5117,5118,536,5121,5124],{},"A hook's own status is kept: the client receives ",[522,5119,5120],{},"402",[522,5122,5123],{},"data.reason",", which it can turn into an upgrade\nprompt. On bulk create every item passes through the hook.",[553,5126,5128],{"id":5127},"_8-the-ui","8. The UI",[5130,5131,5133],"h3",{"id":5132},"billing-state","Billing state",[543,5135,5137],{"className":562,"code":5136,"language":564,"meta":551,"style":551},"\u002F\u002F app\u002Fcomposables\u002FuseBilling.ts\nexport function useBilling() {\n  const { data, refetch } = useAutoApiList('subscriptions', { limit: 1 }) \u002F\u002F tenancy → this workspace's row\n  const sub = computed(() => data.value?.data[0] ?? null)\n  const plans = useRuntimeConfig().public.plans\n  const planId = computed(() => sub.value && ['active', 'trialing', 'past_due'].includes(sub.value.status) ? sub.value.planId : 'free')\n  return {\n    sub,\n    planId,\n    plan: computed(() => plans[planId.value]),\n    isPastDue: computed(() => sub.value?.status === 'past_due'),\n    hasFeature: (name: string) => (plans[planId.value]?.features ?? []).includes(name),\n    refetch,\n  }\n}\n",[522,5138,5139,5144,5157,5203,5243,5263,5353,5359,5366,5373,5403,5440,5494,5501,5505],{"__ignoreMap":551},[568,5140,5141],{"class":570,"line":571},[568,5142,5143],{"class":574},"\u002F\u002F app\u002Fcomposables\u002FuseBilling.ts\n",[568,5145,5146,5148,5150,5153,5155],{"class":570,"line":578},[568,5147,582],{"class":581},[568,5149,2121],{"class":1042},[568,5151,5152],{"class":588}," useBilling",[568,5154,1095],{"class":596},[568,5156,610],{"class":596},[568,5158,5159,5161,5163,5165,5167,5170,5172,5174,5177,5179,5181,5183,5185,5187,5189,5191,5193,5196,5198,5200],{"class":570,"line":600},[568,5160,2133],{"class":1042},[568,5162,621],{"class":596},[568,5164,4889],{"class":592},[568,5166,632],{"class":596},[568,5168,5169],{"class":592}," refetch",[568,5171,759],{"class":596},[568,5173,1744],{"class":596},[568,5175,5176],{"class":588}," useAutoApiList",[568,5178,593],{"class":603},[568,5180,656],{"class":596},[568,5182,524],{"class":652},[568,5184,656],{"class":596},[568,5186,632],{"class":596},[568,5188,621],{"class":596},[568,5190,4848],{"class":603},[568,5192,607],{"class":596},[568,5194,5195],{"class":755}," 1",[568,5197,759],{"class":596},[568,5199,2458],{"class":603},[568,5201,5202],{"class":574},"\u002F\u002F tenancy → this workspace's row\n",[568,5204,5205,5207,5209,5211,5214,5216,5218,5220,5222,5224,5227,5229,5231,5233,5235,5237,5239,5241],{"class":570,"line":613},[568,5206,2133],{"class":1042},[568,5208,3477],{"class":592},[568,5210,1744],{"class":596},[568,5212,5213],{"class":588}," computed",[568,5215,593],{"class":603},[568,5217,1095],{"class":596},[568,5219,1107],{"class":1042},[568,5221,4889],{"class":592},[568,5223,1089],{"class":596},[568,5225,5226],{"class":592},"value",[568,5228,1714],{"class":596},[568,5230,3276],{"class":592},[568,5232,2422],{"class":603},[568,5234,3644],{"class":755},[568,5236,1300],{"class":603},[568,5238,1720],{"class":596},[568,5240,984],{"class":596},[568,5242,1021],{"class":603},[568,5244,5245,5247,5249,5251,5253,5255,5257,5259,5261],{"class":570,"line":665},[568,5246,2133],{"class":1042},[568,5248,3561],{"class":592},[568,5250,1744],{"class":596},[568,5252,2145],{"class":588},[568,5254,1095],{"class":603},[568,5256,1089],{"class":596},[568,5258,2414],{"class":592},[568,5260,1089],{"class":596},[568,5262,3576],{"class":592},[568,5264,5265,5267,5269,5271,5273,5275,5277,5279,5281,5283,5285,5287,5289,5291,5293,5295,5297,5299,5301,5303,5305,5307,5309,5311,5313,5315,5317,5319,5321,5323,5325,5327,5329,5331,5333,5335,5337,5339,5341,5343,5345,5347,5349,5351],{"class":570,"line":675},[568,5266,2133],{"class":1042},[568,5268,2356],{"class":592},[568,5270,1744],{"class":596},[568,5272,5213],{"class":588},[568,5274,593],{"class":603},[568,5276,1095],{"class":596},[568,5278,1107],{"class":1042},[568,5280,3477],{"class":592},[568,5282,1089],{"class":596},[568,5284,5226],{"class":592},[568,5286,4248],{"class":596},[568,5288,728],{"class":603},[568,5290,656],{"class":596},[568,5292,1259],{"class":652},[568,5294,656],{"class":596},[568,5296,632],{"class":596},[568,5298,649],{"class":596},[568,5300,1268],{"class":652},[568,5302,656],{"class":596},[568,5304,632],{"class":596},[568,5306,649],{"class":596},[568,5308,1277],{"class":652},[568,5310,656],{"class":596},[568,5312,738],{"class":603},[568,5314,1089],{"class":596},[568,5316,1703],{"class":588},[568,5318,593],{"class":603},[568,5320,3699],{"class":592},[568,5322,1089],{"class":596},[568,5324,5226],{"class":592},[568,5326,1089],{"class":596},[568,5328,1241],{"class":592},[568,5330,2458],{"class":603},[568,5332,3708],{"class":596},[568,5334,3477],{"class":592},[568,5336,1089],{"class":596},[568,5338,5226],{"class":592},[568,5340,1089],{"class":596},[568,5342,2434],{"class":592},[568,5344,3717],{"class":596},[568,5346,649],{"class":596},[568,5348,1219],{"class":652},[568,5350,656],{"class":596},[568,5352,1021],{"class":603},[568,5354,5355,5357],{"class":570,"line":685},[568,5356,2216],{"class":581},[568,5358,610],{"class":596},[568,5360,5361,5364],{"class":570,"line":765},[568,5362,5363],{"class":592},"    sub",[568,5365,851],{"class":596},[568,5367,5368,5371],{"class":570,"line":775},[568,5369,5370],{"class":592},"    planId",[568,5372,851],{"class":596},[568,5374,5375,5378,5380,5382,5384,5386,5388,5390,5392,5394,5396,5398,5401],{"class":570,"line":854},[568,5376,5377],{"class":603},"    plan",[568,5379,607],{"class":596},[568,5381,5213],{"class":588},[568,5383,593],{"class":603},[568,5385,1095],{"class":596},[568,5387,1107],{"class":1042},[568,5389,3561],{"class":592},[568,5391,2422],{"class":603},[568,5393,2434],{"class":592},[568,5395,1089],{"class":596},[568,5397,5226],{"class":592},[568,5399,5400],{"class":603},"])",[568,5402,851],{"class":596},[568,5404,5405,5408,5410,5412,5414,5416,5418,5420,5422,5424,5426,5428,5430,5432,5434,5436,5438],{"class":570,"line":873},[568,5406,5407],{"class":603},"    isPastDue",[568,5409,607],{"class":596},[568,5411,5213],{"class":588},[568,5413,593],{"class":603},[568,5415,1095],{"class":596},[568,5417,1107],{"class":1042},[568,5419,3477],{"class":592},[568,5421,1089],{"class":596},[568,5423,5226],{"class":592},[568,5425,1714],{"class":596},[568,5427,1241],{"class":592},[568,5429,3629],{"class":596},[568,5431,649],{"class":596},[568,5433,1277],{"class":652},[568,5435,656],{"class":596},[568,5437,1086],{"class":603},[568,5439,851],{"class":596},[568,5441,5442,5445,5447,5449,5451,5453,5455,5457,5459,5461,5463,5465,5467,5469,5471,5473,5475,5477,5479,5482,5484,5486,5488,5490,5492],{"class":570,"line":879},[568,5443,5444],{"class":588},"    hasFeature",[568,5446,607],{"class":596},[568,5448,1665],{"class":596},[568,5450,4516],{"class":1668},[568,5452,607],{"class":596},[568,5454,2570],{"class":1674},[568,5456,1086],{"class":596},[568,5458,1107],{"class":1042},[568,5460,1665],{"class":603},[568,5462,2419],{"class":592},[568,5464,2422],{"class":603},[568,5466,2434],{"class":592},[568,5468,1089],{"class":596},[568,5470,5226],{"class":592},[568,5472,738],{"class":603},[568,5474,1714],{"class":596},[568,5476,4390],{"class":592},[568,5478,3510],{"class":596},[568,5480,5481],{"class":603}," [])",[568,5483,1089],{"class":596},[568,5485,1703],{"class":588},[568,5487,593],{"class":603},[568,5489,4516],{"class":592},[568,5491,1086],{"class":603},[568,5493,851],{"class":596},[568,5495,5496,5499],{"class":570,"line":889},[568,5497,5498],{"class":592},"    refetch",[568,5500,851],{"class":596},[568,5502,5503],{"class":570,"line":971},[568,5504,3126],{"class":596},[568,5506,5507],{"class":570,"line":992},[568,5508,1894],{"class":596},[5130,5510,5512,5513],{"id":5511},"pricing-page-with-upricingplans","Pricing page with ",[522,5514,5515],{},"UPricingPlans",[543,5517,5521],{"className":5518,"code":5519,"language":5520,"meta":551,"style":551},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003C!-- app\u002Fpages\u002Fapp\u002Fbilling.vue -->\n\u003Cscript setup lang=\"ts\">\nconst { planId, sub, isPastDue, refetch } = useBilling()\nconst plans = useRuntimeConfig().public.plans\nconst toast = useToast()\nconst route = useRoute()\n\nconst checkout = useAutoApiEndpointMutation\u003C{ url: string }, { planId: string }>('\u002Fapi\u002Fbilling\u002Fcheckout', {\n  onSuccess: res => navigateTo((res as any).data.url, { external: true }),\n  onError: error => toast.add({ title: 'Could not start checkout', description: error.message, color: 'error' }),\n})\nconst portal = useAutoApiEndpointQuery\u003C{ url: string }>('\u002Fapi\u002Fbilling\u002Fportal', undefined, {\n  queryKey: ['billing', 'portal'],\n  unwrap: true,\n  enabled: false, \u002F\u002F fetched on click\n})\n\nconst pricing = computed(() => Object.entries(plans).map(([id, p]: [string, any]) => ({\n  title: p.label,\n  price: p.price,\n  billingCycle: '\u002Fmonth',\n  features: p.features.map((f: string) => ({ title: f, icon: 'i-lucide-check' })),\n  highlight: id === 'pro',\n  badge: id === planId.value ? 'Current plan' : undefined,\n  button: id === planId.value || !p.productId\n    ? { label: id === planId.value ? 'Current plan' : 'Included', disabled: true, variant: 'outline' as const }\n    : { label: `Upgrade to ${p.label}`, loading: checkout.isPending.value, onClick: () => checkout.mutate({ planId: id }) },\n})))\n\nasync function openPortal() {\n  const { data } = await portal.refetch()\n  if (data?.url) await navigateTo(data.url, { external: true })\n}\n\n\u002F\u002F Back from checkout: the webhook may land a moment later.\nonMounted(() => {\n  if (route.query.success) setTimeout(refetch, 2000)\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CUContainer class=\"space-y-8 py-10\">\n    \u003CUAlert\n      v-if=\"isPastDue\"\n      color=\"error\"\n      icon=\"i-lucide-credit-card\"\n      title=\"Payment failed\"\n      description=\"Update your payment method to keep access to paid features.\"\n      :actions=\"[{ label: 'Update payment', onClick: openPortal }]\"\n    \u002F>\n\n    \u003CUCard v-if=\"sub?.providerSubscriptionId\">\n      \u003Cdiv class=\"flex items-center justify-between\">\n        \u003Cdiv>\n          \u003Cp class=\"font-medium\">{{ plans[planId]?.label }} plan\u003C\u002Fp>\n          \u003Cp v-if=\"sub.currentPeriodEnd\" class=\"text-sm text-muted\">\n            Renews {{ new Date(sub.currentPeriodEnd).toLocaleDateString() }}\n          \u003C\u002Fp>\n        \u003C\u002Fdiv>\n        \u003CUButton label=\"Manage billing\" variant=\"outline\" icon=\"i-lucide-external-link\" @click=\"openPortal\" \u002F>\n      \u003C\u002Fdiv>\n    \u003C\u002FUCard>\n\n    \u003CUPricingPlans :plans=\"pricing\" \u002F>\n  \u003C\u002FUContainer>\n\u003C\u002Ftemplate>\n","vue",[522,5522,5523,5528,5554,5584,5605,5619,5633,5637,5684,5734,5803,5809,5847,5874,5885,5899,5905,5909,5971,5987,6003,6019,6082,6104,6136,6166,6233,6313,6319,6323,6336,6360,6401,6405,6409,6414,6427,6462,6468,6477,6481,6490,6512,6520,6535,6549,6564,6579,6594,6609,6615,6620,6642,6664,6674,6705,6736,6742,6752,6762,6817,6827,6837,6842,6863,6873],{"__ignoreMap":551},[568,5524,5525],{"class":570,"line":571},[568,5526,5527],{"class":574},"\u003C!-- app\u002Fpages\u002Fapp\u002Fbilling.vue -->\n",[568,5529,5530,5533,5536,5539,5542,5544,5547,5549,5551],{"class":570,"line":578},[568,5531,5532],{"class":596},"\u003C",[568,5534,5535],{"class":603},"script",[568,5537,5538],{"class":1042}," setup",[568,5540,5541],{"class":1042}," lang",[568,5543,1049],{"class":596},[568,5545,5546],{"class":596},"\"",[568,5548,564],{"class":652},[568,5550,5546],{"class":596},[568,5552,5553],{"class":596},">\n",[568,5555,5556,5558,5560,5562,5564,5566,5568,5571,5573,5576,5578,5580,5582],{"class":570,"line":600},[568,5557,1657],{"class":1042},[568,5559,621],{"class":596},[568,5561,2356],{"class":592},[568,5563,632],{"class":596},[568,5565,3477],{"class":592},[568,5567,632],{"class":596},[568,5569,5570],{"class":592}," isPastDue",[568,5572,632],{"class":596},[568,5574,5575],{"class":592}," refetch ",[568,5577,1018],{"class":596},[568,5579,1744],{"class":596},[568,5581,5152],{"class":588},[568,5583,2148],{"class":592},[568,5585,5586,5588,5591,5593,5595,5597,5599,5601,5603],{"class":570,"line":613},[568,5587,1657],{"class":1042},[568,5589,5590],{"class":592}," plans ",[568,5592,1049],{"class":596},[568,5594,2145],{"class":588},[568,5596,1095],{"class":592},[568,5598,1089],{"class":596},[568,5600,2414],{"class":592},[568,5602,1089],{"class":596},[568,5604,3576],{"class":592},[568,5606,5607,5609,5612,5614,5617],{"class":570,"line":665},[568,5608,1657],{"class":1042},[568,5610,5611],{"class":592}," toast ",[568,5613,1049],{"class":596},[568,5615,5616],{"class":588}," useToast",[568,5618,2148],{"class":592},[568,5620,5621,5623,5626,5628,5631],{"class":570,"line":675},[568,5622,1657],{"class":1042},[568,5624,5625],{"class":592}," route ",[568,5627,1049],{"class":596},[568,5629,5630],{"class":588}," useRoute",[568,5632,2148],{"class":592},[568,5634,5635],{"class":570,"line":685},[568,5636,1452],{"emptyLinePlaceholder":1451},[568,5638,5639,5641,5644,5646,5649,5652,5654,5656,5658,5660,5662,5664,5666,5668,5671,5673,5675,5678,5680,5682],{"class":570,"line":765},[568,5640,1657],{"class":1042},[568,5642,5643],{"class":592}," checkout ",[568,5645,1049],{"class":596},[568,5647,5648],{"class":588}," useAutoApiEndpointMutation",[568,5650,5651],{"class":596},"\u003C{",[568,5653,2679],{"class":603},[568,5655,607],{"class":596},[568,5657,2570],{"class":1674},[568,5659,659],{"class":596},[568,5661,621],{"class":596},[568,5663,2356],{"class":603},[568,5665,607],{"class":596},[568,5667,2570],{"class":1674},[568,5669,5670],{"class":596}," }>",[568,5672,593],{"class":592},[568,5674,656],{"class":596},[568,5676,5677],{"class":652},"\u002Fapi\u002Fbilling\u002Fcheckout",[568,5679,656],{"class":596},[568,5681,632],{"class":596},[568,5683,610],{"class":596},[568,5685,5686,5689,5691,5694,5696,5699,5702,5705,5707,5709,5711,5713,5715,5717,5719,5721,5724,5726,5728,5730,5732],{"class":570,"line":775},[568,5687,5688],{"class":588},"  onSuccess",[568,5690,607],{"class":596},[568,5692,5693],{"class":1668}," res",[568,5695,1107],{"class":1042},[568,5697,5698],{"class":588}," navigateTo",[568,5700,5701],{"class":592},"((res ",[568,5703,5704],{"class":581},"as",[568,5706,3616],{"class":1674},[568,5708,1086],{"class":592},[568,5710,1089],{"class":596},[568,5712,3276],{"class":592},[568,5714,1089],{"class":596},[568,5716,2688],{"class":592},[568,5718,632],{"class":596},[568,5720,621],{"class":596},[568,5722,5723],{"class":603}," external",[568,5725,607],{"class":596},[568,5727,2330],{"class":1791},[568,5729,759],{"class":596},[568,5731,1086],{"class":592},[568,5733,851],{"class":596},[568,5735,5736,5739,5741,5744,5746,5749,5751,5754,5756,5758,5761,5763,5765,5768,5770,5772,5775,5777,5779,5781,5784,5786,5789,5791,5793,5795,5797,5799,5801],{"class":570,"line":854},[568,5737,5738],{"class":588},"  onError",[568,5740,607],{"class":596},[568,5742,5743],{"class":1668}," error",[568,5745,1107],{"class":1042},[568,5747,5748],{"class":592}," toast",[568,5750,1089],{"class":596},[568,5752,5753],{"class":588},"add",[568,5755,593],{"class":592},[568,5757,2165],{"class":596},[568,5759,5760],{"class":603}," title",[568,5762,607],{"class":596},[568,5764,649],{"class":596},[568,5766,5767],{"class":652},"Could not start checkout",[568,5769,656],{"class":596},[568,5771,632],{"class":596},[568,5773,5774],{"class":603}," description",[568,5776,607],{"class":596},[568,5778,5743],{"class":592},[568,5780,1089],{"class":596},[568,5782,5783],{"class":592},"message",[568,5785,632],{"class":596},[568,5787,5788],{"class":603}," color",[568,5790,607],{"class":596},[568,5792,649],{"class":596},[568,5794,3136],{"class":652},[568,5796,656],{"class":596},[568,5798,759],{"class":596},[568,5800,1086],{"class":592},[568,5802,851],{"class":596},[568,5804,5805,5807],{"class":570,"line":873},[568,5806,1018],{"class":596},[568,5808,1021],{"class":592},[568,5810,5811,5813,5816,5818,5821,5823,5825,5827,5829,5831,5833,5835,5838,5840,5842,5845],{"class":570,"line":879},[568,5812,1657],{"class":1042},[568,5814,5815],{"class":592}," portal ",[568,5817,1049],{"class":596},[568,5819,5820],{"class":588}," useAutoApiEndpointQuery",[568,5822,5651],{"class":596},[568,5824,2679],{"class":603},[568,5826,607],{"class":596},[568,5828,2570],{"class":1674},[568,5830,5670],{"class":596},[568,5832,593],{"class":592},[568,5834,656],{"class":596},[568,5836,5837],{"class":652},"\u002Fapi\u002Fbilling\u002Fportal",[568,5839,656],{"class":596},[568,5841,632],{"class":596},[568,5843,5844],{"class":596}," undefined,",[568,5846,610],{"class":596},[568,5848,5849,5852,5854,5856,5858,5860,5862,5864,5866,5868,5870,5872],{"class":570,"line":889},[568,5850,5851],{"class":603},"  queryKey",[568,5853,607],{"class":596},[568,5855,728],{"class":592},[568,5857,656],{"class":596},[568,5859,4547],{"class":652},[568,5861,656],{"class":596},[568,5863,632],{"class":596},[568,5865,649],{"class":596},[568,5867,2765],{"class":652},[568,5869,656],{"class":596},[568,5871,738],{"class":592},[568,5873,851],{"class":596},[568,5875,5876,5879,5881,5883],{"class":570,"line":971},[568,5877,5878],{"class":603},"  unwrap",[568,5880,607],{"class":596},[568,5882,2330],{"class":1791},[568,5884,851],{"class":596},[568,5886,5887,5890,5892,5894,5896],{"class":570,"line":992},[568,5888,5889],{"class":603},"  enabled",[568,5891,607],{"class":596},[568,5893,1792],{"class":1791},[568,5895,632],{"class":596},[568,5897,5898],{"class":574}," \u002F\u002F fetched on click\n",[568,5900,5901,5903],{"class":570,"line":997},[568,5902,1018],{"class":596},[568,5904,1021],{"class":592},[568,5906,5907],{"class":570,"line":1003},[568,5908,1452],{"emptyLinePlaceholder":1451},[568,5910,5911,5913,5916,5918,5920,5922,5924,5926,5928,5930,5932,5935,5937,5940,5942,5945,5947,5949,5951,5953,5955,5957,5959,5961,5963,5965,5967,5969],{"class":570,"line":1009},[568,5912,1657],{"class":1042},[568,5914,5915],{"class":592}," pricing ",[568,5917,1049],{"class":596},[568,5919,5213],{"class":588},[568,5921,593],{"class":592},[568,5923,1095],{"class":596},[568,5925,1107],{"class":1042},[568,5927,3082],{"class":592},[568,5929,1089],{"class":596},[568,5931,3101],{"class":588},[568,5933,5934],{"class":592},"(plans)",[568,5936,1089],{"class":596},[568,5938,5939],{"class":588},"map",[568,5941,593],{"class":592},[568,5943,5944],{"class":596},"([",[568,5946,1081],{"class":1668},[568,5948,632],{"class":596},[568,5950,3610],{"class":1668},[568,5952,3613],{"class":596},[568,5954,728],{"class":592},[568,5956,2365],{"class":1674},[568,5958,632],{"class":596},[568,5960,3616],{"class":1674},[568,5962,738],{"class":592},[568,5964,1086],{"class":596},[568,5966,1107],{"class":1042},[568,5968,1665],{"class":592},[568,5970,597],{"class":596},[568,5972,5973,5976,5978,5980,5982,5985],{"class":570,"line":1015},[568,5974,5975],{"class":603},"  title",[568,5977,607],{"class":596},[568,5979,3610],{"class":592},[568,5981,1089],{"class":596},[568,5983,5984],{"class":592},"label",[568,5986,851],{"class":596},[568,5988,5989,5992,5994,5996,5998,6001],{"class":570,"line":2663},[568,5990,5991],{"class":603},"  price",[568,5993,607],{"class":596},[568,5995,3610],{"class":592},[568,5997,1089],{"class":596},[568,5999,6000],{"class":592},"price",[568,6002,851],{"class":596},[568,6004,6005,6008,6010,6012,6015,6017],{"class":570,"line":2671},[568,6006,6007],{"class":603},"  billingCycle",[568,6009,607],{"class":596},[568,6011,649],{"class":596},[568,6013,6014],{"class":652},"\u002Fmonth",[568,6016,656],{"class":596},[568,6018,851],{"class":596},[568,6020,6021,6024,6026,6028,6030,6032,6034,6036,6038,6040,6043,6045,6047,6049,6051,6053,6055,6057,6059,6062,6064,6067,6069,6071,6074,6076,6078,6080],{"class":570,"line":2694},[568,6022,6023],{"class":603},"  features",[568,6025,607],{"class":596},[568,6027,3610],{"class":592},[568,6029,1089],{"class":596},[568,6031,4390],{"class":592},[568,6033,1089],{"class":596},[568,6035,5939],{"class":588},[568,6037,593],{"class":592},[568,6039,593],{"class":596},[568,6041,6042],{"class":1668},"f",[568,6044,607],{"class":596},[568,6046,2570],{"class":1674},[568,6048,1086],{"class":596},[568,6050,1107],{"class":1042},[568,6052,1665],{"class":592},[568,6054,2165],{"class":596},[568,6056,5760],{"class":603},[568,6058,607],{"class":596},[568,6060,6061],{"class":592}," f",[568,6063,632],{"class":596},[568,6065,6066],{"class":603}," icon",[568,6068,607],{"class":596},[568,6070,649],{"class":596},[568,6072,6073],{"class":652},"i-lucide-check",[568,6075,656],{"class":596},[568,6077,759],{"class":596},[568,6079,3343],{"class":592},[568,6081,851],{"class":596},[568,6083,6084,6087,6089,6092,6095,6097,6100,6102],{"class":570,"line":2699},[568,6085,6086],{"class":603},"  highlight",[568,6088,607],{"class":596},[568,6090,6091],{"class":592}," id ",[568,6093,6094],{"class":596},"===",[568,6096,649],{"class":596},[568,6098,6099],{"class":652},"pro",[568,6101,656],{"class":596},[568,6103,851],{"class":596},[568,6105,6106,6109,6111,6113,6115,6117,6119,6122,6124,6126,6129,6131,6133],{"class":570,"line":3435},[568,6107,6108],{"class":603},"  badge",[568,6110,607],{"class":596},[568,6112,6091],{"class":592},[568,6114,6094],{"class":596},[568,6116,2356],{"class":592},[568,6118,1089],{"class":596},[568,6120,6121],{"class":592},"value ",[568,6123,3708],{"class":596},[568,6125,649],{"class":596},[568,6127,6128],{"class":652},"Current plan",[568,6130,656],{"class":596},[568,6132,3717],{"class":596},[568,6134,6135],{"class":596}," undefined,\n",[568,6137,6138,6141,6143,6145,6147,6149,6151,6153,6156,6159,6161,6163],{"class":570,"line":3440},[568,6139,6140],{"class":603},"  button",[568,6142,607],{"class":596},[568,6144,6091],{"class":592},[568,6146,6094],{"class":596},[568,6148,2356],{"class":592},[568,6150,1089],{"class":596},[568,6152,6121],{"class":592},[568,6154,6155],{"class":596},"||",[568,6157,6158],{"class":596}," !",[568,6160,518],{"class":592},[568,6162,1089],{"class":596},[568,6164,6165],{"class":592},"productId\n",[568,6167,6168,6171,6173,6175,6177,6179,6181,6183,6185,6187,6189,6191,6193,6195,6197,6199,6202,6204,6206,6209,6211,6213,6215,6218,6220,6222,6225,6227,6229,6231],{"class":570,"line":3472},[568,6169,6170],{"class":596},"    ?",[568,6172,621],{"class":596},[568,6174,695],{"class":603},[568,6176,607],{"class":596},[568,6178,6091],{"class":592},[568,6180,6094],{"class":596},[568,6182,2356],{"class":592},[568,6184,1089],{"class":596},[568,6186,6121],{"class":592},[568,6188,3708],{"class":596},[568,6190,649],{"class":596},[568,6192,6128],{"class":652},[568,6194,656],{"class":596},[568,6196,3717],{"class":596},[568,6198,649],{"class":596},[568,6200,6201],{"class":652},"Included",[568,6203,656],{"class":596},[568,6205,632],{"class":596},[568,6207,6208],{"class":603}," disabled",[568,6210,607],{"class":596},[568,6212,2330],{"class":1791},[568,6214,632],{"class":596},[568,6216,6217],{"class":603}," variant",[568,6219,607],{"class":596},[568,6221,649],{"class":596},[568,6223,6224],{"class":652},"outline",[568,6226,656],{"class":596},[568,6228,2192],{"class":581},[568,6230,1043],{"class":1042},[568,6232,2691],{"class":596},[568,6234,6235,6238,6240,6242,6244,6246,6249,6251,6253,6255,6257,6260,6262,6265,6267,6269,6271,6274,6276,6278,6280,6283,6285,6288,6290,6292,6294,6297,6299,6301,6303,6305,6307,6309,6311],{"class":570,"line":3489},[568,6236,6237],{"class":596},"    :",[568,6239,621],{"class":596},[568,6241,695],{"class":603},[568,6243,607],{"class":596},[568,6245,5043],{"class":596},[568,6247,6248],{"class":652},"Upgrade to ",[568,6250,3269],{"class":596},[568,6252,518],{"class":592},[568,6254,1089],{"class":596},[568,6256,5984],{"class":592},[568,6258,6259],{"class":596},"}`",[568,6261,632],{"class":596},[568,6263,6264],{"class":603}," loading",[568,6266,607],{"class":596},[568,6268,2505],{"class":592},[568,6270,1089],{"class":596},[568,6272,6273],{"class":592},"isPending",[568,6275,1089],{"class":596},[568,6277,5226],{"class":592},[568,6279,632],{"class":596},[568,6281,6282],{"class":588}," onClick",[568,6284,607],{"class":596},[568,6286,6287],{"class":596}," ()",[568,6289,1107],{"class":1042},[568,6291,2505],{"class":592},[568,6293,1089],{"class":596},[568,6295,6296],{"class":588},"mutate",[568,6298,593],{"class":592},[568,6300,2165],{"class":596},[568,6302,2356],{"class":603},[568,6304,607],{"class":596},[568,6306,6091],{"class":592},[568,6308,1018],{"class":596},[568,6310,2458],{"class":592},[568,6312,2629],{"class":596},[568,6314,6315,6317],{"class":570,"line":3525},[568,6316,1018],{"class":596},[568,6318,4998],{"class":592},[568,6320,6321],{"class":570,"line":3551},[568,6322,1452],{"emptyLinePlaceholder":1451},[568,6324,6325,6327,6329,6332,6334],{"class":570,"line":3556},[568,6326,2978],{"class":1042},[568,6328,2121],{"class":1042},[568,6330,6331],{"class":588}," openPortal",[568,6333,1095],{"class":596},[568,6335,610],{"class":596},[568,6337,6338,6340,6342,6344,6346,6348,6350,6353,6355,6358],{"class":570,"line":3579},[568,6339,2133],{"class":1042},[568,6341,621],{"class":596},[568,6343,4889],{"class":592},[568,6345,759],{"class":596},[568,6347,1744],{"class":596},[568,6349,2510],{"class":581},[568,6351,6352],{"class":592}," portal",[568,6354,1089],{"class":596},[568,6356,6357],{"class":588},"refetch",[568,6359,2148],{"class":603},[568,6361,6362,6364,6366,6368,6370,6372,6374,6377,6379,6381,6383,6385,6387,6389,6391,6393,6395,6397,6399],{"class":570,"line":3657},[568,6363,3014],{"class":581},[568,6365,1665],{"class":603},[568,6367,3276],{"class":592},[568,6369,1714],{"class":596},[568,6371,2688],{"class":592},[568,6373,2458],{"class":603},[568,6375,6376],{"class":581},"await",[568,6378,5698],{"class":588},[568,6380,593],{"class":603},[568,6382,3276],{"class":592},[568,6384,1089],{"class":596},[568,6386,2688],{"class":592},[568,6388,632],{"class":596},[568,6390,621],{"class":596},[568,6392,5723],{"class":603},[568,6394,607],{"class":596},[568,6396,2330],{"class":1791},[568,6398,759],{"class":596},[568,6400,1021],{"class":603},[568,6402,6403],{"class":570,"line":3726},[568,6404,1894],{"class":596},[568,6406,6407],{"class":570,"line":3738},[568,6408,1452],{"emptyLinePlaceholder":1451},[568,6410,6411],{"class":570,"line":3746},[568,6412,6413],{"class":574},"\u002F\u002F Back from checkout: the webhook may land a moment later.\n",[568,6415,6416,6419,6421,6423,6425],{"class":570,"line":3762},[568,6417,6418],{"class":588},"onMounted",[568,6420,593],{"class":592},[568,6422,1095],{"class":596},[568,6424,1107],{"class":1042},[568,6426,610],{"class":596},[568,6428,6429,6431,6433,6436,6438,6441,6443,6446,6448,6451,6453,6455,6457,6460],{"class":570,"line":3795},[568,6430,3014],{"class":581},[568,6432,1665],{"class":603},[568,6434,6435],{"class":592},"route",[568,6437,1089],{"class":596},[568,6439,6440],{"class":592},"query",[568,6442,1089],{"class":596},[568,6444,6445],{"class":592},"success",[568,6447,2458],{"class":603},[568,6449,6450],{"class":588},"setTimeout",[568,6452,593],{"class":603},[568,6454,6357],{"class":592},[568,6456,632],{"class":596},[568,6458,6459],{"class":755}," 2000",[568,6461,1021],{"class":603},[568,6463,6464,6466],{"class":570,"line":3803},[568,6465,1018],{"class":596},[568,6467,1021],{"class":592},[568,6469,6470,6473,6475],{"class":570,"line":3839},[568,6471,6472],{"class":596},"\u003C\u002F",[568,6474,5535],{"class":603},[568,6476,5553],{"class":596},[568,6478,6479],{"class":570,"line":3845},[568,6480,1452],{"emptyLinePlaceholder":1451},[568,6482,6483,6485,6488],{"class":570,"line":3873},[568,6484,5532],{"class":596},[568,6486,6487],{"class":603},"template",[568,6489,5553],{"class":596},[568,6491,6492,6495,6498,6501,6503,6505,6508,6510],{"class":570,"line":3910},[568,6493,6494],{"class":596},"  \u003C",[568,6496,6497],{"class":603},"UContainer",[568,6499,6500],{"class":1042}," class",[568,6502,1049],{"class":596},[568,6504,5546],{"class":596},[568,6506,6507],{"class":652},"space-y-8 py-10",[568,6509,5546],{"class":596},[568,6511,5553],{"class":596},[568,6513,6514,6517],{"class":570,"line":3915},[568,6515,6516],{"class":596},"    \u003C",[568,6518,6519],{"class":603},"UAlert\n",[568,6521,6522,6525,6527,6529,6532],{"class":570,"line":3930},[568,6523,6524],{"class":1042},"      v-if",[568,6526,1049],{"class":596},[568,6528,5546],{"class":596},[568,6530,6531],{"class":652},"isPastDue",[568,6533,6534],{"class":596},"\"\n",[568,6536,6538,6541,6543,6545,6547],{"class":570,"line":6537},45,[568,6539,6540],{"class":1042},"      color",[568,6542,1049],{"class":596},[568,6544,5546],{"class":596},[568,6546,3136],{"class":652},[568,6548,6534],{"class":596},[568,6550,6552,6555,6557,6559,6562],{"class":570,"line":6551},46,[568,6553,6554],{"class":1042},"      icon",[568,6556,1049],{"class":596},[568,6558,5546],{"class":596},[568,6560,6561],{"class":652},"i-lucide-credit-card",[568,6563,6534],{"class":596},[568,6565,6567,6570,6572,6574,6577],{"class":570,"line":6566},47,[568,6568,6569],{"class":1042},"      title",[568,6571,1049],{"class":596},[568,6573,5546],{"class":596},[568,6575,6576],{"class":652},"Payment failed",[568,6578,6534],{"class":596},[568,6580,6582,6585,6587,6589,6592],{"class":570,"line":6581},48,[568,6583,6584],{"class":1042},"      description",[568,6586,1049],{"class":596},[568,6588,5546],{"class":596},[568,6590,6591],{"class":652},"Update your payment method to keep access to paid features.",[568,6593,6534],{"class":596},[568,6595,6597,6600,6602,6604,6607],{"class":570,"line":6596},49,[568,6598,6599],{"class":1042},"      :actions",[568,6601,1049],{"class":596},[568,6603,5546],{"class":596},[568,6605,6606],{"class":652},"[{ label: 'Update payment', onClick: openPortal }]",[568,6608,6534],{"class":596},[568,6610,6612],{"class":570,"line":6611},50,[568,6613,6614],{"class":596},"    \u002F>\n",[568,6616,6618],{"class":570,"line":6617},51,[568,6619,1452],{"emptyLinePlaceholder":1451},[568,6621,6623,6625,6628,6631,6633,6635,6638,6640],{"class":570,"line":6622},52,[568,6624,6516],{"class":596},[568,6626,6627],{"class":603},"UCard",[568,6629,6630],{"class":1042}," v-if",[568,6632,1049],{"class":596},[568,6634,5546],{"class":596},[568,6636,6637],{"class":652},"sub?.providerSubscriptionId",[568,6639,5546],{"class":596},[568,6641,5553],{"class":596},[568,6643,6645,6648,6651,6653,6655,6657,6660,6662],{"class":570,"line":6644},53,[568,6646,6647],{"class":596},"      \u003C",[568,6649,6650],{"class":603},"div",[568,6652,6500],{"class":1042},[568,6654,1049],{"class":596},[568,6656,5546],{"class":596},[568,6658,6659],{"class":652},"flex items-center justify-between",[568,6661,5546],{"class":596},[568,6663,5553],{"class":596},[568,6665,6667,6670,6672],{"class":570,"line":6666},54,[568,6668,6669],{"class":596},"        \u003C",[568,6671,6650],{"class":603},[568,6673,5553],{"class":596},[568,6675,6677,6680,6682,6684,6686,6688,6691,6693,6696,6699,6701,6703],{"class":570,"line":6676},55,[568,6678,6679],{"class":596},"          \u003C",[568,6681,518],{"class":603},[568,6683,6500],{"class":1042},[568,6685,1049],{"class":596},[568,6687,5546],{"class":596},[568,6689,6690],{"class":652},"font-medium",[568,6692,5546],{"class":596},[568,6694,6695],{"class":596},">",[568,6697,6698],{"class":592},"{{ plans[planId]?.label }} plan",[568,6700,6472],{"class":596},[568,6702,518],{"class":603},[568,6704,5553],{"class":596},[568,6706,6708,6710,6712,6714,6716,6718,6721,6723,6725,6727,6729,6732,6734],{"class":570,"line":6707},56,[568,6709,6679],{"class":596},[568,6711,518],{"class":603},[568,6713,6630],{"class":1042},[568,6715,1049],{"class":596},[568,6717,5546],{"class":596},[568,6719,6720],{"class":652},"sub.currentPeriodEnd",[568,6722,5546],{"class":596},[568,6724,6500],{"class":1042},[568,6726,1049],{"class":596},[568,6728,5546],{"class":596},[568,6730,6731],{"class":652},"text-sm text-muted",[568,6733,5546],{"class":596},[568,6735,5553],{"class":596},[568,6737,6739],{"class":570,"line":6738},57,[568,6740,6741],{"class":592},"            Renews {{ new Date(sub.currentPeriodEnd).toLocaleDateString() }}\n",[568,6743,6745,6748,6750],{"class":570,"line":6744},58,[568,6746,6747],{"class":596},"          \u003C\u002F",[568,6749,518],{"class":603},[568,6751,5553],{"class":596},[568,6753,6755,6758,6760],{"class":570,"line":6754},59,[568,6756,6757],{"class":596},"        \u003C\u002F",[568,6759,6650],{"class":603},[568,6761,5553],{"class":596},[568,6763,6765,6767,6770,6772,6774,6776,6779,6781,6783,6785,6787,6789,6791,6793,6795,6797,6800,6802,6805,6807,6809,6812,6814],{"class":570,"line":6764},60,[568,6766,6669],{"class":596},[568,6768,6769],{"class":603},"UButton",[568,6771,695],{"class":1042},[568,6773,1049],{"class":596},[568,6775,5546],{"class":596},[568,6777,6778],{"class":652},"Manage billing",[568,6780,5546],{"class":596},[568,6782,6217],{"class":1042},[568,6784,1049],{"class":596},[568,6786,5546],{"class":596},[568,6788,6224],{"class":652},[568,6790,5546],{"class":596},[568,6792,6066],{"class":1042},[568,6794,1049],{"class":596},[568,6796,5546],{"class":596},[568,6798,6799],{"class":652},"i-lucide-external-link",[568,6801,5546],{"class":596},[568,6803,6804],{"class":1042}," @click",[568,6806,1049],{"class":596},[568,6808,5546],{"class":596},[568,6810,6811],{"class":652},"openPortal",[568,6813,5546],{"class":596},[568,6815,6816],{"class":596}," \u002F>\n",[568,6818,6820,6823,6825],{"class":570,"line":6819},61,[568,6821,6822],{"class":596},"      \u003C\u002F",[568,6824,6650],{"class":603},[568,6826,5553],{"class":596},[568,6828,6830,6833,6835],{"class":570,"line":6829},62,[568,6831,6832],{"class":596},"    \u003C\u002F",[568,6834,6627],{"class":603},[568,6836,5553],{"class":596},[568,6838,6840],{"class":570,"line":6839},63,[568,6841,1452],{"emptyLinePlaceholder":1451},[568,6843,6845,6847,6849,6852,6854,6856,6859,6861],{"class":570,"line":6844},64,[568,6846,6516],{"class":596},[568,6848,5515],{"class":603},[568,6850,6851],{"class":1042}," :plans",[568,6853,1049],{"class":596},[568,6855,5546],{"class":596},[568,6857,6858],{"class":652},"pricing",[568,6860,5546],{"class":596},[568,6862,6816],{"class":596},[568,6864,6866,6869,6871],{"class":570,"line":6865},65,[568,6867,6868],{"class":596},"  \u003C\u002F",[568,6870,6497],{"class":603},[568,6872,5553],{"class":596},[568,6874,6876,6878,6880],{"class":570,"line":6875},66,[568,6877,6472],{"class":596},[568,6879,6487],{"class":603},[568,6881,5553],{"class":596},[5130,6883,6885],{"id":6884},"upgrade-prompts-where-the-feature-lives","Upgrade prompts where the feature lives",[543,6887,6889],{"className":5518,"code":6888,"language":5520,"meta":551,"style":551},"\u003C!-- app\u002Fcomponents\u002FFeatureGate.vue -->\n\u003Cscript setup lang=\"ts\">\nconst props = defineProps\u003C{ resource: string, feature: string }>()\nconst { canRead, isLoading } = usePermissions(props.resource) \u002F\u002F the server's decision, plan included\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CUSkeleton v-if=\"isLoading\" class=\"h-32 w-full\" \u002F>\n  \u003Cslot v-else-if=\"canRead\" \u002F>\n  \u003CUCard v-else>\n    \u003Cdiv class=\"flex items-center gap-4\">\n      \u003CUIcon name=\"i-lucide-sparkles\" class=\"size-6 text-primary\" \u002F>\n      \u003Cdiv class=\"flex-1\">\n        \u003Cp class=\"font-medium\">Unlock {{ feature }}\u003C\u002Fp>\n        \u003Cp class=\"text-sm text-muted\">Available on the Pro plan and above.\u003C\u002Fp>\n      \u003C\u002Fdiv>\n      \u003CUButton label=\"See plans\" to=\"\u002Fapp\u002Fbilling\" \u002F>\n    \u003C\u002Fdiv>\n  \u003C\u002FUCard>\n\u003C\u002Ftemplate>\n",[522,6890,6891,6896,6916,6950,6982,6990,6994,7002,7033,7054,7065,7084,7116,7135,7162,7189,7197,7228,7236,7244],{"__ignoreMap":551},[568,6892,6893],{"class":570,"line":571},[568,6894,6895],{"class":574},"\u003C!-- app\u002Fcomponents\u002FFeatureGate.vue -->\n",[568,6897,6898,6900,6902,6904,6906,6908,6910,6912,6914],{"class":570,"line":578},[568,6899,5532],{"class":596},[568,6901,5535],{"class":603},[568,6903,5538],{"class":1042},[568,6905,5541],{"class":1042},[568,6907,1049],{"class":596},[568,6909,5546],{"class":596},[568,6911,564],{"class":652},[568,6913,5546],{"class":596},[568,6915,5553],{"class":596},[568,6917,6918,6920,6923,6925,6928,6930,6933,6935,6937,6939,6942,6944,6946,6948],{"class":570,"line":600},[568,6919,1657],{"class":1042},[568,6921,6922],{"class":592}," props ",[568,6924,1049],{"class":596},[568,6926,6927],{"class":588}," defineProps",[568,6929,5651],{"class":596},[568,6931,6932],{"class":603}," resource",[568,6934,607],{"class":596},[568,6936,2570],{"class":1674},[568,6938,632],{"class":596},[568,6940,6941],{"class":603}," feature",[568,6943,607],{"class":596},[568,6945,2570],{"class":1674},[568,6947,5670],{"class":596},[568,6949,2148],{"class":592},[568,6951,6952,6954,6956,6959,6961,6964,6966,6968,6971,6974,6976,6979],{"class":570,"line":613},[568,6953,1657],{"class":1042},[568,6955,621],{"class":596},[568,6957,6958],{"class":592}," canRead",[568,6960,632],{"class":596},[568,6962,6963],{"class":592}," isLoading ",[568,6965,1018],{"class":596},[568,6967,1744],{"class":596},[568,6969,6970],{"class":588}," usePermissions",[568,6972,6973],{"class":592},"(props",[568,6975,1089],{"class":596},[568,6977,6978],{"class":592},"resource) ",[568,6980,6981],{"class":574},"\u002F\u002F the server's decision, plan included\n",[568,6983,6984,6986,6988],{"class":570,"line":665},[568,6985,6472],{"class":596},[568,6987,5535],{"class":603},[568,6989,5553],{"class":596},[568,6991,6992],{"class":570,"line":675},[568,6993,1452],{"emptyLinePlaceholder":1451},[568,6995,6996,6998,7000],{"class":570,"line":685},[568,6997,5532],{"class":596},[568,6999,6487],{"class":603},[568,7001,5553],{"class":596},[568,7003,7004,7006,7009,7011,7013,7015,7018,7020,7022,7024,7026,7029,7031],{"class":570,"line":765},[568,7005,6494],{"class":596},[568,7007,7008],{"class":603},"USkeleton",[568,7010,6630],{"class":1042},[568,7012,1049],{"class":596},[568,7014,5546],{"class":596},[568,7016,7017],{"class":652},"isLoading",[568,7019,5546],{"class":596},[568,7021,6500],{"class":1042},[568,7023,1049],{"class":596},[568,7025,5546],{"class":596},[568,7027,7028],{"class":652},"h-32 w-full",[568,7030,5546],{"class":596},[568,7032,6816],{"class":596},[568,7034,7035,7037,7040,7043,7045,7047,7050,7052],{"class":570,"line":775},[568,7036,6494],{"class":596},[568,7038,7039],{"class":603},"slot",[568,7041,7042],{"class":1042}," v-else-if",[568,7044,1049],{"class":596},[568,7046,5546],{"class":596},[568,7048,7049],{"class":652},"canRead",[568,7051,5546],{"class":596},[568,7053,6816],{"class":596},[568,7055,7056,7058,7060,7063],{"class":570,"line":854},[568,7057,6494],{"class":596},[568,7059,6627],{"class":603},[568,7061,7062],{"class":1042}," v-else",[568,7064,5553],{"class":596},[568,7066,7067,7069,7071,7073,7075,7077,7080,7082],{"class":570,"line":873},[568,7068,6516],{"class":596},[568,7070,6650],{"class":603},[568,7072,6500],{"class":1042},[568,7074,1049],{"class":596},[568,7076,5546],{"class":596},[568,7078,7079],{"class":652},"flex items-center gap-4",[568,7081,5546],{"class":596},[568,7083,5553],{"class":596},[568,7085,7086,7088,7091,7094,7096,7098,7101,7103,7105,7107,7109,7112,7114],{"class":570,"line":879},[568,7087,6647],{"class":596},[568,7089,7090],{"class":603},"UIcon",[568,7092,7093],{"class":1042}," name",[568,7095,1049],{"class":596},[568,7097,5546],{"class":596},[568,7099,7100],{"class":652},"i-lucide-sparkles",[568,7102,5546],{"class":596},[568,7104,6500],{"class":1042},[568,7106,1049],{"class":596},[568,7108,5546],{"class":596},[568,7110,7111],{"class":652},"size-6 text-primary",[568,7113,5546],{"class":596},[568,7115,6816],{"class":596},[568,7117,7118,7120,7122,7124,7126,7128,7131,7133],{"class":570,"line":889},[568,7119,6647],{"class":596},[568,7121,6650],{"class":603},[568,7123,6500],{"class":1042},[568,7125,1049],{"class":596},[568,7127,5546],{"class":596},[568,7129,7130],{"class":652},"flex-1",[568,7132,5546],{"class":596},[568,7134,5553],{"class":596},[568,7136,7137,7139,7141,7143,7145,7147,7149,7151,7153,7156,7158,7160],{"class":570,"line":971},[568,7138,6669],{"class":596},[568,7140,518],{"class":603},[568,7142,6500],{"class":1042},[568,7144,1049],{"class":596},[568,7146,5546],{"class":596},[568,7148,6690],{"class":652},[568,7150,5546],{"class":596},[568,7152,6695],{"class":596},[568,7154,7155],{"class":592},"Unlock {{ feature }}",[568,7157,6472],{"class":596},[568,7159,518],{"class":603},[568,7161,5553],{"class":596},[568,7163,7164,7166,7168,7170,7172,7174,7176,7178,7180,7183,7185,7187],{"class":570,"line":992},[568,7165,6669],{"class":596},[568,7167,518],{"class":603},[568,7169,6500],{"class":1042},[568,7171,1049],{"class":596},[568,7173,5546],{"class":596},[568,7175,6731],{"class":652},[568,7177,5546],{"class":596},[568,7179,6695],{"class":596},[568,7181,7182],{"class":592},"Available on the Pro plan and above.",[568,7184,6472],{"class":596},[568,7186,518],{"class":603},[568,7188,5553],{"class":596},[568,7190,7191,7193,7195],{"class":570,"line":997},[568,7192,6822],{"class":596},[568,7194,6650],{"class":603},[568,7196,5553],{"class":596},[568,7198,7199,7201,7203,7205,7207,7209,7212,7214,7217,7219,7221,7224,7226],{"class":570,"line":1003},[568,7200,6647],{"class":596},[568,7202,6769],{"class":603},[568,7204,695],{"class":1042},[568,7206,1049],{"class":596},[568,7208,5546],{"class":596},[568,7210,7211],{"class":652},"See plans",[568,7213,5546],{"class":596},[568,7215,7216],{"class":1042}," to",[568,7218,1049],{"class":596},[568,7220,5546],{"class":596},[568,7222,7223],{"class":652},"\u002Fapp\u002Fbilling",[568,7225,5546],{"class":596},[568,7227,6816],{"class":596},[568,7229,7230,7232,7234],{"class":570,"line":1009},[568,7231,6832],{"class":596},[568,7233,6650],{"class":603},[568,7235,5553],{"class":596},[568,7237,7238,7240,7242],{"class":570,"line":1015},[568,7239,6868],{"class":596},[568,7241,6627],{"class":603},[568,7243,5553],{"class":596},[568,7245,7246,7248,7250],{"class":570,"line":2663},[568,7247,6472],{"class":596},[568,7249,6487],{"class":603},[568,7251,5553],{"class":596},[543,7253,7255],{"className":5518,"code":7254,"language":5520,"meta":551,"style":551},"\u003CFeatureGate resource=\"reports\" feature=\"Reports\">\n  \u003CReportsDashboard \u002F>\n\u003C\u002FFeatureGate>\n",[522,7256,7257,7287,7292],{"__ignoreMap":551},[568,7258,7259,7261,7264,7266,7268,7270,7272,7274,7276,7278,7280,7283,7285],{"class":570,"line":571},[568,7260,5532],{"class":596},[568,7262,7263],{"class":603},"FeatureGate",[568,7265,6932],{"class":1042},[568,7267,1049],{"class":596},[568,7269,5546],{"class":596},[568,7271,835],{"class":652},[568,7273,5546],{"class":596},[568,7275,6941],{"class":1042},[568,7277,1049],{"class":596},[568,7279,5546],{"class":596},[568,7281,7282],{"class":652},"Reports",[568,7284,5546],{"class":596},[568,7286,5553],{"class":596},[568,7288,7289],{"class":570,"line":578},[568,7290,7291],{"class":592},"  \u003CReportsDashboard \u002F>\n",[568,7293,7294,7296,7298],{"class":570,"line":600},[568,7295,6472],{"class":596},[568,7297,7263],{"class":603},[568,7299,5553],{"class":596},[5130,7301,7303],{"id":7302},"quota-errors","Quota errors",[543,7305,7307],{"className":562,"code":7306,"language":564,"meta":551,"style":551},"const toast = useToast()\nconst { mutate } = useAutoApiCreate('projects', {\n  onError: (error: any) => {\n    const quota = error.data?.data?.reason === 'quota'\n    toast.add({\n      title: quota ? 'Plan limit reached' : 'Could not create project',\n      description: error.data?.message ?? error.message,\n      color: quota ? 'warning' : 'error',\n      actions: quota ? [{ label: 'Upgrade', to: '\u002Fapp\u002Fbilling' }] : undefined,\n    })\n  },\n})\n",[522,7308,7309,7321,7349,7369,7401,7414,7442,7468,7495,7541,7547,7551],{"__ignoreMap":551},[568,7310,7311,7313,7315,7317,7319],{"class":570,"line":571},[568,7312,1657],{"class":1042},[568,7314,5611],{"class":592},[568,7316,1049],{"class":596},[568,7318,5616],{"class":588},[568,7320,2148],{"class":592},[568,7322,7323,7325,7327,7330,7332,7334,7337,7339,7341,7343,7345,7347],{"class":570,"line":578},[568,7324,1657],{"class":1042},[568,7326,621],{"class":596},[568,7328,7329],{"class":592}," mutate ",[568,7331,1018],{"class":596},[568,7333,1744],{"class":596},[568,7335,7336],{"class":588}," useAutoApiCreate",[568,7338,593],{"class":592},[568,7340,656],{"class":596},[568,7342,733],{"class":652},[568,7344,656],{"class":596},[568,7346,632],{"class":596},[568,7348,610],{"class":596},[568,7350,7351,7353,7355,7357,7359,7361,7363,7365,7367],{"class":570,"line":600},[568,7352,5738],{"class":588},[568,7354,607],{"class":596},[568,7356,1665],{"class":596},[568,7358,3136],{"class":1668},[568,7360,607],{"class":596},[568,7362,3616],{"class":1674},[568,7364,1086],{"class":596},[568,7366,1107],{"class":1042},[568,7368,610],{"class":596},[568,7370,7371,7373,7376,7378,7380,7382,7384,7386,7388,7390,7393,7395,7397,7399],{"class":570,"line":613},[568,7372,2400],{"class":1042},[568,7374,7375],{"class":592}," quota",[568,7377,1744],{"class":596},[568,7379,5743],{"class":592},[568,7381,1089],{"class":596},[568,7383,3276],{"class":592},[568,7385,1714],{"class":596},[568,7387,3276],{"class":592},[568,7389,1714],{"class":596},[568,7391,7392],{"class":592},"reason",[568,7394,3629],{"class":596},[568,7396,649],{"class":596},[568,7398,5074],{"class":652},[568,7400,1648],{"class":596},[568,7402,7403,7406,7408,7410,7412],{"class":570,"line":665},[568,7404,7405],{"class":592},"    toast",[568,7407,1089],{"class":596},[568,7409,5753],{"class":588},[568,7411,593],{"class":603},[568,7413,597],{"class":596},[568,7415,7416,7418,7420,7422,7424,7426,7429,7431,7433,7435,7438,7440],{"class":570,"line":675},[568,7417,6569],{"class":603},[568,7419,607],{"class":596},[568,7421,7375],{"class":592},[568,7423,3780],{"class":596},[568,7425,649],{"class":596},[568,7427,7428],{"class":652},"Plan limit reached",[568,7430,656],{"class":596},[568,7432,3717],{"class":596},[568,7434,649],{"class":596},[568,7436,7437],{"class":652},"Could not create project",[568,7439,656],{"class":596},[568,7441,851],{"class":596},[568,7443,7444,7446,7448,7450,7452,7454,7456,7458,7460,7462,7464,7466],{"class":570,"line":685},[568,7445,6584],{"class":603},[568,7447,607],{"class":596},[568,7449,5743],{"class":592},[568,7451,1089],{"class":596},[568,7453,3276],{"class":592},[568,7455,1714],{"class":596},[568,7457,5783],{"class":592},[568,7459,3510],{"class":596},[568,7461,5743],{"class":592},[568,7463,1089],{"class":596},[568,7465,5783],{"class":592},[568,7467,851],{"class":596},[568,7469,7470,7472,7474,7476,7478,7480,7483,7485,7487,7489,7491,7493],{"class":570,"line":765},[568,7471,6540],{"class":603},[568,7473,607],{"class":596},[568,7475,7375],{"class":592},[568,7477,3780],{"class":596},[568,7479,649],{"class":596},[568,7481,7482],{"class":652},"warning",[568,7484,656],{"class":596},[568,7486,3717],{"class":596},[568,7488,649],{"class":596},[568,7490,3136],{"class":652},[568,7492,656],{"class":596},[568,7494,851],{"class":596},[568,7496,7497,7500,7502,7504,7506,7508,7510,7512,7514,7516,7519,7521,7523,7525,7527,7529,7531,7533,7535,7537,7539],{"class":570,"line":775},[568,7498,7499],{"class":603},"      actions",[568,7501,607],{"class":596},[568,7503,7375],{"class":592},[568,7505,3780],{"class":596},[568,7507,728],{"class":603},[568,7509,2165],{"class":596},[568,7511,695],{"class":603},[568,7513,607],{"class":596},[568,7515,649],{"class":596},[568,7517,7518],{"class":652},"Upgrade",[568,7520,656],{"class":596},[568,7522,632],{"class":596},[568,7524,7216],{"class":603},[568,7526,607],{"class":596},[568,7528,649],{"class":596},[568,7530,7223],{"class":652},[568,7532,656],{"class":596},[568,7534,759],{"class":596},[568,7536,1300],{"class":603},[568,7538,607],{"class":596},[568,7540,6135],{"class":596},[568,7542,7543,7545],{"class":570,"line":854},[568,7544,2666],{"class":596},[568,7546,1021],{"class":603},[568,7548,7549],{"class":570,"line":873},[568,7550,1012],{"class":596},[568,7552,7553,7555],{"class":570,"line":879},[568,7554,1018],{"class":596},[568,7556,1021],{"class":592},[553,7558,7560],{"id":7559},"checklist","Checklist",[7562,7563,7566,7584,7597,7603,7616,7622],"ul",{"className":7564},[7565],"contains-task-list",[7567,7568,7571,7575,7576,7579,7580,7583],"li",{"className":7569},[7570],"task-list-item",[7572,7573],"input",{"disabled":1451,"type":7574},"checkbox"," The subscription row is written ",[538,7577,7578],{},"only"," by the verified webhook (",[522,7581,7582],{},"create\u002Fupdate: false"," on the resource).",[7567,7585,7587,7589,7590,7592,7593,7596],{"className":7586},[7570],[7572,7588],{"disabled":1451,"type":7574}," Checkout and portal derive the customer from ",[522,7591,2870],{}," \u002F ",[522,7594,7595],{},"ctx.user",", never from the body.",[7567,7598,7600,7602],{"className":7599},[7570],[7572,7601],{"disabled":1451,"type":7574}," The webhook is idempotent (event id table) and fast.",[7567,7604,7606,7608,7609,7612,7613,7615],{"className":7605},[7570],[7572,7607],{"disabled":1451,"type":7574}," Feature gates are permissions (so ",[522,7610,7611],{},"\u002Fapi\u002Fpermissions"," and the UI agree); quotas are ",[522,7614,4735],{}," hooks.",[7567,7617,7619,7621],{"className":7618},[7570],[7572,7620],{"disabled":1451,"type":7574}," A canceled \u002F past-due subscription falls back to Free limits in the context extender.",[7567,7623,7625,7627],{"className":7624},[7570],[7572,7626],{"disabled":1451,"type":7574}," Test: a Free workspace gets 403 on a Pro resource and 402 past the quota; a webhook replay is a no-op.",[7629,7630,7631],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":551,"searchDepth":578,"depth":578,"links":7633},[7634,7635,7636,7637,7638,7639,7640,7641,7648],{"id":555,"depth":578,"text":556},{"id":1024,"depth":578,"text":1025},{"id":2055,"depth":578,"text":2056},{"id":2874,"depth":578,"text":2875},{"id":3948,"depth":578,"text":3949},{"id":4488,"depth":578,"text":4489},{"id":4728,"depth":578,"text":4729},{"id":5127,"depth":578,"text":5128,"children":7642},[7643,7644,7646,7647],{"id":5132,"depth":600,"text":5133},{"id":5511,"depth":600,"text":7645},"Pricing page with UPricingPlans",{"id":6884,"depth":600,"text":6885},{"id":7302,"depth":600,"text":7303},{"id":7559,"depth":578,"text":7560},"Subscriptions, plan-gated features and usage quotas on top of auto-api: the payment provider owns the money, a\nwebhook keeps a subscriptions table in sync, a context extender puts the caller's plan on every request, and\nresources gate on it like on any other permission. The UI uses Nuxt UI's pricing components.","md",null,{},{"title":247,"description":7649},"C7ue0FzvjzTfIlCO2vn3eTedM1YjsQov1BA1W9RZQUI",[7656,7658],{"title":243,"path":244,"stem":245,"description":7657,"children":-1},"Recipes for fine-grained access control, from \"public blog\" to \"plan-gated, per-field, per-organization\". The\nconcepts behind them are in Authentication & Authorization.",{"title":251,"path":252,"stem":253,"description":7659,"children":-1},"@websideproject\u002Fnuxt-auto-api uses Zod for validation with automatic schema generation from Drizzle tables via drizzle-zod.",1791106762717]