[{"data":1,"prerenderedAt":1415},["ShallowReactive",2],{"navigation:en":3,"\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fasync-jobs: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":118,"body":313,"description":1410,"extension":1411,"meta":1412,"navigation":22,"path":119,"seo":1413,"stem":120,"__hash__":1414},"docs\u002Fdocs\u002F1.saas-starter-kit\u002F2.features\u002F7.async-jobs.md",{"type":314,"value":315,"toc":1399},"minimark",[316,321,325,366,371,393,903,907,930,932,956,959,1168,1270,1274,1295,1299,1380,1384,1395],[317,318,320],"h2",{"id":319},"architecture","Architecture",[322,323,324],"p",{},"Nuxfire processes background work with two independent Cloudflare primitives:",[326,327,328,345],"ol",{},[329,330,331,335,336,340,341,344],"li",{},[332,333,334],"strong",{},"Queue-based jobs"," — Cloudflare Queues, behind the ",[337,338,339],"code",{},"jobsQueue"," flag in ",[337,342,343],{},"config.ts",".",[329,346,347,350,351,354,355,358,359,361,362,365],{},[332,348,349],{},"Cron"," — a Cloudflare Cron Trigger, deployed unconditionally today; ",[337,352,353],{},"infra\u002Fcron.ts"," doesn't currently check the ",[337,356,357],{},"cron"," flag declared in ",[337,360,343],{},"'s ",[337,363,364],{},"flags"," type.",[367,368,370],"h3",{"id":369},"queue-consumer","Queue consumer",[322,372,373,376,377,380,381,384,385,388,389,392],{},[337,374,375],{},"apps\u002Ffunctions\u002Fsrc\u002Fjobs-consumer.ts"," processes messages in batches. On success it marks the job ",[337,378,379],{},"completed"," and writes a ",[337,382,383],{},"job completed"," log line; on failure it marks the job ",[337,386,387],{},"failed",", writes the real error text to a log line, and calls ",[337,390,391],{},"message.retry()",":",[394,395,400],"pre",{"className":396,"code":397,"filename":375,"language":398,"meta":399,"style":399},"language-typescript shiki shiki-themes material-theme-lighter github-light github-dark monokai","for (const message of batch.messages) {\n  const job = message.body as typeof schema.jobs.$inferSelect;\n  try {\n    await db.update(schema.jobs).set({ status: \"completed\" }).where(eq(schema.jobs.id, job.id));\n    await db.insert(schema.logs).values({ jobId: job.id, message: \"job completed\" });\n    message.ack();\n  } catch (err) {\n    await db.update(schema.jobs).set({ status: \"failed\" }).where(eq(schema.jobs.id, job.id));\n    await db.insert(schema.logs).values({\n      jobId: job.id,\n      message: `job failed: ${err instanceof Error ? err.message : String(err)}`,\n    });\n    message.retry(); \u002F\u002F governed by infra\u002Fjobs.ts's retry_delay\u002Fmax_retries\n  }\n}\n","typescript","",[337,401,402,439,480,489,585,646,662,681,756,785,802,862,872,891,897],{"__ignoreMap":399},[403,404,407,411,415,419,423,427,430,433,436],"span",{"class":405,"line":406},"line",1,[403,408,410],{"class":409},"sRxSC","for",[403,412,414],{"class":413},"ss--_"," (",[403,416,418],{"class":417},"srJo8","const",[403,420,422],{"class":421},"s91G_"," message",[403,424,426],{"class":425},"sGXK2"," of",[403,428,429],{"class":413}," batch",[403,431,344],{"class":432},"swvn1",[403,434,435],{"class":413},"messages) ",[403,437,438],{"class":432},"{\n",[403,440,442,445,448,451,453,455,458,461,464,467,469,472,474,477],{"class":405,"line":441},2,[403,443,444],{"class":417},"  const",[403,446,447],{"class":421}," job",[403,449,450],{"class":425}," =",[403,452,422],{"class":413},[403,454,344],{"class":432},[403,456,457],{"class":413},"body",[403,459,460],{"class":409}," as",[403,462,463],{"class":425}," typeof",[403,465,466],{"class":413}," schema",[403,468,344],{"class":432},[403,470,471],{"class":413},"jobs",[403,473,344],{"class":432},[403,475,476],{"class":413},"$inferSelect",[403,478,479],{"class":432},";\n",[403,481,483,486],{"class":405,"line":482},3,[403,484,485],{"class":409},"  try",[403,487,488],{"class":432}," {\n",[403,490,492,495,498,500,504,508,511,513,515,518,520,523,525,528,531,533,537,540,543,546,548,550,553,555,558,560,562,564,566,568,571,574,576,578,580,583],{"class":405,"line":491},4,[403,493,494],{"class":409},"    await",[403,496,497],{"class":413}," db",[403,499,344],{"class":432},[403,501,503],{"class":502},"sD0ED","update",[403,505,507],{"class":506},"squCx","(",[403,509,510],{"class":413},"schema",[403,512,344],{"class":432},[403,514,471],{"class":413},[403,516,517],{"class":506},")",[403,519,344],{"class":432},[403,521,522],{"class":502},"set",[403,524,507],{"class":506},[403,526,527],{"class":432},"{",[403,529,530],{"class":506}," status",[403,532,392],{"class":432},[403,534,536],{"class":535},"siCPE"," \"",[403,538,379],{"class":539},"sLACW",[403,541,542],{"class":535},"\"",[403,544,545],{"class":432}," }",[403,547,517],{"class":506},[403,549,344],{"class":432},[403,551,552],{"class":502},"where",[403,554,507],{"class":506},[403,556,557],{"class":502},"eq",[403,559,507],{"class":506},[403,561,510],{"class":413},[403,563,344],{"class":432},[403,565,471],{"class":413},[403,567,344],{"class":432},[403,569,570],{"class":413},"id",[403,572,573],{"class":432},",",[403,575,447],{"class":413},[403,577,344],{"class":432},[403,579,570],{"class":413},[403,581,582],{"class":506},"))",[403,584,479],{"class":432},[403,586,588,590,592,594,597,599,601,603,606,608,610,613,615,617,620,622,624,626,628,630,632,634,636,638,640,642,644],{"class":405,"line":587},5,[403,589,494],{"class":409},[403,591,497],{"class":413},[403,593,344],{"class":432},[403,595,596],{"class":502},"insert",[403,598,507],{"class":506},[403,600,510],{"class":413},[403,602,344],{"class":432},[403,604,605],{"class":413},"logs",[403,607,517],{"class":506},[403,609,344],{"class":432},[403,611,612],{"class":502},"values",[403,614,507],{"class":506},[403,616,527],{"class":432},[403,618,619],{"class":506}," jobId",[403,621,392],{"class":432},[403,623,447],{"class":413},[403,625,344],{"class":432},[403,627,570],{"class":413},[403,629,573],{"class":432},[403,631,422],{"class":506},[403,633,392],{"class":432},[403,635,536],{"class":535},[403,637,383],{"class":539},[403,639,542],{"class":535},[403,641,545],{"class":432},[403,643,517],{"class":506},[403,645,479],{"class":432},[403,647,649,652,654,657,660],{"class":405,"line":648},6,[403,650,651],{"class":413},"    message",[403,653,344],{"class":432},[403,655,656],{"class":502},"ack",[403,658,659],{"class":506},"()",[403,661,479],{"class":432},[403,663,665,668,671,673,676,679],{"class":405,"line":664},7,[403,666,667],{"class":432},"  }",[403,669,670],{"class":409}," catch",[403,672,414],{"class":506},[403,674,675],{"class":413},"err",[403,677,678],{"class":506},") ",[403,680,438],{"class":432},[403,682,684,686,688,690,692,694,696,698,700,702,704,706,708,710,712,714,716,718,720,722,724,726,728,730,732,734,736,738,740,742,744,746,748,750,752,754],{"class":405,"line":683},8,[403,685,494],{"class":409},[403,687,497],{"class":413},[403,689,344],{"class":432},[403,691,503],{"class":502},[403,693,507],{"class":506},[403,695,510],{"class":413},[403,697,344],{"class":432},[403,699,471],{"class":413},[403,701,517],{"class":506},[403,703,344],{"class":432},[403,705,522],{"class":502},[403,707,507],{"class":506},[403,709,527],{"class":432},[403,711,530],{"class":506},[403,713,392],{"class":432},[403,715,536],{"class":535},[403,717,387],{"class":539},[403,719,542],{"class":535},[403,721,545],{"class":432},[403,723,517],{"class":506},[403,725,344],{"class":432},[403,727,552],{"class":502},[403,729,507],{"class":506},[403,731,557],{"class":502},[403,733,507],{"class":506},[403,735,510],{"class":413},[403,737,344],{"class":432},[403,739,471],{"class":413},[403,741,344],{"class":432},[403,743,570],{"class":413},[403,745,573],{"class":432},[403,747,447],{"class":413},[403,749,344],{"class":432},[403,751,570],{"class":413},[403,753,582],{"class":506},[403,755,479],{"class":432},[403,757,759,761,763,765,767,769,771,773,775,777,779,781,783],{"class":405,"line":758},9,[403,760,494],{"class":409},[403,762,497],{"class":413},[403,764,344],{"class":432},[403,766,596],{"class":502},[403,768,507],{"class":506},[403,770,510],{"class":413},[403,772,344],{"class":432},[403,774,605],{"class":413},[403,776,517],{"class":506},[403,778,344],{"class":432},[403,780,612],{"class":502},[403,782,507],{"class":506},[403,784,438],{"class":432},[403,786,788,791,793,795,797,799],{"class":405,"line":787},10,[403,789,790],{"class":506},"      jobId",[403,792,392],{"class":432},[403,794,447],{"class":413},[403,796,344],{"class":432},[403,798,570],{"class":413},[403,800,801],{"class":432},",\n",[403,803,805,808,810,813,816,820,822,825,829,832,835,838,841,844,847,850,852,854,857,860],{"class":405,"line":804},11,[403,806,807],{"class":506},"      message",[403,809,392],{"class":432},[403,811,812],{"class":535}," `",[403,814,815],{"class":539},"job failed: ",[403,817,819],{"class":818},"slF1C","${",[403,821,675],{"class":413},[403,823,824],{"class":425}," instanceof",[403,826,828],{"class":827},"sKvfc"," Error",[403,830,831],{"class":425}," ?",[403,833,834],{"class":413}," err",[403,836,344],{"class":837},"s00De",[403,839,840],{"class":413},"message",[403,842,843],{"class":425}," :",[403,845,846],{"class":502}," String",[403,848,507],{"class":849},"s-euU",[403,851,675],{"class":413},[403,853,517],{"class":849},[403,855,856],{"class":818},"}",[403,858,859],{"class":535},"`",[403,861,801],{"class":432},[403,863,865,868,870],{"class":405,"line":864},12,[403,866,867],{"class":432},"    }",[403,869,517],{"class":506},[403,871,479],{"class":432},[403,873,875,877,879,882,884,887],{"class":405,"line":874},13,[403,876,651],{"class":413},[403,878,344],{"class":432},[403,880,881],{"class":502},"retry",[403,883,659],{"class":506},[403,885,886],{"class":432},";",[403,888,890],{"class":889},"ss7Ak"," \u002F\u002F governed by infra\u002Fjobs.ts's retry_delay\u002Fmax_retries\n",[403,892,894],{"class":405,"line":893},14,[403,895,896],{"class":432},"  }\n",[403,898,900],{"class":405,"line":899},15,[403,901,902],{"class":432},"}\n",[367,904,906],{"id":905},"dead-letter-queue","Dead-letter queue",[322,908,909,910,913,914,917,918,921,922,925,926,929],{},"Once Cloudflare Queues exhausts the retries configured in ",[337,911,912],{},"infra\u002Fjobs.ts",", the message routes to ",[337,915,916],{},"JobDLQ",". ",[337,919,920],{},"apps\u002Ffunctions\u002Fsrc\u002Fdlq-consumer.ts"," reads the job's most recent log line (written by the consumer above) as the failure reason, inserts a ",[337,923,924],{},"DeadLetterRecord"," holding the original ",[337,927,928],{},"Job"," object verbatim, and acknowledges — it never retries itself. Replaying a dead-lettered message is a deliberate human action, not automatic.",[367,931,349],{"id":357},[322,933,934,937,938,414,940,943,944,947,948,951,952,955],{},[337,935,936],{},"apps\u002Ffunctions\u002Fsrc\u002Fcron.ts"," runs on the schedule set in ",[337,939,353],{},[337,941,942],{},"0 * * * *"," — every hour) and writes a ",[337,945,946],{},"Log"," row; ",[337,949,950],{},"jobId"," defaults to ",[337,953,954],{},"\"cron\""," for these scheduled runs, distinguishing them from queue-job logs.",[317,957,39],{"id":958},"configuration",[394,960,962],{"className":396,"code":961,"filename":912,"language":398,"meta":399,"style":399},"export const jobsDeadLetterQueue = flags.jobsQueue\n  ? new sst.cloudflare.Queue(\"JobDLQ\")\n  : undefined;\n\nexport const jobsQueue = flags.jobsQueue\n  ? new sst.cloudflare.Queue(\"JobQueue\", {\n      maxConcurrency: 5,\n      dlq: { queue: jobsDeadLetterQueue!.nodes.queue.queueName, retry: 3, retryDelay: \"60 seconds\" },\n    })\n  : undefined;\n",[337,963,964,985,1017,1028,1033,1050,1079,1092,1154,1160],{"__ignoreMap":399},[403,965,966,969,972,975,977,980,982],{"class":405,"line":406},[403,967,968],{"class":409},"export",[403,970,971],{"class":417}," const",[403,973,974],{"class":421}," jobsDeadLetterQueue",[403,976,450],{"class":425},[403,978,979],{"class":413}," flags",[403,981,344],{"class":432},[403,983,984],{"class":413},"jobsQueue\n",[403,986,987,990,993,996,998,1001,1003,1006,1008,1010,1012,1014],{"class":405,"line":441},[403,988,989],{"class":425},"  ?",[403,991,992],{"class":425}," new",[403,994,995],{"class":413}," sst",[403,997,344],{"class":432},[403,999,1000],{"class":413},"cloudflare",[403,1002,344],{"class":432},[403,1004,1005],{"class":502},"Queue",[403,1007,507],{"class":413},[403,1009,542],{"class":535},[403,1011,916],{"class":539},[403,1013,542],{"class":535},[403,1015,1016],{"class":413},")\n",[403,1018,1019,1022,1026],{"class":405,"line":482},[403,1020,1021],{"class":425},"  :",[403,1023,1025],{"class":1024},"sMTiH"," undefined",[403,1027,479],{"class":432},[403,1029,1030],{"class":405,"line":491},[403,1031,1032],{"emptyLinePlaceholder":22},"\n",[403,1034,1035,1037,1039,1042,1044,1046,1048],{"class":405,"line":587},[403,1036,968],{"class":409},[403,1038,971],{"class":417},[403,1040,1041],{"class":421}," jobsQueue",[403,1043,450],{"class":425},[403,1045,979],{"class":413},[403,1047,344],{"class":432},[403,1049,984],{"class":413},[403,1051,1052,1054,1056,1058,1060,1062,1064,1066,1068,1070,1073,1075,1077],{"class":405,"line":648},[403,1053,989],{"class":425},[403,1055,992],{"class":425},[403,1057,995],{"class":413},[403,1059,344],{"class":432},[403,1061,1000],{"class":413},[403,1063,344],{"class":432},[403,1065,1005],{"class":502},[403,1067,507],{"class":413},[403,1069,542],{"class":535},[403,1071,1072],{"class":539},"JobQueue",[403,1074,542],{"class":535},[403,1076,573],{"class":432},[403,1078,488],{"class":432},[403,1080,1081,1084,1086,1090],{"class":405,"line":664},[403,1082,1083],{"class":506},"      maxConcurrency",[403,1085,392],{"class":432},[403,1087,1089],{"class":1088},"sYThS"," 5",[403,1091,801],{"class":432},[403,1093,1094,1097,1099,1102,1105,1107,1109,1112,1114,1117,1119,1122,1124,1127,1129,1132,1134,1137,1139,1142,1144,1146,1149,1151],{"class":405,"line":683},[403,1095,1096],{"class":506},"      dlq",[403,1098,392],{"class":432},[403,1100,1101],{"class":432}," {",[403,1103,1104],{"class":506}," queue",[403,1106,392],{"class":432},[403,1108,974],{"class":413},[403,1110,1111],{"class":425},"!",[403,1113,344],{"class":432},[403,1115,1116],{"class":413},"nodes",[403,1118,344],{"class":432},[403,1120,1121],{"class":413},"queue",[403,1123,344],{"class":432},[403,1125,1126],{"class":413},"queueName",[403,1128,573],{"class":432},[403,1130,1131],{"class":506}," retry",[403,1133,392],{"class":432},[403,1135,1136],{"class":1088}," 3",[403,1138,573],{"class":432},[403,1140,1141],{"class":506}," retryDelay",[403,1143,392],{"class":432},[403,1145,536],{"class":535},[403,1147,1148],{"class":539},"60 seconds",[403,1150,542],{"class":535},[403,1152,1153],{"class":432}," },\n",[403,1155,1156,1158],{"class":405,"line":758},[403,1157,867],{"class":432},[403,1159,1016],{"class":413},[403,1161,1162,1164,1166],{"class":405,"line":787},[403,1163,1021],{"class":425},[403,1165,1025],{"class":1024},[403,1167,479],{"class":432},[394,1169,1171],{"className":396,"code":1170,"filename":353,"language":398,"meta":399,"style":399},"export const emails = new sst.cloudflare.Cron(\"Cron\", {\n  schedules: [\"0 * * * *\"],\n  job: { handler: \"apps\u002Ffunctions\u002Fsrc\u002Fcron.ts\", link: [database] },\n});\n",[337,1172,1173,1208,1229,1262],{"__ignoreMap":399},[403,1174,1175,1177,1179,1182,1184,1186,1188,1190,1192,1194,1196,1198,1200,1202,1204,1206],{"class":405,"line":406},[403,1176,968],{"class":409},[403,1178,971],{"class":417},[403,1180,1181],{"class":421}," emails",[403,1183,450],{"class":425},[403,1185,992],{"class":425},[403,1187,995],{"class":413},[403,1189,344],{"class":432},[403,1191,1000],{"class":413},[403,1193,344],{"class":432},[403,1195,349],{"class":502},[403,1197,507],{"class":413},[403,1199,542],{"class":535},[403,1201,349],{"class":539},[403,1203,542],{"class":535},[403,1205,573],{"class":432},[403,1207,488],{"class":432},[403,1209,1210,1213,1215,1218,1220,1222,1224,1227],{"class":405,"line":441},[403,1211,1212],{"class":506},"  schedules",[403,1214,392],{"class":432},[403,1216,1217],{"class":413}," [",[403,1219,542],{"class":535},[403,1221,942],{"class":539},[403,1223,542],{"class":535},[403,1225,1226],{"class":413},"]",[403,1228,801],{"class":432},[403,1230,1231,1234,1236,1238,1241,1243,1245,1247,1249,1251,1254,1256,1259],{"class":405,"line":482},[403,1232,1233],{"class":506},"  job",[403,1235,392],{"class":432},[403,1237,1101],{"class":432},[403,1239,1240],{"class":506}," handler",[403,1242,392],{"class":432},[403,1244,536],{"class":535},[403,1246,936],{"class":539},[403,1248,542],{"class":535},[403,1250,573],{"class":432},[403,1252,1253],{"class":506}," link",[403,1255,392],{"class":432},[403,1257,1258],{"class":413}," [database] ",[403,1260,1261],{"class":432},"},\n",[403,1263,1264,1266,1268],{"class":405,"line":491},[403,1265,856],{"class":432},[403,1267,517],{"class":413},[403,1269,479],{"class":432},[317,1271,1273],{"id":1272},"admin-operations","Admin operations",[322,1275,1276,1277,1280,1281,1284,1285,1287,1288,1290,1291,1294],{},"Queue health, dead-letter messages, and recent cron log lines are surfaced at ",[337,1278,1279],{},"\u002Fadmin\u002Fqueue"," — see ",[1282,1283,94],"a",{"href":95},". Replaying a dead-lettered message re-sends the original ",[337,1286,928],{}," object to ",[337,1289,1072],{}," and stamps ",[337,1292,1293],{},"replayedAt"," (never cleared, so a message can only be replayed once from the console without a direct database edit).",[317,1296,1298],{"id":1297},"database-schema","Database Schema",[1300,1301,1302,1333,1352],"ul",{},[329,1303,1304,1308,1309,1311,1312,1311,1314,414,1317,1320,1321,1320,1324,1320,1326,1328,1329,1332],{},[332,1305,1306],{},[337,1307,928],{}," — ",[337,1310,570],{},", ",[337,1313,840],{},[337,1315,1316],{},"status",[337,1318,1319],{},"pending"," \u002F ",[337,1322,1323],{},"processing",[337,1325,379],{},[337,1327,387],{},"), ",[337,1330,1331],{},"teamId",", timestamps.",[329,1334,1335,1308,1339,1311,1341,1311,1343,1345,1346,1348,1349,344],{},[332,1336,1337],{},[337,1338,946],{},[337,1340,570],{},[337,1342,840],{},[337,1344,950],{}," (defaults to ",[337,1347,954],{}," for scheduled runs), ",[337,1350,1351],{},"createdAt",[329,1353,1354,1308,1358,1311,1360,1362,1363,1365,1366,1311,1369,1371,1372,1311,1374,414,1376,1379],{},[332,1355,1356],{},[337,1357,924],{},[337,1359,570],{},[337,1361,840],{}," (jsonb — the whole original ",[337,1364,928],{}," object, so it can be replayed as-is), ",[337,1367,1368],{},"error",[337,1370,1331],{}," (nullable — a malformed message can still be recorded), ",[337,1373,1351],{},[337,1375,1293],{},[337,1377,1378],{},"null"," = never replayed).",[317,1381,1383],{"id":1382},"current-scope","Current scope",[322,1385,1386,1387,1390,1391,1394],{},"Job creation isn't exposed to tenants today. ",[337,1388,1389],{},"jobsQueue.send()"," is called only from the admin replay action (",[337,1392,1393],{},"admin.queue.replayDeadLetter",") — there's no tenant-facing UI or tRPC procedure yet that enqueues a new job. The queue and cron infrastructure are real and process actual Worker-originated messages; a self-service tenant job queue was intentionally scoped out of the current release.",[1396,1397,1398],"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 .ss--_, html code.shiki .ss--_{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}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}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 .swvn1, html code.shiki .swvn1{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}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 .squCx, html code.shiki .squCx{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}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 .slF1C, html code.shiki .slF1C{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#F92672}html pre.shiki code .sKvfc, html code.shiki .sKvfc{--shiki-light:#E2931D;--shiki-light-text-decoration:inherit;--shiki-default:#6F42C1;--shiki-default-text-decoration:inherit;--shiki-dark:#B392F0;--shiki-dark-text-decoration:inherit;--shiki-sepia:#A6E22E;--shiki-sepia-text-decoration:underline}html pre.shiki code .s00De, html code.shiki .s00De{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#F8F8F2}html pre.shiki code .s-euU, html code.shiki .s-euU{--shiki-light:#90A4AE;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#F8F8F2}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 .sMTiH, html code.shiki .sMTiH{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}html pre.shiki code .sYThS, html code.shiki .sYThS{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}",{"title":399,"searchDepth":441,"depth":441,"links":1400},[1401,1406,1407,1408,1409],{"id":319,"depth":441,"text":320,"children":1402},[1403,1404,1405],{"id":369,"depth":482,"text":370},{"id":905,"depth":482,"text":906},{"id":357,"depth":482,"text":349},{"id":958,"depth":441,"text":39},{"id":1272,"depth":441,"text":1273},{"id":1297,"depth":441,"text":1298},{"id":1382,"depth":441,"text":1383},"Background job processing with Cloudflare Queues, a dead-letter queue, and hourly Cron.","md",{},{"title":118,"description":1410},"svvQ-QzFlzzrj_P_-6i6Jii8P9JiZDxCVGIaI32AkHM",1790707485288]