-
Resume
Aug 29, 2025
Hey there, I'm Wesam. I'm a full-stack software engineer with 5+ years of experience building scalable e-commerce platforms and consumer applications. I specialize in end-to-end development from database design to frontend interfaces, with expertise in modern frameworks, cloud infrastructure, and team leadership. Print this webpage to get my resume in PDF form. <button class="email-btn" onclick="window.location.href='mailto:wesamjabali@icloud.com'">📧</button> <button class="print-btn" onclick="window.print()">🖨️</button> <p class="print-only center" style="font-weight: bold;"> Wesam Jabali </p> <p class="print-only center"> wesamjabali@icloud.com • wesamjabali.com </p> #### Work Experience | | | | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 2026 - Present | [Senior Software Engineer - McMaster-Carr](https://www.mcmaster.com/)<br><div class="skills">C# • .NET • React • SQL</div> | | 2021 - 2026 | [Software Development Engineer III - ALDI](https://aldi.us/)<br><ul><li>Building the future of ALDI's global digital commerce experience ([aldi.us](https://aldi.us), [aldi.ie](https://aldi.ie), [aldi.co.uk](https://aldi.co.uk), and more)</li><li>Leading end-to-end development of product catalog systems serving millions of users</li><li>Creating AI-powered applications for product data enhancement, workflow automation, and other internal tools</li><li>Leading a team of developers to deliver high-impact and critical software which drives the backbone of ALDI US's digital commerce revenue.</li></ul><div class="skills">Vue • Nuxt • React • Next.js • TypeScript • C# • .NET • Azure • SQL • Redis • DevOps</div> | | 2023 - 2025 | [Software Development Engineer III - ALDI SÜD](https://www.aldi.us/)<br><ul><li>Built the future of ALDI's global digital commerce experience on an international assignment in Düsseldorf, Germany</li><li>Developed consumer-facing e-commerce applications</li><li>Led cross-functional collaboration with European product teams to implement scalable shopping experiences</li><li>Architected and built company-wide component libraries used across all ALDI applications.</li></ul><div class="skills">Vue • Nuxt • Ruby on Rails • TypeScript • SCSS • Jest • Express • MySQL</div> | | 2019 - 2021 | [Software Developer - ChiBatterySystems](https://chibatterysystems.com/)<br><ul><li>Built full-stack Manufacturing Quality Assurance platform for electric vehicle battery production</li><li>Developed complete end-to-end solutions including frontend interfaces, backend APIs, database design, and hardware integration</li><li>Built full-stack time tracking software which exported data to various platforms including the Quickbooks API</li><li>Implemented real-time monitoring systems with hardware integration</li></ul><div class="skills">Vue • Nuxt • Ruby on Rails • React • TypeScript • Arduino • C • AWS</div> | | 2019 - 2020 | [Research Assistant - Lewis University](https://lewisu.edu/)<br><ul><li>Analyzed C. elegans somatic nervous system development using machine learning to predict each neuron's function</li><li>The algorithm was able to predict correctly with 81% accuracy</li><li>The results were presented to other scientists and financial supporters</li></ul><div class="skills">Python • Pandas • Numpy</div> | #### Education | | | | ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 2021 | DePaul University <br><ul><li>Graduated Summa Cum Laude at DePaul University</li><li>Created automation tools used by thousands of students and faculty to facilitate courses during the COVID-19 pandemic</li></ul><div class="skills">3.94 GPA • B.S. • Computer Science </div> | #### Personal Projects | | | | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | present | _AssemblyCore_<br><ul><li>Comprehensive full-stack Manufacturing Quality Assurance platform helping small American manufacturers optimize their processes</li><li>Built with modern architecture including GraphQL APIs, microservices, containerized deployment, and responsive web interfaces</li></ul><div class="skills">GraphQL • React • TypeScript • Vue • Node.js • AWS • Docker • PostgreSQL • Microservices</div> | | present | _eInk Desktop Dashboard_<br><ul><li>Dashboard for displaying information on eInk screns, as well as speaker drivers for notifications and audio.</li><li>Driven by home-server backend written in Bun, serving customizable HTML, audios, and formats.</li></ul><div class="skills">C++ • Arduino • Microcontrollers • PCB Design • Drivers • BunSh backend</div> | | 2023 | _[Resume Template](https://github.com/wesamjabali/resume)_<br><ul><li>A minimal resume template website built with Vue - responsive, printable, and deployable</li><li>Features build optimization, responsive design, and print-friendly styling</li></ul><div class="skills">Vue • TypeScript • Vite • CSS • Build Tools</div> | | 2021 | _[BlueDaemon v2](https://github.com/wesamjabali/bluedaemon-v2)_<br><ul><li>Discord bot that allows universities to create and manage their own servers</li><li>Built scalable infrastructure and supported over 6 thousand concurrent users with real-time features</li><li>This was used by DePaul university's CDM department to teach classes during COVID</li></ul><div class="skills">Node • Ruby on Rails • React • TypeScript • PostgresQL • Express • Prisma • Docker • AWS • Redis</div> | <style> :root { --text: rgba(255,255,255,0.7); --text-highlighted: rgba(255,255,255,0.9); --text-lowlighted: rgba(255,255,255,0.6); } @page { size: A4; margin: 0.5in 0.75in !important; } .print-only { display: none; } @media print { .print-only { display: block; } } .print-btn { position: fixed; bottom: 20px; right: 20px; background-color: rgba(255, 255, 255, 0.1); color: rgba(255, 255, 255, 0.6); border: 1px solid rgba(255, 255, 255, 0.2); padding: 8px; border-radius: 6px; cursor: pointer; font-size: 16px; width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; transition: opacity 0.2s ease; filter: grayscale(1); } .print-btn:hover { background-color: rgba(255, 255, 255, 0.15); color: rgba(255, 255, 255, 0.8); } @media print { .email-btn, .print-btn { display: none !important; } } .center { text-align: center; } .email-btn { position: fixed; bottom: 66px; right: 20px; background-color: rgba(255, 255, 255, 0.1); color: rgba(255, 255, 255, 0.6); border: 1px solid rgba(255, 255, 255, 0.2); padding: 8px; border-radius: 6px; cursor: pointer; font-size: 16px; width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; transition: opacity 0.2s ease; filter: grayscale(1); } .email-btn:hover { background-color: rgba(255, 255, 255, 0.15); color: rgba(255, 255, 255, 0.8); } .skills { margin-top: 0.3rem; display: block; text-align: center; color: white; } @media print { *, *>* { background-color: white !important; color: black !important; border-spacing: 0; text-decoration: none; } html, body { font-size: 12px !important; line-height: 1.3 !important; margin: 0 !important; padding: 0 !important; } header, footer, br + br + br, .title, .hero-image, p:not([class]) { display: none; } h1,h2,h3,h4,h5,h6 { font-weight: bold; color: black !important; margin: 0.8rem 0 0 0 !important; font-size: 1rem; } h4 { font-size: 1.2rem; } .skills { margin-top: 0.3rem; display: block; font-size: 0.8rem !important; text-align: center; } th, td { padding: 0.6rem 0 !important; vertical-align: top; } table { border-spacing: 0.3rem 0 !important; margin: 0.5rem 0 !important; } td::first-line { color: black !important; font-weight: bold; } } body, body > *, p { background-color: black; color: var(--text); } h1,h2,h3,h4,h5,h6 { color: var(--text-highlighted); } th:first-of-type { width: 20%; } table { border-spacing: 1rem 0; } th, td { padding: 0.5rem 0 0.5rem 0; align-content: start; } /* First line of text in tables, not the year */ th::first-line, td:not(:first-of-type)::first-line, td > em, td > a { color: var(--text-highlighted) } /* Year */ td:first-of-type { color: var(--text-lowlighted) } @media (max-width: 768px) { table { display: block; border-spacing: 0; } tr { display: block; margin-bottom: 1rem; } td { display: block; padding: 0.5rem 0; } } </style>
-
TSC to file checklist
Mar 27, 2026
When doing a big upgrade on a Typescript project, keeping track of your `tsc` output can be difficult. To get a list of files in a markdown checklist, you can dump your `tsc` output to a file, and run this script on that file, and it'll give you a markdown list of the files you need to go through, one on one. > Notes: - This excludes any `.spec` files. - This is usable with vue-tsc or any other tsc extension ```js const fs = require('fs') const errorsFile = '~/file-of-errors.txt' const outputFile = './errorFiles.md' const errorsString = fs.readFileSync(errorsFile, 'utf8') const errorsArrayExcludeStrings = ['>', 'spec', 'ELIFECYCLE'] const errorsArray = errorsString.split('\n').filter(error => !error.startsWith(' ') && error !== '' && !errorsArrayExcludeStrings.some(s => error.includes(s))) const duplicatedFilesArray = errorsArray.map((error) => { const errorParts = error.split('(') const output = `[ ] ${errorParts[0]}` return output }) const files = [...new Set(duplicatedFilesArray)].sort().sort((a,_b) => a.endsWith('.ts') ? 1 : -1) fs.writeFileSync(outputFile, files.join('\n')) console.log(`${errorsArray.length} errors across ${files.length} files remaining. Output: ${outputFile}`) ```
-
Atomic functions
Mar 27, 2026
Often, we see functions that require an entire object to be passed in, even if only 1 or 2 attributes from that object are used. For example: ```ts function getProductImageFromCart(product: Product): string { const { imageUrl } = useProducts(); const image = getImageWithWidth(product, 64); return getImageUrl(image, product.name) || fallbackUrl; } ``` This approach can make functions rigid and less flexible, especially when dealing with partial objects or different types of objects. For instance, a `MiniProduct` might have all the necessary data but cannot be used because it is not specifically a `Product`. A better approach is to create **atomic functions** that only take the data they need. However, making this change can be time-consuming. An alternative solution is to use a functional type like this: ```ts export type RequireSome<T, K extends keyof T> = Partial<T> & { [P in K]-?: T[P]; }; ``` This type creates a partial version of the type you want to use and makes the specified attributes required. ```ts function getProductImageFromCart( product: RequireSome<Product & MiniProduct, "name" | "images"> ): string; ``` This allows us to pass any type that includes the required attributes, ignoring the rest without causing errors. It also enables the use of well-typed objects with only the necessary data, making the function atomic. So, we can use it like this: ```ts getProductImageFromCart({ name: product.name, images: product.images }); ``` This approach provides an atomic structure and dynamic type safety. As you address these TypeScript issues, consider using this method to make functions more flexible and maintainable.
-
Fix Obsidian URLs with Remark (Astro)
Feb 28, 2024
For those deeply invested in knowledge management and note-taking, Obsidian has become a cornerstone application. Its unique features and customizability cater to diverse workflows, but one challenge can arise when integrating Obsidian with a website: URL formatting discrepancies. This blog post delves into the technical aspects of how to create a plugin designed to address this very issue. We'll explore its functionality and implementation details. #### Understanding the Challenge Obsidian employs a specific markdown syntax for internal links, which can differ from the URL structures used on traditional websites. This inconsistency can lead to broken links when transferring content from Obsidian to a website, hindering the user experience and creating maintenance overhead. #### The Remark Internal Url Fixer: A Solution The remark Internal Url Fixer tackles this challenge head-on by seamlessly transforming internal URLs within your Obsidian notes to align with your website's URL format. This plugin operates within the remark ecosystem, a popular toolkit for markdown processing. > Note: This code snippet does not problems with images. This is left as an exercise for the reader, but has a very similar fix. **Prerequisites:** - `Use [[Wikilinks]]` is off - `New link format` is set to `Absolute path in vault` - `Default location for new attachments` is set to `In subfolder under current folder`  #### The code **Short and sweet:** ```ts import { slugifyFileName } from "../utils/sitemap.util"; export const remarkInternalUrlFixer = () => { return (tree: any) => { function visit(node: any) { if (node.children) { node.children.forEach(visit) } if (node.type === 'link' && node.url) { const isInternalLink = !node.url.includes('://') if (isInternalLink) { node.url = `/${slugifyFileName(decodeURI(node.url))}` } } } visit(tree); }; }; export default remarkInternalUrlFixer; ``` - **`remarkInternalUrlFixer`**: This function serves as the entry point, accepting a markdown document represented as a tree structure. - **`visit`**: This recursive function iterates through the entire tree, examining each node for relevant information. If a node possesses child elements, the function calls itself recursively to process them as well. - **`node.type === 'link'`**: This condition checks if the current node represents a link element, indicating the presence of a URL. - **`!node.url.includes('://')`**: This conditional statement determines if the URL is internal. Internal URLs lack the protocol specifier (e.g., `http` or `https`) and are specific to your website's structure. - **`node.url =`/${slugifyFileName(decodeURI(node.url))}`**: If the URL is deemed internal, the plugin rewrites it. This involves: - **Decoding**: Any encoded characters within the URL are decoded using `decodeURI` to ensure proper handling. - **Slugification**: The filename component of the URL is transformed into a slug format suitable for website URLs using `slugifyFileName`. This ensures consistency and adherence to your website's URL structure. - **Path creation**: A forward slash (`/`) is prepended to the transformed URL, creating a valid website path. This plugin should then be used with Astro/Remark like this: ```ts export default defineConfig({ // ... markdown: { remarkPlugins: [remarkInternalUrlFixer] } // ... }); ``` #### The Advantages of this plugin By leveraging the remark Internal Url Fixer, you can: - **Maintain your preferred workflow**: Continue using Obsidian's internal linking system without the need to manually adjust URLs for your website. - **Effortless integration**: The plugin operates silently in the background, automatically transforming URLs during the markdown processing stage. - **Enhanced user experience**: Visitors to your website will encounter only functional links, leading to a smoother and more positive experience. #### Conclusion: A Bridge Between Obsidian and Your Website The remark Internal Url Fixer demonstrates the power of custom plugins in enhancing Obsidian's capabilities. By addressing the URL formatting discrepancy, this plugin fosters a seamless workflow for knowledge management and content creation, allowing you to leverage the strengths of both Obsidian and Remark.
-
Hiding pages from sitemap in Astro Content
Feb 28, 2024
Astro is a modern static site generator that brings together the best of traditional static site generators and modern build tools. When working with content-driven websites, there might be cases where you want to hide certain pages from public access. In this blog post, we'll explore a TypeScript code snippet that accomplishes this task using Astro. ## Understanding the Code Let's delve into the TypeScript code responsible for filtering out non-public or standalone pages. ```typescript import fs from 'fs'; import yaml from 'js-yaml'; import path from 'path'; interface FileData { slug: string; public: boolean; standalone: boolean; } const directory = path.join(process.cwd(), 'src', 'content'); const collectionName = 'notes'; export function getFilteredSlugs(url: string): string[] { const slugs: string[] = []; function traverseDirectory(dir: string) { const files = fs.readdirSync(dir); files.forEach((file) => { const filePath = path.join(dir, file); const stat = fs.statSync(filePath); if (stat.isDirectory()) { traverseDirectory(filePath); } else if (path.extname(file) === '.md' || path.extname(file) === '.mdx') { const fileContent = fs.readFileSync(filePath, 'utf8'); const yamlData = fileContent.split('---')[1]; const fileData: FileData = yaml.load(yamlData) as FileData; const fileNameSlug = file.replace(/\.mdx?$/, '').replace(/ /g, '-').toLowerCase().replace(/[^a-z0-9-]/g, ''); const preSlugPath = path.dirname(filePath).replace(directory, '').replace(collectionName, '') const slug = fileData.slug ? `/${fileData.slug}` : path.join(preSlugPath, fileNameSlug); if (!fileData.public || fileData.standalone) { slugs.push(`${url}${slug}/`); } } }); } traverseDirectory(directory); return slugs; } ``` ## Code Breakdown 1. **FileData Interface**: Describes the structure of the YAML front matter in each markdown file, including the `slug`, `public`, and `standalone` properties. 2. **Configuration Variables**: Defines the root directory and collection name for content files. 3. **getFilteredSlugs Function**: This function takes a base URL as a parameter and returns an array of filtered slugs. 4. **traverseDirectory Function**: Recursively traverses the content directory, extracting information from markdown files. 5. **YAML Parsing**: Reads the YAML front matter of each markdown file to obtain metadata such as `slug`, `public`, and `standalone`. 6. **Slug Generation**: Generates slugs based on file content and YAML data, considering the specified rules for replacement and transformation. 7. **Filtering**: Adds slugs to the result array only if the page is non-public or standalone. ## Implementation in Astro To use this code in an Astro project, import the `getFilteredSlugs` function and call it with the base URL. The returned array of slugs can then be utilized in your Astro configuration to control the visibility of pages. ```typescript import { getFilteredSlugs } from './src/utils/sitemap.util.ts'; const site = 'https://notes.wesamjabali.com' const filteredSlugs = getFilteredSlugs(site); export default defineConfig({ site, integrations: [mdx(), sitemap({ filter: (pageUrl) => !sitemapIgnoreUrls.includes(pageUrl) })], image: { service: squooshImageService() } }); ``` With this setup, you can easily manage the visibility of pages in your Astro-generated site based on the specified criteria in the YAML front matter. In conclusion, this TypeScript code offers a flexible solution for hiding non-public or standalone pages in an Astro content-driven website, giving you control over which pages are accessible to the public.