[{"data":1,"prerenderedAt":2328},["ShallowReactive",2],{"home-intro":3,"home-projects":48,"latest-posts":100,"latest-notes":733},{"home":4,"career":34,"asOf":47},{"id":5,"title":6,"body":7,"description":24,"employerPrefix":25,"extension":26,"meta":27,"navigation":28,"path":29,"rolePrefix":30,"seo":31,"stem":32,"__hash__":33},"home\u002Findex.md","Hi, I'm Joël Kuijper.",{"type":8,"value":9,"toc":20},"minimark",[10],[11,12,13,19],"p",{},[14,15],"current-role",{":employer-prefix":16,":role-prefix":17,":selection":18},"employerPrefix","rolePrefix","currentSelection"," I write about what I build and learn.",{"title":21,"searchDepth":22,"depth":22,"links":23},"",2,[],"I write about what I build and learn.","at the","md",{},true,"\u002F","I'm a",{"title":6,"description":24},"index","jUCw7s3kJ4-lmof1ELcrNXPJf9h_4joc5mFK92_fmwk",{"id":35,"extension":36,"meta":37,"roles":38,"stem":45,"__hash__":46},"career\u002Fcareer.yml","yml",{},[39],{"id":40,"employer":41,"title":43,"start":44},"eo-full-stack-developer-2026",{"name":42},"Evangelische Omroep","Full-stack Developer","2026-02","career","8ntVGcqZwr7mNHK10pD8qRE636U_Wh4WennYAN95TAA","2026-11-02",[49,73],{"id":50,"title":51,"body":52,"coverImage":59,"description":60,"draft":61,"extension":26,"link":62,"meta":63,"navigation":28,"order":64,"path":65,"seo":66,"stem":67,"techStack":68,"__hash__":72},"projects\u002Fprojects\u002Fpowerpoint-inverter.md","PowerPoint Inverter",{"type":8,"value":53,"toc":57},[54],[11,55,56],{},"A small command-line utility built to invert the colours of image-heavy\nPowerPoint presentations. Many worship teams ship slides as flat images with a\nmetadata textbox below them, which makes a simple theme switch impossible. This\ntool walks each slide, inverts the embedded images, and rewrites the textboxes\nso the whole deck reads cleanly on a dark background.",{"title":21,"searchDepth":22,"depth":22,"links":58},[],null,"A Python tool that inverts the colours of image-based PowerPoint slides, turning light worship-lyric decks into dark, projector-friendly versions while keeping text legible.",false,"https:\u002F\u002Fgithub.com\u002FJoehoel\u002Fpowerpoint-inverter",{},1,"\u002Fprojects\u002Fpowerpoint-inverter",{"title":51,"description":60},"projects\u002Fpowerpoint-inverter",[69,70,71],"Python","python-pptx","Pillow","Z9wWmMDU7shgtOIlplGq2nLfmM9O4TvVlSi_zRYOUHE",{"id":74,"title":75,"body":76,"coverImage":59,"description":88,"draft":61,"extension":26,"link":89,"meta":90,"navigation":28,"order":91,"path":92,"seo":93,"stem":94,"techStack":95,"__hash__":99},"projects\u002Fprojects\u002Fpersonal-site.md","joelkuijper.me",{"type":8,"value":77,"toc":86},[78],[11,79,80,81,85],{},"The site you are reading. Built on Nuxt 4 with ",[82,83,84],"code",{},"srcDir: app\u002F",", content lives in\nMarkdown collections (posts, notes, projects) validated by Zod schemas. Search\nuses Nuxt Content's search sections. Styling uses shared design tokens.\nDeployed to Vercel.",{"title":21,"searchDepth":22,"depth":22,"links":87},[],"My personal site, built with Nuxt 4, Nuxt Content and Tailwind CSS.","https:\u002F\u002Fgithub.com\u002FJoehoel\u002Fsite",{},3,"\u002Fprojects\u002Fpersonal-site",{"title":75,"description":88},"projects\u002Fpersonal-site",[96,97,98],"Nuxt 4","Nuxt Content","Tailwind CSS","KfnuLJ_73tXNKcXVIOeDVjiZpnk4XTvBw4FJ6PSPhGo",[101],{"id":102,"title":103,"body":104,"coverImage":59,"description":723,"draft":61,"extension":26,"meta":724,"navigation":28,"ogImage":59,"path":725,"publishDate":726,"seo":727,"stem":728,"tags":729,"updatedDate":59,"__hash__":732},"posts\u002Flog\u002Fpowerpoint-inverter.md","Inverting PowerPoints with Python",{"type":8,"value":105,"toc":719},[106,109,112,119,122,125,128,138,141,148,196,207,310,313,348,353,364,367,416,424,607,611,614,712,715],[11,107,108],{},"I've worked with several community team members from churches that use PowerPoint to present the lyrics of their hymns and songs. These slides contain the lyrics along with music notation above them. Most of the time, the entire slide is an image, with a textbox containing metadata below it, separate from the image.",[11,110,111],{},"An example of a presentation might look like this:",[11,113,114],{},[115,116],"img",{"alt":117,"src":118},"Desktop with a PowerPoint window opened showing multiple white slides with text with musical notation","\u002Fimages\u002Fpowerpoint-inverter\u002Fexample-powerpoint.jpg",[11,120,121],{},"I noticed that they were manually inverting these presentations from black-on-white to white-on-black by extracting each image, pasting it into an external program, inverting it, and then pasting it back into the slide. After that, they would adjust the text colors for each slide and save the file.",[11,123,124],{},"This process had to be repeated for hundreds of presentations.",[11,126,127],{},"With my developer optimism, I thought I could automate it. And I did!",[11,129,130,131,137],{},"I came across a Python library called ",[132,133,70],"a",{"href":134,"rel":135},"https:\u002F\u002Fpython-pptx.readthedocs.io\u002Fen\u002Flatest\u002F",[136],"nofollow",", which allows for easy modification of existing PowerPoints.",[11,139,140],{},"Leaving all the CLI argument parsing for another time, here's how I loaded the PowerPoint and processed it.",[11,142,143,144,147],{},"We read the file into memory and pass it to the ",[82,145,146],{},"invert_presentation"," function.",[149,150,154],"pre",{"className":151,"code":152,"language":153,"meta":21,"style":21},"language-python shiki shiki-themes github-light github-light github-dark","with open(input_path, \"rb\") as f:\n    inverted_pptx = invert_presentation(f)\n","python",[82,155,156,185],{"__ignoreMap":21},[157,158,160,164,168,172,176,179,182],"span",{"class":159,"line":64},"line",[157,161,163],{"style":162},"--shiki-light:#D73A49;--shiki-default:#D73A49;--shiki-dark:#F97583","with",[157,165,167],{"style":166},"--shiki-light:#005CC5;--shiki-default:#005CC5;--shiki-dark:#79B8FF"," open",[157,169,171],{"style":170},"--shiki-light:#24292E;--shiki-default:#24292E;--shiki-dark:#E1E4E8","(input_path, ",[157,173,175],{"style":174},"--shiki-light:#032F62;--shiki-default:#032F62;--shiki-dark:#9ECBFF","\"rb\"",[157,177,178],{"style":170},") ",[157,180,181],{"style":162},"as",[157,183,184],{"style":170}," f:\n",[157,186,187,190,193],{"class":159,"line":22},[157,188,189],{"style":170},"    inverted_pptx ",[157,191,192],{"style":162},"=",[157,194,195],{"style":170}," invert_presentation(f)\n",[11,197,198,199,202,203,206],{},"This function takes the bytes and creates a ",[82,200,201],{},"Presentation"," object using ",[132,204,70],{"href":134,"rel":205},[136],". After that, it's very easy to loop over each slide and make modifications.",[149,208,210],{"className":151,"code":209,"language":153,"meta":21,"style":21},"def invert_presentation(bytes_data):\n    \"\"\"Inverts colors in a PowerPoint presentation\"\"\"\n    presentation = Presentation(bytes_data)\n\n    for slide in presentation.slides:\n        # 1. Invert slide background\n        # 2. Invert images\n        # 3. Invert other shapes (text elements)\n\n    output = BytesIO()\n    presentation.save(output)\n    return output.getvalue()\n",[82,211,212,224,229,239,245,260,267,273,279,284,295,301],{"__ignoreMap":21},[157,213,214,217,221],{"class":159,"line":64},[157,215,216],{"style":162},"def",[157,218,220],{"style":219},"--shiki-light:#6F42C1;--shiki-default:#6F42C1;--shiki-dark:#B392F0"," invert_presentation",[157,222,223],{"style":170},"(bytes_data):\n",[157,225,226],{"class":159,"line":22},[157,227,228],{"style":174},"    \"\"\"Inverts colors in a PowerPoint presentation\"\"\"\n",[157,230,231,234,236],{"class":159,"line":91},[157,232,233],{"style":170},"    presentation ",[157,235,192],{"style":162},[157,237,238],{"style":170}," Presentation(bytes_data)\n",[157,240,242],{"class":159,"line":241},4,[157,243,244],{"emptyLinePlaceholder":28},"\n",[157,246,248,251,254,257],{"class":159,"line":247},5,[157,249,250],{"style":162},"    for",[157,252,253],{"style":170}," slide ",[157,255,256],{"style":162},"in",[157,258,259],{"style":170}," presentation.slides:\n",[157,261,263],{"class":159,"line":262},6,[157,264,266],{"style":265},"--shiki-light:#6A737D;--shiki-default:#6A737D;--shiki-dark:#6A737D","        # 1. Invert slide background\n",[157,268,270],{"class":159,"line":269},7,[157,271,272],{"style":265},"        # 2. Invert images\n",[157,274,276],{"class":159,"line":275},8,[157,277,278],{"style":265},"        # 3. Invert other shapes (text elements)\n",[157,280,282],{"class":159,"line":281},9,[157,283,244],{"emptyLinePlaceholder":28},[157,285,287,290,292],{"class":159,"line":286},10,[157,288,289],{"style":170},"    output ",[157,291,192],{"style":162},[157,293,294],{"style":170}," BytesIO()\n",[157,296,298],{"class":159,"line":297},11,[157,299,300],{"style":170},"    presentation.save(output)\n",[157,302,304,307],{"class":159,"line":303},12,[157,305,306],{"style":162},"    return",[157,308,309],{"style":170}," output.getvalue()\n",[11,311,312],{},"For example, in just two lines, we can change the background color:",[149,314,316],{"className":151,"code":315,"language":153,"meta":21,"style":21},"slide.background.fill.solid()\nslide.background.fill.fore_color.rgb = RGBColor(0, 0, 0)\n",[82,317,318,323],{"__ignoreMap":21},[157,319,320],{"class":159,"line":64},[157,321,322],{"style":170},"slide.background.fill.solid()\n",[157,324,325,328,330,333,336,339,341,343,345],{"class":159,"line":22},[157,326,327],{"style":170},"slide.background.fill.fore_color.rgb ",[157,329,192],{"style":162},[157,331,332],{"style":170}," RGBColor(",[157,334,335],{"style":166},"0",[157,337,338],{"style":170},", ",[157,340,335],{"style":166},[157,342,338],{"style":170},[157,344,335],{"style":166},[157,346,347],{"style":170},")\n",[349,350,352],"h3",{"id":351},"extracting-and-inverting-images","Extracting and Inverting Images",[11,354,355,356,359,360,363],{},"Next, I had to figure out how to extract images from the PowerPoint. The library didn't have good documentation on how to extract an image and reinsert it after processing. At first, I tried converting the ",[82,357,358],{},".pptx"," file to a ",[82,361,362],{},".zip"," archive, where images are stored as actual files. This worked but required a lot of code for extracting, processing, and reinserting the images. With some help from ChatGPT, I found a much simpler way.",[11,365,366],{},"Each slide contains a list of shapes, which we can filter to find only images:",[149,368,370],{"className":151,"code":369,"language":153,"meta":21,"style":21},"for shape in list(slide.shapes):\n    if shape.shape_type == MSO_SHAPE_TYPE.PICTURE:\n        process_image(shape, slide)\n",[82,371,372,388,411],{"__ignoreMap":21},[157,373,374,377,380,382,385],{"class":159,"line":64},[157,375,376],{"style":162},"for",[157,378,379],{"style":170}," shape ",[157,381,256],{"style":162},[157,383,384],{"style":166}," list",[157,386,387],{"style":170},"(slide.shapes):\n",[157,389,390,393,396,399,402,405,408],{"class":159,"line":22},[157,391,392],{"style":162},"    if",[157,394,395],{"style":170}," shape.shape_type ",[157,397,398],{"style":162},"==",[157,400,401],{"style":166}," MSO_SHAPE_TYPE",[157,403,404],{"style":170},".",[157,406,407],{"style":166},"PICTURE",[157,409,410],{"style":170},":\n",[157,412,413],{"class":159,"line":91},[157,414,415],{"style":170},"        process_image(shape, slide)\n",[11,417,418,419,423],{},"Each image shape has access to its image data (blob), which we can read and process. For image inversion, I used the ",[132,420,71],{"href":421,"rel":422},"https:\u002F\u002Fpillow.readthedocs.io\u002Fen\u002Fstable\u002F",[136]," library, which is a popular library for handling image processing.",[149,425,427],{"className":151,"code":426,"language":153,"meta":21,"style":21},"def process_image(shape, slide):\n    \"\"\"Process and invert a single image shape\"\"\"\n    image_stream = BytesIO(shape.image.blob)\n    with managed_image(image_stream) as img:\n        if img.mode not in (\"RGB\", \"RGBA\"):\n            img = img.convert(\"RGB\")\n        inverted_img = ImageOps.invert(img)\n        img_stream = BytesIO()\n        inverted_img.save(img_stream, format=\"PNG\")\n        img_stream.seek(0)\n\n        # Store shape properties before removal\n        left, top = shape.left, shape.top\n        width, height = shape.width, shape.height\n\n        # Remove old shape and add new one\n        slide.shapes._spTree.remove(shape._element)\n        slide.shapes.add_picture(img_stream, left, top, width, height)\n",[82,428,429,439,444,454,467,495,509,519,528,544,553,557,562,573,584,589,595,601],{"__ignoreMap":21},[157,430,431,433,436],{"class":159,"line":64},[157,432,216],{"style":162},[157,434,435],{"style":219}," process_image",[157,437,438],{"style":170},"(shape, slide):\n",[157,440,441],{"class":159,"line":22},[157,442,443],{"style":174},"    \"\"\"Process and invert a single image shape\"\"\"\n",[157,445,446,449,451],{"class":159,"line":91},[157,447,448],{"style":170},"    image_stream ",[157,450,192],{"style":162},[157,452,453],{"style":170}," BytesIO(shape.image.blob)\n",[157,455,456,459,462,464],{"class":159,"line":241},[157,457,458],{"style":162},"    with",[157,460,461],{"style":170}," managed_image(image_stream) ",[157,463,181],{"style":162},[157,465,466],{"style":170}," img:\n",[157,468,469,472,475,478,481,484,487,489,492],{"class":159,"line":247},[157,470,471],{"style":162},"        if",[157,473,474],{"style":170}," img.mode ",[157,476,477],{"style":162},"not",[157,479,480],{"style":162}," in",[157,482,483],{"style":170}," (",[157,485,486],{"style":174},"\"RGB\"",[157,488,338],{"style":170},[157,490,491],{"style":174},"\"RGBA\"",[157,493,494],{"style":170},"):\n",[157,496,497,500,502,505,507],{"class":159,"line":262},[157,498,499],{"style":170},"            img ",[157,501,192],{"style":162},[157,503,504],{"style":170}," img.convert(",[157,506,486],{"style":174},[157,508,347],{"style":170},[157,510,511,514,516],{"class":159,"line":269},[157,512,513],{"style":170},"        inverted_img ",[157,515,192],{"style":162},[157,517,518],{"style":170}," ImageOps.invert(img)\n",[157,520,521,524,526],{"class":159,"line":275},[157,522,523],{"style":170},"        img_stream ",[157,525,192],{"style":162},[157,527,294],{"style":170},[157,529,530,533,537,539,542],{"class":159,"line":281},[157,531,532],{"style":170},"        inverted_img.save(img_stream, ",[157,534,536],{"style":535},"--shiki-light:#E36209;--shiki-default:#E36209;--shiki-dark:#FFAB70","format",[157,538,192],{"style":162},[157,540,541],{"style":174},"\"PNG\"",[157,543,347],{"style":170},[157,545,546,549,551],{"class":159,"line":286},[157,547,548],{"style":170},"        img_stream.seek(",[157,550,335],{"style":166},[157,552,347],{"style":170},[157,554,555],{"class":159,"line":297},[157,556,244],{"emptyLinePlaceholder":28},[157,558,559],{"class":159,"line":303},[157,560,561],{"style":265},"        # Store shape properties before removal\n",[157,563,565,568,570],{"class":159,"line":564},13,[157,566,567],{"style":170},"        left, top ",[157,569,192],{"style":162},[157,571,572],{"style":170}," shape.left, shape.top\n",[157,574,576,579,581],{"class":159,"line":575},14,[157,577,578],{"style":170},"        width, height ",[157,580,192],{"style":162},[157,582,583],{"style":170}," shape.width, shape.height\n",[157,585,587],{"class":159,"line":586},15,[157,588,244],{"emptyLinePlaceholder":28},[157,590,592],{"class":159,"line":591},16,[157,593,594],{"style":265},"        # Remove old shape and add new one\n",[157,596,598],{"class":159,"line":597},17,[157,599,600],{"style":170},"        slide.shapes._spTree.remove(shape._element)\n",[157,602,604],{"class":159,"line":603},18,[157,605,606],{"style":170},"        slide.shapes.add_picture(img_stream, left, top, width, height)\n",[349,608,610],{"id":609},"inverting-text-colors","Inverting Text Colors",[11,612,613],{},"Finally, the remaining task was to adjust the text elements. This was as simple as modifying the colours on all shapes that are text frames.",[149,615,617],{"className":151,"code":616,"language":153,"meta":21,"style":21},"for shape in list(slide.shapes):\n    if shape.shape_type == MSO_SHAPE_TYPE.PICTURE:\n        process_image(shape, slide)\n    elif shape.has_text_frame:\n        for paragraph in shape.text_frame.paragraphs:\n            for run in paragraph.runs:\n                # Set text color to white since the background is now black\n                run.font.color.rgb = RGBColor(255, 255, 255)\n",[82,618,619,631,647,651,659,672,685,690],{"__ignoreMap":21},[157,620,621,623,625,627,629],{"class":159,"line":64},[157,622,376],{"style":162},[157,624,379],{"style":170},[157,626,256],{"style":162},[157,628,384],{"style":166},[157,630,387],{"style":170},[157,632,633,635,637,639,641,643,645],{"class":159,"line":22},[157,634,392],{"style":162},[157,636,395],{"style":170},[157,638,398],{"style":162},[157,640,401],{"style":166},[157,642,404],{"style":170},[157,644,407],{"style":166},[157,646,410],{"style":170},[157,648,649],{"class":159,"line":91},[157,650,415],{"style":170},[157,652,653,656],{"class":159,"line":241},[157,654,655],{"style":162},"    elif",[157,657,658],{"style":170}," shape.has_text_frame:\n",[157,660,661,664,667,669],{"class":159,"line":247},[157,662,663],{"style":162},"        for",[157,665,666],{"style":170}," paragraph ",[157,668,256],{"style":162},[157,670,671],{"style":170}," shape.text_frame.paragraphs:\n",[157,673,674,677,680,682],{"class":159,"line":262},[157,675,676],{"style":162},"            for",[157,678,679],{"style":170}," run ",[157,681,256],{"style":162},[157,683,684],{"style":170}," paragraph.runs:\n",[157,686,687],{"class":159,"line":269},[157,688,689],{"style":265},"                # Set text color to white since the background is now black\n",[157,691,692,695,697,699,702,704,706,708,710],{"class":159,"line":275},[157,693,694],{"style":170},"                run.font.color.rgb ",[157,696,192],{"style":162},[157,698,332],{"style":170},[157,700,701],{"style":166},"255",[157,703,338],{"style":170},[157,705,701],{"style":166},[157,707,338],{"style":170},[157,709,701],{"style":166},[157,711,347],{"style":170},[11,713,714],{},"And that's it! What started as a tedious manual task turned into an automated solution with just a bit of Python magic. No more copy-pasting images, no more color adjustments slide by slide—just run the script and let it do the work. While this approach isn't perfect (some images did work), it already saves hours (if not days) of effort.",[716,717,718],"style",{},"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);}",{"title":21,"searchDepth":22,"depth":22,"links":720},[721,722],{"id":351,"depth":91,"text":352},{"id":609,"depth":91,"text":610},"How to invert PowerPoints with Python",{},"\u002Flog\u002Fpowerpoint-inverter","2025-01-30",{"title":103,"description":723},"log\u002Fpowerpoint-inverter",[69,730,731],"PowerPoints","Automation","kXtYAG-YvPGBnd3dHozNAfWEqE3ux1LmsqkGxpbl2SE",[734,1953,2215],{"id":735,"title":736,"body":737,"description":741,"draft":61,"extension":26,"meta":1947,"navigation":28,"path":1948,"publishDate":1949,"seo":1950,"stem":1951,"__hash__":1952},"notes\u002Fnotes\u002Flaravel-nixpacks.md","Nixpacks configuration for deploying Laravel on Railway",{"type":8,"value":738,"toc":1945},[739,742,1943],[11,740,741],{},"This is the Nixpacks configuration file that I use to deploy Laravel apps on Railway.",[149,743,747],{"className":744,"code":745,"language":746,"meta":21,"style":21},"language-toml shiki shiki-themes github-light github-light github-dark","[phases.setup]\nnixPkgs = [\"...\", \"python311Packages.supervisor\"]\n\n[phases.build]\ncmds = [\n    \"mkdir -p \u002Fetc\u002Fsupervisor\u002Fconf.d\u002F\",\n    \"cp \u002Fassets\u002Fworker-*.conf \u002Fetc\u002Fsupervisor\u002Fconf.d\u002F\",\n    \"cp \u002Fassets\u002Fsupervisord.conf \u002Fetc\u002Fsupervisord.conf\",\n    \"chmod +x \u002Fassets\u002Fstart.sh\",\n    \"...\"\n]\n\n[start]\ncmd = 'bash \u002Fassets\u002Fstart.sh'\n\n[staticAssets]\n\"start.sh\" = '''\n#!\u002Fbin\u002Fbash\n\n# Transform the nginx configuration\nnode \u002Fassets\u002Fscripts\u002Fprestart.mjs \u002Fassets\u002Fnginx.template.conf \u002Fetc\u002Fnginx.conf\n\n# Start supervisor\nsupervisord -c \u002Fassets\u002Fsupervisord.conf -n\n'''\n\n\n\"supervisord.conf\" = '''\n[unix_http_server]\nfile=\u002Fassets\u002Fsupervisor.sock\n\n[supervisord]\nlogfile=\u002Fvar\u002Flog\u002Fsupervisord.log\nlogfile_maxbytes=50MB\nlogfile_backups=10\nloglevel=info\npidfile=\u002Fassets\u002Fsupervisord.pid\nnodaemon=false\nsilent=false\nminfds=1024\nminprocs=200\nuser=root\n\n[rpcinterface:supervisor]\nsupervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface\n\n[supervisorctl]\nserverurl=unix:\u002F\u002F\u002Fassets\u002Fsupervisor.sock\n\n[include]\nfiles = \u002Fassets\u002Fworker-*.conf\n'''\n\n\"worker-nginx.conf\" = '''\n[program:worker-nginx]\nprocess_name=%(program_name)s_%(process_num)02d\ncommand=nginx -c \u002Fetc\u002Fnginx.conf\nautostart=true\nautorestart=true\nstdout_logfile=\u002Fvar\u002Flog\u002Fworker-nginx.log\nstderr_logfile=\u002Fvar\u002Flog\u002Fworker-nginx.log\n'''\n\n\"worker-phpfpm.conf\" = '''\n[program:worker-phpfpm]\nprocess_name=%(program_name)s_%(process_num)02d\ncommand=php-fpm -y \u002Fassets\u002Fphp-fpm.conf -F\nautostart=true\nautorestart=true\nstdout_logfile=\u002Fvar\u002Flog\u002Fworker-phpfpm.log\nstderr_logfile=\u002Fvar\u002Flog\u002Fworker-phpfpm.log\n'''\n\n\"worker-laravel.conf\" = '''\n[program:worker-laravel]\nprocess_name=%(program_name)s_%(process_num)02d\ncommand=bash -c 'exec php \u002Fapp\u002Fartisan queue:work --sleep=3 --tries=3 --max-time=3600'\nautostart=true\nautorestart=true\nstopasgroup=true\nkillasgroup=true\nnumprocs=2\nstartsecs=0\nstopwaitsecs=3600\nstdout_logfile=\u002Fvar\u002Flog\u002Fworker-laravel.log\nstderr_logfile=\u002Fvar\u002Flog\u002Fworker-laravel.log\n'''\n\n\"worker-inertia-ssr.conf\" = '''\n[program:inertia-ssr]\nprocess_name=%(program_name)s_%(process_num)02d\ncommand=bash -c 'exec php \u002Fapp\u002Fartisan inertia:start-ssr'\nautostart=true\nautorestart=true\nstderr_logfile=\u002Fvar\u002Flog\u002Fworker-inertia-ssr.log\nstdout_logfile=\u002Fvar\u002Flog\u002Fworker-inertia-ssr.log\n'''\n\n\"worker-nightwatch.conf\" = '''\n[program:nightwatch]\nprocess_name=%(program_name)s_%(process_num)02d\ncommand=bash -c 'exec php \u002Fapp\u002Fartisan nightwatch:agent'\nautostart=true\nautorestart=true\nstdout_logfile=\u002Fvar\u002Flog\u002Fworker-nightwatch.log\nstderr_logfile=\u002Fvar\u002Flog\u002Fworker-nightwatch.log\n'''\n\n\"php-fpm.conf\" = '''\n[www]\nlisten = 127.0.0.1:9000\nuser = www-data\ngroup = www-data\nlisten.owner = www-data\nlisten.group = www-data\npm = dynamic\npm.max_children = 20\npm.min_spare_servers = 2\npm.max_spare_servers = 8\npm.start_servers = 4\nclear_env = no\nphp_admin_value[post_max_size] = 35M\nphp_admin_value[upload_max_filesize] = 30M\n'''\n\n\"nginx.template.conf\" = '''\nuser www-data www-data;\nworker_processes 5;\ndaemon off;\n\nworker_rlimit_nofile 8192;\n\nevents {\n  worker_connections  4096;  # Default: 1024\n}\n\nhttp {\n    include    $!{nginx}\u002Fconf\u002Fmime.types;\n    index    index.html index.htm index.php;\n\n    default_type application\u002Foctet-stream;\n    log_format   main '$remote_addr - $remote_user [$time_local]  $status '\n        '\"$request\" $body_bytes_sent \"$http_referer\" '\n        '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n    access_log \u002Fvar\u002Flog\u002Fnginx-access.log;\n    error_log \u002Fvar\u002Flog\u002Fnginx-error.log;\n    sendfile     on;\n    tcp_nopush   on;\n    server_names_hash_bucket_size 128; # this seems to be required for some vhosts\n\n    server {\n        listen ${PORT};\n        listen [::]:${PORT};\n        server_name localhost;\n\n        $if(NIXPACKS_PHP_ROOT_DIR) (\n            root ${NIXPACKS_PHP_ROOT_DIR};\n        ) else (\n            root \u002Fapp;\n        )\n\n        add_header X-Content-Type-Options \"nosniff\";\n\n        client_max_body_size 35M;\n\n        index index.php;\n\n        charset utf-8;\n\n\n        $if(NIXPACKS_PHP_FALLBACK_PATH) (\n            location \u002F {\n                try_files $uri $uri\u002F ${NIXPACKS_PHP_FALLBACK_PATH}?$query_string;\n            }\n        ) else (\n            location \u002F {\n                try_files $uri $uri\u002F \u002Findex.php?$query_string;\n            }\n        )\n\n        location = \u002Ffavicon.ico { access_log off; log_not_found off; }\n        location = \u002Frobots.txt  { access_log off; log_not_found off; }\n\n        $if(IS_LARAVEL) (\n            error_page 404 \u002Findex.php;\n        ) else ()\n\n        location ~ \\.php$ {\n            fastcgi_buffer_size 8k;\n            fastcgi_pass 127.0.0.1:9000;\n            fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;\n            include $!{nginx}\u002Fconf\u002Ffastcgi_params;\n            include $!{nginx}\u002Fconf\u002Ffastcgi.conf;\n        }\n\n        location ~ \u002F\\.(?!well-known).* {\n            deny all;\n        }\n    }\n}\n'''\n","toml",[82,748,749,765,780,784,797,802,810,817,824,831,836,840,844,853,861,865,874,882,887,892,898,904,909,915,921,926,931,936,944,950,956,961,967,973,979,985,991,997,1003,1009,1015,1021,1027,1032,1038,1044,1049,1055,1061,1066,1072,1078,1083,1088,1096,1102,1108,1114,1120,1126,1132,1138,1143,1148,1156,1162,1167,1173,1178,1183,1189,1195,1200,1205,1213,1219,1224,1230,1235,1240,1246,1252,1258,1264,1270,1276,1282,1287,1292,1300,1306,1311,1317,1322,1327,1333,1339,1344,1349,1357,1363,1368,1374,1379,1384,1390,1396,1401,1406,1414,1420,1426,1432,1438,1444,1450,1456,1462,1468,1474,1480,1486,1492,1498,1503,1508,1516,1522,1528,1534,1539,1545,1550,1556,1562,1568,1573,1579,1585,1591,1596,1602,1608,1614,1620,1626,1632,1638,1644,1650,1655,1661,1667,1673,1679,1684,1690,1696,1702,1708,1714,1719,1725,1730,1736,1741,1747,1752,1758,1763,1768,1774,1780,1786,1792,1797,1802,1808,1813,1818,1823,1829,1835,1840,1846,1852,1858,1863,1869,1875,1881,1887,1893,1899,1905,1910,1916,1922,1927,1933,1938],{"__ignoreMap":21},[157,750,751,754,757,759,762],{"class":159,"line":64},[157,752,753],{"style":170},"[",[157,755,756],{"style":219},"phases",[157,758,404],{"style":170},[157,760,761],{"style":219},"setup",[157,763,764],{"style":170},"]\n",[157,766,767,770,773,775,778],{"class":159,"line":22},[157,768,769],{"style":170},"nixPkgs = [",[157,771,772],{"style":174},"\"...\"",[157,774,338],{"style":170},[157,776,777],{"style":174},"\"python311Packages.supervisor\"",[157,779,764],{"style":170},[157,781,782],{"class":159,"line":91},[157,783,244],{"emptyLinePlaceholder":28},[157,785,786,788,790,792,795],{"class":159,"line":241},[157,787,753],{"style":170},[157,789,756],{"style":219},[157,791,404],{"style":170},[157,793,794],{"style":219},"build",[157,796,764],{"style":170},[157,798,799],{"class":159,"line":247},[157,800,801],{"style":170},"cmds = [\n",[157,803,804,807],{"class":159,"line":262},[157,805,806],{"style":174},"    \"mkdir -p \u002Fetc\u002Fsupervisor\u002Fconf.d\u002F\"",[157,808,809],{"style":170},",\n",[157,811,812,815],{"class":159,"line":269},[157,813,814],{"style":174},"    \"cp \u002Fassets\u002Fworker-*.conf \u002Fetc\u002Fsupervisor\u002Fconf.d\u002F\"",[157,816,809],{"style":170},[157,818,819,822],{"class":159,"line":275},[157,820,821],{"style":174},"    \"cp \u002Fassets\u002Fsupervisord.conf \u002Fetc\u002Fsupervisord.conf\"",[157,823,809],{"style":170},[157,825,826,829],{"class":159,"line":281},[157,827,828],{"style":174},"    \"chmod +x \u002Fassets\u002Fstart.sh\"",[157,830,809],{"style":170},[157,832,833],{"class":159,"line":286},[157,834,835],{"style":174},"    \"...\"\n",[157,837,838],{"class":159,"line":297},[157,839,764],{"style":170},[157,841,842],{"class":159,"line":303},[157,843,244],{"emptyLinePlaceholder":28},[157,845,846,848,851],{"class":159,"line":564},[157,847,753],{"style":170},[157,849,850],{"style":219},"start",[157,852,764],{"style":170},[157,854,855,858],{"class":159,"line":575},[157,856,857],{"style":170},"cmd = ",[157,859,860],{"style":174},"'bash \u002Fassets\u002Fstart.sh'\n",[157,862,863],{"class":159,"line":586},[157,864,244],{"emptyLinePlaceholder":28},[157,866,867,869,872],{"class":159,"line":591},[157,868,753],{"style":170},[157,870,871],{"style":219},"staticAssets",[157,873,764],{"style":170},[157,875,876,879],{"class":159,"line":597},[157,877,878],{"style":170},"\"start.sh\" = ",[157,880,881],{"style":174},"'''\n",[157,883,884],{"class":159,"line":603},[157,885,886],{"style":174},"#!\u002Fbin\u002Fbash\n",[157,888,890],{"class":159,"line":889},19,[157,891,244],{"emptyLinePlaceholder":28},[157,893,895],{"class":159,"line":894},20,[157,896,897],{"style":174},"# Transform the nginx configuration\n",[157,899,901],{"class":159,"line":900},21,[157,902,903],{"style":174},"node \u002Fassets\u002Fscripts\u002Fprestart.mjs \u002Fassets\u002Fnginx.template.conf \u002Fetc\u002Fnginx.conf\n",[157,905,907],{"class":159,"line":906},22,[157,908,244],{"emptyLinePlaceholder":28},[157,910,912],{"class":159,"line":911},23,[157,913,914],{"style":174},"# Start supervisor\n",[157,916,918],{"class":159,"line":917},24,[157,919,920],{"style":174},"supervisord -c \u002Fassets\u002Fsupervisord.conf -n\n",[157,922,924],{"class":159,"line":923},25,[157,925,881],{"style":174},[157,927,929],{"class":159,"line":928},26,[157,930,244],{"emptyLinePlaceholder":28},[157,932,934],{"class":159,"line":933},27,[157,935,244],{"emptyLinePlaceholder":28},[157,937,939,942],{"class":159,"line":938},28,[157,940,941],{"style":170},"\"supervisord.conf\" = ",[157,943,881],{"style":174},[157,945,947],{"class":159,"line":946},29,[157,948,949],{"style":174},"[unix_http_server]\n",[157,951,953],{"class":159,"line":952},30,[157,954,955],{"style":174},"file=\u002Fassets\u002Fsupervisor.sock\n",[157,957,959],{"class":159,"line":958},31,[157,960,244],{"emptyLinePlaceholder":28},[157,962,964],{"class":159,"line":963},32,[157,965,966],{"style":174},"[supervisord]\n",[157,968,970],{"class":159,"line":969},33,[157,971,972],{"style":174},"logfile=\u002Fvar\u002Flog\u002Fsupervisord.log\n",[157,974,976],{"class":159,"line":975},34,[157,977,978],{"style":174},"logfile_maxbytes=50MB\n",[157,980,982],{"class":159,"line":981},35,[157,983,984],{"style":174},"logfile_backups=10\n",[157,986,988],{"class":159,"line":987},36,[157,989,990],{"style":174},"loglevel=info\n",[157,992,994],{"class":159,"line":993},37,[157,995,996],{"style":174},"pidfile=\u002Fassets\u002Fsupervisord.pid\n",[157,998,1000],{"class":159,"line":999},38,[157,1001,1002],{"style":174},"nodaemon=false\n",[157,1004,1006],{"class":159,"line":1005},39,[157,1007,1008],{"style":174},"silent=false\n",[157,1010,1012],{"class":159,"line":1011},40,[157,1013,1014],{"style":174},"minfds=1024\n",[157,1016,1018],{"class":159,"line":1017},41,[157,1019,1020],{"style":174},"minprocs=200\n",[157,1022,1024],{"class":159,"line":1023},42,[157,1025,1026],{"style":174},"user=root\n",[157,1028,1030],{"class":159,"line":1029},43,[157,1031,244],{"emptyLinePlaceholder":28},[157,1033,1035],{"class":159,"line":1034},44,[157,1036,1037],{"style":174},"[rpcinterface:supervisor]\n",[157,1039,1041],{"class":159,"line":1040},45,[157,1042,1043],{"style":174},"supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface\n",[157,1045,1047],{"class":159,"line":1046},46,[157,1048,244],{"emptyLinePlaceholder":28},[157,1050,1052],{"class":159,"line":1051},47,[157,1053,1054],{"style":174},"[supervisorctl]\n",[157,1056,1058],{"class":159,"line":1057},48,[157,1059,1060],{"style":174},"serverurl=unix:\u002F\u002F\u002Fassets\u002Fsupervisor.sock\n",[157,1062,1064],{"class":159,"line":1063},49,[157,1065,244],{"emptyLinePlaceholder":28},[157,1067,1069],{"class":159,"line":1068},50,[157,1070,1071],{"style":174},"[include]\n",[157,1073,1075],{"class":159,"line":1074},51,[157,1076,1077],{"style":174},"files = \u002Fassets\u002Fworker-*.conf\n",[157,1079,1081],{"class":159,"line":1080},52,[157,1082,881],{"style":174},[157,1084,1086],{"class":159,"line":1085},53,[157,1087,244],{"emptyLinePlaceholder":28},[157,1089,1091,1094],{"class":159,"line":1090},54,[157,1092,1093],{"style":170},"\"worker-nginx.conf\" = ",[157,1095,881],{"style":174},[157,1097,1099],{"class":159,"line":1098},55,[157,1100,1101],{"style":174},"[program:worker-nginx]\n",[157,1103,1105],{"class":159,"line":1104},56,[157,1106,1107],{"style":174},"process_name=%(program_name)s_%(process_num)02d\n",[157,1109,1111],{"class":159,"line":1110},57,[157,1112,1113],{"style":174},"command=nginx -c \u002Fetc\u002Fnginx.conf\n",[157,1115,1117],{"class":159,"line":1116},58,[157,1118,1119],{"style":174},"autostart=true\n",[157,1121,1123],{"class":159,"line":1122},59,[157,1124,1125],{"style":174},"autorestart=true\n",[157,1127,1129],{"class":159,"line":1128},60,[157,1130,1131],{"style":174},"stdout_logfile=\u002Fvar\u002Flog\u002Fworker-nginx.log\n",[157,1133,1135],{"class":159,"line":1134},61,[157,1136,1137],{"style":174},"stderr_logfile=\u002Fvar\u002Flog\u002Fworker-nginx.log\n",[157,1139,1141],{"class":159,"line":1140},62,[157,1142,881],{"style":174},[157,1144,1146],{"class":159,"line":1145},63,[157,1147,244],{"emptyLinePlaceholder":28},[157,1149,1151,1154],{"class":159,"line":1150},64,[157,1152,1153],{"style":170},"\"worker-phpfpm.conf\" = ",[157,1155,881],{"style":174},[157,1157,1159],{"class":159,"line":1158},65,[157,1160,1161],{"style":174},"[program:worker-phpfpm]\n",[157,1163,1165],{"class":159,"line":1164},66,[157,1166,1107],{"style":174},[157,1168,1170],{"class":159,"line":1169},67,[157,1171,1172],{"style":174},"command=php-fpm -y \u002Fassets\u002Fphp-fpm.conf -F\n",[157,1174,1176],{"class":159,"line":1175},68,[157,1177,1119],{"style":174},[157,1179,1181],{"class":159,"line":1180},69,[157,1182,1125],{"style":174},[157,1184,1186],{"class":159,"line":1185},70,[157,1187,1188],{"style":174},"stdout_logfile=\u002Fvar\u002Flog\u002Fworker-phpfpm.log\n",[157,1190,1192],{"class":159,"line":1191},71,[157,1193,1194],{"style":174},"stderr_logfile=\u002Fvar\u002Flog\u002Fworker-phpfpm.log\n",[157,1196,1198],{"class":159,"line":1197},72,[157,1199,881],{"style":174},[157,1201,1203],{"class":159,"line":1202},73,[157,1204,244],{"emptyLinePlaceholder":28},[157,1206,1208,1211],{"class":159,"line":1207},74,[157,1209,1210],{"style":170},"\"worker-laravel.conf\" = ",[157,1212,881],{"style":174},[157,1214,1216],{"class":159,"line":1215},75,[157,1217,1218],{"style":174},"[program:worker-laravel]\n",[157,1220,1222],{"class":159,"line":1221},76,[157,1223,1107],{"style":174},[157,1225,1227],{"class":159,"line":1226},77,[157,1228,1229],{"style":174},"command=bash -c 'exec php \u002Fapp\u002Fartisan queue:work --sleep=3 --tries=3 --max-time=3600'\n",[157,1231,1233],{"class":159,"line":1232},78,[157,1234,1119],{"style":174},[157,1236,1238],{"class":159,"line":1237},79,[157,1239,1125],{"style":174},[157,1241,1243],{"class":159,"line":1242},80,[157,1244,1245],{"style":174},"stopasgroup=true\n",[157,1247,1249],{"class":159,"line":1248},81,[157,1250,1251],{"style":174},"killasgroup=true\n",[157,1253,1255],{"class":159,"line":1254},82,[157,1256,1257],{"style":174},"numprocs=2\n",[157,1259,1261],{"class":159,"line":1260},83,[157,1262,1263],{"style":174},"startsecs=0\n",[157,1265,1267],{"class":159,"line":1266},84,[157,1268,1269],{"style":174},"stopwaitsecs=3600\n",[157,1271,1273],{"class":159,"line":1272},85,[157,1274,1275],{"style":174},"stdout_logfile=\u002Fvar\u002Flog\u002Fworker-laravel.log\n",[157,1277,1279],{"class":159,"line":1278},86,[157,1280,1281],{"style":174},"stderr_logfile=\u002Fvar\u002Flog\u002Fworker-laravel.log\n",[157,1283,1285],{"class":159,"line":1284},87,[157,1286,881],{"style":174},[157,1288,1290],{"class":159,"line":1289},88,[157,1291,244],{"emptyLinePlaceholder":28},[157,1293,1295,1298],{"class":159,"line":1294},89,[157,1296,1297],{"style":170},"\"worker-inertia-ssr.conf\" = ",[157,1299,881],{"style":174},[157,1301,1303],{"class":159,"line":1302},90,[157,1304,1305],{"style":174},"[program:inertia-ssr]\n",[157,1307,1309],{"class":159,"line":1308},91,[157,1310,1107],{"style":174},[157,1312,1314],{"class":159,"line":1313},92,[157,1315,1316],{"style":174},"command=bash -c 'exec php \u002Fapp\u002Fartisan inertia:start-ssr'\n",[157,1318,1320],{"class":159,"line":1319},93,[157,1321,1119],{"style":174},[157,1323,1325],{"class":159,"line":1324},94,[157,1326,1125],{"style":174},[157,1328,1330],{"class":159,"line":1329},95,[157,1331,1332],{"style":174},"stderr_logfile=\u002Fvar\u002Flog\u002Fworker-inertia-ssr.log\n",[157,1334,1336],{"class":159,"line":1335},96,[157,1337,1338],{"style":174},"stdout_logfile=\u002Fvar\u002Flog\u002Fworker-inertia-ssr.log\n",[157,1340,1342],{"class":159,"line":1341},97,[157,1343,881],{"style":174},[157,1345,1347],{"class":159,"line":1346},98,[157,1348,244],{"emptyLinePlaceholder":28},[157,1350,1352,1355],{"class":159,"line":1351},99,[157,1353,1354],{"style":170},"\"worker-nightwatch.conf\" = ",[157,1356,881],{"style":174},[157,1358,1360],{"class":159,"line":1359},100,[157,1361,1362],{"style":174},"[program:nightwatch]\n",[157,1364,1366],{"class":159,"line":1365},101,[157,1367,1107],{"style":174},[157,1369,1371],{"class":159,"line":1370},102,[157,1372,1373],{"style":174},"command=bash -c 'exec php \u002Fapp\u002Fartisan nightwatch:agent'\n",[157,1375,1377],{"class":159,"line":1376},103,[157,1378,1119],{"style":174},[157,1380,1382],{"class":159,"line":1381},104,[157,1383,1125],{"style":174},[157,1385,1387],{"class":159,"line":1386},105,[157,1388,1389],{"style":174},"stdout_logfile=\u002Fvar\u002Flog\u002Fworker-nightwatch.log\n",[157,1391,1393],{"class":159,"line":1392},106,[157,1394,1395],{"style":174},"stderr_logfile=\u002Fvar\u002Flog\u002Fworker-nightwatch.log\n",[157,1397,1399],{"class":159,"line":1398},107,[157,1400,881],{"style":174},[157,1402,1404],{"class":159,"line":1403},108,[157,1405,244],{"emptyLinePlaceholder":28},[157,1407,1409,1412],{"class":159,"line":1408},109,[157,1410,1411],{"style":170},"\"php-fpm.conf\" = ",[157,1413,881],{"style":174},[157,1415,1417],{"class":159,"line":1416},110,[157,1418,1419],{"style":174},"[www]\n",[157,1421,1423],{"class":159,"line":1422},111,[157,1424,1425],{"style":174},"listen = 127.0.0.1:9000\n",[157,1427,1429],{"class":159,"line":1428},112,[157,1430,1431],{"style":174},"user = www-data\n",[157,1433,1435],{"class":159,"line":1434},113,[157,1436,1437],{"style":174},"group = www-data\n",[157,1439,1441],{"class":159,"line":1440},114,[157,1442,1443],{"style":174},"listen.owner = www-data\n",[157,1445,1447],{"class":159,"line":1446},115,[157,1448,1449],{"style":174},"listen.group = www-data\n",[157,1451,1453],{"class":159,"line":1452},116,[157,1454,1455],{"style":174},"pm = dynamic\n",[157,1457,1459],{"class":159,"line":1458},117,[157,1460,1461],{"style":174},"pm.max_children = 20\n",[157,1463,1465],{"class":159,"line":1464},118,[157,1466,1467],{"style":174},"pm.min_spare_servers = 2\n",[157,1469,1471],{"class":159,"line":1470},119,[157,1472,1473],{"style":174},"pm.max_spare_servers = 8\n",[157,1475,1477],{"class":159,"line":1476},120,[157,1478,1479],{"style":174},"pm.start_servers = 4\n",[157,1481,1483],{"class":159,"line":1482},121,[157,1484,1485],{"style":174},"clear_env = no\n",[157,1487,1489],{"class":159,"line":1488},122,[157,1490,1491],{"style":174},"php_admin_value[post_max_size] = 35M\n",[157,1493,1495],{"class":159,"line":1494},123,[157,1496,1497],{"style":174},"php_admin_value[upload_max_filesize] = 30M\n",[157,1499,1501],{"class":159,"line":1500},124,[157,1502,881],{"style":174},[157,1504,1506],{"class":159,"line":1505},125,[157,1507,244],{"emptyLinePlaceholder":28},[157,1509,1511,1514],{"class":159,"line":1510},126,[157,1512,1513],{"style":170},"\"nginx.template.conf\" = ",[157,1515,881],{"style":174},[157,1517,1519],{"class":159,"line":1518},127,[157,1520,1521],{"style":174},"user www-data www-data;\n",[157,1523,1525],{"class":159,"line":1524},128,[157,1526,1527],{"style":174},"worker_processes 5;\n",[157,1529,1531],{"class":159,"line":1530},129,[157,1532,1533],{"style":174},"daemon off;\n",[157,1535,1537],{"class":159,"line":1536},130,[157,1538,244],{"emptyLinePlaceholder":28},[157,1540,1542],{"class":159,"line":1541},131,[157,1543,1544],{"style":174},"worker_rlimit_nofile 8192;\n",[157,1546,1548],{"class":159,"line":1547},132,[157,1549,244],{"emptyLinePlaceholder":28},[157,1551,1553],{"class":159,"line":1552},133,[157,1554,1555],{"style":174},"events {\n",[157,1557,1559],{"class":159,"line":1558},134,[157,1560,1561],{"style":174},"  worker_connections  4096;  # Default: 1024\n",[157,1563,1565],{"class":159,"line":1564},135,[157,1566,1567],{"style":174},"}\n",[157,1569,1571],{"class":159,"line":1570},136,[157,1572,244],{"emptyLinePlaceholder":28},[157,1574,1576],{"class":159,"line":1575},137,[157,1577,1578],{"style":174},"http {\n",[157,1580,1582],{"class":159,"line":1581},138,[157,1583,1584],{"style":174},"    include    $!{nginx}\u002Fconf\u002Fmime.types;\n",[157,1586,1588],{"class":159,"line":1587},139,[157,1589,1590],{"style":174},"    index    index.html index.htm index.php;\n",[157,1592,1594],{"class":159,"line":1593},140,[157,1595,244],{"emptyLinePlaceholder":28},[157,1597,1599],{"class":159,"line":1598},141,[157,1600,1601],{"style":174},"    default_type application\u002Foctet-stream;\n",[157,1603,1605],{"class":159,"line":1604},142,[157,1606,1607],{"style":174},"    log_format   main '$remote_addr - $remote_user [$time_local]  $status '\n",[157,1609,1611],{"class":159,"line":1610},143,[157,1612,1613],{"style":174},"        '\"$request\" $body_bytes_sent \"$http_referer\" '\n",[157,1615,1617],{"class":159,"line":1616},144,[157,1618,1619],{"style":174},"        '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n",[157,1621,1623],{"class":159,"line":1622},145,[157,1624,1625],{"style":174},"    access_log \u002Fvar\u002Flog\u002Fnginx-access.log;\n",[157,1627,1629],{"class":159,"line":1628},146,[157,1630,1631],{"style":174},"    error_log \u002Fvar\u002Flog\u002Fnginx-error.log;\n",[157,1633,1635],{"class":159,"line":1634},147,[157,1636,1637],{"style":174},"    sendfile     on;\n",[157,1639,1641],{"class":159,"line":1640},148,[157,1642,1643],{"style":174},"    tcp_nopush   on;\n",[157,1645,1647],{"class":159,"line":1646},149,[157,1648,1649],{"style":174},"    server_names_hash_bucket_size 128; # this seems to be required for some vhosts\n",[157,1651,1653],{"class":159,"line":1652},150,[157,1654,244],{"emptyLinePlaceholder":28},[157,1656,1658],{"class":159,"line":1657},151,[157,1659,1660],{"style":174},"    server {\n",[157,1662,1664],{"class":159,"line":1663},152,[157,1665,1666],{"style":174},"        listen ${PORT};\n",[157,1668,1670],{"class":159,"line":1669},153,[157,1671,1672],{"style":174},"        listen [::]:${PORT};\n",[157,1674,1676],{"class":159,"line":1675},154,[157,1677,1678],{"style":174},"        server_name localhost;\n",[157,1680,1682],{"class":159,"line":1681},155,[157,1683,244],{"emptyLinePlaceholder":28},[157,1685,1687],{"class":159,"line":1686},156,[157,1688,1689],{"style":174},"        $if(NIXPACKS_PHP_ROOT_DIR) (\n",[157,1691,1693],{"class":159,"line":1692},157,[157,1694,1695],{"style":174},"            root ${NIXPACKS_PHP_ROOT_DIR};\n",[157,1697,1699],{"class":159,"line":1698},158,[157,1700,1701],{"style":174},"        ) else (\n",[157,1703,1705],{"class":159,"line":1704},159,[157,1706,1707],{"style":174},"            root \u002Fapp;\n",[157,1709,1711],{"class":159,"line":1710},160,[157,1712,1713],{"style":174},"        )\n",[157,1715,1717],{"class":159,"line":1716},161,[157,1718,244],{"emptyLinePlaceholder":28},[157,1720,1722],{"class":159,"line":1721},162,[157,1723,1724],{"style":174},"        add_header X-Content-Type-Options \"nosniff\";\n",[157,1726,1728],{"class":159,"line":1727},163,[157,1729,244],{"emptyLinePlaceholder":28},[157,1731,1733],{"class":159,"line":1732},164,[157,1734,1735],{"style":174},"        client_max_body_size 35M;\n",[157,1737,1739],{"class":159,"line":1738},165,[157,1740,244],{"emptyLinePlaceholder":28},[157,1742,1744],{"class":159,"line":1743},166,[157,1745,1746],{"style":174},"        index index.php;\n",[157,1748,1750],{"class":159,"line":1749},167,[157,1751,244],{"emptyLinePlaceholder":28},[157,1753,1755],{"class":159,"line":1754},168,[157,1756,1757],{"style":174},"        charset utf-8;\n",[157,1759,1761],{"class":159,"line":1760},169,[157,1762,244],{"emptyLinePlaceholder":28},[157,1764,1766],{"class":159,"line":1765},170,[157,1767,244],{"emptyLinePlaceholder":28},[157,1769,1771],{"class":159,"line":1770},171,[157,1772,1773],{"style":174},"        $if(NIXPACKS_PHP_FALLBACK_PATH) (\n",[157,1775,1777],{"class":159,"line":1776},172,[157,1778,1779],{"style":174},"            location \u002F {\n",[157,1781,1783],{"class":159,"line":1782},173,[157,1784,1785],{"style":174},"                try_files $uri $uri\u002F ${NIXPACKS_PHP_FALLBACK_PATH}?$query_string;\n",[157,1787,1789],{"class":159,"line":1788},174,[157,1790,1791],{"style":174},"            }\n",[157,1793,1795],{"class":159,"line":1794},175,[157,1796,1701],{"style":174},[157,1798,1800],{"class":159,"line":1799},176,[157,1801,1779],{"style":174},[157,1803,1805],{"class":159,"line":1804},177,[157,1806,1807],{"style":174},"                try_files $uri $uri\u002F \u002Findex.php?$query_string;\n",[157,1809,1811],{"class":159,"line":1810},178,[157,1812,1791],{"style":174},[157,1814,1816],{"class":159,"line":1815},179,[157,1817,1713],{"style":174},[157,1819,1821],{"class":159,"line":1820},180,[157,1822,244],{"emptyLinePlaceholder":28},[157,1824,1826],{"class":159,"line":1825},181,[157,1827,1828],{"style":174},"        location = \u002Ffavicon.ico { access_log off; log_not_found off; }\n",[157,1830,1832],{"class":159,"line":1831},182,[157,1833,1834],{"style":174},"        location = \u002Frobots.txt  { access_log off; log_not_found off; }\n",[157,1836,1838],{"class":159,"line":1837},183,[157,1839,244],{"emptyLinePlaceholder":28},[157,1841,1843],{"class":159,"line":1842},184,[157,1844,1845],{"style":174},"        $if(IS_LARAVEL) (\n",[157,1847,1849],{"class":159,"line":1848},185,[157,1850,1851],{"style":174},"            error_page 404 \u002Findex.php;\n",[157,1853,1855],{"class":159,"line":1854},186,[157,1856,1857],{"style":174},"        ) else ()\n",[157,1859,1861],{"class":159,"line":1860},187,[157,1862,244],{"emptyLinePlaceholder":28},[157,1864,1866],{"class":159,"line":1865},188,[157,1867,1868],{"style":174},"        location ~ \\.php$ {\n",[157,1870,1872],{"class":159,"line":1871},189,[157,1873,1874],{"style":174},"            fastcgi_buffer_size 8k;\n",[157,1876,1878],{"class":159,"line":1877},190,[157,1879,1880],{"style":174},"            fastcgi_pass 127.0.0.1:9000;\n",[157,1882,1884],{"class":159,"line":1883},191,[157,1885,1886],{"style":174},"            fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;\n",[157,1888,1890],{"class":159,"line":1889},192,[157,1891,1892],{"style":174},"            include $!{nginx}\u002Fconf\u002Ffastcgi_params;\n",[157,1894,1896],{"class":159,"line":1895},193,[157,1897,1898],{"style":174},"            include $!{nginx}\u002Fconf\u002Ffastcgi.conf;\n",[157,1900,1902],{"class":159,"line":1901},194,[157,1903,1904],{"style":174},"        }\n",[157,1906,1908],{"class":159,"line":1907},195,[157,1909,244],{"emptyLinePlaceholder":28},[157,1911,1913],{"class":159,"line":1912},196,[157,1914,1915],{"style":174},"        location ~ \u002F\\.(?!well-known).* {\n",[157,1917,1919],{"class":159,"line":1918},197,[157,1920,1921],{"style":174},"            deny all;\n",[157,1923,1925],{"class":159,"line":1924},198,[157,1926,1904],{"style":174},[157,1928,1930],{"class":159,"line":1929},199,[157,1931,1932],{"style":174},"    }\n",[157,1934,1936],{"class":159,"line":1935},200,[157,1937,1567],{"style":174},[157,1939,1941],{"class":159,"line":1940},201,[157,1942,881],{"style":174},[716,1944,718],{},{"title":21,"searchDepth":22,"depth":22,"links":1946},[],{},"\u002Fnotes\u002Flaravel-nixpacks","2025-06-22",{"title":736,"description":741},"notes\u002Flaravel-nixpacks","dnbQQT7tQXv6IN8eDy-LuS8eZGuI3CghcTm6S8aq0u4",{"id":1954,"title":1955,"body":1956,"description":21,"draft":61,"extension":26,"meta":2209,"navigation":28,"path":2210,"publishDate":2211,"seo":2212,"stem":2213,"__hash__":2214},"notes\u002Fnotes\u002Ffiltered-array-schema.md","Zod filtered array schema",{"type":8,"value":1957,"toc":2207},[1958,2205],[149,1959,1963],{"className":1960,"code":1961,"language":1962,"meta":21,"style":21},"language-ts shiki shiki-themes github-light github-light github-dark","\u002F**\n * Creates a Zod schema for an array that filters out invalid elements during preprocessing.\n *\n * @template T - Type extending ZodSchema\n * @param {T} schema - The Zod schema to validate individual array elements\n * @returns {ZodEffects\u003CZodArray\u003CT>>} A new Zod schema that:\n * 1. Converts non-array inputs into single-element arrays\n * 2. Filters out elements that don't match the provided schema\n * 3. Validates the resulting array against the schema\n *\n * @example\n * const numberSchema = makeFilteredArraySchema(z.number());\n * numberSchema.parse(['1', 2, '3', 4]); \u002F\u002F Returns [2, 4]\n *\u002F\nfunction makeFilteredArraySchema\u003CT extends ZodSchema>(schema: T) {\n  return z.preprocess((val) => {\n    const array = Array.isArray(val) ? val : [val];\n    return array.filter((item: unknown) => schema.safeParse(item).success);\n  }, z.array(schema));\n}\n","ts",[82,1964,1965,1970,1975,1980,1994,2010,2023,2028,2033,2038,2042,2049,2054,2062,2067,2101,2126,2157,2190,2201],{"__ignoreMap":21},[157,1966,1967],{"class":159,"line":64},[157,1968,1969],{"style":265},"\u002F**\n",[157,1971,1972],{"class":159,"line":22},[157,1973,1974],{"style":265}," * Creates a Zod schema for an array that filters out invalid elements during preprocessing.\n",[157,1976,1977],{"class":159,"line":91},[157,1978,1979],{"style":265}," *\n",[157,1981,1982,1985,1988,1991],{"class":159,"line":241},[157,1983,1984],{"style":265}," * ",[157,1986,1987],{"style":162},"@template",[157,1989,1990],{"style":170}," T",[157,1992,1993],{"style":265}," - Type extending ZodSchema\n",[157,1995,1996,1998,2001,2004,2007],{"class":159,"line":247},[157,1997,1984],{"style":265},[157,1999,2000],{"style":162},"@param",[157,2002,2003],{"style":219}," {T}",[157,2005,2006],{"style":170}," schema",[157,2008,2009],{"style":265}," - The Zod schema to validate individual array elements\n",[157,2011,2012,2014,2017,2020],{"class":159,"line":262},[157,2013,1984],{"style":265},[157,2015,2016],{"style":162},"@returns",[157,2018,2019],{"style":219}," {ZodEffects\u003CZodArray\u003CT>>}",[157,2021,2022],{"style":265}," A new Zod schema that:\n",[157,2024,2025],{"class":159,"line":269},[157,2026,2027],{"style":265}," * 1. Converts non-array inputs into single-element arrays\n",[157,2029,2030],{"class":159,"line":275},[157,2031,2032],{"style":265}," * 2. Filters out elements that don't match the provided schema\n",[157,2034,2035],{"class":159,"line":281},[157,2036,2037],{"style":265}," * 3. Validates the resulting array against the schema\n",[157,2039,2040],{"class":159,"line":286},[157,2041,1979],{"style":265},[157,2043,2044,2046],{"class":159,"line":297},[157,2045,1984],{"style":265},[157,2047,2048],{"style":162},"@example\n",[157,2050,2051],{"class":159,"line":303},[157,2052,2053],{"style":265}," * const numberSchema = makeFilteredArraySchema(z.number());\n",[157,2055,2056,2059],{"class":159,"line":564},[157,2057,2058],{"style":265}," * numberSchema.parse(['1', 2, '3', 4]);",[157,2060,2061],{"style":265}," \u002F\u002F Returns [2, 4]\n",[157,2063,2064],{"class":159,"line":575},[157,2065,2066],{"style":265}," *\u002F\n",[157,2068,2069,2072,2075,2078,2081,2084,2087,2090,2093,2096,2098],{"class":159,"line":586},[157,2070,2071],{"style":162},"function",[157,2073,2074],{"style":219}," makeFilteredArraySchema",[157,2076,2077],{"style":170},"\u003C",[157,2079,2080],{"style":219},"T",[157,2082,2083],{"style":162}," extends",[157,2085,2086],{"style":219}," ZodSchema",[157,2088,2089],{"style":170},">(",[157,2091,2092],{"style":535},"schema",[157,2094,2095],{"style":162},":",[157,2097,1990],{"style":219},[157,2099,2100],{"style":170},") {\n",[157,2102,2103,2106,2109,2112,2115,2118,2120,2123],{"class":159,"line":591},[157,2104,2105],{"style":162},"  return",[157,2107,2108],{"style":170}," z.",[157,2110,2111],{"style":219},"preprocess",[157,2113,2114],{"style":170},"((",[157,2116,2117],{"style":535},"val",[157,2119,178],{"style":170},[157,2121,2122],{"style":162},"=>",[157,2124,2125],{"style":170}," {\n",[157,2127,2128,2131,2134,2137,2140,2143,2146,2149,2152,2154],{"class":159,"line":597},[157,2129,2130],{"style":162},"    const",[157,2132,2133],{"style":166}," array",[157,2135,2136],{"style":162}," =",[157,2138,2139],{"style":170}," Array.",[157,2141,2142],{"style":219},"isArray",[157,2144,2145],{"style":170},"(val) ",[157,2147,2148],{"style":162},"?",[157,2150,2151],{"style":170}," val ",[157,2153,2095],{"style":162},[157,2155,2156],{"style":170}," [val];\n",[157,2158,2159,2161,2164,2167,2169,2172,2174,2177,2179,2181,2184,2187],{"class":159,"line":603},[157,2160,306],{"style":162},[157,2162,2163],{"style":170}," array.",[157,2165,2166],{"style":219},"filter",[157,2168,2114],{"style":170},[157,2170,2171],{"style":535},"item",[157,2173,2095],{"style":162},[157,2175,2176],{"style":166}," unknown",[157,2178,178],{"style":170},[157,2180,2122],{"style":162},[157,2182,2183],{"style":170}," schema.",[157,2185,2186],{"style":219},"safeParse",[157,2188,2189],{"style":170},"(item).success);\n",[157,2191,2192,2195,2198],{"class":159,"line":889},[157,2193,2194],{"style":170},"  }, z.",[157,2196,2197],{"style":219},"array",[157,2199,2200],{"style":170},"(schema));\n",[157,2202,2203],{"class":159,"line":894},[157,2204,1567],{"style":170},[716,2206,718],{},{"title":21,"searchDepth":22,"depth":22,"links":2208},[],{},"\u002Fnotes\u002Ffiltered-array-schema","2025-04-08",{"title":1955,"description":21},"notes\u002Ffiltered-array-schema","jEcMFAVXCbsijOTFj2xMLAO7tqSCeUDAP8BGbQhvQ00",{"id":2216,"title":2217,"body":2218,"description":2322,"draft":61,"extension":26,"meta":2323,"navigation":28,"path":2324,"publishDate":726,"seo":2325,"stem":2326,"__hash__":2327},"notes\u002Fnotes\u002Ftype-safe-object-entries.md","Typesafe Object.entries()",{"type":8,"value":2219,"toc":2320},[2220,2318],[149,2221,2223],{"className":1960,"code":2222,"language":1962,"meta":21,"style":21},"export function objectEntries\u003CT extends object>(obj: T): [keyof T, T[keyof T]][] {\n  return Object.entries(obj) as [keyof T, T[keyof T]][];\n}\n",[82,2224,2225,2280,2314],{"__ignoreMap":21},[157,2226,2227,2230,2233,2236,2238,2240,2242,2245,2247,2250,2252,2254,2257,2259,2262,2265,2267,2269,2271,2273,2275,2277],{"class":159,"line":64},[157,2228,2229],{"style":162},"export",[157,2231,2232],{"style":162}," function",[157,2234,2235],{"style":219}," objectEntries",[157,2237,2077],{"style":170},[157,2239,2080],{"style":219},[157,2241,2083],{"style":162},[157,2243,2244],{"style":166}," object",[157,2246,2089],{"style":170},[157,2248,2249],{"style":535},"obj",[157,2251,2095],{"style":162},[157,2253,1990],{"style":219},[157,2255,2256],{"style":170},")",[157,2258,2095],{"style":162},[157,2260,2261],{"style":170}," [",[157,2263,2264],{"style":162},"keyof",[157,2266,1990],{"style":219},[157,2268,338],{"style":170},[157,2270,2080],{"style":219},[157,2272,753],{"style":170},[157,2274,2264],{"style":162},[157,2276,1990],{"style":219},[157,2278,2279],{"style":170},"]][] {\n",[157,2281,2282,2284,2287,2290,2293,2295,2297,2299,2301,2303,2305,2307,2309,2311],{"class":159,"line":22},[157,2283,2105],{"style":162},[157,2285,2286],{"style":170}," Object.",[157,2288,2289],{"style":219},"entries",[157,2291,2292],{"style":170},"(obj) ",[157,2294,181],{"style":162},[157,2296,2261],{"style":170},[157,2298,2264],{"style":162},[157,2300,1990],{"style":219},[157,2302,338],{"style":170},[157,2304,2080],{"style":219},[157,2306,753],{"style":170},[157,2308,2264],{"style":162},[157,2310,1990],{"style":219},[157,2312,2313],{"style":170},"]][];\n",[157,2315,2316],{"class":159,"line":91},[157,2317,1567],{"style":170},[716,2319,718],{},{"title":21,"searchDepth":22,"depth":22,"links":2321},[],"Simple utility function to have typesafe values for Object.entries()",{},"\u002Fnotes\u002Ftype-safe-object-entries",{"title":2217,"description":2322},"notes\u002Ftype-safe-object-entries","XLC9DK66XNU3yNDf-A4mpl9cYZ83iMprMyxtN1fWhzQ",1793647953271]