[{"data":1,"prerenderedAt":888},["ShallowReactive",2],{"navigation:en":3,"\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fstorage:en":311},[4],{"title":5,"path":6,"stem":7,"children":8,"icon":10},"Nuxfire Docs","\u002Fdocs","docs\u002F0.index",[9,11,152],{"title":5,"path":6,"stem":7,"icon":10},"i-lucide-layout-grid",{"title":12,"path":13,"stem":14,"children":15,"icon":151},"SaaS Starter Kit","\u002Fdocs\u002Fsaas-starter-kit","docs\u002F1.saas-starter-kit\u002F0.index",[16,19,63,129],{"title":17,"path":13,"stem":14,"icon":18},"What is the SaaS Starter Kit?","i-lucide-info",{"title":20,"icon":21,"hide":22,"path":23,"stem":24,"children":25,"page":62},"Getting Started","i-lucide-play",true,"\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started","docs\u002F1.saas-starter-kit\u002F1.getting-started",[26,30,34,38,42,46,50,54,58],{"title":27,"path":28,"stem":29},"Quick Start","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fquick-start","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F0.quick-start",{"title":31,"path":32,"stem":33},"Installation","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Finstallation","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F1.installation",{"title":35,"path":36,"stem":37},"Cloudflare Access","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fcloudflare-access","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F2.cloudflare-access",{"title":39,"path":40,"stem":41},"Configuration","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fconfiguration","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F3.configuration",{"title":43,"path":44,"stem":45},"Secrets","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fsecrets","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F4.secrets",{"title":47,"path":48,"stem":49},"Environment Variables","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fenvironment-variables","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F5.environment-variables",{"title":51,"path":52,"stem":53},"Deploy","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fdeploy","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F6.deploy",{"title":55,"path":56,"stem":57},"Local Development","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Flocal-development","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F7.local-development",{"title":59,"path":60,"stem":61},"Project Structure","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fproject-structure","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F8.project-structure",false,{"title":64,"icon":65,"hide":22,"path":66,"stem":67,"children":68,"page":62},"Features","i-lucide-box","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures","docs\u002F1.saas-starter-kit\u002F2.features",[69,73,77,81,85,89,93,97,101,105,109,113,117,121,125],{"title":70,"path":71,"stem":72},"Authentication","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fauth","docs\u002F1.saas-starter-kit\u002F2.features\u002F1.auth",{"title":74,"path":75,"stem":76},"Rate Limiting","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Frate-limiting","docs\u002F1.saas-starter-kit\u002F2.features\u002F10.rate-limiting",{"title":78,"path":79,"stem":80},"WebSockets","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fwebsockets","docs\u002F1.saas-starter-kit\u002F2.features\u002F11.websockets",{"title":82,"path":83,"stem":84},"Blog","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fblog","docs\u002F1.saas-starter-kit\u002F2.features\u002F14.blog",{"title":86,"path":87,"stem":88},"Documentation","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fdocumentation","docs\u002F1.saas-starter-kit\u002F2.features\u002F15.documentation",{"title":90,"path":91,"stem":92},"Analytics","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fanalytics","docs\u002F1.saas-starter-kit\u002F2.features\u002F16.analytics",{"title":94,"path":95,"stem":96},"Platform Admin Console","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fplatform-admin","docs\u002F1.saas-starter-kit\u002F2.features\u002F17.platform-admin",{"title":98,"path":99,"stem":100},"Database","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fdatabase","docs\u002F1.saas-starter-kit\u002F2.features\u002F2.database",{"title":102,"path":103,"stem":104},"Payments & Billing","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fpayments","docs\u002F1.saas-starter-kit\u002F2.features\u002F3.payments",{"title":106,"path":107,"stem":108},"Emails","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Femails","docs\u002F1.saas-starter-kit\u002F2.features\u002F4.emails",{"title":110,"path":111,"stem":112},"Notifications","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fnotifications","docs\u002F1.saas-starter-kit\u002F2.features\u002F5.notifications",{"title":114,"path":115,"stem":116},"Role-Based Access Control","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Frbac","docs\u002F1.saas-starter-kit\u002F2.features\u002F6.rbac",{"title":118,"path":119,"stem":120},"Async Jobs","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fasync-jobs","docs\u002F1.saas-starter-kit\u002F2.features\u002F7.async-jobs",{"title":122,"path":123,"stem":124},"Storage","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fstorage","docs\u002F1.saas-starter-kit\u002F2.features\u002F8.storage",{"title":126,"path":127,"stem":128},"Internationalization","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Finternationalization","docs\u002F1.saas-starter-kit\u002F2.features\u002F9.internationalization",{"title":130,"icon":131,"path":132,"stem":133,"children":134,"page":62},"Concepts","i-lucide-graduation-cap","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts","docs\u002F1.saas-starter-kit\u002F3.concepts",[135,139,143,147],{"title":136,"path":137,"stem":138},"Technology Stack","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Ftech-stack","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F0.tech-stack",{"title":140,"path":141,"stem":142},"SST.dev","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Fsst","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F1.sst",{"title":144,"path":145,"stem":146},"NuxtHub","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Fnuxt-hub","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F2.nuxt-hub",{"title":148,"path":149,"stem":150},"Design System & Branding","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Fdesign-system","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F3.design-system","i-lucide-zap",{"title":153,"path":154,"stem":155,"children":156,"icon":159},"Fireskills","\u002Fdocs\u002Ffireskills","docs\u002F2.fireskills\u002F0.index",[157,160,219,249,275,293],{"title":158,"path":154,"stem":155,"icon":159},"What is Fireskills?","i-lucide-flame",{"title":20,"icon":21,"hide":22,"path":161,"stem":162,"children":163,"page":62},"\u002Fdocs\u002Ffireskills\u002Fgetting-started","docs\u002F2.fireskills\u002F1.getting-started",[164,167,171,175,179,183,187,191,195,199,203,207,211,215],{"title":27,"path":165,"stem":166},"\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fquick-start","docs\u002F2.fireskills\u002F1.getting-started\u002F00.quick-start",{"title":168,"path":169,"stem":170},"Workflow & Governance","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fworkflow","docs\u002F2.fireskills\u002F1.getting-started\u002F01.workflow",{"title":172,"path":173,"stem":174},"Project Context","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fproject-context","docs\u002F2.fireskills\u002F1.getting-started\u002F02.project-context",{"title":176,"path":177,"stem":178},"Step 0: Setup","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-setup","docs\u002F2.fireskills\u002F1.getting-started\u002F03.step-setup",{"title":180,"path":181,"stem":182},"Step 1: Inbox","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-inbox","docs\u002F2.fireskills\u002F1.getting-started\u002F04.step-inbox",{"title":184,"path":185,"stem":186},"Step 2: Backlog","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-backlog","docs\u002F2.fireskills\u002F1.getting-started\u002F05.step-backlog",{"title":188,"path":189,"stem":190},"Step 3: Specify","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-specify","docs\u002F2.fireskills\u002F1.getting-started\u002F06.step-specify",{"title":192,"path":193,"stem":194},"Step 4: Validate","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-validate","docs\u002F2.fireskills\u002F1.getting-started\u002F07.step-validate",{"title":196,"path":197,"stem":198},"Step 5: Tasks","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-tasks","docs\u002F2.fireskills\u002F1.getting-started\u002F08.step-tasks",{"title":200,"path":201,"stem":202},"Step 6: TDD\u002FBDD","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-tdd-bdd","docs\u002F2.fireskills\u002F1.getting-started\u002F09.step-tdd-bdd",{"title":204,"path":205,"stem":206},"Step 7: Implement","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-implement","docs\u002F2.fireskills\u002F1.getting-started\u002F10.step-implement",{"title":208,"path":209,"stem":210},"Step 8: Documentator","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-documentator","docs\u002F2.fireskills\u002F1.getting-started\u002F11.step-documentator",{"title":212,"path":213,"stem":214},"Late Changes","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Flate-changes","docs\u002F2.fireskills\u002F1.getting-started\u002F12.late-changes",{"title":216,"path":217,"stem":218},"Other Entry Points","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fother-entry-points","docs\u002F2.fireskills\u002F1.getting-started\u002F13.other-entry-points",{"title":220,"icon":221,"hide":22,"path":222,"stem":223,"children":224,"page":62},"Methodology","i-lucide-git-branch","\u002Fdocs\u002Ffireskills\u002Fmethodology","docs\u002F2.fireskills\u002F2.methodology",[225,229,233,237,241,245],{"title":226,"path":227,"stem":228},"The Three Acts","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fthree-acts","docs\u002F2.fireskills\u002F2.methodology\u002F0.three-acts",{"title":230,"path":231,"stem":232},"The Three Gates","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fthree-gates","docs\u002F2.fireskills\u002F2.methodology\u002F1.three-gates",{"title":234,"path":235,"stem":236},"The Canonical Spec","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fcanonical-spec","docs\u002F2.fireskills\u002F2.methodology\u002F2.canonical-spec",{"title":238,"path":239,"stem":240},"Lifecycle States","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Flifecycle-states","docs\u002F2.fireskills\u002F2.methodology\u002F3.lifecycle-states",{"title":242,"path":243,"stem":244},"Effort Scale","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Feffort-scale","docs\u002F2.fireskills\u002F2.methodology\u002F4.effort-scale",{"title":246,"path":247,"stem":248},"Milestones & Governance","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fgovernance-and-milestones","docs\u002F2.fireskills\u002F2.methodology\u002F5.governance-and-milestones",{"title":250,"icon":251,"hide":22,"path":252,"stem":253,"children":254,"page":62},"Skills","i-lucide-layers","\u002Fdocs\u002Ffireskills\u002Fskills","docs\u002F2.fireskills\u002F3.skills",[255,259,263,267,271],{"title":256,"path":257,"stem":258},"Catalog","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fcatalog","docs\u002F2.fireskills\u002F3.skills\u002F0.catalog",{"title":260,"path":261,"stem":262},"Act I: Define","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fact-1-define","docs\u002F2.fireskills\u002F3.skills\u002F1.act-1-define",{"title":264,"path":265,"stem":266},"Act II: Design & Prove","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fact-2-design-prove","docs\u002F2.fireskills\u002F3.skills\u002F2.act-2-design-prove",{"title":268,"path":269,"stem":270},"Act III: Deliver","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fact-3-deliver","docs\u002F2.fireskills\u002F3.skills\u002F3.act-3-deliver",{"title":272,"path":273,"stem":274},"Governance & Decisions","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fgovernance-and-decisions","docs\u002F2.fireskills\u002F3.skills\u002F4.governance-and-decisions",{"title":276,"icon":277,"hide":22,"path":278,"stem":279,"children":280,"page":62},"Specialists","i-lucide-sparkles","\u002Fdocs\u002Ffireskills\u002Fspecialists","docs\u002F2.fireskills\u002F4.specialists",[281,285,289],{"title":282,"path":283,"stem":284},"Overview","\u002Fdocs\u002Ffireskills\u002Fspecialists\u002Foverview","docs\u002F2.fireskills\u002F4.specialists\u002F0.overview",{"title":286,"path":287,"stem":288},"Nuxfire Stack","\u002Fdocs\u002Ffireskills\u002Fspecialists\u002Fnuxfire-stack","docs\u002F2.fireskills\u002F4.specialists\u002F1.nuxfire-stack",{"title":290,"path":291,"stem":292},"Engineering Specialists","\u002Fdocs\u002Ffireskills\u002Fspecialists\u002Fengineering-specialists","docs\u002F2.fireskills\u002F4.specialists\u002F2.engineering-specialists",{"title":294,"icon":295,"hide":22,"path":296,"stem":297,"children":298,"page":62},"CLI & Tooling","i-lucide-terminal","\u002Fdocs\u002Ffireskills\u002Fcli","docs\u002F2.fireskills\u002F5.cli",[299,303,307],{"title":300,"path":301,"stem":302},"CLI Overview","\u002Fdocs\u002Ffireskills\u002Fcli\u002Foverview","docs\u002F2.fireskills\u002F5.cli\u002F0.overview",{"title":304,"path":305,"stem":306},"Commands","\u002Fdocs\u002Ffireskills\u002Fcli\u002Fcommands","docs\u002F2.fireskills\u002F5.cli\u002F1.commands",{"title":308,"path":309,"stem":310},"Safety Scripts","\u002Fdocs\u002Ffireskills\u002Fcli\u002Fgovernance-scripts","docs\u002F2.fireskills\u002F5.cli\u002F2.governance-scripts",{"id":312,"title":122,"body":313,"description":883,"extension":884,"meta":885,"navigation":22,"path":123,"seo":886,"stem":124,"__hash__":887},"docs\u002Fdocs\u002F1.saas-starter-kit\u002F2.features\u002F8.storage.md",{"type":314,"value":315,"toc":877},"minimark",[316,320,336,340,343,452,455,596,600,743,748,873],[317,318,319],"p",{},"Nuxfire uses Cloudflare R2 for blob storage — profile pictures and team logos today.",[317,321,322,323,327,328,331,332,335],{},"It uses the ",[324,325,326],"code",{},"@nuxt-hub\u002Fcore"," module's utilities for this specifically, even though the database is Postgres via Hyperdrive rather than a NuxtHub-managed store (see ",[329,330,98],"a",{"href":99},"): NuxtHub's self-hosted mode expects a binding literally named ",[324,333,334],{},"BLOB"," (fixed by NuxtHub itself), so the R2 bucket is named to match rather than being deployed through NuxtHub's own platform.",[337,338,39],"h2",{"id":339},"configuration",[317,341,342],{},"R2 is configured in two places:",[344,345,351],"pre",{"className":346,"code":347,"filename":348,"language":349,"meta":350,"style":350},"language-typescript shiki shiki-themes material-theme-lighter github-light github-dark monokai","export default defineNuxtConfig({\n  hub: {\n    blob: true, \u002F\u002F enables R2 storage using NuxtHub's utilities\n    kv: true,\n    cache: true,\n  },\n});\n","apps\u002Fapp\u002Fnuxt.config.ts","typescript","",[324,352,353,377,390,409,422,434,440],{"__ignoreMap":350},[354,355,358,362,365,369,373],"span",{"class":356,"line":357},"line",1,[354,359,361],{"class":360},"sRxSC","export",[354,363,364],{"class":360}," default",[354,366,368],{"class":367},"sD0ED"," defineNuxtConfig",[354,370,372],{"class":371},"ss--_","(",[354,374,376],{"class":375},"swvn1","{\n",[354,378,380,384,387],{"class":356,"line":379},2,[354,381,383],{"class":382},"squCx","  hub",[354,385,386],{"class":375},":",[354,388,389],{"class":375}," {\n",[354,391,393,396,398,402,405],{"class":356,"line":392},3,[354,394,395],{"class":382},"    blob",[354,397,386],{"class":375},[354,399,401],{"class":400},"s8HiA"," true",[354,403,404],{"class":375},",",[354,406,408],{"class":407},"ss7Ak"," \u002F\u002F enables R2 storage using NuxtHub's utilities\n",[354,410,412,415,417,419],{"class":356,"line":411},4,[354,413,414],{"class":382},"    kv",[354,416,386],{"class":375},[354,418,401],{"class":400},[354,420,421],{"class":375},",\n",[354,423,425,428,430,432],{"class":356,"line":424},5,[354,426,427],{"class":382},"    cache",[354,429,386],{"class":375},[354,431,401],{"class":400},[354,433,421],{"class":375},[354,435,437],{"class":356,"line":436},6,[354,438,439],{"class":375},"  },\n",[354,441,443,446,449],{"class":356,"line":442},7,[354,444,445],{"class":375},"}",[354,447,448],{"class":371},")",[354,450,451],{"class":375},";\n",[317,453,454],{},"Infra as code:",[344,456,459],{"className":346,"code":457,"filename":458,"language":349,"meta":350,"style":350},"\u002F\u002F NuxtHub's self-hosted mode expects this binding named exactly BLOB —\n\u002F\u002F that's fixed by NuxtHub itself, not something `link` can rename.\nlink.push(\n  database,\n  new sst.cloudflare.Bucket(\"BLOB\"),\n  new sst.cloudflare.Kv(\"KV\"),\n  new sst.cloudflare.Kv(\"CACHE\"),\n  emails,\n);\n","infra\u002Futils\u002Fnuxt.ts",[324,460,461,466,471,485,492,526,554,581,589],{"__ignoreMap":350},[354,462,463],{"class":356,"line":357},[354,464,465],{"class":407},"\u002F\u002F NuxtHub's self-hosted mode expects this binding named exactly BLOB —\n",[354,467,468],{"class":356,"line":379},[354,469,470],{"class":407},"\u002F\u002F that's fixed by NuxtHub itself, not something `link` can rename.\n",[354,472,473,476,479,482],{"class":356,"line":392},[354,474,475],{"class":371},"link",[354,477,478],{"class":375},".",[354,480,481],{"class":367},"push",[354,483,484],{"class":371},"(\n",[354,486,487,490],{"class":356,"line":411},[354,488,489],{"class":371},"  database",[354,491,421],{"class":375},[354,493,494,498,501,503,506,508,511,513,517,520,522,524],{"class":356,"line":424},[354,495,497],{"class":496},"sGXK2","  new",[354,499,500],{"class":371}," sst",[354,502,478],{"class":375},[354,504,505],{"class":371},"cloudflare",[354,507,478],{"class":375},[354,509,510],{"class":367},"Bucket",[354,512,372],{"class":371},[354,514,516],{"class":515},"siCPE","\"",[354,518,334],{"class":519},"sLACW",[354,521,516],{"class":515},[354,523,448],{"class":371},[354,525,421],{"class":375},[354,527,528,530,532,534,536,538,541,543,545,548,550,552],{"class":356,"line":436},[354,529,497],{"class":496},[354,531,500],{"class":371},[354,533,478],{"class":375},[354,535,505],{"class":371},[354,537,478],{"class":375},[354,539,540],{"class":367},"Kv",[354,542,372],{"class":371},[354,544,516],{"class":515},[354,546,547],{"class":519},"KV",[354,549,516],{"class":515},[354,551,448],{"class":371},[354,553,421],{"class":375},[354,555,556,558,560,562,564,566,568,570,572,575,577,579],{"class":356,"line":442},[354,557,497],{"class":496},[354,559,500],{"class":371},[354,561,478],{"class":375},[354,563,505],{"class":371},[354,565,478],{"class":375},[354,567,540],{"class":367},[354,569,372],{"class":371},[354,571,516],{"class":515},[354,573,574],{"class":519},"CACHE",[354,576,516],{"class":515},[354,578,448],{"class":371},[354,580,421],{"class":375},[354,582,584,587],{"class":356,"line":583},8,[354,585,586],{"class":371},"  emails",[354,588,421],{"class":375},[354,590,592,594],{"class":356,"line":591},9,[354,593,448],{"class":371},[354,595,451],{"class":375},[337,597,599],{"id":598},"usage","Usage",[344,601,603],{"className":346,"code":602,"language":349,"meta":350,"style":350},"\u002F\u002F Get the blob client\nconst blob = hubBlob();\n\n\u002F\u002F Upload a file\nawait blob.put(\"path\u002Fto\u002Ffile.jpg\", fileContent);\n\n\u002F\u002F Download a file\nconst file = await blob.get(\"path\u002Fto\u002Ffile.jpg\");\n\n\u002F\u002F Delete a file\nawait blob.del(\"path\u002Fto\u002Ffile.jpg\");\n",[324,604,605,610,631,636,641,669,673,678,709,713,719],{"__ignoreMap":350},[354,606,607],{"class":356,"line":357},[354,608,609],{"class":407},"\u002F\u002F Get the blob client\n",[354,611,612,616,620,623,626,629],{"class":356,"line":379},[354,613,615],{"class":614},"srJo8","const",[354,617,619],{"class":618},"s91G_"," blob",[354,621,622],{"class":496}," =",[354,624,625],{"class":367}," hubBlob",[354,627,628],{"class":371},"()",[354,630,451],{"class":375},[354,632,633],{"class":356,"line":392},[354,634,635],{"emptyLinePlaceholder":22},"\n",[354,637,638],{"class":356,"line":411},[354,639,640],{"class":407},"\u002F\u002F Upload a file\n",[354,642,643,646,648,650,653,655,657,660,662,664,667],{"class":356,"line":424},[354,644,645],{"class":360},"await",[354,647,619],{"class":371},[354,649,478],{"class":375},[354,651,652],{"class":367},"put",[354,654,372],{"class":371},[354,656,516],{"class":515},[354,658,659],{"class":519},"path\u002Fto\u002Ffile.jpg",[354,661,516],{"class":515},[354,663,404],{"class":375},[354,665,666],{"class":371}," fileContent)",[354,668,451],{"class":375},[354,670,671],{"class":356,"line":436},[354,672,635],{"emptyLinePlaceholder":22},[354,674,675],{"class":356,"line":442},[354,676,677],{"class":407},"\u002F\u002F Download a file\n",[354,679,680,682,685,687,690,692,694,697,699,701,703,705,707],{"class":356,"line":583},[354,681,615],{"class":614},[354,683,684],{"class":618}," file",[354,686,622],{"class":496},[354,688,689],{"class":360}," await",[354,691,619],{"class":371},[354,693,478],{"class":375},[354,695,696],{"class":367},"get",[354,698,372],{"class":371},[354,700,516],{"class":515},[354,702,659],{"class":519},[354,704,516],{"class":515},[354,706,448],{"class":371},[354,708,451],{"class":375},[354,710,711],{"class":356,"line":591},[354,712,635],{"emptyLinePlaceholder":22},[354,714,716],{"class":356,"line":715},10,[354,717,718],{"class":407},"\u002F\u002F Delete a file\n",[354,720,722,724,726,728,731,733,735,737,739,741],{"class":356,"line":721},11,[354,723,645],{"class":360},[354,725,619],{"class":371},[354,727,478],{"class":375},[354,729,730],{"class":367},"del",[354,732,372],{"class":371},[354,734,516],{"class":515},[354,736,659],{"class":519},[354,738,516],{"class":515},[354,740,448],{"class":371},[354,742,451],{"class":375},[744,745,747],"h3",{"id":746},"real-world-example-profile-pictures","Real-world example: profile pictures",[344,749,751],{"className":346,"code":750,"language":349,"meta":350,"style":350},"\u002F\u002F Delete the old profile picture before storing the new one\nconst previousKey = user.image.slice(baseUrl.length);\nif (previousKey.startsWith(\"profile\u002F\")) {\n  try {\n    await hubBlob().del(previousKey);\n  } catch {\n    \u002F\u002F didn't exist\n  }\n}\n",[324,752,753,758,792,819,826,848,858,863,868],{"__ignoreMap":350},[354,754,755],{"class":356,"line":357},[354,756,757],{"class":407},"\u002F\u002F Delete the old profile picture before storing the new one\n",[354,759,760,762,765,767,770,772,775,777,780,783,785,788,790],{"class":356,"line":379},[354,761,615],{"class":614},[354,763,764],{"class":618}," previousKey",[354,766,622],{"class":496},[354,768,769],{"class":371}," user",[354,771,478],{"class":375},[354,773,774],{"class":371},"image",[354,776,478],{"class":375},[354,778,779],{"class":367},"slice",[354,781,782],{"class":371},"(baseUrl",[354,784,478],{"class":375},[354,786,787],{"class":618},"length",[354,789,448],{"class":371},[354,791,451],{"class":375},[354,793,794,797,800,802,805,807,809,812,814,817],{"class":356,"line":392},[354,795,796],{"class":360},"if",[354,798,799],{"class":371}," (previousKey",[354,801,478],{"class":375},[354,803,804],{"class":367},"startsWith",[354,806,372],{"class":371},[354,808,516],{"class":515},[354,810,811],{"class":519},"profile\u002F",[354,813,516],{"class":515},[354,815,816],{"class":371},")) ",[354,818,376],{"class":375},[354,820,821,824],{"class":356,"line":411},[354,822,823],{"class":360},"  try",[354,825,389],{"class":375},[354,827,828,831,833,835,837,839,841,844,846],{"class":356,"line":424},[354,829,830],{"class":360},"    await",[354,832,625],{"class":367},[354,834,628],{"class":382},[354,836,478],{"class":375},[354,838,730],{"class":367},[354,840,372],{"class":382},[354,842,843],{"class":371},"previousKey",[354,845,448],{"class":382},[354,847,451],{"class":375},[354,849,850,853,856],{"class":356,"line":436},[354,851,852],{"class":375},"  }",[354,854,855],{"class":360}," catch",[354,857,389],{"class":375},[354,859,860],{"class":356,"line":442},[354,861,862],{"class":407},"    \u002F\u002F didn't exist\n",[354,864,865],{"class":356,"line":583},[354,866,867],{"class":375},"  }\n",[354,869,870],{"class":356,"line":591},[354,871,872],{"class":375},"}\n",[874,875,876],"style",{},"html pre.shiki code .sRxSC, html code.shiki .sRxSC{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit;--shiki-sepia:#F92672;--shiki-sepia-font-style:inherit}html pre.shiki code .sD0ED, html code.shiki .sD0ED{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}html pre.shiki code .ss--_, html code.shiki .ss--_{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .swvn1, html code.shiki .swvn1{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .squCx, html code.shiki .squCx{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .s8HiA, html code.shiki .s8HiA{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}html pre.shiki code .ss7Ak, html code.shiki .ss7Ak{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit;--shiki-sepia:#88846F;--shiki-sepia-font-style:inherit}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 .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sGXK2, html code.shiki .sGXK2{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .siCPE, html code.shiki .siCPE{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html pre.shiki code .sLACW, html code.shiki .sLACW{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html pre.shiki code .srJo8, html code.shiki .srJo8{--shiki-light:#9C3EDA;--shiki-light-font-style:inherit;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit;--shiki-sepia:#66D9EF;--shiki-sepia-font-style:italic}html pre.shiki code .s91G_, html code.shiki .s91G_{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#F8F8F2}",{"title":350,"searchDepth":379,"depth":379,"links":878},[879,880],{"id":339,"depth":379,"text":39},{"id":598,"depth":379,"text":599,"children":881},[882],{"id":746,"depth":392,"text":747},"Cloudflare R2 for blob storage, via NuxtHub's core utilities.","md",{},{"title":122,"description":883},"u2HNXeKr1SFZt4OZYyZYVvLYEriPHl2WShutRnIA4c0",1790707485288]