[{"data":1,"prerenderedAt":3045},["ShallowReactive",2],{"search-de":3,"content-de-developer\u002Fconvex":4,"surround-de-\u002Fdeveloper\u002Fconvex":3036},[],{"id":5,"title":6,"body":7,"description":13,"extension":3030,"meta":3031,"navigation":424,"path":3032,"seo":3033,"stem":3034,"__hash__":3035},"content_de\u002F3.developer\u002F4.convex.md","Convex-Backend",{"type":8,"value":9,"toc":3005},"minimark",[10,14,19,69,72,76,102,138,296,315,345,349,354,357,596,600,606,811,815,834,976,980,991,1149,1153,1172,1460,1477,1481,1485,1633,1637,1812,1816,1986,1990,1994,1997,2043,2047,2054,2219,2237,2310,2333,2337,2477,2481,2490,2590,2597,2601,2852,2856,2859,2863,2882,3001],[11,12,13],"p",{},"Owlat nutzt Convex als serverloses Backend und erhält damit Echtzeit-Subscriptions, ACID-Transaktionen und eine TypeScript-first-Entwicklung.",[15,16,18],"h2",{"id":17},"verzeichnisstruktur","Verzeichnisstruktur",[11,20,21,22,26,27,31,32,31,35,31,38,31,41,31,44,31,47,31,50,31,53,56,57,60,61,64,65,68],{},"Das Backend ist in ",[23,24,25],"strong",{},"Domänenordner"," gegliedert (",[28,29,30],"code",{},"contacts\u002F",", ",[28,33,34],{},"campaigns\u002F",[28,36,37],{},"topics\u002F",[28,39,40],{},"mail\u002F",[28,42,43],{},"automations\u002F",[28,45,46],{},"inbox\u002F",[28,48,49],{},"domains\u002F",[28,51,52],{},"delivery\u002F",[28,54,55],{},"webhooks\u002F",", …). Die Pfade der Convex-Functions spiegeln das Ordnerlayout wider: Eine Query in ",[28,58,59],{},"contacts\u002Fcontacts.ts"," wird über ",[28,62,63],{},"api.contacts.contacts.\u003CfuncName>"," erreicht. Die Konventionen für das Aufteilen von Dateien, die Benennung und den Ablageort für neuen Code stehen in ",[28,66,67],{},"apps\u002Fapi\u002Fconvex\u002FCONVENTIONS.md",".",[70,71],"convex-structure",{},[15,73,75],{"id":74},"secure-by-default-builder","Secure-by-default-Builder",[11,77,78,79,82,83,86,87,90,91,90,94,97,98,101],{},"Convex veröffentlicht ",[23,80,81],{},"jede"," nicht als ",[28,84,85],{},"internal"," markierte ",[28,88,89],{},"query"," \u002F ",[28,92,93],{},"mutation",[28,95,96],{},"action"," auf der öffentlichen Client-API des Deployments — jeder anonyme Aufrufer, der die Deployment-URL kennt, kann sie aufrufen. Zu den nackten Buildern aus ",[28,99,100],{},"_generated\u002Fserver"," zu greifen bedeutet daher „ich habe persönlich verifiziert, dass es sicher ist, dies unauthentifiziert offenzulegen“, was praktisch nie zutrifft.",[11,103,104,105,108,109,112,113,116,117,120,121,133,134,137],{},"Damit der sichere Weg der Standard ist, läuft jede öffentliche Function über einen der Wrapper in ",[28,106,107],{},"apps\u002Fapi\u002Fconvex\u002Flib\u002FauthedFunctions.ts"," statt über die rohen Builder. Der Lint ",[28,110,111],{},"apps\u002Fapi\u002Fscripts\u002Fcheck-public-functions.sh"," (in ",[28,114,115],{},"apps\u002Fapi"," an ",[28,118,119],{},"bun run lint"," angebunden) ",[23,122,123,124,90,127,90,130],{},"verbietet die nackten Builder ",[28,125,126],{},"query(",[28,128,129],{},"mutation(",[28,131,132],{},"action("," überall außer in ",[28,135,136],{},"authedFunctions.ts",", sodass ein vergessenes Gate die CI zum Scheitern bringt, statt stillschweigend einen offenen Endpunkt auszuliefern.",[139,140,141,157],"table",{},[142,143,144],"thead",{},[145,146,147,151,154],"tr",{},[148,149,150],"th",{},"Builder",[148,152,153],{},"Auth-Mindestniveau",[148,155,156],{},"Verwenden für",[158,159,160,178,196,215,231,247,261,277],"tbody",{},[145,161,162,168,175],{},[163,164,165],"td",{},[28,166,167],{},"authedQuery",[163,169,170,171,174],{},"Authentifiziertes ",[23,172,173],{},"Organisationsmitglied"," (aktive Organisation + Rolle)",[163,176,177],{},"Reaktive Lesezugriffe für angemeldete Benutzer",[145,179,180,185,189],{},[163,181,182],{},[28,183,184],{},"authedMutation",[163,186,170,187,174],{},[23,188,173],{},[163,190,191,192,195],{},"Der Standard für Schreibzugriffe; für privilegierte zusätzlich ein ",[28,193,194],{},"requirePermission(...)"," einziehen",[145,197,198,203,212],{},[163,199,200],{},[28,201,202],{},"authedAction",[163,204,170,205,207,208,211],{},[23,206,173],{}," (durchgesetzt über die interne Query ",[28,209,210],{},"auth.membership.assertOrgMember",")",[163,213,214],{},"Actions, die externe Dienste aufrufen",[145,216,217,222,228],{},[163,218,219],{},[28,220,221],{},"adminMutation",[163,223,224,225,211],{},"Owner-\u002FAdmin-Mitglied (",[28,226,227],{},"organization:manage",[163,229,230],{},"Schreibzugriffe nur für Admins — die Rollenprüfung ist eingebaut",[145,232,233,238,244],{},[163,234,235],{},[28,236,237],{},"ownerMutation",[163,239,240,241,211],{},"Owner-Rolle (",[28,242,243],{},"organization:delete",[163,245,246],{},"Destruktive Schreibzugriffe auf Organisationsebene, die selbst Admins nicht dürfen",[145,248,249,254,258],{},[163,250,251],{},[28,252,253],{},"adminQuery",[163,255,224,256,211],{},[28,257,227],{},[163,259,260],{},"Sensible Lesezugriffe (API-Keys, Webhook-Secrets, geteiltes Postfach)",[145,262,263,268,271],{},[163,264,265],{},[28,266,267],{},"authedIdentityMutation",[163,269,270],{},"Nur authentifizierte Identität (keine Organisationsmitgliedschaft)",[163,272,273,276],{},[23,274,275],{},"Ausschließlich"," der schmale Registrierungspfad vor Bestehen einer Organisation",[145,278,279,290,293],{},[163,280,281,90,284,90,287],{},[28,282,283],{},"publicQuery",[28,285,286],{},"publicMutation",[28,288,289],{},"publicAction",[163,291,292],{},"Keines — explizites Opt-out",[163,294,295],{},"Token-geschützte Links, signaturgeprüfte Webhooks, Tracking-Pixel, die Setup-Seite vor der Authentifizierung",[297,298,301],"callout",{"title":299,"type":300},"Öffentliche Functions brauchen eine Begründung","warning",[11,302,303,304,90,306,90,308,310,311,314],{},"Jede Verwendung von ",[28,305,283],{},[28,307,286],{},[28,309,289],{}," MUSS an der Aufrufstelle einen Kommentar ",[28,312,313],{},"\u002F\u002F public: \u003Creason>"," tragen — das macht „dies ist absichtlich öffentlich“ zu einer expliziten, greppbaren, per Lint-Allowlist erfassten Entscheidung statt zum Standard.",[11,316,317,318,321,322,325,326,90,328,330,331,333,334,337,338,341,342,344],{},"Ein separater Lint, ",[28,319,320],{},"apps\u002Fapi\u002Fscripts\u002Fcheck-permissions.sh",", verlangt zusätzlich, dass jede ",[23,323,324],{},"zustandsverändernde"," ",[28,327,184],{},[28,329,202],{}," eine explizite Autorisierungsentscheidung trifft (ein rollenführender Wrapper, ein ",[28,332,194],{}," im Handler oder ein Opt-out-Kommentar ",[28,335,336],{},"\u002F\u002F authz: \u003Creason>"," bzw. ",[28,339,340],{},"\u002F\u002F all-members: \u003Creason>","). Siehe ",[28,343,67],{}," § Permissions.",[15,346,348],{"id":347},"function-typen","Function-Typen",[350,351,353],"h3",{"id":352},"queries-nur-lesend-in-echtzeit","Queries (nur lesend, in Echtzeit)",[11,355,356],{},"Queries sind reaktiv — Clients erhalten automatisch Aktualisierungen, wenn sich Daten ändern.",[358,359,364],"pre",{"className":360,"code":361,"language":362,"meta":363,"style":363},"language-typescript shiki shiki-themes github-light github-dark-dimmed","import { authedQuery } from '.\u002Flib\u002FauthedFunctions';\nimport { getUserIdFromSession } from '.\u002Flib\u002FsessionOrganization';\nimport { v } from 'convex\u002Fvalues';\n\nexport const list = authedQuery({\n    args: { organizationId: v.string() },\n    handler: async (ctx, args) => {\n        await getUserIdFromSession(ctx); \u002F\u002F the wrapper already rejects anonymous callers\n        return ctx.db\n            .query('contacts')\n            .withIndex('by_org', (q) => q.eq('organizationId', args.organizationId))\n            .collect();\n    },\n});\n","typescript","",[28,365,366,389,404,419,426,449,461,494,510,519,536,573,584,590],{"__ignoreMap":363},[367,368,371,375,379,382,386],"span",{"class":369,"line":370},"line",1,[367,372,374],{"class":373},"s7YZ4","import",[367,376,378],{"class":377},"sYgZi"," { authedQuery } ",[367,380,381],{"class":373},"from",[367,383,385],{"class":384},"s-HuK"," '.\u002Flib\u002FauthedFunctions'",[367,387,388],{"class":377},";\n",[367,390,392,394,397,399,402],{"class":369,"line":391},2,[367,393,374],{"class":373},[367,395,396],{"class":377}," { getUserIdFromSession } ",[367,398,381],{"class":373},[367,400,401],{"class":384}," '.\u002Flib\u002FsessionOrganization'",[367,403,388],{"class":377},[367,405,407,409,412,414,417],{"class":369,"line":406},3,[367,408,374],{"class":373},[367,410,411],{"class":377}," { v } ",[367,413,381],{"class":373},[367,415,416],{"class":384}," 'convex\u002Fvalues'",[367,418,388],{"class":377},[367,420,422],{"class":369,"line":421},4,[367,423,425],{"emptyLinePlaceholder":424},true,"\n",[367,427,429,432,435,439,442,446],{"class":369,"line":428},5,[367,430,431],{"class":373},"export",[367,433,434],{"class":373}," const",[367,436,438],{"class":437},"sviXB"," list",[367,440,441],{"class":373}," =",[367,443,445],{"class":444},"sPO5f"," authedQuery",[367,447,448],{"class":377},"({\n",[367,450,452,455,458],{"class":369,"line":451},6,[367,453,454],{"class":377},"    args: { organizationId: v.",[367,456,457],{"class":444},"string",[367,459,460],{"class":377},"() },\n",[367,462,464,467,470,473,476,480,482,485,488,491],{"class":369,"line":463},7,[367,465,466],{"class":444},"    handler",[367,468,469],{"class":377},": ",[367,471,472],{"class":373},"async",[367,474,475],{"class":377}," (",[367,477,479],{"class":478},"stnAF","ctx",[367,481,31],{"class":377},[367,483,484],{"class":478},"args",[367,486,487],{"class":377},") ",[367,489,490],{"class":373},"=>",[367,492,493],{"class":377}," {\n",[367,495,497,500,503,506],{"class":369,"line":496},8,[367,498,499],{"class":373},"        await",[367,501,502],{"class":444}," getUserIdFromSession",[367,504,505],{"class":377},"(ctx); ",[367,507,509],{"class":508},"sDN9O","\u002F\u002F the wrapper already rejects anonymous callers\n",[367,511,513,516],{"class":369,"line":512},9,[367,514,515],{"class":373},"        return",[367,517,518],{"class":377}," ctx.db\n",[367,520,522,525,527,530,533],{"class":369,"line":521},10,[367,523,524],{"class":377},"            .",[367,526,89],{"class":444},[367,528,529],{"class":377},"(",[367,531,532],{"class":384},"'contacts'",[367,534,535],{"class":377},")\n",[367,537,539,541,544,546,549,552,555,557,559,562,565,567,570],{"class":369,"line":538},11,[367,540,524],{"class":377},[367,542,543],{"class":444},"withIndex",[367,545,529],{"class":377},[367,547,548],{"class":384},"'by_org'",[367,550,551],{"class":377},", (",[367,553,554],{"class":478},"q",[367,556,487],{"class":377},[367,558,490],{"class":373},[367,560,561],{"class":377}," q.",[367,563,564],{"class":444},"eq",[367,566,529],{"class":377},[367,568,569],{"class":384},"'organizationId'",[367,571,572],{"class":377},", args.organizationId))\n",[367,574,576,578,581],{"class":369,"line":575},12,[367,577,524],{"class":377},[367,579,580],{"class":444},"collect",[367,582,583],{"class":377},"();\n",[367,585,587],{"class":369,"line":586},13,[367,588,589],{"class":377},"    },\n",[367,591,593],{"class":369,"line":592},14,[367,594,595],{"class":377},"});\n",[350,597,599],{"id":598},"mutations-lesend-und-schreibend-transaktional","Mutations (lesend und schreibend, transaktional)",[11,601,602,603,605],{},"Mutations sind ACID — alle Änderungen gelingen oder scheitern gemeinsam. ",[28,604,184],{}," erzwingt ein authentifiziertes Organisationsmitglied, bevor der Handler läuft.",[358,607,609],{"className":360,"code":608,"language":362,"meta":363,"style":363},"import { authedMutation } from '.\u002Flib\u002FauthedFunctions';\nimport { v } from 'convex\u002Fvalues';\n\nexport const create = authedMutation({\n    args: {\n        organizationId: v.string(),\n        email: v.string(),\n        firstName: v.optional(v.string()),\n    },\n    handler: async (ctx, args) => {\n        const contactId = await ctx.db.insert('contacts', {\n            ...args,\n            source: 'api',\n            createdAt: Date.now(),\n            updatedAt: Date.now(),\n        });\n        return contactId;\n    },\n});\n",[28,610,611,624,636,640,656,661,671,680,696,700,722,748,756,767,777,787,793,801,806],{"__ignoreMap":363},[367,612,613,615,618,620,622],{"class":369,"line":370},[367,614,374],{"class":373},[367,616,617],{"class":377}," { authedMutation } ",[367,619,381],{"class":373},[367,621,385],{"class":384},[367,623,388],{"class":377},[367,625,626,628,630,632,634],{"class":369,"line":391},[367,627,374],{"class":373},[367,629,411],{"class":377},[367,631,381],{"class":373},[367,633,416],{"class":384},[367,635,388],{"class":377},[367,637,638],{"class":369,"line":406},[367,639,425],{"emptyLinePlaceholder":424},[367,641,642,644,646,649,651,654],{"class":369,"line":421},[367,643,431],{"class":373},[367,645,434],{"class":373},[367,647,648],{"class":437}," create",[367,650,441],{"class":373},[367,652,653],{"class":444}," authedMutation",[367,655,448],{"class":377},[367,657,658],{"class":369,"line":428},[367,659,660],{"class":377},"    args: {\n",[367,662,663,666,668],{"class":369,"line":451},[367,664,665],{"class":377},"        organizationId: v.",[367,667,457],{"class":444},[367,669,670],{"class":377},"(),\n",[367,672,673,676,678],{"class":369,"line":463},[367,674,675],{"class":377},"        email: v.",[367,677,457],{"class":444},[367,679,670],{"class":377},[367,681,682,685,688,691,693],{"class":369,"line":496},[367,683,684],{"class":377},"        firstName: v.",[367,686,687],{"class":444},"optional",[367,689,690],{"class":377},"(v.",[367,692,457],{"class":444},[367,694,695],{"class":377},"()),\n",[367,697,698],{"class":369,"line":512},[367,699,589],{"class":377},[367,701,702,704,706,708,710,712,714,716,718,720],{"class":369,"line":521},[367,703,466],{"class":444},[367,705,469],{"class":377},[367,707,472],{"class":373},[367,709,475],{"class":377},[367,711,479],{"class":478},[367,713,31],{"class":377},[367,715,484],{"class":478},[367,717,487],{"class":377},[367,719,490],{"class":373},[367,721,493],{"class":377},[367,723,724,727,730,732,735,738,741,743,745],{"class":369,"line":538},[367,725,726],{"class":373},"        const",[367,728,729],{"class":437}," contactId",[367,731,441],{"class":373},[367,733,734],{"class":373}," await",[367,736,737],{"class":377}," ctx.db.",[367,739,740],{"class":444},"insert",[367,742,529],{"class":377},[367,744,532],{"class":384},[367,746,747],{"class":377},", {\n",[367,749,750,753],{"class":369,"line":575},[367,751,752],{"class":373},"            ...",[367,754,755],{"class":377},"args,\n",[367,757,758,761,764],{"class":369,"line":586},[367,759,760],{"class":377},"            source: ",[367,762,763],{"class":384},"'api'",[367,765,766],{"class":377},",\n",[367,768,769,772,775],{"class":369,"line":592},[367,770,771],{"class":377},"            createdAt: Date.",[367,773,774],{"class":444},"now",[367,776,670],{"class":377},[367,778,780,783,785],{"class":369,"line":779},15,[367,781,782],{"class":377},"            updatedAt: Date.",[367,784,774],{"class":444},[367,786,670],{"class":377},[367,788,790],{"class":369,"line":789},16,[367,791,792],{"class":377},"        });\n",[367,794,796,798],{"class":369,"line":795},17,[367,797,515],{"class":373},[367,799,800],{"class":377}," contactId;\n",[367,802,804],{"class":369,"line":803},18,[367,805,589],{"class":377},[367,807,809],{"class":369,"line":808},19,[367,810,595],{"class":377},[350,812,814],{"id":813},"actions-externe-apis-seiteneffekte","Actions (externe APIs, Seiteneffekte)",[11,816,817,818,820,821,823,824,826,827,90,830,833],{},"Actions können externe Dienste aufrufen, haben aber keine Transaktionen. ",[28,819,202],{}," verlangt ein authentifiziertes ",[23,822,173],{}," (vorab durchgesetzt über die interne Query ",[28,825,210],{},"); Prüfungen einzelner Berechtigungen bzw. Rollen finden weiterhin in den Mutations\u002FQueries statt, die die Action über ",[28,828,829],{},"ctx.runQuery",[28,831,832],{},"ctx.runMutation"," aufruft.",[358,835,837],{"className":360,"code":836,"language":362,"meta":363,"style":363},"import { authedAction } from '.\u002Flib\u002FauthedFunctions';\nimport { v } from 'convex\u002Fvalues';\n\nexport const sendEmail = authedAction({\n    args: {\n        to: v.string(),\n        subject: v.string(),\n        html: v.string(),\n    },\n    handler: async (ctx, args) => {\n        const provider = getEmailProvider();\n        return provider.sendEmail(args);\n    },\n});\n",[28,838,839,852,864,868,884,888,897,906,915,919,941,955,968,972],{"__ignoreMap":363},[367,840,841,843,846,848,850],{"class":369,"line":370},[367,842,374],{"class":373},[367,844,845],{"class":377}," { authedAction } ",[367,847,381],{"class":373},[367,849,385],{"class":384},[367,851,388],{"class":377},[367,853,854,856,858,860,862],{"class":369,"line":391},[367,855,374],{"class":373},[367,857,411],{"class":377},[367,859,381],{"class":373},[367,861,416],{"class":384},[367,863,388],{"class":377},[367,865,866],{"class":369,"line":406},[367,867,425],{"emptyLinePlaceholder":424},[367,869,870,872,874,877,879,882],{"class":369,"line":421},[367,871,431],{"class":373},[367,873,434],{"class":373},[367,875,876],{"class":437}," sendEmail",[367,878,441],{"class":373},[367,880,881],{"class":444}," authedAction",[367,883,448],{"class":377},[367,885,886],{"class":369,"line":428},[367,887,660],{"class":377},[367,889,890,893,895],{"class":369,"line":451},[367,891,892],{"class":377},"        to: v.",[367,894,457],{"class":444},[367,896,670],{"class":377},[367,898,899,902,904],{"class":369,"line":463},[367,900,901],{"class":377},"        subject: v.",[367,903,457],{"class":444},[367,905,670],{"class":377},[367,907,908,911,913],{"class":369,"line":496},[367,909,910],{"class":377},"        html: v.",[367,912,457],{"class":444},[367,914,670],{"class":377},[367,916,917],{"class":369,"line":512},[367,918,589],{"class":377},[367,920,921,923,925,927,929,931,933,935,937,939],{"class":369,"line":521},[367,922,466],{"class":444},[367,924,469],{"class":377},[367,926,472],{"class":373},[367,928,475],{"class":377},[367,930,479],{"class":478},[367,932,31],{"class":377},[367,934,484],{"class":478},[367,936,487],{"class":377},[367,938,490],{"class":373},[367,940,493],{"class":377},[367,942,943,945,948,950,953],{"class":369,"line":538},[367,944,726],{"class":373},[367,946,947],{"class":437}," provider",[367,949,441],{"class":373},[367,951,952],{"class":444}," getEmailProvider",[367,954,583],{"class":377},[367,956,957,959,962,965],{"class":369,"line":575},[367,958,515],{"class":373},[367,960,961],{"class":377}," provider.",[367,963,964],{"class":444},"sendEmail",[367,966,967],{"class":377},"(args);\n",[367,969,970],{"class":369,"line":586},[367,971,589],{"class":377},[367,973,974],{"class":369,"line":592},[367,975,595],{"class":377},[350,977,979],{"id":978},"http-actions-rest-endpunkte","HTTP-Actions (REST-Endpunkte)",[11,981,982,983,986,987,990],{},"HTTP-Actions bedienen externe API-Anfragen. Sie laufen in einer separaten, separat authentifizierten Runtime und verwenden deshalb direkt den rohen Builder ",[28,984,985],{},"httpAction"," (die Secure-by-default-Wrapper greifen hier nicht). Eine HTTP-Route, die sessiongeschützte Daten anfassen muss, ruft ein ",[28,988,989],{},"internal*","-Geschwister auf.",[358,992,994],{"className":360,"code":993,"language":362,"meta":363,"style":363},"import { httpAction } from '.\u002F_generated\u002Fserver';\n\nexport const handleWebhook = httpAction(async (ctx, request) => {\n    const body = await request.json();\n    \u002F\u002F Process webhook\n    return new Response('OK', { status: 200 });\n});\n\n\u002F\u002F In http.ts\nhttp.route({\n    path: '\u002Fapi\u002Fv1\u002Fwebhook',\n    method: 'POST',\n    handler: handleWebhook,\n});\n",[28,995,996,1010,1014,1047,1067,1072,1097,1101,1105,1110,1120,1130,1140,1145],{"__ignoreMap":363},[367,997,998,1000,1003,1005,1008],{"class":369,"line":370},[367,999,374],{"class":373},[367,1001,1002],{"class":377}," { httpAction } ",[367,1004,381],{"class":373},[367,1006,1007],{"class":384}," '.\u002F_generated\u002Fserver'",[367,1009,388],{"class":377},[367,1011,1012],{"class":369,"line":391},[367,1013,425],{"emptyLinePlaceholder":424},[367,1015,1016,1018,1020,1023,1025,1028,1030,1032,1034,1036,1038,1041,1043,1045],{"class":369,"line":406},[367,1017,431],{"class":373},[367,1019,434],{"class":373},[367,1021,1022],{"class":437}," handleWebhook",[367,1024,441],{"class":373},[367,1026,1027],{"class":444}," httpAction",[367,1029,529],{"class":377},[367,1031,472],{"class":373},[367,1033,475],{"class":377},[367,1035,479],{"class":478},[367,1037,31],{"class":377},[367,1039,1040],{"class":478},"request",[367,1042,487],{"class":377},[367,1044,490],{"class":373},[367,1046,493],{"class":377},[367,1048,1049,1052,1055,1057,1059,1062,1065],{"class":369,"line":421},[367,1050,1051],{"class":373},"    const",[367,1053,1054],{"class":437}," body",[367,1056,441],{"class":373},[367,1058,734],{"class":373},[367,1060,1061],{"class":377}," request.",[367,1063,1064],{"class":444},"json",[367,1066,583],{"class":377},[367,1068,1069],{"class":369,"line":428},[367,1070,1071],{"class":508},"    \u002F\u002F Process webhook\n",[367,1073,1074,1077,1080,1083,1085,1088,1091,1094],{"class":369,"line":451},[367,1075,1076],{"class":373},"    return",[367,1078,1079],{"class":373}," new",[367,1081,1082],{"class":444}," Response",[367,1084,529],{"class":377},[367,1086,1087],{"class":384},"'OK'",[367,1089,1090],{"class":377},", { status: ",[367,1092,1093],{"class":437},"200",[367,1095,1096],{"class":377}," });\n",[367,1098,1099],{"class":369,"line":463},[367,1100,595],{"class":377},[367,1102,1103],{"class":369,"line":496},[367,1104,425],{"emptyLinePlaceholder":424},[367,1106,1107],{"class":369,"line":512},[367,1108,1109],{"class":508},"\u002F\u002F In http.ts\n",[367,1111,1112,1115,1118],{"class":369,"line":521},[367,1113,1114],{"class":377},"http.",[367,1116,1117],{"class":444},"route",[367,1119,448],{"class":377},[367,1121,1122,1125,1128],{"class":369,"line":538},[367,1123,1124],{"class":377},"    path: ",[367,1126,1127],{"class":384},"'\u002Fapi\u002Fv1\u002Fwebhook'",[367,1129,766],{"class":377},[367,1131,1132,1135,1138],{"class":369,"line":575},[367,1133,1134],{"class":377},"    method: ",[367,1136,1137],{"class":384},"'POST'",[367,1139,766],{"class":377},[367,1141,1142],{"class":369,"line":586},[367,1143,1144],{"class":377},"    handler: handleWebhook,\n",[367,1146,1147],{"class":369,"line":592},[367,1148,595],{"class":377},[15,1150,1152],{"id":1151},"sessionbasierte-functions","Sessionbasierte Functions",[11,1154,1155,1156,1159,1160,1163,1164,1167,1168,1171],{},"Owlat ist ",[23,1157,1158],{},"eine Organisation pro Deployment",": Jedes Deployment beherbergt genau eine BetterAuth-Organisation (beim Setup angelegt, und ",[28,1161,1162],{},"auth\u002Forganization\u002Fcreate"," ist deaktiviert). Da es keine Organisation gibt, die pro Anfrage übergeben werden müsste, lesen die Session-Helfer Identität und Rolle des Benutzers aus der aktiven Session statt aus einem Request-Argument. ",[28,1165,1166],{},"assertSingletonOrgInvariant"," in ",[28,1169,1170],{},"lib\u002FsessionOrganization.ts"," ist die Laufzeitabsicherung — es bestätigt, dass genau eine Organisation existiert und dass die aktive Organisation der Session dieser entspricht (nach dem ersten Treffer prozessweit gecacht).",[358,1173,1175],{"className":360,"code":1174,"language":362,"meta":363,"style":363},"import { authedQuery, authedMutation } from '.\u002Flib\u002FauthedFunctions';\nimport { getUserIdFromSession, getMutationContext } from '.\u002Flib\u002FsessionOrganization';\nimport { v } from 'convex\u002Fvalues';\n\n\u002F\u002F Query using session context — `authedQuery` rejects anonymous callers\nexport const listFromSession = authedQuery({\n    args: {},\n    handler: async (ctx) => {\n        await getUserIdFromSession(ctx);\n        \u002F\u002F Single org per deployment: no organizationId argument to thread through.\n        return ctx.db.query('contacts').collect();\n    },\n});\n\n\u002F\u002F Mutation using session context — `authedMutation` enforces an authenticated org member\nexport const createFromSession = authedMutation({\n    args: { email: v.string() },\n    handler: async (ctx, args) => {\n        const { userId, role } = await getMutationContext(ctx);\n        \u002F\u002F `userId` \u002F `role` available for auditing and permission checks.\n\n        return ctx.db.insert('contacts', {\n            email: args.email,\n            source: 'api',\n            createdAt: Date.now(),\n            updatedAt: Date.now(),\n        });\n    },\n});\n",[28,1176,1177,1190,1203,1215,1219,1224,1239,1244,1262,1271,1276,1295,1299,1303,1307,1312,1327,1336,1358,1386,1392,1397,1412,1418,1427,1436,1445,1450,1455],{"__ignoreMap":363},[367,1178,1179,1181,1184,1186,1188],{"class":369,"line":370},[367,1180,374],{"class":373},[367,1182,1183],{"class":377}," { authedQuery, authedMutation } ",[367,1185,381],{"class":373},[367,1187,385],{"class":384},[367,1189,388],{"class":377},[367,1191,1192,1194,1197,1199,1201],{"class":369,"line":391},[367,1193,374],{"class":373},[367,1195,1196],{"class":377}," { getUserIdFromSession, getMutationContext } ",[367,1198,381],{"class":373},[367,1200,401],{"class":384},[367,1202,388],{"class":377},[367,1204,1205,1207,1209,1211,1213],{"class":369,"line":406},[367,1206,374],{"class":373},[367,1208,411],{"class":377},[367,1210,381],{"class":373},[367,1212,416],{"class":384},[367,1214,388],{"class":377},[367,1216,1217],{"class":369,"line":421},[367,1218,425],{"emptyLinePlaceholder":424},[367,1220,1221],{"class":369,"line":428},[367,1222,1223],{"class":508},"\u002F\u002F Query using session context — `authedQuery` rejects anonymous callers\n",[367,1225,1226,1228,1230,1233,1235,1237],{"class":369,"line":451},[367,1227,431],{"class":373},[367,1229,434],{"class":373},[367,1231,1232],{"class":437}," listFromSession",[367,1234,441],{"class":373},[367,1236,445],{"class":444},[367,1238,448],{"class":377},[367,1240,1241],{"class":369,"line":463},[367,1242,1243],{"class":377},"    args: {},\n",[367,1245,1246,1248,1250,1252,1254,1256,1258,1260],{"class":369,"line":496},[367,1247,466],{"class":444},[367,1249,469],{"class":377},[367,1251,472],{"class":373},[367,1253,475],{"class":377},[367,1255,479],{"class":478},[367,1257,487],{"class":377},[367,1259,490],{"class":373},[367,1261,493],{"class":377},[367,1263,1264,1266,1268],{"class":369,"line":512},[367,1265,499],{"class":373},[367,1267,502],{"class":444},[367,1269,1270],{"class":377},"(ctx);\n",[367,1272,1273],{"class":369,"line":521},[367,1274,1275],{"class":508},"        \u002F\u002F Single org per deployment: no organizationId argument to thread through.\n",[367,1277,1278,1280,1282,1284,1286,1288,1291,1293],{"class":369,"line":538},[367,1279,515],{"class":373},[367,1281,737],{"class":377},[367,1283,89],{"class":444},[367,1285,529],{"class":377},[367,1287,532],{"class":384},[367,1289,1290],{"class":377},").",[367,1292,580],{"class":444},[367,1294,583],{"class":377},[367,1296,1297],{"class":369,"line":575},[367,1298,589],{"class":377},[367,1300,1301],{"class":369,"line":586},[367,1302,595],{"class":377},[367,1304,1305],{"class":369,"line":592},[367,1306,425],{"emptyLinePlaceholder":424},[367,1308,1309],{"class":369,"line":779},[367,1310,1311],{"class":508},"\u002F\u002F Mutation using session context — `authedMutation` enforces an authenticated org member\n",[367,1313,1314,1316,1318,1321,1323,1325],{"class":369,"line":789},[367,1315,431],{"class":373},[367,1317,434],{"class":373},[367,1319,1320],{"class":437}," createFromSession",[367,1322,441],{"class":373},[367,1324,653],{"class":444},[367,1326,448],{"class":377},[367,1328,1329,1332,1334],{"class":369,"line":795},[367,1330,1331],{"class":377},"    args: { email: v.",[367,1333,457],{"class":444},[367,1335,460],{"class":377},[367,1337,1338,1340,1342,1344,1346,1348,1350,1352,1354,1356],{"class":369,"line":803},[367,1339,466],{"class":444},[367,1341,469],{"class":377},[367,1343,472],{"class":373},[367,1345,475],{"class":377},[367,1347,479],{"class":478},[367,1349,31],{"class":377},[367,1351,484],{"class":478},[367,1353,487],{"class":377},[367,1355,490],{"class":373},[367,1357,493],{"class":377},[367,1359,1360,1362,1365,1368,1370,1373,1376,1379,1381,1384],{"class":369,"line":808},[367,1361,726],{"class":373},[367,1363,1364],{"class":377}," { ",[367,1366,1367],{"class":437},"userId",[367,1369,31],{"class":377},[367,1371,1372],{"class":437},"role",[367,1374,1375],{"class":377}," } ",[367,1377,1378],{"class":373},"=",[367,1380,734],{"class":373},[367,1382,1383],{"class":444}," getMutationContext",[367,1385,1270],{"class":377},[367,1387,1389],{"class":369,"line":1388},20,[367,1390,1391],{"class":508},"        \u002F\u002F `userId` \u002F `role` available for auditing and permission checks.\n",[367,1393,1395],{"class":369,"line":1394},21,[367,1396,425],{"emptyLinePlaceholder":424},[367,1398,1400,1402,1404,1406,1408,1410],{"class":369,"line":1399},22,[367,1401,515],{"class":373},[367,1403,737],{"class":377},[367,1405,740],{"class":444},[367,1407,529],{"class":377},[367,1409,532],{"class":384},[367,1411,747],{"class":377},[367,1413,1415],{"class":369,"line":1414},23,[367,1416,1417],{"class":377},"            email: args.email,\n",[367,1419,1421,1423,1425],{"class":369,"line":1420},24,[367,1422,760],{"class":377},[367,1424,763],{"class":384},[367,1426,766],{"class":377},[367,1428,1430,1432,1434],{"class":369,"line":1429},25,[367,1431,771],{"class":377},[367,1433,774],{"class":444},[367,1435,670],{"class":377},[367,1437,1439,1441,1443],{"class":369,"line":1438},26,[367,1440,782],{"class":377},[367,1442,774],{"class":444},[367,1444,670],{"class":377},[367,1446,1448],{"class":369,"line":1447},27,[367,1449,792],{"class":377},[367,1451,1453],{"class":369,"line":1452},28,[367,1454,589],{"class":377},[367,1456,1458],{"class":369,"line":1457},29,[367,1459,595],{"class":377},[11,1461,1462,1465,1466,1469,1470,1473,1474,68],{},[28,1463,1464],{},"getMutationContext(ctx)"," liefert ",[28,1467,1468],{},"{ userId, role }"," (Typ ",[28,1471,1472],{},"MutationSessionContext",") — im Rückgabewert gibt es keine ",[28,1475,1476],{},"organizationId",[15,1478,1480],{"id":1479},"datenbank-muster","Datenbank-Muster",[350,1482,1484],{"id":1483},"immer-indizes-verwenden","Immer Indizes verwenden",[358,1486,1488],{"className":360,"code":1487,"language":362,"meta":363,"style":363},"\u002F\u002F Good - uses index\nconst contacts = await ctx.db\n    .query('contacts')\n    .withIndex('by_org', (q) => q.eq('organizationId', organizationId))\n    .collect();\n\n\u002F\u002F Bad - full table scan\nconst contacts = await ctx.db\n    .query('contacts')\n    .filter((q) => q.eq(q.field('organizationId'), organizationId))\n    .collect();\n",[28,1489,1490,1495,1509,1522,1551,1559,1563,1568,1580,1592,1625],{"__ignoreMap":363},[367,1491,1492],{"class":369,"line":370},[367,1493,1494],{"class":508},"\u002F\u002F Good - uses index\n",[367,1496,1497,1500,1503,1505,1507],{"class":369,"line":391},[367,1498,1499],{"class":373},"const",[367,1501,1502],{"class":437}," contacts",[367,1504,441],{"class":373},[367,1506,734],{"class":373},[367,1508,518],{"class":377},[367,1510,1511,1514,1516,1518,1520],{"class":369,"line":406},[367,1512,1513],{"class":377},"    .",[367,1515,89],{"class":444},[367,1517,529],{"class":377},[367,1519,532],{"class":384},[367,1521,535],{"class":377},[367,1523,1524,1526,1528,1530,1532,1534,1536,1538,1540,1542,1544,1546,1548],{"class":369,"line":421},[367,1525,1513],{"class":377},[367,1527,543],{"class":444},[367,1529,529],{"class":377},[367,1531,548],{"class":384},[367,1533,551],{"class":377},[367,1535,554],{"class":478},[367,1537,487],{"class":377},[367,1539,490],{"class":373},[367,1541,561],{"class":377},[367,1543,564],{"class":444},[367,1545,529],{"class":377},[367,1547,569],{"class":384},[367,1549,1550],{"class":377},", organizationId))\n",[367,1552,1553,1555,1557],{"class":369,"line":428},[367,1554,1513],{"class":377},[367,1556,580],{"class":444},[367,1558,583],{"class":377},[367,1560,1561],{"class":369,"line":451},[367,1562,425],{"emptyLinePlaceholder":424},[367,1564,1565],{"class":369,"line":463},[367,1566,1567],{"class":508},"\u002F\u002F Bad - full table scan\n",[367,1569,1570,1572,1574,1576,1578],{"class":369,"line":496},[367,1571,1499],{"class":373},[367,1573,1502],{"class":437},[367,1575,441],{"class":373},[367,1577,734],{"class":373},[367,1579,518],{"class":377},[367,1581,1582,1584,1586,1588,1590],{"class":369,"line":512},[367,1583,1513],{"class":377},[367,1585,89],{"class":444},[367,1587,529],{"class":377},[367,1589,532],{"class":384},[367,1591,535],{"class":377},[367,1593,1594,1596,1599,1602,1604,1606,1608,1610,1612,1615,1618,1620,1622],{"class":369,"line":521},[367,1595,1513],{"class":377},[367,1597,1598],{"class":444},"filter",[367,1600,1601],{"class":377},"((",[367,1603,554],{"class":478},[367,1605,487],{"class":377},[367,1607,490],{"class":373},[367,1609,561],{"class":377},[367,1611,564],{"class":444},[367,1613,1614],{"class":377},"(q.",[367,1616,1617],{"class":444},"field",[367,1619,529],{"class":377},[367,1621,569],{"class":384},[367,1623,1624],{"class":377},"), organizationId))\n",[367,1626,1627,1629,1631],{"class":369,"line":538},[367,1628,1513],{"class":377},[367,1630,580],{"class":444},[367,1632,583],{"class":377},[350,1634,1636],{"id":1635},"schemadefinition","Schemadefinition",[358,1638,1640],{"className":360,"code":1639,"language":362,"meta":363,"style":363},"import { defineSchema, defineTable } from 'convex\u002Fserver';\nimport { v } from 'convex\u002Fvalues';\n\nexport default defineSchema({\n    contacts: defineTable({\n        organizationId: v.string(),\n        email: v.string(),\n        firstName: v.optional(v.string()),\n        lastName: v.optional(v.string()),\n        createdAt: v.number(),\n        updatedAt: v.number(),\n    })\n        .index('by_org', ['organizationId'])\n        .index('by_org_and_email', ['organizationId', 'email']),\n});\n",[28,1641,1642,1656,1668,1672,1685,1695,1703,1711,1723,1736,1746,1755,1760,1780,1805],{"__ignoreMap":363},[367,1643,1644,1646,1649,1651,1654],{"class":369,"line":370},[367,1645,374],{"class":373},[367,1647,1648],{"class":377}," { defineSchema, defineTable } ",[367,1650,381],{"class":373},[367,1652,1653],{"class":384}," 'convex\u002Fserver'",[367,1655,388],{"class":377},[367,1657,1658,1660,1662,1664,1666],{"class":369,"line":391},[367,1659,374],{"class":373},[367,1661,411],{"class":377},[367,1663,381],{"class":373},[367,1665,416],{"class":384},[367,1667,388],{"class":377},[367,1669,1670],{"class":369,"line":406},[367,1671,425],{"emptyLinePlaceholder":424},[367,1673,1674,1676,1679,1682],{"class":369,"line":421},[367,1675,431],{"class":373},[367,1677,1678],{"class":373}," default",[367,1680,1681],{"class":444}," defineSchema",[367,1683,448],{"class":1684},"s6WnN",[367,1686,1687,1690,1693],{"class":369,"line":428},[367,1688,1689],{"class":377},"    contacts: ",[367,1691,1692],{"class":444},"defineTable",[367,1694,448],{"class":377},[367,1696,1697,1699,1701],{"class":369,"line":451},[367,1698,665],{"class":377},[367,1700,457],{"class":444},[367,1702,670],{"class":377},[367,1704,1705,1707,1709],{"class":369,"line":463},[367,1706,675],{"class":377},[367,1708,457],{"class":444},[367,1710,670],{"class":377},[367,1712,1713,1715,1717,1719,1721],{"class":369,"line":496},[367,1714,684],{"class":377},[367,1716,687],{"class":444},[367,1718,690],{"class":377},[367,1720,457],{"class":444},[367,1722,695],{"class":377},[367,1724,1725,1728,1730,1732,1734],{"class":369,"line":512},[367,1726,1727],{"class":377},"        lastName: v.",[367,1729,687],{"class":444},[367,1731,690],{"class":377},[367,1733,457],{"class":444},[367,1735,695],{"class":377},[367,1737,1738,1741,1744],{"class":369,"line":521},[367,1739,1740],{"class":377},"        createdAt: v.",[367,1742,1743],{"class":444},"number",[367,1745,670],{"class":377},[367,1747,1748,1751,1753],{"class":369,"line":538},[367,1749,1750],{"class":377},"        updatedAt: v.",[367,1752,1743],{"class":444},[367,1754,670],{"class":377},[367,1756,1757],{"class":369,"line":575},[367,1758,1759],{"class":377},"    })\n",[367,1761,1762,1765,1768,1770,1772,1775,1777],{"class":369,"line":586},[367,1763,1764],{"class":377},"        .",[367,1766,1767],{"class":444},"index",[367,1769,529],{"class":377},[367,1771,548],{"class":384},[367,1773,1774],{"class":377},", [",[367,1776,569],{"class":384},[367,1778,1779],{"class":377},"])\n",[367,1781,1782,1784,1786,1788,1791,1793,1795,1797,1800,1803],{"class":369,"line":592},[367,1783,1764],{"class":377},[367,1785,1767],{"class":444},[367,1787,529],{"class":377},[367,1789,1790],{"class":384},"'by_org_and_email'",[367,1792,1774],{"class":377},[367,1794,569],{"class":384},[367,1796,31],{"class":377},[367,1798,1799],{"class":384},"'email'",[367,1801,1802],{"class":377},"])",[367,1804,766],{"class":1684},[367,1806,1807,1810],{"class":369,"line":779},[367,1808,1809],{"class":1684},"})",[367,1811,388],{"class":377},[350,1813,1815],{"id":1814},"häufige-feldmuster","Häufige Feldmuster",[358,1817,1819],{"className":360,"code":1818,"language":362,"meta":363,"style":363},"\u002F\u002F ID references\norganizationId: v.string();\ncontactId: v.id('contacts');\n\n\u002F\u002F Timestamps (always milliseconds)\ncreatedAt: v.number();\nupdatedAt: v.number();\n\n\u002F\u002F Status enums\nstatus: v.union(v.literal('draft'), v.literal('published'), v.literal('archived'));\n\n\u002F\u002F Optional with default\ndescription: v.optional(v.string());\n\n\u002F\u002F JSON stored as string\nfilters: v.string(); \u002F\u002F Parse with JSON.parse()\n",[28,1820,1821,1826,1838,1855,1859,1864,1875,1886,1890,1895,1937,1941,1946,1962,1966,1971],{"__ignoreMap":363},[367,1822,1823],{"class":369,"line":370},[367,1824,1825],{"class":508},"\u002F\u002F ID references\n",[367,1827,1828,1831,1834,1836],{"class":369,"line":391},[367,1829,1476],{"class":1830},"sOLd2",[367,1832,1833],{"class":377},": v.",[367,1835,457],{"class":444},[367,1837,583],{"class":377},[367,1839,1840,1843,1845,1848,1850,1852],{"class":369,"line":406},[367,1841,1842],{"class":1830},"contactId",[367,1844,1833],{"class":377},[367,1846,1847],{"class":444},"id",[367,1849,529],{"class":377},[367,1851,532],{"class":384},[367,1853,1854],{"class":377},");\n",[367,1856,1857],{"class":369,"line":421},[367,1858,425],{"emptyLinePlaceholder":424},[367,1860,1861],{"class":369,"line":428},[367,1862,1863],{"class":508},"\u002F\u002F Timestamps (always milliseconds)\n",[367,1865,1866,1869,1871,1873],{"class":369,"line":451},[367,1867,1868],{"class":1830},"createdAt",[367,1870,1833],{"class":377},[367,1872,1743],{"class":444},[367,1874,583],{"class":377},[367,1876,1877,1880,1882,1884],{"class":369,"line":463},[367,1878,1879],{"class":1830},"updatedAt",[367,1881,1833],{"class":377},[367,1883,1743],{"class":444},[367,1885,583],{"class":377},[367,1887,1888],{"class":369,"line":496},[367,1889,425],{"emptyLinePlaceholder":424},[367,1891,1892],{"class":369,"line":512},[367,1893,1894],{"class":508},"\u002F\u002F Status enums\n",[367,1896,1897,1900,1902,1905,1907,1910,1912,1915,1918,1920,1922,1925,1927,1929,1931,1934],{"class":369,"line":521},[367,1898,1899],{"class":1830},"status",[367,1901,1833],{"class":377},[367,1903,1904],{"class":444},"union",[367,1906,690],{"class":377},[367,1908,1909],{"class":444},"literal",[367,1911,529],{"class":377},[367,1913,1914],{"class":384},"'draft'",[367,1916,1917],{"class":377},"), v.",[367,1919,1909],{"class":444},[367,1921,529],{"class":377},[367,1923,1924],{"class":384},"'published'",[367,1926,1917],{"class":377},[367,1928,1909],{"class":444},[367,1930,529],{"class":377},[367,1932,1933],{"class":384},"'archived'",[367,1935,1936],{"class":377},"));\n",[367,1938,1939],{"class":369,"line":538},[367,1940,425],{"emptyLinePlaceholder":424},[367,1942,1943],{"class":369,"line":575},[367,1944,1945],{"class":508},"\u002F\u002F Optional with default\n",[367,1947,1948,1951,1953,1955,1957,1959],{"class":369,"line":586},[367,1949,1950],{"class":1830},"description",[367,1952,1833],{"class":377},[367,1954,687],{"class":444},[367,1956,690],{"class":377},[367,1958,457],{"class":444},[367,1960,1961],{"class":377},"());\n",[367,1963,1964],{"class":369,"line":592},[367,1965,425],{"emptyLinePlaceholder":424},[367,1967,1968],{"class":369,"line":779},[367,1969,1970],{"class":508},"\u002F\u002F JSON stored as string\n",[367,1972,1973,1976,1978,1980,1983],{"class":369,"line":789},[367,1974,1975],{"class":1830},"filters",[367,1977,1833],{"class":377},[367,1979,457],{"class":444},[367,1981,1982],{"class":377},"(); ",[367,1984,1985],{"class":508},"\u002F\u002F Parse with JSON.parse()\n",[15,1987,1989],{"id":1988},"berechtigungssystem","Berechtigungssystem",[350,1991,1993],{"id":1992},"rollen","Rollen",[1995,1996],"roles-table",{},[11,1998,1999,2000,2003,2004,31,2007,31,2010,31,2013,31,2016,31,2019,31,2022,31,2025,31,2027,31,2030,31,2032,2035,2036,2038,2039,2042],{},"Berechtigungen sind eine typisierte ",[28,2001,2002],{},"Permission","-Union (",[28,2005,2006],{},"campaigns:send",[28,2008,2009],{},"campaigns:manage",[28,2011,2012],{},"contacts:manage",[28,2014,2015],{},"topics:manage",[28,2017,2018],{},"segments:manage",[28,2020,2021],{},"templates:manage",[28,2023,2024],{},"automations:manage",[28,2026,227],{},[28,2028,2029],{},"settings:manage",[28,2031,243],{},[28,2033,2034],{},"chat:participate",", …), die in ",[28,2037,1170],{}," auf Rollen abgebildet wird. Prüfen Sie eine Fähigkeit mit ",[28,2040,2041],{},"hasPermission(role, '\u003Cscope>:\u003Cverb>')",", statt Rollen direkt zu testen.",[350,2044,2046],{"id":2045},"berechtigungsprüfungen","Berechtigungsprüfungen",[11,2048,2049,2050,2053],{},"Nehmen Sie für einen privilegierten Schreibzugriff den Mutation-Kontext und stellen Sie die konkrete Fähigkeit mit ",[28,2051,2052],{},"requirePermission(hasPermission(role, '\u003Cscope>:\u003Cverb>'), message)"," sicher:",[358,2055,2057],{"className":360,"code":2056,"language":362,"meta":363,"style":363},"import { authedMutation } from '.\u002Flib\u002FauthedFunctions';\nimport { getMutationContext, requirePermission, hasPermission } from '.\u002Flib\u002FsessionOrganization';\n\nexport const deleteContact = authedMutation({\n    args: { contactId: v.id('contacts') },\n    handler: async (ctx, args) => {\n        const { role } = await getMutationContext(ctx);\n\n        requirePermission(\n            hasPermission(role, 'contacts:manage'),\n            'Only owners and admins can delete contacts',\n        );\n\n        await ctx.db.delete(args.contactId);\n    },\n});\n",[28,2058,2059,2071,2084,2088,2103,2117,2139,2157,2161,2169,2183,2190,2195,2199,2211,2215],{"__ignoreMap":363},[367,2060,2061,2063,2065,2067,2069],{"class":369,"line":370},[367,2062,374],{"class":373},[367,2064,617],{"class":377},[367,2066,381],{"class":373},[367,2068,385],{"class":384},[367,2070,388],{"class":377},[367,2072,2073,2075,2078,2080,2082],{"class":369,"line":391},[367,2074,374],{"class":373},[367,2076,2077],{"class":377}," { getMutationContext, requirePermission, hasPermission } ",[367,2079,381],{"class":373},[367,2081,401],{"class":384},[367,2083,388],{"class":377},[367,2085,2086],{"class":369,"line":406},[367,2087,425],{"emptyLinePlaceholder":424},[367,2089,2090,2092,2094,2097,2099,2101],{"class":369,"line":421},[367,2091,431],{"class":373},[367,2093,434],{"class":373},[367,2095,2096],{"class":437}," deleteContact",[367,2098,441],{"class":373},[367,2100,653],{"class":444},[367,2102,448],{"class":377},[367,2104,2105,2108,2110,2112,2114],{"class":369,"line":428},[367,2106,2107],{"class":377},"    args: { contactId: v.",[367,2109,1847],{"class":444},[367,2111,529],{"class":377},[367,2113,532],{"class":384},[367,2115,2116],{"class":377},") },\n",[367,2118,2119,2121,2123,2125,2127,2129,2131,2133,2135,2137],{"class":369,"line":451},[367,2120,466],{"class":444},[367,2122,469],{"class":377},[367,2124,472],{"class":373},[367,2126,475],{"class":377},[367,2128,479],{"class":478},[367,2130,31],{"class":377},[367,2132,484],{"class":478},[367,2134,487],{"class":377},[367,2136,490],{"class":373},[367,2138,493],{"class":377},[367,2140,2141,2143,2145,2147,2149,2151,2153,2155],{"class":369,"line":463},[367,2142,726],{"class":373},[367,2144,1364],{"class":377},[367,2146,1372],{"class":437},[367,2148,1375],{"class":377},[367,2150,1378],{"class":373},[367,2152,734],{"class":373},[367,2154,1383],{"class":444},[367,2156,1270],{"class":377},[367,2158,2159],{"class":369,"line":496},[367,2160,425],{"emptyLinePlaceholder":424},[367,2162,2163,2166],{"class":369,"line":512},[367,2164,2165],{"class":444},"        requirePermission",[367,2167,2168],{"class":377},"(\n",[367,2170,2171,2174,2177,2180],{"class":369,"line":521},[367,2172,2173],{"class":444},"            hasPermission",[367,2175,2176],{"class":377},"(role, ",[367,2178,2179],{"class":384},"'contacts:manage'",[367,2181,2182],{"class":377},"),\n",[367,2184,2185,2188],{"class":369,"line":538},[367,2186,2187],{"class":384},"            'Only owners and admins can delete contacts'",[367,2189,766],{"class":377},[367,2191,2192],{"class":369,"line":575},[367,2193,2194],{"class":377},"        );\n",[367,2196,2197],{"class":369,"line":586},[367,2198,425],{"emptyLinePlaceholder":424},[367,2200,2201,2203,2205,2208],{"class":369,"line":592},[367,2202,499],{"class":373},[367,2204,737],{"class":377},[367,2206,2207],{"class":444},"delete",[367,2209,2210],{"class":377},"(args.contactId);\n",[367,2212,2213],{"class":369,"line":779},[367,2214,589],{"class":377},[367,2216,2217],{"class":369,"line":789},[367,2218,595],{"class":377},[11,2220,2221,2222,2226,2227,2229,2230,2232,2233,2236],{},"Für Schreibzugriffe, die ",[2223,2224,2225],"em",{},"immer"," nur Admins erlaubt sind, ist ",[28,2228,221],{}," vorzuziehen — es backt die ",[28,2231,227],{},"-Prüfung in den Wrapper ein, sodass kein ",[28,2234,2235],{},"requirePermission"," im Handler nötig ist:",[358,2238,2240],{"className":360,"code":2239,"language":362,"meta":363,"style":363},"import { adminMutation } from '.\u002Flib\u002FauthedFunctions';\n\nexport const purgeContacts = adminMutation({\n    args: {},\n    handler: async (ctx) => {\n        \u002F\u002F Floor is \"owner\u002Fadmin\"; reaching the body means the check passed.\n    },\n});\n",[28,2241,2242,2255,2259,2275,2279,2297,2302,2306],{"__ignoreMap":363},[367,2243,2244,2246,2249,2251,2253],{"class":369,"line":370},[367,2245,374],{"class":373},[367,2247,2248],{"class":377}," { adminMutation } ",[367,2250,381],{"class":373},[367,2252,385],{"class":384},[367,2254,388],{"class":377},[367,2256,2257],{"class":369,"line":391},[367,2258,425],{"emptyLinePlaceholder":424},[367,2260,2261,2263,2265,2268,2270,2273],{"class":369,"line":406},[367,2262,431],{"class":373},[367,2264,434],{"class":373},[367,2266,2267],{"class":437}," purgeContacts",[367,2269,441],{"class":373},[367,2271,2272],{"class":444}," adminMutation",[367,2274,448],{"class":377},[367,2276,2277],{"class":369,"line":421},[367,2278,1243],{"class":377},[367,2280,2281,2283,2285,2287,2289,2291,2293,2295],{"class":369,"line":428},[367,2282,466],{"class":444},[367,2284,469],{"class":377},[367,2286,472],{"class":373},[367,2288,475],{"class":377},[367,2290,479],{"class":478},[367,2292,487],{"class":377},[367,2294,490],{"class":373},[367,2296,493],{"class":377},[367,2298,2299],{"class":369,"line":451},[367,2300,2301],{"class":508},"        \u002F\u002F Floor is \"owner\u002Fadmin\"; reaching the body means the check passed.\n",[367,2303,2304],{"class":369,"line":463},[367,2305,589],{"class":377},[367,2307,2308],{"class":369,"line":496},[367,2309,595],{"class":377},[11,2311,2312,2315,2316,2319,2320,2323,2324,90,2327,2330,2331,68],{},[28,2313,2314],{},"requirePermission(hasPermission, message)"," nimmt einen ",[23,2317,2318],{},"Boolean"," entgegen (das Ergebnis von ",[28,2321,2322],{},"hasPermission(...)",") sowie eine optionale Nachricht — nicht eine Rolle und einen Berechtigungsstring. Die alten Helfer ",[28,2325,2326],{},"isAdminRole",[28,2328,2329],{},"isOwnerRole"," wurden entfernt; verwenden Sie ",[28,2332,2041],{},[15,2334,2336],{"id":2335},"fehlerbehandlung","Fehlerbehandlung",[358,2338,2340],{"className":360,"code":2339,"language":362,"meta":363,"style":363},"\u002F\u002F Throw descriptive errors\nif (!contact) {\n    throw new Error('Contact not found');\n}\n\nif (!domain.verified) {\n    throw new Error(`Cannot send email: domain ${domain.name} is not verified`);\n}\n\n\u002F\u002F Validation errors\nif (!args.email.includes('@')) {\n    throw new Error('Invalid email address');\n}\n",[28,2341,2342,2347,2360,2377,2382,2386,2397,2423,2427,2431,2436,2458,2473],{"__ignoreMap":363},[367,2343,2344],{"class":369,"line":370},[367,2345,2346],{"class":508},"\u002F\u002F Throw descriptive errors\n",[367,2348,2349,2352,2354,2357],{"class":369,"line":391},[367,2350,2351],{"class":373},"if",[367,2353,475],{"class":377},[367,2355,2356],{"class":373},"!",[367,2358,2359],{"class":377},"contact) {\n",[367,2361,2362,2365,2367,2370,2372,2375],{"class":369,"line":406},[367,2363,2364],{"class":373},"    throw",[367,2366,1079],{"class":373},[367,2368,2369],{"class":444}," Error",[367,2371,529],{"class":377},[367,2373,2374],{"class":384},"'Contact not found'",[367,2376,1854],{"class":377},[367,2378,2379],{"class":369,"line":421},[367,2380,2381],{"class":377},"}\n",[367,2383,2384],{"class":369,"line":428},[367,2385,425],{"emptyLinePlaceholder":424},[367,2387,2388,2390,2392,2394],{"class":369,"line":451},[367,2389,2351],{"class":373},[367,2391,475],{"class":377},[367,2393,2356],{"class":373},[367,2395,2396],{"class":377},"domain.verified) {\n",[367,2398,2399,2401,2403,2405,2407,2410,2413,2415,2418,2421],{"class":369,"line":463},[367,2400,2364],{"class":373},[367,2402,1079],{"class":373},[367,2404,2369],{"class":444},[367,2406,529],{"class":377},[367,2408,2409],{"class":384},"`Cannot send email: domain ${",[367,2411,2412],{"class":377},"domain",[367,2414,68],{"class":384},[367,2416,2417],{"class":377},"name",[367,2419,2420],{"class":384},"} is not verified`",[367,2422,1854],{"class":377},[367,2424,2425],{"class":369,"line":496},[367,2426,2381],{"class":377},[367,2428,2429],{"class":369,"line":512},[367,2430,425],{"emptyLinePlaceholder":424},[367,2432,2433],{"class":369,"line":521},[367,2434,2435],{"class":508},"\u002F\u002F Validation errors\n",[367,2437,2438,2440,2442,2444,2447,2450,2452,2455],{"class":369,"line":538},[367,2439,2351],{"class":373},[367,2441,475],{"class":377},[367,2443,2356],{"class":373},[367,2445,2446],{"class":377},"args.email.",[367,2448,2449],{"class":444},"includes",[367,2451,529],{"class":377},[367,2453,2454],{"class":384},"'@'",[367,2456,2457],{"class":377},")) {\n",[367,2459,2460,2462,2464,2466,2468,2471],{"class":369,"line":575},[367,2461,2364],{"class":373},[367,2463,1079],{"class":373},[367,2465,2369],{"class":444},[367,2467,529],{"class":377},[367,2469,2470],{"class":384},"'Invalid email address'",[367,2472,1854],{"class":377},[367,2474,2475],{"class":369,"line":586},[367,2476,2381],{"class":377},[15,2478,2480],{"id":2479},"scheduler-hintergrundjobs","Scheduler (Hintergrundjobs)",[11,2482,2483,2484,2486,2487,1290],{},"Interne Ziele werden über das generierte ",[28,2485,85],{},"-Objekt adressiert, das das Layout der Domänenordner widerspiegelt (",[28,2488,2489],{},"internal.\u003Cdomain>.\u003Cfile>.\u003Cfunc>",[358,2491,2493],{"className":360,"code":2492,"language":362,"meta":363,"style":363},"import { internal } from '.\u002F_generated\u002Fapi';\n\n\u002F\u002F Schedule the next automation step (e.g. after a delay step).\n\u002F\u002F Real target: apps\u002Fapi\u002Fconvex\u002Fautomations\u002FstepWalker.ts → executeStep\nawait ctx.scheduler.runAfter(delayMs, internal.automations.stepWalker.executeStep, {\n    automationRunId,\n    stepRunId,\n});\n\n\u002F\u002F Kick off background work immediately (fire-and-forget).\n\u002F\u002F Real target: apps\u002Fapi\u002Fconvex\u002Fsegments.ts → refreshSingleSegmentCount\nawait ctx.scheduler.runAfter(0, internal.segments.refreshSingleSegmentCount, {\n    segmentId,\n});\n",[28,2494,2495,2509,2513,2518,2523,2537,2542,2547,2551,2555,2560,2565,2581,2586],{"__ignoreMap":363},[367,2496,2497,2499,2502,2504,2507],{"class":369,"line":370},[367,2498,374],{"class":373},[367,2500,2501],{"class":377}," { internal } ",[367,2503,381],{"class":373},[367,2505,2506],{"class":384}," '.\u002F_generated\u002Fapi'",[367,2508,388],{"class":377},[367,2510,2511],{"class":369,"line":391},[367,2512,425],{"emptyLinePlaceholder":424},[367,2514,2515],{"class":369,"line":406},[367,2516,2517],{"class":508},"\u002F\u002F Schedule the next automation step (e.g. after a delay step).\n",[367,2519,2520],{"class":369,"line":421},[367,2521,2522],{"class":508},"\u002F\u002F Real target: apps\u002Fapi\u002Fconvex\u002Fautomations\u002FstepWalker.ts → executeStep\n",[367,2524,2525,2528,2531,2534],{"class":369,"line":428},[367,2526,2527],{"class":373},"await",[367,2529,2530],{"class":377}," ctx.scheduler.",[367,2532,2533],{"class":444},"runAfter",[367,2535,2536],{"class":377},"(delayMs, internal.automations.stepWalker.executeStep, {\n",[367,2538,2539],{"class":369,"line":451},[367,2540,2541],{"class":377},"    automationRunId,\n",[367,2543,2544],{"class":369,"line":463},[367,2545,2546],{"class":377},"    stepRunId,\n",[367,2548,2549],{"class":369,"line":496},[367,2550,595],{"class":377},[367,2552,2553],{"class":369,"line":512},[367,2554,425],{"emptyLinePlaceholder":424},[367,2556,2557],{"class":369,"line":521},[367,2558,2559],{"class":508},"\u002F\u002F Kick off background work immediately (fire-and-forget).\n",[367,2561,2562],{"class":369,"line":538},[367,2563,2564],{"class":508},"\u002F\u002F Real target: apps\u002Fapi\u002Fconvex\u002Fsegments.ts → refreshSingleSegmentCount\n",[367,2566,2567,2569,2571,2573,2575,2578],{"class":369,"line":575},[367,2568,2527],{"class":373},[367,2570,2530],{"class":377},[367,2572,2533],{"class":444},[367,2574,529],{"class":377},[367,2576,2577],{"class":437},"0",[367,2579,2580],{"class":377},", internal.segments.refreshSingleSegmentCount, {\n",[367,2582,2583],{"class":369,"line":586},[367,2584,2585],{"class":377},"    segmentId,\n",[367,2587,2588],{"class":369,"line":592},[367,2589,595],{"class":377},[11,2591,2592,2593,2596],{},"Registrieren Sie wiederkehrende Jobs in ",[28,2594,2595],{},"crons.ts",", statt sie sich selbst neu planen zu lassen.",[15,2598,2600],{"id":2599},"dateispeicher","Dateispeicher",[358,2602,2604],{"className":360,"code":2603,"language":362,"meta":363,"style":363},"import { authedQuery, authedMutation } from '.\u002Flib\u002FauthedFunctions';\nimport { v } from 'convex\u002Fvalues';\n\n\u002F\u002F Generate upload URL (for client upload)\nexport const generateUploadUrl = authedMutation({\n    args: {},\n    handler: async (ctx) => {\n        return ctx.storage.generateUploadUrl();\n    },\n});\n\n\u002F\u002F Get file URL\nexport const getUrl = authedQuery({\n    args: { storageId: v.id('_storage') },\n    handler: async (ctx, args) => {\n        return ctx.storage.getUrl(args.storageId);\n    },\n});\n\n\u002F\u002F Delete file\nexport const deleteFile = authedMutation({\n    args: { storageId: v.id('_storage') },\n    handler: async (ctx, args) => {\n        await ctx.storage.delete(args.storageId);\n    },\n});\n",[28,2605,2606,2618,2630,2634,2639,2654,2658,2676,2688,2692,2696,2700,2705,2720,2734,2756,2768,2772,2776,2780,2785,2800,2812,2834,2844,2848],{"__ignoreMap":363},[367,2607,2608,2610,2612,2614,2616],{"class":369,"line":370},[367,2609,374],{"class":373},[367,2611,1183],{"class":377},[367,2613,381],{"class":373},[367,2615,385],{"class":384},[367,2617,388],{"class":377},[367,2619,2620,2622,2624,2626,2628],{"class":369,"line":391},[367,2621,374],{"class":373},[367,2623,411],{"class":377},[367,2625,381],{"class":373},[367,2627,416],{"class":384},[367,2629,388],{"class":377},[367,2631,2632],{"class":369,"line":406},[367,2633,425],{"emptyLinePlaceholder":424},[367,2635,2636],{"class":369,"line":421},[367,2637,2638],{"class":508},"\u002F\u002F Generate upload URL (for client upload)\n",[367,2640,2641,2643,2645,2648,2650,2652],{"class":369,"line":428},[367,2642,431],{"class":373},[367,2644,434],{"class":373},[367,2646,2647],{"class":437}," generateUploadUrl",[367,2649,441],{"class":373},[367,2651,653],{"class":444},[367,2653,448],{"class":377},[367,2655,2656],{"class":369,"line":451},[367,2657,1243],{"class":377},[367,2659,2660,2662,2664,2666,2668,2670,2672,2674],{"class":369,"line":463},[367,2661,466],{"class":444},[367,2663,469],{"class":377},[367,2665,472],{"class":373},[367,2667,475],{"class":377},[367,2669,479],{"class":478},[367,2671,487],{"class":377},[367,2673,490],{"class":373},[367,2675,493],{"class":377},[367,2677,2678,2680,2683,2686],{"class":369,"line":496},[367,2679,515],{"class":373},[367,2681,2682],{"class":377}," ctx.storage.",[367,2684,2685],{"class":444},"generateUploadUrl",[367,2687,583],{"class":377},[367,2689,2690],{"class":369,"line":512},[367,2691,589],{"class":377},[367,2693,2694],{"class":369,"line":521},[367,2695,595],{"class":377},[367,2697,2698],{"class":369,"line":538},[367,2699,425],{"emptyLinePlaceholder":424},[367,2701,2702],{"class":369,"line":575},[367,2703,2704],{"class":508},"\u002F\u002F Get file URL\n",[367,2706,2707,2709,2711,2714,2716,2718],{"class":369,"line":586},[367,2708,431],{"class":373},[367,2710,434],{"class":373},[367,2712,2713],{"class":437}," getUrl",[367,2715,441],{"class":373},[367,2717,445],{"class":444},[367,2719,448],{"class":377},[367,2721,2722,2725,2727,2729,2732],{"class":369,"line":592},[367,2723,2724],{"class":377},"    args: { storageId: v.",[367,2726,1847],{"class":444},[367,2728,529],{"class":377},[367,2730,2731],{"class":384},"'_storage'",[367,2733,2116],{"class":377},[367,2735,2736,2738,2740,2742,2744,2746,2748,2750,2752,2754],{"class":369,"line":779},[367,2737,466],{"class":444},[367,2739,469],{"class":377},[367,2741,472],{"class":373},[367,2743,475],{"class":377},[367,2745,479],{"class":478},[367,2747,31],{"class":377},[367,2749,484],{"class":478},[367,2751,487],{"class":377},[367,2753,490],{"class":373},[367,2755,493],{"class":377},[367,2757,2758,2760,2762,2765],{"class":369,"line":789},[367,2759,515],{"class":373},[367,2761,2682],{"class":377},[367,2763,2764],{"class":444},"getUrl",[367,2766,2767],{"class":377},"(args.storageId);\n",[367,2769,2770],{"class":369,"line":795},[367,2771,589],{"class":377},[367,2773,2774],{"class":369,"line":803},[367,2775,595],{"class":377},[367,2777,2778],{"class":369,"line":808},[367,2779,425],{"emptyLinePlaceholder":424},[367,2781,2782],{"class":369,"line":1388},[367,2783,2784],{"class":508},"\u002F\u002F Delete file\n",[367,2786,2787,2789,2791,2794,2796,2798],{"class":369,"line":1394},[367,2788,431],{"class":373},[367,2790,434],{"class":373},[367,2792,2793],{"class":437}," deleteFile",[367,2795,441],{"class":373},[367,2797,653],{"class":444},[367,2799,448],{"class":377},[367,2801,2802,2804,2806,2808,2810],{"class":369,"line":1399},[367,2803,2724],{"class":377},[367,2805,1847],{"class":444},[367,2807,529],{"class":377},[367,2809,2731],{"class":384},[367,2811,2116],{"class":377},[367,2813,2814,2816,2818,2820,2822,2824,2826,2828,2830,2832],{"class":369,"line":1414},[367,2815,466],{"class":444},[367,2817,469],{"class":377},[367,2819,472],{"class":373},[367,2821,475],{"class":377},[367,2823,479],{"class":478},[367,2825,31],{"class":377},[367,2827,484],{"class":478},[367,2829,487],{"class":377},[367,2831,490],{"class":373},[367,2833,493],{"class":377},[367,2835,2836,2838,2840,2842],{"class":369,"line":1420},[367,2837,499],{"class":373},[367,2839,2682],{"class":377},[367,2841,2207],{"class":444},[367,2843,2767],{"class":377},[367,2845,2846],{"class":369,"line":1429},[367,2847,589],{"class":377},[367,2849,2850],{"class":369,"line":1438},[367,2851,595],{"class":377},[15,2853,2855],{"id":2854},"wichtige-api-dateien","Wichtige API-Dateien",[2857,2858],"convex-api-files",{},[15,2860,2862],{"id":2861},"frontend-integration","Frontend-Integration",[11,2864,2865,2866,2869,2870,2873,2874,2877,2878,2881],{},"In ",[28,2867,2868],{},"apps\u002Fweb"," laufen reaktive Lesezugriffe über das Composable ",[28,2871,2872],{},"useConvexQuery"," und Schreibzugriffe über ",[28,2875,2876],{},"useBackendOperation"," (beide werden automatisch importiert). Die Function-Referenzen stammen aus dem generierten ",[28,2879,2880],{},"api","-Objekt und folgen dem Pfad der Domänenordner.",[358,2883,2885],{"className":360,"code":2884,"language":362,"meta":363,"style":363},"\u002F\u002F In a Vue component or composable\nimport { api } from '@owlat\u002Fapi';\n\n\u002F\u002F Query (reactive) — returns { data, error, isLoading } refs\nconst { data: contacts } = useConvexQuery(api.contacts.contacts.list, {});\n\n\u002F\u002F Mutation — useBackendOperation normalizes errors and returns a `run()`\nconst { run: createContact } = useBackendOperation(api.contacts.contacts.create, {\n    label: 'Create contact',\n});\nawait createContact({ email: 'user@example.com' });\n",[28,2886,2887,2892,2906,2910,2915,2939,2943,2948,2972,2982,2986],{"__ignoreMap":363},[367,2888,2889],{"class":369,"line":370},[367,2890,2891],{"class":508},"\u002F\u002F In a Vue component or composable\n",[367,2893,2894,2896,2899,2901,2904],{"class":369,"line":391},[367,2895,374],{"class":373},[367,2897,2898],{"class":377}," { api } ",[367,2900,381],{"class":373},[367,2902,2903],{"class":384}," '@owlat\u002Fapi'",[367,2905,388],{"class":377},[367,2907,2908],{"class":369,"line":406},[367,2909,425],{"emptyLinePlaceholder":424},[367,2911,2912],{"class":369,"line":421},[367,2913,2914],{"class":508},"\u002F\u002F Query (reactive) — returns { data, error, isLoading } refs\n",[367,2916,2917,2919,2921,2924,2926,2929,2931,2933,2936],{"class":369,"line":428},[367,2918,1499],{"class":373},[367,2920,1364],{"class":377},[367,2922,2923],{"class":478},"data",[367,2925,469],{"class":377},[367,2927,2928],{"class":437},"contacts",[367,2930,1375],{"class":377},[367,2932,1378],{"class":373},[367,2934,2935],{"class":444}," useConvexQuery",[367,2937,2938],{"class":377},"(api.contacts.contacts.list, {});\n",[367,2940,2941],{"class":369,"line":451},[367,2942,425],{"emptyLinePlaceholder":424},[367,2944,2945],{"class":369,"line":463},[367,2946,2947],{"class":508},"\u002F\u002F Mutation — useBackendOperation normalizes errors and returns a `run()`\n",[367,2949,2950,2952,2954,2957,2959,2962,2964,2966,2969],{"class":369,"line":496},[367,2951,1499],{"class":373},[367,2953,1364],{"class":377},[367,2955,2956],{"class":478},"run",[367,2958,469],{"class":377},[367,2960,2961],{"class":437},"createContact",[367,2963,1375],{"class":377},[367,2965,1378],{"class":373},[367,2967,2968],{"class":444}," useBackendOperation",[367,2970,2971],{"class":377},"(api.contacts.contacts.create, {\n",[367,2973,2974,2977,2980],{"class":369,"line":512},[367,2975,2976],{"class":377},"    label: ",[367,2978,2979],{"class":384},"'Create contact'",[367,2981,766],{"class":377},[367,2983,2984],{"class":369,"line":521},[367,2985,595],{"class":377},[367,2987,2988,2990,2993,2996,2999],{"class":369,"line":538},[367,2989,2527],{"class":373},[367,2991,2992],{"class":444}," createContact",[367,2994,2995],{"class":377},"({ email: ",[367,2997,2998],{"class":384},"'user@example.com'",[367,3000,1096],{"class":377},[3002,3003,3004],"style",{},"html pre.shiki code .s7YZ4, html code.shiki .s7YZ4{--shiki-default:#D73A49;--shiki-dark:#F47067}html pre.shiki code .sYgZi, html code.shiki .sYgZi{--shiki-default:#24292E;--shiki-dark:#ADBAC7}html pre.shiki code .s-HuK, html code.shiki .s-HuK{--shiki-default:#032F62;--shiki-dark:#96D0FF}html pre.shiki code .sviXB, html code.shiki .sviXB{--shiki-default:#005CC5;--shiki-dark:#6CB6FF}html pre.shiki code .sPO5f, html code.shiki .sPO5f{--shiki-default:#6F42C1;--shiki-dark:#DCBDFB}html pre.shiki code .stnAF, html code.shiki .stnAF{--shiki-default:#E36209;--shiki-dark:#F69D50}html pre.shiki code .sDN9O, html code.shiki .sDN9O{--shiki-default:#6A737D;--shiki-dark:#768390}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 .s6WnN, html code.shiki .s6WnN{--shiki-default:#24292E;--shiki-dark:#F69D50}html pre.shiki code .sOLd2, html code.shiki .sOLd2{--shiki-default:#6F42C1;--shiki-dark:#F69D50}",{"title":363,"searchDepth":391,"depth":391,"links":3006},[3007,3008,3009,3015,3016,3021,3025,3026,3027,3028,3029],{"id":17,"depth":391,"text":18},{"id":74,"depth":391,"text":75},{"id":347,"depth":391,"text":348,"children":3010},[3011,3012,3013,3014],{"id":352,"depth":406,"text":353},{"id":598,"depth":406,"text":599},{"id":813,"depth":406,"text":814},{"id":978,"depth":406,"text":979},{"id":1151,"depth":391,"text":1152},{"id":1479,"depth":391,"text":1480,"children":3017},[3018,3019,3020],{"id":1483,"depth":406,"text":1484},{"id":1635,"depth":406,"text":1636},{"id":1814,"depth":406,"text":1815},{"id":1988,"depth":391,"text":1989,"children":3022},[3023,3024],{"id":1992,"depth":406,"text":1993},{"id":2045,"depth":406,"text":2046},{"id":2335,"depth":391,"text":2336},{"id":2479,"depth":391,"text":2480},{"id":2599,"depth":391,"text":2600},{"id":2854,"depth":391,"text":2855},{"id":2861,"depth":391,"text":2862},"md",{},"\u002Fdeveloper\u002Fconvex",{"title":6,"description":13},"3.developer\u002F4.convex","OGUpbZTU7rrbeyZmKDHg6GNBUGv2gz7Klp1qCKfd6WE",[3037,3041],{"title":3038,"path":3039,"stem":3040,"children":-1},"Wiederherstellung nach DNS-Blocklisten-Eintrag","\u002Fdeveloper\u002Fdnsbl-delisting","3.developer\u002F38.dnsbl-delisting",{"title":3042,"path":3043,"stem":3044,"children":-1},"Plugin-Plattform","\u002Fdeveloper\u002Fplugin-platform","3.developer\u002F40.plugin-platform",1786915107420]