[{"data":1,"prerenderedAt":5134},["ShallowReactive",2],{"navigation-landing-en":3,"navigation-nuxt-auto-en":180,"navigation-nuxt-protokit-en":338,"/docs/nuxt-auto/auto-api/plugin-catalog-en":444,"/docs/nuxt-auto/auto-api/plugin-catalog-surround-en":5130},[4,8,13,28,41,51,64,77,94,110,134,150,157,172],{"title":5,"path":6,"stem":7},"Overview","/docs/landing","0.docs/1.landing/001.index",{"title":9,"path":10,"stem":11,"badge":12},"Built-in Features","/docs/landing/built-in-features","0.docs/1.landing/002.built-in-features","New",{"title":14,"path":15,"stem":16,"children":17,"icon":27},"Content Foundation","/docs/landing/content","0.docs/1.landing/02.content/1.index",[18,19,23],{"title":5,"path":15,"stem":16},{"title":20,"path":21,"stem":22},"Details","/docs/landing/content/details","0.docs/1.landing/02.content/2.details",{"title":24,"path":25,"stem":26},"Technical","/docs/landing/content/technical","0.docs/1.landing/02.content/4.technical","i-heroicons-document-text",{"title":29,"path":30,"stem":31,"children":32,"icon":40},"Regional Content","/docs/landing/regional","0.docs/1.landing/03.regional/1.index",[33,34,37],{"title":5,"path":30,"stem":31},{"title":20,"path":35,"stem":36},"/docs/landing/regional/details","0.docs/1.landing/03.regional/2.details",{"title":24,"path":38,"stem":39},"/docs/landing/regional/technical","0.docs/1.landing/03.regional/4.technical","i-heroicons-globe-alt",{"title":42,"path":43,"stem":44,"children":45,"icon":50},"Multi-language","/docs/landing/multilang","0.docs/1.landing/04.multilang/1.index",[46,47],{"title":5,"path":43,"stem":44},{"title":20,"path":48,"stem":49},"/docs/landing/multilang/details","0.docs/1.landing/04.multilang/2.details","i-heroicons-language",{"title":52,"path":53,"stem":54,"children":55,"icon":63},"Blog","/docs/landing/blog","0.docs/1.landing/05.blog/1.index",[56,57,60],{"title":5,"path":53,"stem":54},{"title":20,"path":58,"stem":59},"/docs/landing/blog/details","0.docs/1.landing/05.blog/2.details",{"title":24,"path":61,"stem":62},"/docs/landing/blog/technical","0.docs/1.landing/05.blog/4.technical","i-heroicons-pencil-square",{"title":65,"path":66,"stem":67,"children":68,"icon":76},"Documentation","/docs/landing/docs","0.docs/1.landing/06.docs/1.index",[69,70,73],{"title":5,"path":66,"stem":67},{"title":20,"path":71,"stem":72},"/docs/landing/docs/details","0.docs/1.landing/06.docs/2.details",{"title":24,"path":74,"stem":75},"/docs/landing/docs/technical","0.docs/1.landing/06.docs/4.technical","i-heroicons-book-open",{"title":78,"path":79,"stem":80,"children":81,"icon":93},"Forms","/docs/landing/forms","0.docs/1.landing/07.forms/1.index",[82,83,86,90],{"title":5,"path":79,"stem":80},{"title":20,"path":84,"stem":85},"/docs/landing/forms/details","0.docs/1.landing/07.forms/2.details",{"title":87,"path":88,"stem":89},"Admin","/docs/landing/forms/admin","0.docs/1.landing/07.forms/3.admin",{"title":24,"path":91,"stem":92},"/docs/landing/forms/technical","0.docs/1.landing/07.forms/4.technical","i-heroicons-clipboard-document-list",{"title":95,"path":96,"stem":97,"children":98,"icon":109},"Email","/docs/landing/email","0.docs/1.landing/08.email/1.index",[99,100,103,106],{"title":5,"path":96,"stem":97},{"title":20,"path":101,"stem":102},"/docs/landing/email/details","0.docs/1.landing/08.email/2.details",{"title":87,"path":104,"stem":105},"/docs/landing/email/admin","0.docs/1.landing/08.email/3.admin",{"title":24,"path":107,"stem":108},"/docs/landing/email/technical","0.docs/1.landing/08.email/4.technical","i-heroicons-envelope",{"title":111,"path":112,"stem":113,"children":114,"icon":133},"Feedback Platform","/docs/landing/feedback","0.docs/1.landing/09.feedback/1.index",[115,116,119,122,125,129],{"title":5,"path":112,"stem":113},{"title":20,"path":117,"stem":118},"/docs/landing/feedback/details","0.docs/1.landing/09.feedback/2.details",{"title":87,"path":120,"stem":121},"/docs/landing/feedback/admin","0.docs/1.landing/09.feedback/3.admin",{"title":24,"path":123,"stem":124},"/docs/landing/feedback/technical","0.docs/1.landing/09.feedback/4.technical",{"title":126,"path":127,"stem":128},"Compare vs SaaS","/docs/landing/feedback/compare","0.docs/1.landing/09.feedback/5.compare",{"title":130,"path":131,"stem":132},"FAQ","/docs/landing/feedback/faq","0.docs/1.landing/09.feedback/6.faq","i-heroicons-chat-bubble-left-right",{"title":135,"path":136,"stem":137,"children":138,"icon":149},"Storage","/docs/landing/storage","0.docs/1.landing/10.storage/1.index",[139,140,143,146],{"title":5,"path":136,"stem":137},{"title":20,"path":141,"stem":142},"/docs/landing/storage/details","0.docs/1.landing/10.storage/2.details",{"title":87,"path":144,"stem":145},"/docs/landing/storage/admin","0.docs/1.landing/10.storage/3.admin",{"title":24,"path":147,"stem":148},"/docs/landing/storage/technical","0.docs/1.landing/10.storage/4.technical","i-heroicons-circle-stack",{"title":151,"path":152,"stem":153,"children":154,"icon":156},"Offline First","/docs/landing/offline-first","0.docs/1.landing/11.offline-first/1.index",[155],{"title":151,"path":152,"stem":153},"i-heroicons-users",{"title":158,"path":159,"stem":160,"children":161,"icon":156},"Yjs Sync","/docs/landing/yjs-sync","0.docs/1.landing/12.yjs-sync/1.index",[162,163,166,169],{"title":5,"path":159,"stem":160},{"title":20,"path":164,"stem":165},"/docs/landing/yjs-sync/details","0.docs/1.landing/12.yjs-sync/2.details",{"title":87,"path":167,"stem":168},"/docs/landing/yjs-sync/admin","0.docs/1.landing/12.yjs-sync/3.admin",{"title":24,"path":170,"stem":171},"/docs/landing/yjs-sync/technical","0.docs/1.landing/12.yjs-sync/4.technical",{"title":173,"path":174,"stem":175,"children":176,"badge":178,"icon":179},"Newsletter","/docs/landing/newsletter","0.docs/1.landing/13.newsletter/index",[177],{"title":173,"path":174,"stem":175,"badge":178},"Coming Soon","i-lucide-send",[181,184,202,298],{"title":5,"path":182,"stem":183},"/docs/nuxt-auto","0.docs/3.nuxt-auto/index",{"title":185,"path":186,"stem":187,"children":188,"page":201},"Getting Started","/docs/nuxt-auto/getting-started","0.docs/3.nuxt-auto/1.getting-started",[189,193,197],{"title":190,"path":191,"stem":192},"Introduction","/docs/nuxt-auto/getting-started/introduction","0.docs/3.nuxt-auto/1.getting-started/1.introduction",{"title":194,"path":195,"stem":196},"Installation","/docs/nuxt-auto/getting-started/installation","0.docs/3.nuxt-auto/1.getting-started/2.installation",{"title":198,"path":199,"stem":200},"Quick Start","/docs/nuxt-auto/getting-started/quick-start","0.docs/3.nuxt-auto/1.getting-started/3.quick-start",false,{"title":203,"path":204,"stem":205,"children":206,"page":201},"Auto API","/docs/nuxt-auto/auto-api","0.docs/3.nuxt-auto/2.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],{"title":185,"path":208,"stem":209},"/docs/nuxt-auto/auto-api/getting-started","0.docs/3.nuxt-auto/2.auto-api/1.getting-started",{"title":211,"path":212,"stem":213},"Aggregations","/docs/nuxt-auto/auto-api/aggregations","0.docs/3.nuxt-auto/2.auto-api/10.aggregations",{"title":215,"path":216,"stem":217},"Lifecycle Hooks","/docs/nuxt-auto/auto-api/lifecycle-hooks","0.docs/3.nuxt-auto/2.auto-api/11.lifecycle-hooks",{"title":219,"path":220,"stem":221},"Many-to-Many (M2M) Relationships","/docs/nuxt-auto/auto-api/m2m-relationships","0.docs/3.nuxt-auto/2.auto-api/12.m2m-relationships",{"title":223,"path":224,"stem":225},"Plugin System","/docs/nuxt-auto/auto-api/plugin-system","0.docs/3.nuxt-auto/2.auto-api/13.plugin-system",{"title":227,"path":228,"stem":229},"Database Adapters","/docs/nuxt-auto/auto-api/database-adapters","0.docs/3.nuxt-auto/2.auto-api/14.database-adapters",{"title":231,"path":232,"stem":233},"Custom Endpoints","/docs/nuxt-auto/auto-api/custom-endpoints","0.docs/3.nuxt-auto/2.auto-api/15.custom-endpoints",{"title":235,"path":236,"stem":237},"Multi-Tenancy","/docs/nuxt-auto/auto-api/multi-tenancy","0.docs/3.nuxt-auto/2.auto-api/16.multi-tenancy",{"title":239,"path":240,"stem":241},"Validation","/docs/nuxt-auto/auto-api/validation","0.docs/3.nuxt-auto/2.auto-api/2.validation",{"title":243,"path":244,"stem":245},"Rate Limiting","/docs/nuxt-auto/auto-api/rate-limiting","0.docs/3.nuxt-auto/2.auto-api/20.rate-limiting",{"title":247,"path":248,"stem":249},"Request Metadata Plugin","/docs/nuxt-auto/auto-api/request-metadata","0.docs/3.nuxt-auto/2.auto-api/21.request-metadata",{"title":251,"path":252,"stem":253},"Plugin Catalog","/docs/nuxt-auto/auto-api/plugin-catalog","0.docs/3.nuxt-auto/2.auto-api/22.plugin-catalog",{"title":255,"path":256,"stem":257},"Handler Overrides","/docs/nuxt-auto/auto-api/handler-overrides","0.docs/3.nuxt-auto/2.auto-api/3.handler-overrides",{"title":259,"path":260,"stem":261},"Cloudflare D1","/docs/nuxt-auto/auto-api/cloudflare-d1","0.docs/3.nuxt-auto/2.auto-api/30.cloudflare-d1",{"title":263,"path":264,"stem":265},"SQLite to D1 Migration","/docs/nuxt-auto/auto-api/migration-sqlite-d1","0.docs/3.nuxt-auto/2.auto-api/31.migration-sqlite-d1",{"title":267,"path":268,"stem":269},"Frontend Composables","/docs/nuxt-auto/auto-api/frontend-composables","0.docs/3.nuxt-auto/2.auto-api/32.frontend-composables",{"title":271,"path":272,"stem":273},"Testing","/docs/nuxt-auto/auto-api/testing","0.docs/3.nuxt-auto/2.auto-api/33.testing",{"title":275,"path":276,"stem":277},"Pagination","/docs/nuxt-auto/auto-api/pagination","0.docs/3.nuxt-auto/2.auto-api/4.pagination",{"title":279,"path":280,"stem":281},"Soft Deletes","/docs/nuxt-auto/auto-api/soft-deletes","0.docs/3.nuxt-auto/2.auto-api/5.soft-deletes",{"title":283,"path":284,"stem":285},"Authentication & Authorization","/docs/nuxt-auto/auto-api/authentication-authorization","0.docs/3.nuxt-auto/2.auto-api/6.authentication-authorization",{"title":287,"path":288,"stem":289},"Better-Auth Integration","/docs/nuxt-auto/auto-api/better-auth","0.docs/3.nuxt-auto/2.auto-api/7.better-auth",{"title":291,"path":292,"stem":293},"Nested Relations","/docs/nuxt-auto/auto-api/nested-relationships","0.docs/3.nuxt-auto/2.auto-api/8.nested-relationships",{"title":295,"path":296,"stem":297},"Bulk Operations","/docs/nuxt-auto/auto-api/bulk-operations","0.docs/3.nuxt-auto/2.auto-api/9.bulk-operations",{"title":299,"path":300,"stem":301,"children":302,"page":201},"Auto Admin","/docs/nuxt-auto/auto-admin","0.docs/3.nuxt-auto/3.auto-admin",[303,306,310,314,318,322,326,330,334],{"title":185,"path":304,"stem":305},"/docs/nuxt-auto/auto-admin/getting-started","0.docs/3.nuxt-auto/3.auto-admin/1.getting-started",{"title":307,"path":308,"stem":309},"Configuration & Theming","/docs/nuxt-auto/auto-admin/configuration-theming","0.docs/3.nuxt-auto/3.auto-admin/2.configuration-theming",{"title":311,"path":312,"stem":313},"Resource Configuration","/docs/nuxt-auto/auto-admin/resource-configuration","0.docs/3.nuxt-auto/3.auto-admin/3.resource-configuration",{"title":315,"path":316,"stem":317},"Form Fields & Widgets","/docs/nuxt-auto/auto-admin/form-fields-widgets","0.docs/3.nuxt-auto/3.auto-admin/4.form-fields-widgets",{"title":319,"path":320,"stem":321},"Permissions","/docs/nuxt-auto/auto-admin/permissions","0.docs/3.nuxt-auto/3.auto-admin/5.permissions",{"title":323,"path":324,"stem":325},"Custom Pages","/docs/nuxt-auto/auto-admin/custom-pages","0.docs/3.nuxt-auto/3.auto-admin/6.custom-pages",{"title":327,"path":328,"stem":329},"M2M Relationships","/docs/nuxt-auto/auto-admin/m2m-relationships","0.docs/3.nuxt-auto/3.auto-admin/7.m2m-relationships",{"title":331,"path":332,"stem":333},"Custom Actions","/docs/nuxt-auto/auto-admin/custom-actions","0.docs/3.nuxt-auto/3.auto-admin/8.custom-actions",{"title":335,"path":336,"stem":337},"Composables","/docs/nuxt-auto/auto-admin/composables","0.docs/3.nuxt-auto/3.auto-admin/9.composables",[339,342,355,382,400,415,425],{"title":5,"path":340,"stem":341},"/docs/nuxt-protokit","0.docs/4.nuxt-protokit/index",{"title":185,"path":343,"stem":344,"children":345,"icon":354},"/docs/nuxt-protokit/getting-started","0.docs/4.nuxt-protokit/1.getting-started/1.index",[346,347,351],{"title":190,"path":343,"stem":344},{"title":348,"path":349,"stem":350},"Core Concepts","/docs/nuxt-protokit/getting-started/concepts","0.docs/4.nuxt-protokit/1.getting-started/2.concepts",{"title":198,"path":352,"stem":353},"/docs/nuxt-protokit/getting-started/quick-start","0.docs/4.nuxt-protokit/1.getting-started/3.quick-start","i-lucide-rocket",{"title":356,"path":357,"stem":358,"children":359,"icon":381},"Schemas","/docs/nuxt-protokit/schemas","0.docs/4.nuxt-protokit/2.schemas/1.index",[360,361,365,369,373,377],{"title":5,"path":357,"stem":358},{"title":362,"path":363,"stem":364},"Field Types","/docs/nuxt-protokit/schemas/fields","0.docs/4.nuxt-protokit/2.schemas/2.fields",{"title":366,"path":367,"stem":368},"Collections","/docs/nuxt-protokit/schemas/collections","0.docs/4.nuxt-protokit/2.schemas/3.collections",{"title":370,"path":371,"stem":372},"Derived & Computed","/docs/nuxt-protokit/schemas/derived-computed","0.docs/4.nuxt-protokit/2.schemas/4.derived-computed",{"title":374,"path":375,"stem":376},"Connections","/docs/nuxt-protokit/schemas/connections","0.docs/4.nuxt-protokit/2.schemas/5.connections",{"title":378,"path":379,"stem":380},"Visualizations & Layouts","/docs/nuxt-protokit/schemas/visualizations","0.docs/4.nuxt-protokit/2.schemas/6.visualizations","i-lucide-file-code",{"title":335,"path":383,"stem":384,"children":385,"icon":399},"/docs/nuxt-protokit/composables","0.docs/4.nuxt-protokit/3.composables/1.index",[386,387,391,395],{"title":5,"path":383,"stem":384},{"title":388,"path":389,"stem":390},"usePrototype","/docs/nuxt-protokit/composables/use-prototype","0.docs/4.nuxt-protokit/3.composables/2.use-prototype",{"title":392,"path":393,"stem":394},"useProtoDoc","/docs/nuxt-protokit/composables/use-proto-doc","0.docs/4.nuxt-protokit/3.composables/3.use-proto-doc",{"title":396,"path":397,"stem":398},"useProtoCollection","/docs/nuxt-protokit/composables/use-proto-collection","0.docs/4.nuxt-protokit/3.composables/4.use-proto-collection","i-lucide-layers",{"title":401,"path":402,"stem":403,"children":404,"icon":414},"Components","/docs/nuxt-protokit/components","0.docs/4.nuxt-protokit/4.components/1.index",[405,406,410],{"title":5,"path":402,"stem":403},{"title":407,"path":408,"stem":409},"ProtoTool","/docs/nuxt-protokit/components/proto-tool","0.docs/4.nuxt-protokit/4.components/2.proto-tool",{"title":411,"path":412,"stem":413},"ProtoCrudModal","/docs/nuxt-protokit/components/proto-crud-modal","0.docs/4.nuxt-protokit/4.components/3.proto-crud-modal","i-lucide-puzzle",{"title":151,"path":416,"stem":417,"children":418,"icon":424},"/docs/nuxt-protokit/offline-first","0.docs/4.nuxt-protokit/5.offline-first/1.index",[419,420],{"title":151,"path":416,"stem":417},{"title":421,"path":422,"stem":423},"Corruption Recovery","/docs/nuxt-protokit/offline-first/corruption-recovery","0.docs/4.nuxt-protokit/5.offline-first/2.corruption-recovery","i-lucide-wifi-off",{"title":426,"icon":427,"path":428,"stem":429,"children":430},"Advanced","i-lucide-graduation-cap","/docs/nuxt-protokit/advanced","0.docs/4.nuxt-protokit/6.advanced/1.index",[431,432,436,440],{"title":5,"path":428,"stem":429},{"title":433,"path":434,"stem":435},"Multi-Tool Apps","/docs/nuxt-protokit/advanced/building-a-toolkit","0.docs/4.nuxt-protokit/6.advanced/1.building-a-toolkit",{"title":437,"path":438,"stem":439},"Schema Patterns","/docs/nuxt-protokit/advanced/custom-schema-patterns","0.docs/4.nuxt-protokit/6.advanced/2.custom-schema-patterns",{"title":441,"path":442,"stem":443},"Custom Fields & Viz","/docs/nuxt-protokit/advanced/extensibility","0.docs/4.nuxt-protokit/6.advanced/3.extensibility",{"page":445,"fallbackPage":5126},{"id":446,"title":251,"body":447,"description":457,"extension":5125,"links":5126,"meta":5127,"navigation":5126,"ogImage":5126,"path":252,"seo":5128,"stem":253,"__hash__":5129},"nuxt_auto/0.docs/3.nuxt-auto/2.auto-api/22.plugin-catalog.md",{"type":448,"value":449,"toc":5088},"minimark",[450,454,458,465,470,473,697,700,704,713,716,744,808,815,818,844,920,927,937,969,1042,1044,1051,1054,1087,1193,1195,1202,1205,1228,1412,1414,1421,1424,1454,1540,1542,1549,1552,1584,1716,1718,1725,1732,1753,1946,1948,1955,1958,1983,2077,2079,2086,2089,2110,2261,2263,2270,2273,2303,2376,2378,2385,2388,2427,2535,2537,2544,2547,2579,2701,2703,2710,2713,2743,2843,2845,2852,2855,2891,2896,2899,2924,2932,2942,2946,3025,3028,3032,3540,3544,3817,3826,3982,3985,3992,4001,4076,4097,4105,4146,4150,4159,4170,4264,4279,4291,4298,4306,4309,4317,4320,4328,4331,4335,4341,4344,4501,4504,4508,4511,4525,4539,4543,4611,4615,4625,4691,4694,4848,4850,4854,4860,4878,4880,4884,5084],[451,452,251],"h1",{"id":453},"plugin-catalog",[455,456,457],"p",{},"This document lists all shipped plugins for @websideproject/nuxt-auto-api.",[455,459,460,461,464],{},"See ",[462,463,223],"a",{"href":224}," for how plugins work and how to create your own.",[466,467,469],"h2",{"id":468},"pipeline-capabilities-reference","Pipeline Capabilities Reference",[455,471,472],{},"Before reading the plugin list, understand what the plugin system can do:",[474,475,476,492],"table",{},[477,478,479],"thead",{},[480,481,482,486,489],"tr",{},[483,484,485],"th",{},"Capability",[483,487,488],{},"How",[483,490,491],{},"Notes",[493,494,495,518,530,551,570,586,605,632,656,676],"tbody",{},[480,496,497,504,515],{},[498,499,500],"td",{},[501,502,503],"strong",{},"Modify data before write",[498,505,506,510,511,514],{},[507,508,509],"code",{},"beforeCreate","/",[507,512,513],{},"beforeUpdate"," return modified data",[498,516,517],{},"Works",[480,519,520,525,528],{},[498,521,522],{},[501,523,524],{},"Block an operation",[498,526,527],{},"Throw from any middleware or before-hook",[498,529,517],{},[480,531,532,537,549],{},[498,533,534],{},[501,535,536],{},"Side effects after mutation",[498,538,539,510,542,510,545,548],{},[507,540,541],{},"afterCreate",[507,543,544],{},"afterUpdate",[507,546,547],{},"afterDelete"," hooks",[498,550,517],{},[480,552,553,558,567],{},[498,554,555],{},[501,556,557],{},"Enrich request context",[498,559,560,563,564],{},[507,561,562],{},"extendContext()"," — adds data to ",[507,565,566],{},"HandlerContext",[498,568,569],{},"Runs once per request",[480,571,572,577,583],{},[498,573,574],{},[501,575,576],{},"Access database",[498,578,579,582],{},[507,580,581],{},"context.db"," (full Drizzle instance) in any hook/middleware",[498,584,585],{},"No transaction control across hooks",[480,587,588,593,602],{},[498,589,590],{},[501,591,592],{},"Add new endpoints",[498,594,595,598,599],{},[507,596,597],{},"buildSetup"," → ",[507,600,601],{},"addServerHandler()",[498,603,604],{},"Build-time only",[480,606,607,612,625],{},[498,608,609],{},[501,610,611],{},"Modify response data",[498,613,614,615,510,618,510,621,510,623,548],{},"Return values from ",[507,616,617],{},"afterGet",[507,619,620],{},"afterList",[507,622,541],{},[507,624,544],{},[498,626,627,628,631],{},"Hooks returning ",[507,629,630],{},"undefined"," are side-effect only (backward compatible)",[480,633,634,639,650],{},[498,635,636],{},[501,637,638],{},"Modify list query/filters",[498,640,641,642,645,646,649],{},"Push SQL conditions into ",[507,643,644],{},"context.additionalFilters"," in ",[507,647,648],{},"beforeList"," or middleware",[498,651,652,653],{},"List handler merges with ",[507,654,655],{},"and()",[480,657,658,663,673],{},[498,659,660],{},[501,661,662],{},"Short-circuit with cached data",[498,664,665,666,645,669,672],{},"Set ",[507,667,668],{},"context.shortCircuit = { data }",[507,670,671],{},"pre-execute"," middleware",[498,674,675],{},"Skips handler, returns cached data directly",[480,677,678,683,691],{},[498,679,680],{},[501,681,682],{},"Access \"before\" state on delete",[498,684,685,686,645,688],{},"Must manually fetch via ",[507,687,581],{},[507,689,690],{},"beforeDelete",[498,692,693,696],{},[507,694,695],{},"beforeDelete(id, context)"," — no data param",[698,699],"hr",{},[466,701,703],{"id":702},"shipped-plugins","Shipped Plugins",[705,706,708,709,712],"h3",{"id":707},"rate-limiting-createratelimitplugin","Rate Limiting (",[507,710,711],{},"createRateLimitPlugin",")",[455,714,715],{},"Protects API endpoints from abuse with in-memory sliding window rate limiting.",[717,718,719,729,738],"ul",{},[720,721,722,725,726,672],"li",{},[501,723,724],{},"Hooks used:"," ",[507,727,728],{},"pre-auth",[720,730,731,734,735],{},[501,732,733],{},"Approach:"," In-memory Map keyed by IP/user with periodic cleanup via ",[507,736,737],{},"setInterval",[720,739,740,743],{},[501,741,742],{},"Use case:"," Prevent brute-force attacks, protect against DDoS, enforce API quotas",[745,746,751],"pre",{"className":747,"code":748,"language":749,"meta":750,"style":750},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","createRateLimitPlugin({ windowMs: 60000, max: 100, byIp: true })\n","typescript","",[507,752,753],{"__ignoreMap":750},[754,755,758,761,765,769,773,776,780,783,786,788,791,793,796,798,802,805],"span",{"class":756,"line":757},"line",1,[754,759,711],{"class":760},"s2Zo4",[754,762,764],{"class":763},"sTEyZ","(",[754,766,768],{"class":767},"sMK4o","{",[754,770,772],{"class":771},"swJcz"," windowMs",[754,774,775],{"class":767},":",[754,777,779],{"class":778},"sbssI"," 60000",[754,781,782],{"class":767},",",[754,784,785],{"class":771}," max",[754,787,775],{"class":767},[754,789,790],{"class":778}," 100",[754,792,782],{"class":767},[754,794,795],{"class":771}," byIp",[754,797,775],{"class":767},[754,799,801],{"class":800},"sfNiH"," true",[754,803,804],{"class":767}," }",[754,806,807],{"class":763},")\n",[705,809,811,812,712],{"id":810},"request-metadata-createrequestmetadataplugin","Request Metadata (",[507,813,814],{},"createRequestMetadataPlugin",[455,816,817],{},"Extracts request metadata (IP, geo, user-agent) from HTTP headers and optionally auto-populates database columns on create/update.",[717,819,820,830,839],{},[720,821,822,824,825,510,827,829],{},[501,823,724],{}," Context extender + ",[507,826,509],{},[507,828,513],{}," global hooks",[720,831,832,834,835,838],{},[501,833,733],{}," Reads Cloudflare / X-Forwarded-For headers, populates ",[507,836,837],{},"context.requestMeta",". Optional before-hooks write metadata to DB columns via data return value.",[720,840,841,843],{},[501,842,742],{}," Signup geo-tracking, last-seen IP, fraud detection, analytics",[745,845,847],{"className":747,"code":846,"language":749,"meta":750,"style":750},"createRequestMetadataPlugin({ autoPopulate: { ip: 'signupIp', country: 'signupCountry' }, resources: ['users'] })\n",[507,848,849],{"__ignoreMap":750},[754,850,851,853,855,857,860,862,865,868,870,873,877,880,882,885,887,889,892,894,897,900,902,905,907,910,912,915,918],{"class":756,"line":757},[754,852,814],{"class":760},[754,854,764],{"class":763},[754,856,768],{"class":767},[754,858,859],{"class":771}," autoPopulate",[754,861,775],{"class":767},[754,863,864],{"class":767}," {",[754,866,867],{"class":771}," ip",[754,869,775],{"class":767},[754,871,872],{"class":767}," '",[754,874,876],{"class":875},"sfazB","signupIp",[754,878,879],{"class":767},"'",[754,881,782],{"class":767},[754,883,884],{"class":771}," country",[754,886,775],{"class":767},[754,888,872],{"class":767},[754,890,891],{"class":875},"signupCountry",[754,893,879],{"class":767},[754,895,896],{"class":767}," },",[754,898,899],{"class":771}," resources",[754,901,775],{"class":767},[754,903,904],{"class":763}," [",[754,906,879],{"class":767},[754,908,909],{"class":875},"users",[754,911,879],{"class":767},[754,913,914],{"class":763},"] ",[754,916,917],{"class":767},"}",[754,919,807],{"class":763},[705,921,923,924,712],{"id":922},"better-auth-createbetterauthplugin","Better Auth (",[507,925,926],{},"createBetterAuthPlugin",[455,928,929,930,936],{},"Integrates ",[462,931,935],{"href":932,"rel":933},"https://better-auth.com",[934],"nofollow","Better Auth"," sessions into the HandlerContext.",[717,938,939,944,964],{},[720,940,941,943],{},[501,942,724],{}," Context extender",[720,945,946,948,949,952,953,956,957,960,961],{},[501,947,733],{}," Reads session from ",[507,950,951],{},"event.context"," or a custom ",[507,954,955],{},"getSession"," function, maps to ",[507,958,959],{},"context.user"," and ",[507,962,963],{},"context.permissions",[720,965,966,968],{},[501,967,742],{}," Authentication for all auto-api endpoints without manual middleware",[745,970,972],{"className":747,"code":971,"language":749,"meta":750,"style":750},"createBetterAuthPlugin({ getSession: async (event) => auth.api.getSession({ headers: event.headers }) })\n",[507,973,974],{"__ignoreMap":750},[754,975,976,978,980,982,985,987,991,994,998,1000,1003,1006,1009,1012,1014,1016,1018,1020,1023,1025,1028,1030,1033,1035,1038,1040],{"class":756,"line":757},[754,977,926],{"class":760},[754,979,764],{"class":763},[754,981,768],{"class":767},[754,983,984],{"class":760}," getSession",[754,986,775],{"class":767},[754,988,990],{"class":989},"spNyl"," async",[754,992,993],{"class":767}," (",[754,995,997],{"class":996},"sHdIc","event",[754,999,712],{"class":767},[754,1001,1002],{"class":989}," =>",[754,1004,1005],{"class":763}," auth",[754,1007,1008],{"class":767},".",[754,1010,1011],{"class":763},"api",[754,1013,1008],{"class":767},[754,1015,955],{"class":760},[754,1017,764],{"class":763},[754,1019,768],{"class":767},[754,1021,1022],{"class":771}," headers",[754,1024,775],{"class":767},[754,1026,1027],{"class":763}," event",[754,1029,1008],{"class":767},[754,1031,1032],{"class":763},"headers ",[754,1034,917],{"class":767},[754,1036,1037],{"class":763},") ",[754,1039,917],{"class":767},[754,1041,807],{"class":763},[698,1043],{},[705,1045,1047,1048,712],{"id":1046},"audit-log-createauditlogplugin","Audit Log (",[507,1049,1050],{},"createAuditLogPlugin",[455,1052,1053],{},"Records every create, update, and delete operation in a dedicated audit log table.",[717,1055,1056,1077,1082],{},[720,1057,1058,725,1060,510,1062,1064,1065,510,1067,510,1069,1071,1072,993,1074,712],{},[501,1059,724],{},[507,1061,513],{},[507,1063,690],{}," (snapshot current state), ",[507,1066,541],{},[507,1068,544],{},[507,1070,547],{}," (write log entry), ",[507,1073,597],{},[507,1075,1076],{},"GET /api/audit-logs",[720,1078,1079,1081],{},[501,1080,733],{}," After each mutating operation, write a row to an audit table with: resource, operation, record ID, user, timestamp, before/after data, IP.",[720,1083,1084,1086],{},[501,1085,742],{}," Compliance (SOC2, GDPR), admin dashboards, debugging",[745,1088,1090],{"className":747,"code":1089,"language":749,"meta":750,"style":750},"createAuditLogPlugin({\n  table: 'auditLogs',\n  resources: ['users', 'orders'], // or '*' for all\n  excludeFields: ['password'],\n  async: true,\n})\n",[507,1091,1092,1101,1119,1153,1174,1186],{"__ignoreMap":750},[754,1093,1094,1096,1098],{"class":756,"line":757},[754,1095,1050],{"class":760},[754,1097,764],{"class":763},[754,1099,1100],{"class":767},"{\n",[754,1102,1104,1107,1109,1111,1114,1116],{"class":756,"line":1103},2,[754,1105,1106],{"class":771},"  table",[754,1108,775],{"class":767},[754,1110,872],{"class":767},[754,1112,1113],{"class":875},"auditLogs",[754,1115,879],{"class":767},[754,1117,1118],{"class":767},",\n",[754,1120,1122,1125,1127,1129,1131,1133,1135,1137,1139,1142,1144,1147,1149],{"class":756,"line":1121},3,[754,1123,1124],{"class":771},"  resources",[754,1126,775],{"class":767},[754,1128,904],{"class":763},[754,1130,879],{"class":767},[754,1132,909],{"class":875},[754,1134,879],{"class":767},[754,1136,782],{"class":767},[754,1138,872],{"class":767},[754,1140,1141],{"class":875},"orders",[754,1143,879],{"class":767},[754,1145,1146],{"class":763},"]",[754,1148,782],{"class":767},[754,1150,1152],{"class":1151},"sHwdD"," // or '*' for all\n",[754,1154,1156,1159,1161,1163,1165,1168,1170,1172],{"class":756,"line":1155},4,[754,1157,1158],{"class":771},"  excludeFields",[754,1160,775],{"class":767},[754,1162,904],{"class":763},[754,1164,879],{"class":767},[754,1166,1167],{"class":875},"password",[754,1169,879],{"class":767},[754,1171,1146],{"class":763},[754,1173,1118],{"class":767},[754,1175,1177,1180,1182,1184],{"class":756,"line":1176},5,[754,1178,1179],{"class":771},"  async",[754,1181,775],{"class":767},[754,1183,801],{"class":800},[754,1185,1118],{"class":767},[754,1187,1189,1191],{"class":756,"line":1188},6,[754,1190,917],{"class":767},[754,1192,807],{"class":763},[698,1194],{},[705,1196,1198,1199,712],{"id":1197},"webhook-createwebhookplugin","Webhook (",[507,1200,1201],{},"createWebhookPlugin",[455,1203,1204],{},"Sends HTTP webhook notifications when resources change.",[717,1206,1207,1218,1223],{},[720,1208,1209,725,1211,1213,1214,1213,1216,829],{},[501,1210,724],{},[507,1212,541],{},", ",[507,1215,544],{},[507,1217,547],{},[720,1219,1220,1222],{},[501,1221,733],{}," After mutations, POST a JSON payload to configured webhook URLs. Fire-and-forget with HMAC signing and retry logic.",[720,1224,1225,1227],{},[501,1226,742],{}," Third-party integrations (Slack, Zapier, n8n), event-driven architectures",[745,1229,1231],{"className":747,"code":1230,"language":749,"meta":750,"style":750},"createWebhookPlugin({\n  endpoints: [\n    { url: 'https://hooks.slack.com/...', events: ['users.create'] },\n    { url: 'https://n8n.example.com/webhook/...', events: ['*'] },\n  ],\n  signing: { secret: process.env.WEBHOOK_SECRET!, algorithm: 'sha256' },\n  retry: { attempts: 3, backoffMs: 1000 },\n})\n",[507,1232,1233,1241,1251,1289,1323,1330,1375,1405],{"__ignoreMap":750},[754,1234,1235,1237,1239],{"class":756,"line":757},[754,1236,1201],{"class":760},[754,1238,764],{"class":763},[754,1240,1100],{"class":767},[754,1242,1243,1246,1248],{"class":756,"line":1103},[754,1244,1245],{"class":771},"  endpoints",[754,1247,775],{"class":767},[754,1249,1250],{"class":763}," [\n",[754,1252,1253,1256,1259,1261,1263,1266,1268,1270,1273,1275,1277,1279,1282,1284,1286],{"class":756,"line":1121},[754,1254,1255],{"class":767},"    {",[754,1257,1258],{"class":771}," url",[754,1260,775],{"class":767},[754,1262,872],{"class":767},[754,1264,1265],{"class":875},"https://hooks.slack.com/...",[754,1267,879],{"class":767},[754,1269,782],{"class":767},[754,1271,1272],{"class":771}," events",[754,1274,775],{"class":767},[754,1276,904],{"class":763},[754,1278,879],{"class":767},[754,1280,1281],{"class":875},"users.create",[754,1283,879],{"class":767},[754,1285,914],{"class":763},[754,1287,1288],{"class":767},"},\n",[754,1290,1291,1293,1295,1297,1299,1302,1304,1306,1308,1310,1312,1314,1317,1319,1321],{"class":756,"line":1155},[754,1292,1255],{"class":767},[754,1294,1258],{"class":771},[754,1296,775],{"class":767},[754,1298,872],{"class":767},[754,1300,1301],{"class":875},"https://n8n.example.com/webhook/...",[754,1303,879],{"class":767},[754,1305,782],{"class":767},[754,1307,1272],{"class":771},[754,1309,775],{"class":767},[754,1311,904],{"class":763},[754,1313,879],{"class":767},[754,1315,1316],{"class":875},"*",[754,1318,879],{"class":767},[754,1320,914],{"class":763},[754,1322,1288],{"class":767},[754,1324,1325,1328],{"class":756,"line":1176},[754,1326,1327],{"class":763},"  ]",[754,1329,1118],{"class":767},[754,1331,1332,1335,1337,1339,1342,1344,1347,1349,1352,1354,1357,1360,1363,1365,1367,1370,1372],{"class":756,"line":1188},[754,1333,1334],{"class":771},"  signing",[754,1336,775],{"class":767},[754,1338,864],{"class":767},[754,1340,1341],{"class":771}," secret",[754,1343,775],{"class":767},[754,1345,1346],{"class":763}," process",[754,1348,1008],{"class":767},[754,1350,1351],{"class":763},"env",[754,1353,1008],{"class":767},[754,1355,1356],{"class":763},"WEBHOOK_SECRET",[754,1358,1359],{"class":767},"!,",[754,1361,1362],{"class":771}," algorithm",[754,1364,775],{"class":767},[754,1366,872],{"class":767},[754,1368,1369],{"class":875},"sha256",[754,1371,879],{"class":767},[754,1373,1374],{"class":767}," },\n",[754,1376,1378,1381,1383,1385,1388,1390,1393,1395,1398,1400,1403],{"class":756,"line":1377},7,[754,1379,1380],{"class":771},"  retry",[754,1382,775],{"class":767},[754,1384,864],{"class":767},[754,1386,1387],{"class":771}," attempts",[754,1389,775],{"class":767},[754,1391,1392],{"class":778}," 3",[754,1394,782],{"class":767},[754,1396,1397],{"class":771}," backoffMs",[754,1399,775],{"class":767},[754,1401,1402],{"class":778}," 1000",[754,1404,1374],{"class":767},[754,1406,1408,1410],{"class":756,"line":1407},8,[754,1409,917],{"class":767},[754,1411,807],{"class":763},[698,1413],{},[705,1415,1417,1418,712],{"id":1416},"activity-feed-createactivityfeedplugin","Activity Feed (",[507,1419,1420],{},"createActivityFeedPlugin",[455,1422,1423],{},"Maintains a user-facing activity feed of changes across resources.",[717,1425,1426,1442,1449],{},[720,1427,1428,725,1430,1213,1432,1213,1434,1436,1437,993,1439,712],{},[501,1429,724],{},[507,1431,541],{},[507,1433,544],{},[507,1435,547],{}," global hooks + ",[507,1438,597],{},[507,1440,1441],{},"GET /api/activities",[720,1443,1444,1446,1447,1008],{},[501,1445,733],{}," After mutations, write a human-readable activity entry to a table via ",[507,1448,581],{},[720,1450,1451,1453],{},[501,1452,742],{}," Dashboard \"recent activity\", notification center, collaboration features",[745,1455,1457],{"className":747,"code":1456,"language":749,"meta":750,"style":750},"createActivityFeedPlugin({\n  table: 'activities',\n  resources: ['articles', 'users', 'comments'],\n  template: '{user.email} {operation}d a {resource}',\n})\n",[507,1458,1459,1467,1482,1518,1534],{"__ignoreMap":750},[754,1460,1461,1463,1465],{"class":756,"line":757},[754,1462,1420],{"class":760},[754,1464,764],{"class":763},[754,1466,1100],{"class":767},[754,1468,1469,1471,1473,1475,1478,1480],{"class":756,"line":1103},[754,1470,1106],{"class":771},[754,1472,775],{"class":767},[754,1474,872],{"class":767},[754,1476,1477],{"class":875},"activities",[754,1479,879],{"class":767},[754,1481,1118],{"class":767},[754,1483,1484,1486,1488,1490,1492,1495,1497,1499,1501,1503,1505,1507,1509,1512,1514,1516],{"class":756,"line":1121},[754,1485,1124],{"class":771},[754,1487,775],{"class":767},[754,1489,904],{"class":763},[754,1491,879],{"class":767},[754,1493,1494],{"class":875},"articles",[754,1496,879],{"class":767},[754,1498,782],{"class":767},[754,1500,872],{"class":767},[754,1502,909],{"class":875},[754,1504,879],{"class":767},[754,1506,782],{"class":767},[754,1508,872],{"class":767},[754,1510,1511],{"class":875},"comments",[754,1513,879],{"class":767},[754,1515,1146],{"class":763},[754,1517,1118],{"class":767},[754,1519,1520,1523,1525,1527,1530,1532],{"class":756,"line":1155},[754,1521,1522],{"class":771},"  template",[754,1524,775],{"class":767},[754,1526,872],{"class":767},[754,1528,1529],{"class":875},"{user.email} {operation}d a {resource}",[754,1531,879],{"class":767},[754,1533,1118],{"class":767},[754,1535,1536,1538],{"class":756,"line":1176},[754,1537,917],{"class":767},[754,1539,807],{"class":763},[698,1541],{},[705,1543,1545,1546,712],{"id":1544},"slug-generation-createslugplugin","Slug Generation (",[507,1547,1548],{},"createSlugPlugin",[455,1550,1551],{},"Automatically generates URL slugs from a source field on create/update.",[717,1553,1554,1564,1579],{},[720,1555,1556,725,1558,1560,1561,1563],{},[501,1557,724],{},[507,1559,509],{}," (generate slug), ",[507,1562,513],{}," (regenerate if source changed)",[720,1565,1566,1568,1569,1571,1572,1213,1575,1578],{},[501,1567,733],{}," Transliterate to ASCII, kebab-case, check uniqueness via ",[507,1570,581],{},", append ",[507,1573,1574],{},"-2",[507,1576,1577],{},"-3"," if needed.",[720,1580,1581,1583],{},[501,1582,742],{}," Blog articles, categories, any resource with URL-friendly identifiers",[745,1585,1587],{"className":747,"code":1586,"language":749,"meta":750,"style":750},"createSlugPlugin({\n  resources: {\n    articles: { source: 'title', target: 'slug' },\n    categories: { source: 'name', target: 'slug' },\n  },\n  separator: '-',\n  maxLength: 80,\n})\n",[507,1588,1589,1597,1606,1643,1677,1682,1698,1710],{"__ignoreMap":750},[754,1590,1591,1593,1595],{"class":756,"line":757},[754,1592,1548],{"class":760},[754,1594,764],{"class":763},[754,1596,1100],{"class":767},[754,1598,1599,1601,1603],{"class":756,"line":1103},[754,1600,1124],{"class":771},[754,1602,775],{"class":767},[754,1604,1605],{"class":767}," {\n",[754,1607,1608,1611,1613,1615,1618,1620,1622,1625,1627,1629,1632,1634,1636,1639,1641],{"class":756,"line":1121},[754,1609,1610],{"class":771},"    articles",[754,1612,775],{"class":767},[754,1614,864],{"class":767},[754,1616,1617],{"class":771}," source",[754,1619,775],{"class":767},[754,1621,872],{"class":767},[754,1623,1624],{"class":875},"title",[754,1626,879],{"class":767},[754,1628,782],{"class":767},[754,1630,1631],{"class":771}," target",[754,1633,775],{"class":767},[754,1635,872],{"class":767},[754,1637,1638],{"class":875},"slug",[754,1640,879],{"class":767},[754,1642,1374],{"class":767},[754,1644,1645,1648,1650,1652,1654,1656,1658,1661,1663,1665,1667,1669,1671,1673,1675],{"class":756,"line":1155},[754,1646,1647],{"class":771},"    categories",[754,1649,775],{"class":767},[754,1651,864],{"class":767},[754,1653,1617],{"class":771},[754,1655,775],{"class":767},[754,1657,872],{"class":767},[754,1659,1660],{"class":875},"name",[754,1662,879],{"class":767},[754,1664,782],{"class":767},[754,1666,1631],{"class":771},[754,1668,775],{"class":767},[754,1670,872],{"class":767},[754,1672,1638],{"class":875},[754,1674,879],{"class":767},[754,1676,1374],{"class":767},[754,1678,1679],{"class":756,"line":1176},[754,1680,1681],{"class":767},"  },\n",[754,1683,1684,1687,1689,1691,1694,1696],{"class":756,"line":1188},[754,1685,1686],{"class":771},"  separator",[754,1688,775],{"class":767},[754,1690,872],{"class":767},[754,1692,1693],{"class":875},"-",[754,1695,879],{"class":767},[754,1697,1118],{"class":767},[754,1699,1700,1703,1705,1708],{"class":756,"line":1377},[754,1701,1702],{"class":771},"  maxLength",[754,1704,775],{"class":767},[754,1706,1707],{"class":778}," 80",[754,1709,1118],{"class":767},[754,1711,1712,1714],{"class":756,"line":1407},[754,1713,917],{"class":767},[754,1715,807],{"class":763},[698,1717],{},[705,1719,1721,1722,712],{"id":1720},"data-validation-createschemavalidationplugin","Data Validation (",[507,1723,1724],{},"createSchemaValidationPlugin",[455,1726,1727,1728,1731],{},"Adds runtime schema validation to create/update operations. Works with any library implementing ",[507,1729,1730],{},".safeParse()"," (Zod, Valibot, ArkType).",[717,1733,1734,1743,1748],{},[720,1735,1736,725,1738,510,1740,1742],{},[501,1737,724],{},[507,1739,509],{},[507,1741,513],{}," hooks (throw on validation failure)",[720,1744,1745,1747],{},[501,1746,733],{}," Run validation schema against incoming data. Throw 422 with structured errors on failure.",[720,1749,1750,1752],{},[501,1751,742],{}," Complex cross-field validation, conditional rules, schema enforcement",[745,1754,1756],{"className":747,"code":1755,"language":749,"meta":750,"style":750},"import { z } from 'zod'\ncreateSchemaValidationPlugin({\n  resources: {\n    users: {\n      create: z.object({ email: z.string().email(), age: z.number().min(18) }),\n      update: z.object({ email: z.string().email().optional() }),\n    },\n  },\n})\n",[507,1757,1758,1782,1790,1798,1807,1881,1930,1935,1939],{"__ignoreMap":750},[754,1759,1760,1764,1766,1769,1771,1774,1776,1779],{"class":756,"line":757},[754,1761,1763],{"class":1762},"s7zQu","import",[754,1765,864],{"class":767},[754,1767,1768],{"class":763}," z",[754,1770,804],{"class":767},[754,1772,1773],{"class":1762}," from",[754,1775,872],{"class":767},[754,1777,1778],{"class":875},"zod",[754,1780,1781],{"class":767},"'\n",[754,1783,1784,1786,1788],{"class":756,"line":1103},[754,1785,1724],{"class":760},[754,1787,764],{"class":763},[754,1789,1100],{"class":767},[754,1791,1792,1794,1796],{"class":756,"line":1121},[754,1793,1124],{"class":771},[754,1795,775],{"class":767},[754,1797,1605],{"class":767},[754,1799,1800,1803,1805],{"class":756,"line":1155},[754,1801,1802],{"class":771},"    users",[754,1804,775],{"class":767},[754,1806,1605],{"class":767},[754,1808,1809,1812,1814,1816,1818,1821,1823,1825,1828,1830,1832,1834,1837,1840,1842,1845,1847,1849,1852,1854,1856,1858,1861,1863,1865,1868,1870,1873,1875,1877,1879],{"class":756,"line":1176},[754,1810,1811],{"class":771},"      create",[754,1813,775],{"class":767},[754,1815,1768],{"class":763},[754,1817,1008],{"class":767},[754,1819,1820],{"class":760},"object",[754,1822,764],{"class":763},[754,1824,768],{"class":767},[754,1826,1827],{"class":771}," email",[754,1829,775],{"class":767},[754,1831,1768],{"class":763},[754,1833,1008],{"class":767},[754,1835,1836],{"class":760},"string",[754,1838,1839],{"class":763},"()",[754,1841,1008],{"class":767},[754,1843,1844],{"class":760},"email",[754,1846,1839],{"class":763},[754,1848,782],{"class":767},[754,1850,1851],{"class":771}," age",[754,1853,775],{"class":767},[754,1855,1768],{"class":763},[754,1857,1008],{"class":767},[754,1859,1860],{"class":760},"number",[754,1862,1839],{"class":763},[754,1864,1008],{"class":767},[754,1866,1867],{"class":760},"min",[754,1869,764],{"class":763},[754,1871,1872],{"class":778},"18",[754,1874,1037],{"class":763},[754,1876,917],{"class":767},[754,1878,712],{"class":763},[754,1880,1118],{"class":767},[754,1882,1883,1886,1888,1890,1892,1894,1896,1898,1900,1902,1904,1906,1908,1910,1912,1914,1916,1918,1921,1924,1926,1928],{"class":756,"line":1188},[754,1884,1885],{"class":771},"      update",[754,1887,775],{"class":767},[754,1889,1768],{"class":763},[754,1891,1008],{"class":767},[754,1893,1820],{"class":760},[754,1895,764],{"class":763},[754,1897,768],{"class":767},[754,1899,1827],{"class":771},[754,1901,775],{"class":767},[754,1903,1768],{"class":763},[754,1905,1008],{"class":767},[754,1907,1836],{"class":760},[754,1909,1839],{"class":763},[754,1911,1008],{"class":767},[754,1913,1844],{"class":760},[754,1915,1839],{"class":763},[754,1917,1008],{"class":767},[754,1919,1920],{"class":760},"optional",[754,1922,1923],{"class":763},"() ",[754,1925,917],{"class":767},[754,1927,712],{"class":763},[754,1929,1118],{"class":767},[754,1931,1932],{"class":756,"line":1377},[754,1933,1934],{"class":767},"    },\n",[754,1936,1937],{"class":756,"line":1407},[754,1938,1681],{"class":767},[754,1940,1942,1944],{"class":756,"line":1941},9,[754,1943,917],{"class":767},[754,1945,807],{"class":763},[698,1947],{},[705,1949,1951,1952,712],{"id":1950},"data-export-createexportplugin","Data Export (",[507,1953,1954],{},"createExportPlugin",[455,1956,1957],{},"Adds CSV/JSON export endpoints to resources.",[717,1959,1960,1969,1978],{},[720,1961,1962,725,1964,993,1966,712],{},[501,1963,724],{},[507,1965,597],{},[507,1967,1968],{},"GET /api/:resource/export",[720,1970,1971,1973,1974,1977],{},[501,1972,733],{}," Register export endpoints via ",[507,1975,1976],{},"buildSetup.addServerHandler()",". Supports field selection and format choice.",[720,1979,1980,1982],{},[501,1981,742],{}," Admin panel \"Export\" buttons, reporting, data migration",[745,1984,1986],{"className":747,"code":1985,"language":749,"meta":750,"style":750},"createExportPlugin({\n  formats: ['csv', 'json'],\n  maxRows: 10000,\n  resources: ['users', 'orders', 'articles'],\n})\n",[507,1987,1988,1996,2025,2037,2071],{"__ignoreMap":750},[754,1989,1990,1992,1994],{"class":756,"line":757},[754,1991,1954],{"class":760},[754,1993,764],{"class":763},[754,1995,1100],{"class":767},[754,1997,1998,2001,2003,2005,2007,2010,2012,2014,2016,2019,2021,2023],{"class":756,"line":1103},[754,1999,2000],{"class":771},"  formats",[754,2002,775],{"class":767},[754,2004,904],{"class":763},[754,2006,879],{"class":767},[754,2008,2009],{"class":875},"csv",[754,2011,879],{"class":767},[754,2013,782],{"class":767},[754,2015,872],{"class":767},[754,2017,2018],{"class":875},"json",[754,2020,879],{"class":767},[754,2022,1146],{"class":763},[754,2024,1118],{"class":767},[754,2026,2027,2030,2032,2035],{"class":756,"line":1121},[754,2028,2029],{"class":771},"  maxRows",[754,2031,775],{"class":767},[754,2033,2034],{"class":778}," 10000",[754,2036,1118],{"class":767},[754,2038,2039,2041,2043,2045,2047,2049,2051,2053,2055,2057,2059,2061,2063,2065,2067,2069],{"class":756,"line":1155},[754,2040,1124],{"class":771},[754,2042,775],{"class":767},[754,2044,904],{"class":763},[754,2046,879],{"class":767},[754,2048,909],{"class":875},[754,2050,879],{"class":767},[754,2052,782],{"class":767},[754,2054,872],{"class":767},[754,2056,1141],{"class":875},[754,2058,879],{"class":767},[754,2060,782],{"class":767},[754,2062,872],{"class":767},[754,2064,1494],{"class":875},[754,2066,879],{"class":767},[754,2068,1146],{"class":763},[754,2070,1118],{"class":767},[754,2072,2073,2075],{"class":756,"line":1176},[754,2074,917],{"class":767},[754,2076,807],{"class":763},[698,2078],{},[705,2080,2082,2083,712],{"id":2081},"file-upload-createfileuploadplugin","File Upload (",[507,2084,2085],{},"createFileUploadPlugin",[455,2087,2088],{},"Handles file uploads tied to resource records.",[717,2090,2091,2100,2105],{},[720,2092,2093,725,2095,993,2097,712],{},[501,2094,724],{},[507,2096,597],{},[507,2098,2099],{},"POST/DELETE /api/:resource/:id/upload",[720,2101,2102,2104],{},[501,2103,733],{}," Register upload/delete endpoints. Store files via local filesystem or NuxtHub Blob. Save URL in a DB column.",[720,2106,2107,2109],{},[501,2108,742],{}," Profile avatars, article cover images, document attachments",[745,2111,2113],{"className":747,"code":2112,"language":749,"meta":750,"style":750},"createFileUploadPlugin({\n  storage: 'local',\n  resources: {\n    users: { field: 'avatarUrl', maxSize: '2mb', accept: ['image/*'] },\n    articles: { field: 'coverImage', maxSize: '5mb', accept: ['image/*'] },\n  },\n})\n",[507,2114,2115,2123,2139,2147,2201,2251,2255],{"__ignoreMap":750},[754,2116,2117,2119,2121],{"class":756,"line":757},[754,2118,2085],{"class":760},[754,2120,764],{"class":763},[754,2122,1100],{"class":767},[754,2124,2125,2128,2130,2132,2135,2137],{"class":756,"line":1103},[754,2126,2127],{"class":771},"  storage",[754,2129,775],{"class":767},[754,2131,872],{"class":767},[754,2133,2134],{"class":875},"local",[754,2136,879],{"class":767},[754,2138,1118],{"class":767},[754,2140,2141,2143,2145],{"class":756,"line":1121},[754,2142,1124],{"class":771},[754,2144,775],{"class":767},[754,2146,1605],{"class":767},[754,2148,2149,2151,2153,2155,2158,2160,2162,2165,2167,2169,2172,2174,2176,2179,2181,2183,2186,2188,2190,2192,2195,2197,2199],{"class":756,"line":1155},[754,2150,1802],{"class":771},[754,2152,775],{"class":767},[754,2154,864],{"class":767},[754,2156,2157],{"class":771}," field",[754,2159,775],{"class":767},[754,2161,872],{"class":767},[754,2163,2164],{"class":875},"avatarUrl",[754,2166,879],{"class":767},[754,2168,782],{"class":767},[754,2170,2171],{"class":771}," maxSize",[754,2173,775],{"class":767},[754,2175,872],{"class":767},[754,2177,2178],{"class":875},"2mb",[754,2180,879],{"class":767},[754,2182,782],{"class":767},[754,2184,2185],{"class":771}," accept",[754,2187,775],{"class":767},[754,2189,904],{"class":763},[754,2191,879],{"class":767},[754,2193,2194],{"class":875},"image/*",[754,2196,879],{"class":767},[754,2198,914],{"class":763},[754,2200,1288],{"class":767},[754,2202,2203,2205,2207,2209,2211,2213,2215,2218,2220,2222,2224,2226,2228,2231,2233,2235,2237,2239,2241,2243,2245,2247,2249],{"class":756,"line":1176},[754,2204,1610],{"class":771},[754,2206,775],{"class":767},[754,2208,864],{"class":767},[754,2210,2157],{"class":771},[754,2212,775],{"class":767},[754,2214,872],{"class":767},[754,2216,2217],{"class":875},"coverImage",[754,2219,879],{"class":767},[754,2221,782],{"class":767},[754,2223,2171],{"class":771},[754,2225,775],{"class":767},[754,2227,872],{"class":767},[754,2229,2230],{"class":875},"5mb",[754,2232,879],{"class":767},[754,2234,782],{"class":767},[754,2236,2185],{"class":771},[754,2238,775],{"class":767},[754,2240,904],{"class":763},[754,2242,879],{"class":767},[754,2244,2194],{"class":875},[754,2246,879],{"class":767},[754,2248,914],{"class":763},[754,2250,1288],{"class":767},[754,2252,2253],{"class":756,"line":1188},[754,2254,1681],{"class":767},[754,2256,2257,2259],{"class":756,"line":1377},[754,2258,917],{"class":767},[754,2260,807],{"class":763},[698,2262],{},[705,2264,2266,2267,712],{"id":2265},"revision-history-createrevisionplugin","Revision History (",[507,2268,2269],{},"createRevisionPlugin",[455,2271,2272],{},"Stores a full history of every version of a record, enabling rollback.",[717,2274,2275,2287,2298],{},[720,2276,2277,725,2279,1064,2281,2283,2284,2286],{},[501,2278,724],{},[507,2280,513],{},[507,2282,544],{}," (write revision), ",[507,2285,597],{}," (list + restore endpoints)",[720,2288,2289,2291,2292,960,2295,1008],{},[501,2290,733],{}," Before each update, snapshot the current record. Provide ",[507,2293,2294],{},"GET /api/:resource/:id/revisions",[507,2296,2297],{},"POST /api/:resource/:id/revisions/:version/restore",[720,2299,2300,2302],{},[501,2301,742],{}," CMS content versioning, document editing, compliance, undo",[745,2304,2306],{"className":747,"code":2305,"language":749,"meta":750,"style":750},"createRevisionPlugin({\n  resources: ['articles', 'pages'],\n  maxRevisionsPerRecord: 50,\n  table: 'revisions',\n})\n",[507,2307,2308,2316,2343,2355,2370],{"__ignoreMap":750},[754,2309,2310,2312,2314],{"class":756,"line":757},[754,2311,2269],{"class":760},[754,2313,764],{"class":763},[754,2315,1100],{"class":767},[754,2317,2318,2320,2322,2324,2326,2328,2330,2332,2334,2337,2339,2341],{"class":756,"line":1103},[754,2319,1124],{"class":771},[754,2321,775],{"class":767},[754,2323,904],{"class":763},[754,2325,879],{"class":767},[754,2327,1494],{"class":875},[754,2329,879],{"class":767},[754,2331,782],{"class":767},[754,2333,872],{"class":767},[754,2335,2336],{"class":875},"pages",[754,2338,879],{"class":767},[754,2340,1146],{"class":763},[754,2342,1118],{"class":767},[754,2344,2345,2348,2350,2353],{"class":756,"line":1121},[754,2346,2347],{"class":771},"  maxRevisionsPerRecord",[754,2349,775],{"class":767},[754,2351,2352],{"class":778}," 50",[754,2354,1118],{"class":767},[754,2356,2357,2359,2361,2363,2366,2368],{"class":756,"line":1155},[754,2358,1106],{"class":771},[754,2360,775],{"class":767},[754,2362,872],{"class":767},[754,2364,2365],{"class":875},"revisions",[754,2367,879],{"class":767},[754,2369,1118],{"class":767},[754,2371,2372,2374],{"class":756,"line":1176},[754,2373,917],{"class":767},[754,2375,807],{"class":763},[698,2377],{},[705,2379,2381,2382,712],{"id":2380},"cache-createcacheplugin","Cache (",[507,2383,2384],{},"createCachePlugin",[455,2386,2387],{},"In-memory caching for list/get operations with automatic invalidation on mutations.",[717,2389,2390,2417,2422],{},[720,2391,2392,725,2394,2396,2397,2400,2401,2404,2405,510,2407,2409,2410,510,2412,510,2414,2416],{},[501,2393,724],{},[507,2395,671],{}," middleware (serve from cache via ",[507,2398,2399],{},"context.shortCircuit","), ",[507,2402,2403],{},"post-execute"," middleware + ",[507,2406,620],{},[507,2408,617],{}," (store in cache), ",[507,2411,541],{},[507,2413,544],{},[507,2415,547],{}," (invalidate)",[720,2418,2419,2421],{},[501,2420,733],{}," In-memory Map with TTL, LRU eviction, and per-resource invalidation.",[720,2423,2424,2426],{},[501,2425,742],{}," Reduce database load for read-heavy workloads",[745,2428,2430],{"className":747,"code":2429,"language":749,"meta":750,"style":750},"createCachePlugin({\n  ttlMs: 30000,\n  maxEntries: 500,\n  resources: ['articles', 'categories'],\n  invalidateOn: ['create', 'update', 'delete'],\n})\n",[507,2431,2432,2440,2452,2464,2491,2529],{"__ignoreMap":750},[754,2433,2434,2436,2438],{"class":756,"line":757},[754,2435,2384],{"class":760},[754,2437,764],{"class":763},[754,2439,1100],{"class":767},[754,2441,2442,2445,2447,2450],{"class":756,"line":1103},[754,2443,2444],{"class":771},"  ttlMs",[754,2446,775],{"class":767},[754,2448,2449],{"class":778}," 30000",[754,2451,1118],{"class":767},[754,2453,2454,2457,2459,2462],{"class":756,"line":1121},[754,2455,2456],{"class":771},"  maxEntries",[754,2458,775],{"class":767},[754,2460,2461],{"class":778}," 500",[754,2463,1118],{"class":767},[754,2465,2466,2468,2470,2472,2474,2476,2478,2480,2482,2485,2487,2489],{"class":756,"line":1155},[754,2467,1124],{"class":771},[754,2469,775],{"class":767},[754,2471,904],{"class":763},[754,2473,879],{"class":767},[754,2475,1494],{"class":875},[754,2477,879],{"class":767},[754,2479,782],{"class":767},[754,2481,872],{"class":767},[754,2483,2484],{"class":875},"categories",[754,2486,879],{"class":767},[754,2488,1146],{"class":763},[754,2490,1118],{"class":767},[754,2492,2493,2496,2498,2500,2502,2505,2507,2509,2511,2514,2516,2518,2520,2523,2525,2527],{"class":756,"line":1176},[754,2494,2495],{"class":771},"  invalidateOn",[754,2497,775],{"class":767},[754,2499,904],{"class":763},[754,2501,879],{"class":767},[754,2503,2504],{"class":875},"create",[754,2506,879],{"class":767},[754,2508,782],{"class":767},[754,2510,872],{"class":767},[754,2512,2513],{"class":875},"update",[754,2515,879],{"class":767},[754,2517,782],{"class":767},[754,2519,872],{"class":767},[754,2521,2522],{"class":875},"delete",[754,2524,879],{"class":767},[754,2526,1146],{"class":763},[754,2528,1118],{"class":767},[754,2530,2531,2533],{"class":756,"line":1188},[754,2532,917],{"class":767},[754,2534,807],{"class":763},[698,2536],{},[705,2538,2540,2541,712],{"id":2539},"search-createsearchplugin","Search (",[507,2542,2543],{},"createSearchPlugin",[455,2545,2546],{},"Adds full-text-like search via SQL LIKE/ILIKE to list queries.",[717,2548,2549,2559,2574],{},[720,2550,2551,2553,2554,2556,2557,712],{},[501,2552,724],{}," Resource-specific ",[507,2555,648],{}," hooks (push conditions to ",[507,2558,644],{},[720,2560,2561,2563,2564,2567,2568,2571,2572,1008],{},[501,2562,733],{}," Read ",[507,2565,2566],{},"?q=searchterm"," from query, build ",[507,2569,2570],{},"OR(ILIKE(col1, '%q%'), ILIKE(col2, '%q%'), ...)"," and push into ",[507,2573,644],{},[720,2575,2576,2578],{},[501,2577,742],{}," Search bars, filtering by text across multiple fields",[745,2580,2582],{"className":747,"code":2581,"language":749,"meta":750,"style":750},"createSearchPlugin({\n  resources: {\n    articles: { fields: ['title', 'body'], minLength: 3 },\n    users: { fields: ['name', 'email'] },\n  },\n  queryParam: 'q',\n})\n",[507,2583,2584,2592,2600,2643,2675,2679,2695],{"__ignoreMap":750},[754,2585,2586,2588,2590],{"class":756,"line":757},[754,2587,2543],{"class":760},[754,2589,764],{"class":763},[754,2591,1100],{"class":767},[754,2593,2594,2596,2598],{"class":756,"line":1103},[754,2595,1124],{"class":771},[754,2597,775],{"class":767},[754,2599,1605],{"class":767},[754,2601,2602,2604,2606,2608,2611,2613,2615,2617,2619,2621,2623,2625,2628,2630,2632,2634,2637,2639,2641],{"class":756,"line":1121},[754,2603,1610],{"class":771},[754,2605,775],{"class":767},[754,2607,864],{"class":767},[754,2609,2610],{"class":771}," fields",[754,2612,775],{"class":767},[754,2614,904],{"class":763},[754,2616,879],{"class":767},[754,2618,1624],{"class":875},[754,2620,879],{"class":767},[754,2622,782],{"class":767},[754,2624,872],{"class":767},[754,2626,2627],{"class":875},"body",[754,2629,879],{"class":767},[754,2631,1146],{"class":763},[754,2633,782],{"class":767},[754,2635,2636],{"class":771}," minLength",[754,2638,775],{"class":767},[754,2640,1392],{"class":778},[754,2642,1374],{"class":767},[754,2644,2645,2647,2649,2651,2653,2655,2657,2659,2661,2663,2665,2667,2669,2671,2673],{"class":756,"line":1155},[754,2646,1802],{"class":771},[754,2648,775],{"class":767},[754,2650,864],{"class":767},[754,2652,2610],{"class":771},[754,2654,775],{"class":767},[754,2656,904],{"class":763},[754,2658,879],{"class":767},[754,2660,1660],{"class":875},[754,2662,879],{"class":767},[754,2664,782],{"class":767},[754,2666,872],{"class":767},[754,2668,1844],{"class":875},[754,2670,879],{"class":767},[754,2672,914],{"class":763},[754,2674,1288],{"class":767},[754,2676,2677],{"class":756,"line":1176},[754,2678,1681],{"class":767},[754,2680,2681,2684,2686,2688,2691,2693],{"class":756,"line":1188},[754,2682,2683],{"class":771},"  queryParam",[754,2685,775],{"class":767},[754,2687,872],{"class":767},[754,2689,2690],{"class":875},"q",[754,2692,879],{"class":767},[754,2694,1118],{"class":767},[754,2696,2697,2699],{"class":756,"line":1377},[754,2698,917],{"class":767},[754,2700,807],{"class":763},[698,2702],{},[705,2704,2706,2707,712],{"id":2705},"field-encryption-createencryptionplugin","Field Encryption (",[507,2708,2709],{},"createEncryptionPlugin",[455,2711,2712],{},"Transparently encrypts fields before write and decrypts after read using AES-256-GCM.",[717,2714,2715,2729,2738],{},[720,2716,2717,725,2719,510,2721,2723,2724,510,2726,2728],{},[501,2718,724],{},[507,2720,509],{},[507,2722,513],{}," (encrypt), ",[507,2725,617],{},[507,2727,620],{}," (decrypt via return value)",[720,2730,2731,2733,2734,2737],{},[501,2732,733],{}," AES-256-GCM with random IV. Format: ",[507,2735,2736],{},"iv:tag:ciphertext"," (base64).",[720,2739,2740,2742],{},[501,2741,742],{}," PII protection, HIPAA compliance, sensitive data at rest",[745,2744,2746],{"className":747,"code":2745,"language":749,"meta":750,"style":750},"createEncryptionPlugin({\n  secret: process.env.ENCRYPTION_KEY!,\n  resources: {\n    users: ['ssn', 'taxId'],\n    payments: ['cardNumber'],\n  },\n})\n",[507,2747,2748,2756,2777,2785,2813,2833,2837],{"__ignoreMap":750},[754,2749,2750,2752,2754],{"class":756,"line":757},[754,2751,2709],{"class":760},[754,2753,764],{"class":763},[754,2755,1100],{"class":767},[754,2757,2758,2761,2763,2765,2767,2769,2771,2774],{"class":756,"line":1103},[754,2759,2760],{"class":771},"  secret",[754,2762,775],{"class":767},[754,2764,1346],{"class":763},[754,2766,1008],{"class":767},[754,2768,1351],{"class":763},[754,2770,1008],{"class":767},[754,2772,2773],{"class":763},"ENCRYPTION_KEY",[754,2775,2776],{"class":767},"!,\n",[754,2778,2779,2781,2783],{"class":756,"line":1121},[754,2780,1124],{"class":771},[754,2782,775],{"class":767},[754,2784,1605],{"class":767},[754,2786,2787,2789,2791,2793,2795,2798,2800,2802,2804,2807,2809,2811],{"class":756,"line":1155},[754,2788,1802],{"class":771},[754,2790,775],{"class":767},[754,2792,904],{"class":763},[754,2794,879],{"class":767},[754,2796,2797],{"class":875},"ssn",[754,2799,879],{"class":767},[754,2801,782],{"class":767},[754,2803,872],{"class":767},[754,2805,2806],{"class":875},"taxId",[754,2808,879],{"class":767},[754,2810,1146],{"class":763},[754,2812,1118],{"class":767},[754,2814,2815,2818,2820,2822,2824,2827,2829,2831],{"class":756,"line":1176},[754,2816,2817],{"class":771},"    payments",[754,2819,775],{"class":767},[754,2821,904],{"class":763},[754,2823,879],{"class":767},[754,2825,2826],{"class":875},"cardNumber",[754,2828,879],{"class":767},[754,2830,1146],{"class":763},[754,2832,1118],{"class":767},[754,2834,2835],{"class":756,"line":1188},[754,2836,1681],{"class":767},[754,2838,2839,2841],{"class":756,"line":1377},[754,2840,917],{"class":767},[754,2842,807],{"class":763},[698,2844],{},[705,2846,2848,2849,712],{"id":2847},"api-token-createapitokenplugin","API Token (",[507,2850,2851],{},"createApiTokenPlugin",[455,2853,2854],{},"Full API token management with Bearer authentication, scope enforcement, caching, and token rotation. This is the most feature-rich shipped plugin — it covers the full lifecycle from token creation to request authentication.",[717,2856,2857,2886],{},[720,2858,2859,2861,2862,2864,2865,510,2867,2869,2870,510,2872,2874,2875,510,2877,2879,2880,2882,2883,2885],{},[501,2860,724],{}," Context extender (Bearer auth), ",[507,2863,728],{}," middleware (scope enforcement), ",[507,2866,509],{},[507,2868,541],{}," (hash + one-time reveal), ",[507,2871,617],{},[507,2873,620],{}," (mask hash), ",[507,2876,513],{},[507,2878,544],{}," (block secret writes, rotation), ",[507,2881,547],{}," (cache invalidation), ",[507,2884,597],{}," (introspection endpoint)",[720,2887,2888,2890],{},[501,2889,742],{}," API key management, service-to-service auth, CI/CD tokens, team/org-scoped tokens",[2892,2893,2895],"h4",{"id":2894},"how-it-works","How It Works",[455,2897,2898],{},"The plugin operates at two levels:",[2900,2901,2902,2908],"ol",{},[720,2903,2904,2907],{},[501,2905,2906],{},"Token CRUD lifecycle"," — hooks on your token resource(s) handle hashing on create, masking on read, rotation on update, and cache invalidation on delete.",[720,2909,2910,2913,2914,2917,2918,2920,2921,2923],{},[501,2911,2912],{},"Request authentication"," — a context extender reads ",[507,2915,2916],{},"Authorization: Bearer \u003Ctoken>",", hashes the raw token, looks it up in the DB (or cache), loads the owning user, and sets ",[507,2919,959],{}," + ",[507,2922,963],{},". A separate pre-auth middleware then enforces token scopes.",[745,2925,2930],{"className":2926,"code":2928,"language":2929},[2927],"language-text","Request with Bearer token\n  │\n  ├─ Context Extender (runs for every request)\n  │   ├─ Already authenticated (session/cookie)? → skip\n  │   ├─ Hash the raw token with SHA-256\n  │   ├─ Check in-memory cache → hit? validate expiry, set context\n  │   ├─ Cache miss → query all configured token tables\n  │   ├─ Load user row via foreign key\n  │   └─ Set context.user, context.permissions, context.tenant (org tokens)\n  │\n  ├─ Pre-auth Middleware (scope enforcement)\n  │   ├─ No scopes or \"*\" → pass through (unrestricted)\n  │   ├─ Map operation: list/get → \"read\"\n  │   └─ Check \"resource:operation\" against token scopes → 403 if denied\n  │\n  └─ Normal authorize() runs (resource auth config — functions, object-level, field-level)\n","text",[507,2931,2928],{"__ignoreMap":750},[455,2933,2934,2935,2938,2939,1008],{},"Both the scope check AND the resource auth config must pass. A token with ",[507,2936,2937],{},"articles:read"," scope still goes through any function-based permission check like ",[507,2940,2941],{},"read: (ctx) => ctx.user?.role === 'editor'",[2892,2943,2945],{"id":2944},"minimal-setup","Minimal Setup",[745,2947,2949],{"className":747,"code":2948,"language":749,"meta":750,"style":750},"createApiTokenPlugin({\n  resources: {\n    apiKeys: {\n      userRelation: { field: 'userId', resource: 'users' },\n    },\n  },\n})\n",[507,2950,2951,2959,2967,2976,3011,3015,3019],{"__ignoreMap":750},[754,2952,2953,2955,2957],{"class":756,"line":757},[754,2954,2851],{"class":760},[754,2956,764],{"class":763},[754,2958,1100],{"class":767},[754,2960,2961,2963,2965],{"class":756,"line":1103},[754,2962,1124],{"class":771},[754,2964,775],{"class":767},[754,2966,1605],{"class":767},[754,2968,2969,2972,2974],{"class":756,"line":1121},[754,2970,2971],{"class":771},"    apiKeys",[754,2973,775],{"class":767},[754,2975,1605],{"class":767},[754,2977,2978,2981,2983,2985,2987,2989,2991,2994,2996,2998,3001,3003,3005,3007,3009],{"class":756,"line":1155},[754,2979,2980],{"class":771},"      userRelation",[754,2982,775],{"class":767},[754,2984,864],{"class":767},[754,2986,2157],{"class":771},[754,2988,775],{"class":767},[754,2990,872],{"class":767},[754,2992,2993],{"class":875},"userId",[754,2995,879],{"class":767},[754,2997,782],{"class":767},[754,2999,3000],{"class":771}," resource",[754,3002,775],{"class":767},[754,3004,872],{"class":767},[754,3006,909],{"class":875},[754,3008,879],{"class":767},[754,3010,1374],{"class":767},[754,3012,3013],{"class":756,"line":1176},[754,3014,1934],{"class":767},[754,3016,3017],{"class":756,"line":1188},[754,3018,1681],{"class":767},[754,3020,3021,3023],{"class":756,"line":1377},[754,3022,917],{"class":767},[754,3024,807],{"class":763},[455,3026,3027],{},"This gives you: token hashing, one-time reveal, Bearer auth, in-memory cache, and hash masking on read. Scopes, expiry, and lastUsedAt are opt-in via additional columns.",[2892,3029,3031],{"id":3030},"full-setup","Full Setup",[745,3033,3035],{"className":747,"code":3034,"language":749,"meta":750,"style":750},"createApiTokenPlugin({\n  resources: {\n    apiKeys: {\n      secretField: 'key',                            // column storing the hash (default: 'key')\n      userRelation: { field: 'userId', resource: 'users' },\n      scopeField: 'scopes',                          // JSON array column\n      expiresField: 'expiresAt',                     // timestamp column\n      lastUsedField: 'lastUsedAt',                   // auto-updated on auth\n    },\n    teamApiKeys: {                                    // second token table (org-scoped)\n      userRelation: { field: 'userId', resource: 'users' },\n      orgField: 'organizationId',                    // sets context.tenant on auth\n      scopeField: 'scopes',\n    },\n  },\n  auth: {\n    tokenPrefix: 'sk_',                              // prefix prepended to generated tokens\n    // header: 'Authorization',                      // default\n    // prefix: 'Bearer',                             // default\n  },\n  // hashAlgorithm: 'sha256',                        // default\n  // tokenLength: 32,                                // bytes of randomness (default)\n  cache: {\n    enabled: true,                                    // default\n    ttlMs: 300_000,                                   // 5 min (default)\n    maxEntries: 1_000,                                // default\n  },\n  lastUsedDebounceMs: 60_000,                         // 1 min (default)\n  mapUser: (row) => ({ id: row.id, email: row.email, roles: [row.role] }),\n  getPermissions: (row) => row.role === 'admin' ? ['admin'] : [],\n})\n",[507,3036,3037,3045,3053,3061,3080,3112,3131,3150,3169,3173,3186,3219,3239,3254,3259,3264,3274,3294,3303,3312,3317,3326,3335,3345,3360,3376,3392,3397,3413,3480,3533],{"__ignoreMap":750},[754,3038,3039,3041,3043],{"class":756,"line":757},[754,3040,2851],{"class":760},[754,3042,764],{"class":763},[754,3044,1100],{"class":767},[754,3046,3047,3049,3051],{"class":756,"line":1103},[754,3048,1124],{"class":771},[754,3050,775],{"class":767},[754,3052,1605],{"class":767},[754,3054,3055,3057,3059],{"class":756,"line":1121},[754,3056,2971],{"class":771},[754,3058,775],{"class":767},[754,3060,1605],{"class":767},[754,3062,3063,3066,3068,3070,3073,3075,3077],{"class":756,"line":1155},[754,3064,3065],{"class":771},"      secretField",[754,3067,775],{"class":767},[754,3069,872],{"class":767},[754,3071,3072],{"class":875},"key",[754,3074,879],{"class":767},[754,3076,782],{"class":767},[754,3078,3079],{"class":1151},"                            // column storing the hash (default: 'key')\n",[754,3081,3082,3084,3086,3088,3090,3092,3094,3096,3098,3100,3102,3104,3106,3108,3110],{"class":756,"line":1176},[754,3083,2980],{"class":771},[754,3085,775],{"class":767},[754,3087,864],{"class":767},[754,3089,2157],{"class":771},[754,3091,775],{"class":767},[754,3093,872],{"class":767},[754,3095,2993],{"class":875},[754,3097,879],{"class":767},[754,3099,782],{"class":767},[754,3101,3000],{"class":771},[754,3103,775],{"class":767},[754,3105,872],{"class":767},[754,3107,909],{"class":875},[754,3109,879],{"class":767},[754,3111,1374],{"class":767},[754,3113,3114,3117,3119,3121,3124,3126,3128],{"class":756,"line":1188},[754,3115,3116],{"class":771},"      scopeField",[754,3118,775],{"class":767},[754,3120,872],{"class":767},[754,3122,3123],{"class":875},"scopes",[754,3125,879],{"class":767},[754,3127,782],{"class":767},[754,3129,3130],{"class":1151},"                          // JSON array column\n",[754,3132,3133,3136,3138,3140,3143,3145,3147],{"class":756,"line":1377},[754,3134,3135],{"class":771},"      expiresField",[754,3137,775],{"class":767},[754,3139,872],{"class":767},[754,3141,3142],{"class":875},"expiresAt",[754,3144,879],{"class":767},[754,3146,782],{"class":767},[754,3148,3149],{"class":1151},"                     // timestamp column\n",[754,3151,3152,3155,3157,3159,3162,3164,3166],{"class":756,"line":1407},[754,3153,3154],{"class":771},"      lastUsedField",[754,3156,775],{"class":767},[754,3158,872],{"class":767},[754,3160,3161],{"class":875},"lastUsedAt",[754,3163,879],{"class":767},[754,3165,782],{"class":767},[754,3167,3168],{"class":1151},"                   // auto-updated on auth\n",[754,3170,3171],{"class":756,"line":1941},[754,3172,1934],{"class":767},[754,3174,3176,3179,3181,3183],{"class":756,"line":3175},10,[754,3177,3178],{"class":771},"    teamApiKeys",[754,3180,775],{"class":767},[754,3182,864],{"class":767},[754,3184,3185],{"class":1151},"                                    // second token table (org-scoped)\n",[754,3187,3189,3191,3193,3195,3197,3199,3201,3203,3205,3207,3209,3211,3213,3215,3217],{"class":756,"line":3188},11,[754,3190,2980],{"class":771},[754,3192,775],{"class":767},[754,3194,864],{"class":767},[754,3196,2157],{"class":771},[754,3198,775],{"class":767},[754,3200,872],{"class":767},[754,3202,2993],{"class":875},[754,3204,879],{"class":767},[754,3206,782],{"class":767},[754,3208,3000],{"class":771},[754,3210,775],{"class":767},[754,3212,872],{"class":767},[754,3214,909],{"class":875},[754,3216,879],{"class":767},[754,3218,1374],{"class":767},[754,3220,3222,3225,3227,3229,3232,3234,3236],{"class":756,"line":3221},12,[754,3223,3224],{"class":771},"      orgField",[754,3226,775],{"class":767},[754,3228,872],{"class":767},[754,3230,3231],{"class":875},"organizationId",[754,3233,879],{"class":767},[754,3235,782],{"class":767},[754,3237,3238],{"class":1151},"                    // sets context.tenant on auth\n",[754,3240,3242,3244,3246,3248,3250,3252],{"class":756,"line":3241},13,[754,3243,3116],{"class":771},[754,3245,775],{"class":767},[754,3247,872],{"class":767},[754,3249,3123],{"class":875},[754,3251,879],{"class":767},[754,3253,1118],{"class":767},[754,3255,3257],{"class":756,"line":3256},14,[754,3258,1934],{"class":767},[754,3260,3262],{"class":756,"line":3261},15,[754,3263,1681],{"class":767},[754,3265,3267,3270,3272],{"class":756,"line":3266},16,[754,3268,3269],{"class":771},"  auth",[754,3271,775],{"class":767},[754,3273,1605],{"class":767},[754,3275,3277,3280,3282,3284,3287,3289,3291],{"class":756,"line":3276},17,[754,3278,3279],{"class":771},"    tokenPrefix",[754,3281,775],{"class":767},[754,3283,872],{"class":767},[754,3285,3286],{"class":875},"sk_",[754,3288,879],{"class":767},[754,3290,782],{"class":767},[754,3292,3293],{"class":1151},"                              // prefix prepended to generated tokens\n",[754,3295,3297,3300],{"class":756,"line":3296},18,[754,3298,3299],{"class":1151},"    // header: 'Authorization',",[754,3301,3302],{"class":1151},"                      // default\n",[754,3304,3306,3309],{"class":756,"line":3305},19,[754,3307,3308],{"class":1151},"    // prefix: 'Bearer',",[754,3310,3311],{"class":1151},"                             // default\n",[754,3313,3315],{"class":756,"line":3314},20,[754,3316,1681],{"class":767},[754,3318,3320,3323],{"class":756,"line":3319},21,[754,3321,3322],{"class":1151},"  // hashAlgorithm: 'sha256',",[754,3324,3325],{"class":1151},"                        // default\n",[754,3327,3329,3332],{"class":756,"line":3328},22,[754,3330,3331],{"class":1151},"  // tokenLength: 32,",[754,3333,3334],{"class":1151},"                                // bytes of randomness (default)\n",[754,3336,3338,3341,3343],{"class":756,"line":3337},23,[754,3339,3340],{"class":771},"  cache",[754,3342,775],{"class":767},[754,3344,1605],{"class":767},[754,3346,3348,3351,3353,3355,3357],{"class":756,"line":3347},24,[754,3349,3350],{"class":771},"    enabled",[754,3352,775],{"class":767},[754,3354,801],{"class":800},[754,3356,782],{"class":767},[754,3358,3359],{"class":1151},"                                    // default\n",[754,3361,3363,3366,3368,3371,3373],{"class":756,"line":3362},25,[754,3364,3365],{"class":771},"    ttlMs",[754,3367,775],{"class":767},[754,3369,3370],{"class":778}," 300_000",[754,3372,782],{"class":767},[754,3374,3375],{"class":1151},"                                   // 5 min (default)\n",[754,3377,3379,3382,3384,3387,3389],{"class":756,"line":3378},26,[754,3380,3381],{"class":771},"    maxEntries",[754,3383,775],{"class":767},[754,3385,3386],{"class":778}," 1_000",[754,3388,782],{"class":767},[754,3390,3391],{"class":1151},"                                // default\n",[754,3393,3395],{"class":756,"line":3394},27,[754,3396,1681],{"class":767},[754,3398,3400,3403,3405,3408,3410],{"class":756,"line":3399},28,[754,3401,3402],{"class":771},"  lastUsedDebounceMs",[754,3404,775],{"class":767},[754,3406,3407],{"class":778}," 60_000",[754,3409,782],{"class":767},[754,3411,3412],{"class":1151},"                         // 1 min (default)\n",[754,3414,3416,3419,3421,3423,3426,3428,3430,3432,3434,3437,3439,3442,3444,3447,3449,3451,3453,3455,3457,3459,3461,3464,3466,3469,3471,3474,3476,3478],{"class":756,"line":3415},29,[754,3417,3418],{"class":760},"  mapUser",[754,3420,775],{"class":767},[754,3422,993],{"class":767},[754,3424,3425],{"class":996},"row",[754,3427,712],{"class":767},[754,3429,1002],{"class":989},[754,3431,993],{"class":763},[754,3433,768],{"class":767},[754,3435,3436],{"class":771}," id",[754,3438,775],{"class":767},[754,3440,3441],{"class":763}," row",[754,3443,1008],{"class":767},[754,3445,3446],{"class":763},"id",[754,3448,782],{"class":767},[754,3450,1827],{"class":771},[754,3452,775],{"class":767},[754,3454,3441],{"class":763},[754,3456,1008],{"class":767},[754,3458,1844],{"class":763},[754,3460,782],{"class":767},[754,3462,3463],{"class":771}," roles",[754,3465,775],{"class":767},[754,3467,3468],{"class":763}," [row",[754,3470,1008],{"class":767},[754,3472,3473],{"class":763},"role] ",[754,3475,917],{"class":767},[754,3477,712],{"class":763},[754,3479,1118],{"class":767},[754,3481,3483,3486,3488,3490,3492,3494,3496,3498,3500,3503,3506,3508,3511,3513,3516,3518,3520,3522,3524,3526,3528,3531],{"class":756,"line":3482},30,[754,3484,3485],{"class":760},"  getPermissions",[754,3487,775],{"class":767},[754,3489,993],{"class":767},[754,3491,3425],{"class":996},[754,3493,712],{"class":767},[754,3495,1002],{"class":989},[754,3497,3441],{"class":763},[754,3499,1008],{"class":767},[754,3501,3502],{"class":763},"role ",[754,3504,3505],{"class":767},"===",[754,3507,872],{"class":767},[754,3509,3510],{"class":875},"admin",[754,3512,879],{"class":767},[754,3514,3515],{"class":767}," ?",[754,3517,904],{"class":763},[754,3519,879],{"class":767},[754,3521,3510],{"class":875},[754,3523,879],{"class":767},[754,3525,914],{"class":763},[754,3527,775],{"class":767},[754,3529,3530],{"class":763}," []",[754,3532,1118],{"class":767},[754,3534,3536,3538],{"class":756,"line":3535},31,[754,3537,917],{"class":767},[754,3539,807],{"class":763},[2892,3541,3543],{"id":3542},"configuration-reference","Configuration Reference",[474,3545,3546,3562],{},[477,3547,3548],{},[480,3549,3550,3553,3556,3559],{},[483,3551,3552],{},"Option",[483,3554,3555],{},"Type",[483,3557,3558],{},"Default",[483,3560,3561],{},"Description",[493,3563,3564,3585,3605,3624,3643,3665,3685,3704,3722,3741,3760,3779,3797],{},[480,3565,3566,3571,3576,3582],{},[498,3567,3568],{},[507,3569,3570],{},"resources",[498,3572,3573],{},[507,3574,3575],{},"Record\u003Cstring, ApiTokenResourceConfig>",[498,3577,3578],{},[3579,3580,3581],"em",{},"required",[498,3583,3584],{},"Token table(s) and their column config",[480,3586,3587,3592,3597,3602],{},[498,3588,3589],{},[507,3590,3591],{},"auth.enabled",[498,3593,3594],{},[507,3595,3596],{},"boolean",[498,3598,3599],{},[507,3600,3601],{},"true",[498,3603,3604],{},"Enable Bearer token authentication",[480,3606,3607,3612,3616,3621],{},[498,3608,3609],{},[507,3610,3611],{},"auth.header",[498,3613,3614],{},[507,3615,1836],{},[498,3617,3618],{},[507,3619,3620],{},"'Authorization'",[498,3622,3623],{},"HTTP header to read",[480,3625,3626,3631,3635,3640],{},[498,3627,3628],{},[507,3629,3630],{},"auth.prefix",[498,3632,3633],{},[507,3634,1836],{},[498,3636,3637],{},[507,3638,3639],{},"'Bearer'",[498,3641,3642],{},"Header value prefix",[480,3644,3645,3650,3654,3659],{},[498,3646,3647],{},[507,3648,3649],{},"auth.tokenPrefix",[498,3651,3652],{},[507,3653,1836],{},[498,3655,3656],{},[507,3657,3658],{},"''",[498,3660,3661,3662,712],{},"Prefix prepended to generated tokens (e.g. ",[507,3663,3664],{},"'sk_'",[480,3666,3667,3672,3677,3682],{},[498,3668,3669],{},[507,3670,3671],{},"hashAlgorithm",[498,3673,3674],{},[507,3675,3676],{},"'sha256' | 'sha512'",[498,3678,3679],{},[507,3680,3681],{},"'sha256'",[498,3683,3684],{},"Hash algorithm for token storage",[480,3686,3687,3692,3696,3701],{},[498,3688,3689],{},[507,3690,3691],{},"tokenLength",[498,3693,3694],{},[507,3695,1860],{},[498,3697,3698],{},[507,3699,3700],{},"32",[498,3702,3703],{},"Random bytes in generated tokens (64 hex chars)",[480,3705,3706,3711,3715,3719],{},[498,3707,3708],{},[507,3709,3710],{},"cache.enabled",[498,3712,3713],{},[507,3714,3596],{},[498,3716,3717],{},[507,3718,3601],{},[498,3720,3721],{},"Enable in-memory token cache",[480,3723,3724,3729,3733,3738],{},[498,3725,3726],{},[507,3727,3728],{},"cache.ttlMs",[498,3730,3731],{},[507,3732,1860],{},[498,3734,3735],{},[507,3736,3737],{},"300000",[498,3739,3740],{},"Cache TTL in milliseconds",[480,3742,3743,3748,3752,3757],{},[498,3744,3745],{},[507,3746,3747],{},"cache.maxEntries",[498,3749,3750],{},[507,3751,1860],{},[498,3753,3754],{},[507,3755,3756],{},"1000",[498,3758,3759],{},"Max cached entries (oldest evicted first)",[480,3761,3762,3767,3771,3776],{},[498,3763,3764],{},[507,3765,3766],{},"lastUsedDebounceMs",[498,3768,3769],{},[507,3770,1860],{},[498,3772,3773],{},[507,3774,3775],{},"60000",[498,3777,3778],{},"Debounce window for lastUsedAt DB writes",[480,3780,3781,3786,3791,3794],{},[498,3782,3783],{},[507,3784,3785],{},"mapUser",[498,3787,3788],{},[507,3789,3790],{},"(row) => AuthUser",[498,3792,3793],{},"auto-map",[498,3795,3796],{},"Custom user mapping from DB row",[480,3798,3799,3804,3809,3814],{},[498,3800,3801],{},[507,3802,3803],{},"getPermissions",[498,3805,3806],{},[507,3807,3808],{},"(row) => string[]",[498,3810,3811],{},[507,3812,3813],{},"user.permissions",[498,3815,3816],{},"Extract permission strings from user row",[455,3818,3819],{},[501,3820,3821,3822,3825],{},"Per-resource config (",[507,3823,3824],{},"ApiTokenResourceConfig","):",[474,3827,3828,3840],{},[477,3829,3830],{},[480,3831,3832,3834,3836,3838],{},[483,3833,3552],{},[483,3835,3555],{},[483,3837,3558],{},[483,3839,3561],{},[493,3841,3842,3861,3880,3899,3916,3932,3948,3964],{},[480,3843,3844,3849,3853,3858],{},[498,3845,3846],{},[507,3847,3848],{},"secretField",[498,3850,3851],{},[507,3852,1836],{},[498,3854,3855],{},[507,3856,3857],{},"'key'",[498,3859,3860],{},"Column that stores the hashed token",[480,3862,3863,3868,3872,3877],{},[498,3864,3865],{},[507,3866,3867],{},"userRelation.field",[498,3869,3870],{},[507,3871,1836],{},[498,3873,3874],{},[507,3875,3876],{},"'userId'",[498,3878,3879],{},"FK column on the token table",[480,3881,3882,3887,3891,3896],{},[498,3883,3884],{},[507,3885,3886],{},"userRelation.resource",[498,3888,3889],{},[507,3890,1836],{},[498,3892,3893],{},[507,3894,3895],{},"'users'",[498,3897,3898],{},"User table name",[480,3900,3901,3906,3910,3913],{},[498,3902,3903],{},[507,3904,3905],{},"orgField",[498,3907,3908],{},[507,3909,1836],{},[498,3911,3912],{},"—",[498,3914,3915],{},"Organization FK column (enables team tokens)",[480,3917,3918,3923,3927,3929],{},[498,3919,3920],{},[507,3921,3922],{},"scopeField",[498,3924,3925],{},[507,3926,1836],{},[498,3928,3912],{},[498,3930,3931],{},"JSON array column for scopes",[480,3933,3934,3939,3943,3945],{},[498,3935,3936],{},[507,3937,3938],{},"expiresField",[498,3940,3941],{},[507,3942,1836],{},[498,3944,3912],{},[498,3946,3947],{},"Timestamp column for token expiry",[480,3949,3950,3955,3959,3961],{},[498,3951,3952],{},[507,3953,3954],{},"lastUsedField",[498,3956,3957],{},[507,3958,1836],{},[498,3960,3912],{},[498,3962,3963],{},"Timestamp column, auto-updated on each auth",[480,3965,3966,3971,3975,3979],{},[498,3967,3968],{},[507,3969,3970],{},"authEnabled",[498,3972,3973],{},[507,3974,3596],{},[498,3976,3977],{},[507,3978,3601],{},[498,3980,3981],{},"Whether tokens from this table can authenticate",[2892,3983,3984],{"id":3123},"Scopes",[455,3986,3987,3988,3991],{},"Scopes are a ",[501,3989,3990],{},"coarse pre-filter"," that runs before the resource auth config. They answer \"is this token allowed to touch this resource/operation at all?\" — not \"does the user have permission?\" (that's the resource auth config's job).",[455,3993,3994,725,3997,4000],{},[501,3995,3996],{},"Format:",[507,3998,3999],{},"resource:operation"," strings stored as a JSON array in the scope column.",[474,4002,4003,4013],{},[477,4004,4005],{},[480,4006,4007,4010],{},[483,4008,4009],{},"Scope",[483,4011,4012],{},"Meaning",[493,4014,4015,4025,4035,4045,4055,4065],{},[480,4016,4017,4022],{},[498,4018,4019],{},[507,4020,4021],{},"\"articles:read\"",[498,4023,4024],{},"Read (list/get) articles only",[480,4026,4027,4032],{},[498,4028,4029],{},[507,4030,4031],{},"\"articles:create\"",[498,4033,4034],{},"Create articles only",[480,4036,4037,4042],{},[498,4038,4039],{},[507,4040,4041],{},"\"articles:*\"",[498,4043,4044],{},"All operations on articles",[480,4046,4047,4052],{},[498,4048,4049],{},[507,4050,4051],{},"\"*:read\"",[498,4053,4054],{},"Read any resource",[480,4056,4057,4062],{},[498,4058,4059],{},[507,4060,4061],{},"\"*\"",[498,4063,4064],{},"Unrestricted (all resources, all operations)",[480,4066,4067,4073],{},[498,4068,4069,4072],{},[507,4070,4071],{},"null"," / empty",[498,4074,4075],{},"Unrestricted (backward compatible — simple tokens without scopes)",[455,4077,4078,4079,960,4082,4085,4086,4089,4090,1213,4092,1213,4094,4096],{},"Operations are mapped: ",[507,4080,4081],{},"list",[507,4083,4084],{},"get"," both check against ",[507,4087,4088],{},"read",". Other operations (",[507,4091,2504],{},[507,4093,2513],{},[507,4095,2522],{},") match directly.",[455,4098,4099],{},[501,4100,4101,4102,775],{},"Example flow — token with ",[507,4103,4104],{},"[\"articles:read\", \"articles:create\"]",[2900,4106,4107,4116,4124,4136],{},[720,4108,4109,4112,4113,4115],{},[507,4110,4111],{},"GET /api/articles"," → scope check: ",[507,4114,2937],{}," matches → pass → resource auth runs",[720,4117,4118,4112,4121,4115],{},[507,4119,4120],{},"POST /api/articles",[507,4122,4123],{},"articles:create",[720,4125,4126,4112,4129,4132,4133],{},[507,4127,4128],{},"DELETE /api/articles/1",[507,4130,4131],{},"articles:delete"," not in scopes → ",[501,4134,4135],{},"403",[720,4137,4138,4112,4141,4132,4144],{},[507,4139,4140],{},"GET /api/users",[507,4142,4143],{},"users:read",[501,4145,4135],{},[2892,4147,4149],{"id":4148},"token-lifecycle","Token Lifecycle",[455,4151,4152,4155,4156],{},[501,4153,4154],{},"Create"," — ",[507,4157,4158],{},"POST /api/apiKeys { \"name\": \"CI Key\", \"scopes\": [\"articles:read\"] }",[455,4160,4161,4162,4165,4166,4169],{},"The plugin generates a random token (e.g. ",[507,4163,4164],{},"sk_a1b2c3d4...","), hashes it with SHA-256, stores the hash. The response includes the raw token ",[501,4167,4168],{},"once"," — it is never returned again.",[745,4171,4174],{"className":4172,"code":4173,"language":2018,"meta":750,"style":750},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{ \"data\": { \"id\": 1, \"name\": \"CI Key\", \"key\": \"sk_a1b2c3d4e5f6...\", \"scopes\": [\"articles:read\"] } }\n",[507,4175,4176],{"__ignoreMap":750},[754,4177,4178,4180,4183,4186,4189,4191,4193,4195,4198,4200,4202,4205,4207,4209,4211,4213,4215,4217,4220,4222,4224,4226,4228,4230,4232,4234,4237,4239,4241,4243,4245,4247,4249,4251,4253,4255,4257,4259,4261],{"class":756,"line":757},[754,4179,768],{"class":767},[754,4181,4182],{"class":767}," \"",[754,4184,4185],{"class":989},"data",[754,4187,4188],{"class":767},"\"",[754,4190,775],{"class":767},[754,4192,864],{"class":767},[754,4194,4182],{"class":767},[754,4196,3446],{"class":4197},"sBMFI",[754,4199,4188],{"class":767},[754,4201,775],{"class":767},[754,4203,4204],{"class":778}," 1",[754,4206,782],{"class":767},[754,4208,4182],{"class":767},[754,4210,1660],{"class":4197},[754,4212,4188],{"class":767},[754,4214,775],{"class":767},[754,4216,4182],{"class":767},[754,4218,4219],{"class":875},"CI Key",[754,4221,4188],{"class":767},[754,4223,782],{"class":767},[754,4225,4182],{"class":767},[754,4227,3072],{"class":4197},[754,4229,4188],{"class":767},[754,4231,775],{"class":767},[754,4233,4182],{"class":767},[754,4235,4236],{"class":875},"sk_a1b2c3d4e5f6...",[754,4238,4188],{"class":767},[754,4240,782],{"class":767},[754,4242,4182],{"class":767},[754,4244,3123],{"class":4197},[754,4246,4188],{"class":767},[754,4248,775],{"class":767},[754,4250,904],{"class":767},[754,4252,4188],{"class":767},[754,4254,2937],{"class":875},[754,4256,4188],{"class":767},[754,4258,1146],{"class":767},[754,4260,804],{"class":767},[754,4262,4263],{"class":767}," }\n",[455,4265,4266,4267,4269,4270,4273,4274,4269,4276,1008],{},"The ",[507,4268,2993],{}," is auto-set from ",[507,4271,4272],{},"context.user.id"," if not provided. For team tokens, ",[507,4275,3905],{},[507,4277,4278],{},"context.tenant.id",[455,4280,4281,4155,4284,4287,4288],{},[501,4282,4283],{},"Read",[507,4285,4286],{},"GET /api/apiKeys"," or ",[507,4289,4290],{},"GET /api/apiKeys/1",[455,4292,4293,4294,4297],{},"The hash is replaced with a masked preview: ",[507,4295,4296],{},"sk_...f6e5",". The full hash is never exposed.",[455,4299,4300,4155,4303],{},[501,4301,4302],{},"Update",[507,4304,4305],{},"PATCH /api/apiKeys/1 { \"name\": \"Renamed Key\" }",[455,4307,4308],{},"Direct writes to the secret field are blocked. The name, scopes, and other fields can be updated normally.",[455,4310,4311,4155,4314],{},[501,4312,4313],{},"Rotate",[507,4315,4316],{},"PATCH /api/apiKeys/1 { \"_rotate\": true }",[455,4318,4319],{},"Generates a new token, hashes it, invalidates the cache for the old token. The response includes the new raw token (one-time). The old token stops working immediately.",[455,4321,4322,4155,4325],{},[501,4323,4324],{},"Delete",[507,4326,4327],{},"DELETE /api/apiKeys/1",[455,4329,4330],{},"Deletes the token record and invalidates the cache entry.",[2892,4332,4334],{"id":4333},"introspection-endpoint","Introspection Endpoint",[455,4336,4337,4340],{},[507,4338,4339],{},"GET /api/_token/introspect"," — requires a valid Bearer token in the request.",[455,4342,4343],{},"Returns metadata about the token without exposing the hash:",[745,4345,4347],{"className":4172,"code":4346,"language":2018,"meta":750,"style":750},"{\n  \"data\": {\n    \"resource\": \"apiKeys\",\n    \"id\": 1,\n    \"name\": \"CI Key\",\n    \"scopes\": [\"articles:read\"],\n    \"expiresAt\": \"2025-12-31T00:00:00.000Z\",\n    \"lastUsedAt\": \"2025-06-15T10:30:00.000Z\",\n    \"organizationId\": null\n  }\n}\n",[507,4348,4349,4353,4366,4387,4401,4419,4440,4459,4478,4491,4496],{"__ignoreMap":750},[754,4350,4351],{"class":756,"line":757},[754,4352,1100],{"class":767},[754,4354,4355,4358,4360,4362,4364],{"class":756,"line":1103},[754,4356,4357],{"class":767},"  \"",[754,4359,4185],{"class":989},[754,4361,4188],{"class":767},[754,4363,775],{"class":767},[754,4365,1605],{"class":767},[754,4367,4368,4371,4374,4376,4378,4380,4383,4385],{"class":756,"line":1121},[754,4369,4370],{"class":767},"    \"",[754,4372,4373],{"class":4197},"resource",[754,4375,4188],{"class":767},[754,4377,775],{"class":767},[754,4379,4182],{"class":767},[754,4381,4382],{"class":875},"apiKeys",[754,4384,4188],{"class":767},[754,4386,1118],{"class":767},[754,4388,4389,4391,4393,4395,4397,4399],{"class":756,"line":1155},[754,4390,4370],{"class":767},[754,4392,3446],{"class":4197},[754,4394,4188],{"class":767},[754,4396,775],{"class":767},[754,4398,4204],{"class":778},[754,4400,1118],{"class":767},[754,4402,4403,4405,4407,4409,4411,4413,4415,4417],{"class":756,"line":1176},[754,4404,4370],{"class":767},[754,4406,1660],{"class":4197},[754,4408,4188],{"class":767},[754,4410,775],{"class":767},[754,4412,4182],{"class":767},[754,4414,4219],{"class":875},[754,4416,4188],{"class":767},[754,4418,1118],{"class":767},[754,4420,4421,4423,4425,4427,4429,4431,4433,4435,4437],{"class":756,"line":1188},[754,4422,4370],{"class":767},[754,4424,3123],{"class":4197},[754,4426,4188],{"class":767},[754,4428,775],{"class":767},[754,4430,904],{"class":767},[754,4432,4188],{"class":767},[754,4434,2937],{"class":875},[754,4436,4188],{"class":767},[754,4438,4439],{"class":767},"],\n",[754,4441,4442,4444,4446,4448,4450,4452,4455,4457],{"class":756,"line":1377},[754,4443,4370],{"class":767},[754,4445,3142],{"class":4197},[754,4447,4188],{"class":767},[754,4449,775],{"class":767},[754,4451,4182],{"class":767},[754,4453,4454],{"class":875},"2025-12-31T00:00:00.000Z",[754,4456,4188],{"class":767},[754,4458,1118],{"class":767},[754,4460,4461,4463,4465,4467,4469,4471,4474,4476],{"class":756,"line":1407},[754,4462,4370],{"class":767},[754,4464,3161],{"class":4197},[754,4466,4188],{"class":767},[754,4468,775],{"class":767},[754,4470,4182],{"class":767},[754,4472,4473],{"class":875},"2025-06-15T10:30:00.000Z",[754,4475,4188],{"class":767},[754,4477,1118],{"class":767},[754,4479,4480,4482,4484,4486,4488],{"class":756,"line":1941},[754,4481,4370],{"class":767},[754,4483,3231],{"class":4197},[754,4485,4188],{"class":767},[754,4487,775],{"class":767},[754,4489,4490],{"class":767}," null\n",[754,4492,4493],{"class":756,"line":3175},[754,4494,4495],{"class":767},"  }\n",[754,4497,4498],{"class":756,"line":3188},[754,4499,4500],{"class":767},"}\n",[455,4502,4503],{},"Returns 401 if the request is not authenticated via an API token.",[2892,4505,4507],{"id":4506},"cache-strategy","Cache Strategy",[455,4509,4510],{},"The plugin maintains an in-memory cache with two maps:",[717,4512,4513,4519],{},[720,4514,4515,4518],{},[507,4516,4517],{},"Map\u003Chash, CachedToken>"," — O(1) lookup by token hash",[720,4520,4521,4524],{},[507,4522,4523],{},"Map\u003C\"resource:id\", hash>"," — O(1) invalidation by record",[455,4526,4527,4528,4531,4532,4534,4535,4538],{},"Cache entries expire after ",[507,4529,4530],{},"ttlMs"," (default 5 min). Periodic cleanup runs on a ",[507,4533,737],{}," (non-blocking, ",[507,4536,4537],{},".unref()","). When max entries is reached, the oldest entry is evicted (Map insertion order). Delete and rotate operations invalidate the cache immediately via the reverse map.",[2892,4540,4542],{"id":4541},"security","Security",[474,4544,4545,4555],{},[477,4546,4547],{},[480,4548,4549,4552],{},[483,4550,4551],{},"Concern",[483,4553,4554],{},"Mitigation",[493,4556,4557,4565,4573,4584,4592,4600],{},[480,4558,4559,4562],{},[498,4560,4561],{},"Brute force",[498,4563,4564],{},"256-bit entropy (32 random bytes) makes guessing infeasible. Pair with rate limit plugin for additional protection.",[480,4566,4567,4570],{},[498,4568,4569],{},"Timing attacks",[498,4571,4572],{},"SHA-256 hash lookup in SQL. Constant-time comparison is not needed because the hash itself is the lookup key — a miss reveals nothing about valid hashes.",[480,4574,4575,4578],{},[498,4576,4577],{},"Token leakage",[498,4579,4580,4581,4583],{},"Configurable prefix (e.g. ",[507,4582,3286],{},") enables secret scanning tools (GitHub, GitGuardian) to detect leaked tokens in commits.",[480,4585,4586,4589],{},[498,4587,4588],{},"Over-privilege",[498,4590,4591],{},"Scopes are a coarse gate. Resource auth config (functions, object-level, field-level) runs on top. Both must pass.",[480,4593,4594,4597],{},[498,4595,4596],{},"Stale tokens",[498,4598,4599],{},"Expiry check on every auth. Cache TTL (5 min default) limits the window for deleted/expired tokens.",[480,4601,4602,4605],{},[498,4603,4604],{},"Hash exposure",[498,4606,4607,4608,4610],{},"Hash replaced with masked preview (",[507,4609,4296],{},") in GET/LIST. Raw token only returned on create and rotate.",[2892,4612,4614],{"id":4613},"usage-with-other-auth","Usage with Other Auth",[455,4616,4617,4618,4621,4622,4624],{},"The token plugin is designed to coexist with any session-based auth. The context extender ",[501,4619,4620],{},"skips"," when ",[507,4623,959],{}," is already set, so session auth always takes priority.",[745,4626,4628],{"className":747,"code":4627,"language":749,"meta":750,"style":750},"// server/autoapi-plugins.ts\nexport default [\n  createBetterAuthPlugin({ ... }),   // Session auth for browsers\n  createApiTokenPlugin({ ... }),     // Token auth for API clients — skips if session already set user\n]\n",[507,4629,4630,4635,4645,4666,4686],{"__ignoreMap":750},[754,4631,4632],{"class":756,"line":757},[754,4633,4634],{"class":1151},"// server/autoapi-plugins.ts\n",[754,4636,4637,4640,4643],{"class":756,"line":1103},[754,4638,4639],{"class":1762},"export",[754,4641,4642],{"class":1762}," default",[754,4644,1250],{"class":763},[754,4646,4647,4650,4652,4654,4657,4659,4661,4663],{"class":756,"line":1121},[754,4648,4649],{"class":760},"  createBetterAuthPlugin",[754,4651,764],{"class":763},[754,4653,768],{"class":767},[754,4655,4656],{"class":767}," ...",[754,4658,804],{"class":767},[754,4660,712],{"class":763},[754,4662,782],{"class":767},[754,4664,4665],{"class":1151},"   // Session auth for browsers\n",[754,4667,4668,4671,4673,4675,4677,4679,4681,4683],{"class":756,"line":1155},[754,4669,4670],{"class":760},"  createApiTokenPlugin",[754,4672,764],{"class":763},[754,4674,768],{"class":767},[754,4676,4656],{"class":767},[754,4678,804],{"class":767},[754,4680,712],{"class":763},[754,4682,782],{"class":767},[754,4684,4685],{"class":1151},"     // Token auth for API clients — skips if session already set user\n",[754,4687,4688],{"class":756,"line":1176},[754,4689,4690],{"class":763},"]\n",[455,4692,4693],{},"With a plain Nitro auth plugin (no Better Auth):",[745,4695,4697],{"className":747,"code":4696,"language":749,"meta":750,"style":750},"// server/plugins/auth.ts\nexport default defineNitroPlugin((nitro) => {\n  nitro.hooks.hook('request', async (event) => {\n    const session = getCookie(event, 'session')\n    if (session) event.context.user = await getUserFromSession(session)\n    // Token plugin handles API requests that don't have a session\n  })\n})\n",[507,4698,4699,4704,4726,4764,4793,4830,4835,4842],{"__ignoreMap":750},[754,4700,4701],{"class":756,"line":757},[754,4702,4703],{"class":1151},"// server/plugins/auth.ts\n",[754,4705,4706,4708,4710,4713,4715,4717,4720,4722,4724],{"class":756,"line":1103},[754,4707,4639],{"class":1762},[754,4709,4642],{"class":1762},[754,4711,4712],{"class":760}," defineNitroPlugin",[754,4714,764],{"class":763},[754,4716,764],{"class":767},[754,4718,4719],{"class":996},"nitro",[754,4721,712],{"class":767},[754,4723,1002],{"class":989},[754,4725,1605],{"class":767},[754,4727,4728,4731,4733,4736,4738,4741,4743,4745,4748,4750,4752,4754,4756,4758,4760,4762],{"class":756,"line":1121},[754,4729,4730],{"class":763},"  nitro",[754,4732,1008],{"class":767},[754,4734,4735],{"class":763},"hooks",[754,4737,1008],{"class":767},[754,4739,4740],{"class":760},"hook",[754,4742,764],{"class":771},[754,4744,879],{"class":767},[754,4746,4747],{"class":875},"request",[754,4749,879],{"class":767},[754,4751,782],{"class":767},[754,4753,990],{"class":989},[754,4755,993],{"class":767},[754,4757,997],{"class":996},[754,4759,712],{"class":767},[754,4761,1002],{"class":989},[754,4763,1605],{"class":767},[754,4765,4766,4769,4772,4775,4778,4780,4782,4784,4786,4789,4791],{"class":756,"line":1155},[754,4767,4768],{"class":989},"    const",[754,4770,4771],{"class":763}," session",[754,4773,4774],{"class":767}," =",[754,4776,4777],{"class":760}," getCookie",[754,4779,764],{"class":771},[754,4781,997],{"class":763},[754,4783,782],{"class":767},[754,4785,872],{"class":767},[754,4787,4788],{"class":875},"session",[754,4790,879],{"class":767},[754,4792,807],{"class":771},[754,4794,4795,4798,4800,4802,4804,4806,4808,4811,4813,4816,4818,4821,4824,4826,4828],{"class":756,"line":1176},[754,4796,4797],{"class":1762},"    if",[754,4799,993],{"class":771},[754,4801,4788],{"class":763},[754,4803,1037],{"class":771},[754,4805,997],{"class":763},[754,4807,1008],{"class":767},[754,4809,4810],{"class":763},"context",[754,4812,1008],{"class":767},[754,4814,4815],{"class":763},"user",[754,4817,4774],{"class":767},[754,4819,4820],{"class":1762}," await",[754,4822,4823],{"class":760}," getUserFromSession",[754,4825,764],{"class":771},[754,4827,4788],{"class":763},[754,4829,807],{"class":771},[754,4831,4832],{"class":756,"line":1188},[754,4833,4834],{"class":1151},"    // Token plugin handles API requests that don't have a session\n",[754,4836,4837,4840],{"class":756,"line":1377},[754,4838,4839],{"class":767},"  }",[754,4841,807],{"class":771},[754,4843,4844,4846],{"class":756,"line":1407},[754,4845,917],{"class":767},[754,4847,807],{"class":763},[698,4849],{},[705,4851,4853],{"id":4852},"soft-delete","Soft Delete",[455,4855,4856,4859],{},[501,4857,4858],{},"Built into core"," — not a plugin.",[455,4861,4862,4863,510,4866,4869,4870,4873,4874,4877],{},"The delete handler automatically checks for a ",[507,4864,4865],{},"deletedAt",[507,4867,4868],{},"deleted_at"," column and does ",[507,4871,4872],{},"UPDATE SET deletedAt = NOW()"," instead of ",[507,4875,4876],{},"DELETE",". List/get handlers auto-filter soft-deleted records.",[698,4879],{},[466,4881,4883],{"id":4882},"summary","Summary",[474,4885,4886,4899],{},[477,4887,4888],{},[480,4889,4890,4893,4896],{},[483,4891,4892],{},"Plugin",[483,4894,4895],{},"Status",[483,4897,4898],{},"Hooks Used",[493,4900,4901,4912,4922,4931,4943,4953,4965,4977,4987,4999,5011,5023,5036,5049,5059,5074],{},[480,4902,4903,4905,4908],{},[498,4904,243],{},[498,4906,4907],{},"Shipped",[498,4909,4910,672],{},[507,4911,728],{},[480,4913,4914,4917,4919],{},[498,4915,4916],{},"Request Metadata",[498,4918,4907],{},[498,4920,4921],{},"Context extender + before hooks",[480,4923,4924,4926,4928],{},[498,4925,935],{},[498,4927,4907],{},[498,4929,4930],{},"Context extender",[480,4932,4933,4936,4938],{},[498,4934,4935],{},"Audit Log",[498,4937,4907],{},[498,4939,4940,4941],{},"before + after hooks + ",[507,4942,597],{},[480,4944,4945,4948,4950],{},[498,4946,4947],{},"Webhook",[498,4949,4907],{},[498,4951,4952],{},"after hooks",[480,4954,4955,4958,4960],{},[498,4956,4957],{},"Activity Feed",[498,4959,4907],{},[498,4961,4962,4963],{},"after hooks + ",[507,4964,597],{},[480,4966,4967,4970,4972],{},[498,4968,4969],{},"Slug Generation",[498,4971,4907],{},[498,4973,4974,4975],{},"before hooks + ",[507,4976,581],{},[480,4978,4979,4982,4984],{},[498,4980,4981],{},"Data Validation",[498,4983,4907],{},[498,4985,4986],{},"before hooks (throw on failure)",[480,4988,4989,4992,4994],{},[498,4990,4991],{},"Data Export",[498,4993,4907],{},[498,4995,4996,4998],{},[507,4997,597],{}," (new endpoints)",[480,5000,5001,5004,5006],{},[498,5002,5003],{},"File Upload",[498,5005,4907],{},[498,5007,5008,5010],{},[507,5009,597],{}," + endpoints",[480,5012,5013,5016,5018],{},[498,5014,5015],{},"Revision History",[498,5017,4907],{},[498,5019,5020,5021],{},"before/after hooks + ",[507,5022,597],{},[480,5024,5025,5028,5030],{},[498,5026,5027],{},"Cache",[498,5029,4907],{},[498,5031,5032,2404,5034],{},[507,5033,671],{},[507,5035,2399],{},[480,5037,5038,5041,5043],{},[498,5039,5040],{},"Search",[498,5042,4907],{},[498,5044,5045,2920,5047],{},[507,5046,648],{},[507,5048,644],{},[480,5050,5051,5054,5056],{},[498,5052,5053],{},"Field Encryption",[498,5055,4907],{},[498,5057,5058],{},"before hooks + after hooks (return value)",[480,5060,5061,5064,5066],{},[498,5062,5063],{},"API Token",[498,5065,4907],{},[498,5067,5068,5069,5071,5072],{},"Context extender + ",[507,5070,728],{}," middleware + before/after hooks + ",[507,5073,597],{},[480,5075,5076,5078,5081],{},[498,5077,4853],{},[498,5079,5080],{},"Core feature",[498,5082,5083],{},"N/A",[5085,5086,5087],"style",{},"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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}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 .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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 .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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":750,"searchDepth":1103,"depth":1103,"links":5089},[5090,5091,5124],{"id":468,"depth":1103,"text":469},{"id":702,"depth":1103,"text":703,"children":5092},[5093,5095,5097,5099,5101,5103,5105,5107,5109,5111,5113,5115,5117,5119,5121,5123],{"id":707,"depth":1121,"text":5094},"Rate Limiting (createRateLimitPlugin)",{"id":810,"depth":1121,"text":5096},"Request Metadata (createRequestMetadataPlugin)",{"id":922,"depth":1121,"text":5098},"Better Auth (createBetterAuthPlugin)",{"id":1046,"depth":1121,"text":5100},"Audit Log (createAuditLogPlugin)",{"id":1197,"depth":1121,"text":5102},"Webhook (createWebhookPlugin)",{"id":1416,"depth":1121,"text":5104},"Activity Feed (createActivityFeedPlugin)",{"id":1544,"depth":1121,"text":5106},"Slug Generation (createSlugPlugin)",{"id":1720,"depth":1121,"text":5108},"Data Validation (createSchemaValidationPlugin)",{"id":1950,"depth":1121,"text":5110},"Data Export (createExportPlugin)",{"id":2081,"depth":1121,"text":5112},"File Upload (createFileUploadPlugin)",{"id":2265,"depth":1121,"text":5114},"Revision History (createRevisionPlugin)",{"id":2380,"depth":1121,"text":5116},"Cache (createCachePlugin)",{"id":2539,"depth":1121,"text":5118},"Search (createSearchPlugin)",{"id":2705,"depth":1121,"text":5120},"Field Encryption (createEncryptionPlugin)",{"id":2847,"depth":1121,"text":5122},"API Token (createApiTokenPlugin)",{"id":4852,"depth":1121,"text":4853},{"id":4882,"depth":1103,"text":4883},"md",null,{},{"title":251,"description":457},"QWNLMHH1cx872J-60EgceEG_to9C3XY4v_o-3srHAOE",[5131,5133],{"title":247,"path":248,"stem":249,"description":5132,"children":-1},"The Request Metadata Plugin automatically captures request metadata (IP address, geolocation, user-agent, etc.) and makes it available throughout your API handlers. It supports both context-only access (for hooks and authorization) and optional database persistence.",{"title":255,"path":256,"stem":257,"description":750,"children":-1},1772977478069]