diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 74450112..1a48ffe9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,10 +45,10 @@ Here's what the workflow for a new member looks like: For a new member to join, they must create an issue asking to join and providing the information requested in our [Join][join] page. -### 2. Create `src/content/members/.json` +### 2. Create `data/members/.json` After verifying that the prospective member's submitted data is correct, create a JSON file in -`src/content/members/.json` with this data. The member slug should contain no spaces. +`data/members/.json` with all the member's information. ### 3. Add logo to `public/images/members` @@ -69,15 +69,7 @@ magick logo.jpg -resize '>800x' -quality 99 -define webp:lossless=false -define Remember that a corresponding `logo.webp.license` file must also be added. -### 4. Add role to `src/memberRoles.json` - -The step which actually causes a member to show up on the website is adding them to `src/memberRoles.json`. For an -entry's key, use the same member slug you used in the JSON filename. Members should have a “Member” role by default, -though members can also have custom roles. - -At this point, the member will show up on the website. - -### 5. (Technically Optional) Add logo to `src/assets/images/members` +### 4. (Technically Optional) Add logo to `src/assets/images/members` For a member's logo to show up on the homepage, it must be added to `src/assets/images/members//minimal.svg`. This should be a white- or greyscale-only version of the member's logo. If in doubt, check the other existing logos. Note @@ -89,13 +81,13 @@ For the logo to actually show up on the homepage, you also need to edit `MemberL appropriate place. Check if giving the `maxHeight` property a specific value will make the new logo fit in better from a visually proportional point of view. -### 6. Add member logo to Google Drive +### 5. Add member logo to Google Drive When onboarding members, we will receive logo files for those members. Make sure to contribute these logo files, and any modified logo files you create, back to our [“Member Logos”][member-logos] Google Drive folder. If you don't have access, contact @selviano. -### 7. Update member information in CRM +### 6. Update member information in CRM Make sure the new member company's information is up to date in our [CRM][crm]. This is important in order to make sure we don't use out of date data. If the company is not yet in the CRM, add it and apply the appropriate labels. If the @@ -111,7 +103,7 @@ plan to renew, or (2) their report due date, plus a grace period of a month, hav When removing a member, take the following steps: -* Remove the member from `src/memberRoles.json`, causing the member to no longer be displayed on the website +* Set `"active": false` in the member's JSON file, causing the member to no longer be displayed on the website * Remove the member's logo from wherever it may be featured on the website, such as `MemberLogoBoard.astro` Do not delete any of the member's files. The member's JSON file, as well as their logo assets, might be useful in the @@ -132,7 +124,7 @@ that we can compactly archive a report into a single file. To archive all reports, run this from the repository root: ``` -$ ./src/memberData/bin/archiveMembers.ts +$ ./src/members/bin/archiveMembers.ts ``` This will archive reports using the following directory structure: diff --git a/README.md b/README.md index e4b9f9bb..65ac72cf 100644 --- a/README.md +++ b/README.md @@ -36,12 +36,25 @@ the code, developer time, and gifts-in-kind that many Pledge member companies al * **Just want to keep up to date?** Follow us on [Mastodon][mastodon] or [Bluesky][bsky]. * **Want to cite our work?** Click “cite this repository” in the top right. +--- + +Open Source Pledge is brought to you by [Sentry][sentry] and contributors. +The core team is: +[Vlad-Stefan Harbuz][vlad.website] (independent), +[Greg Kumparak][greg] (Sentry), +[Michael Selvidge][selviano] (Sentry), and +[Chad Whitacre][chad] (Sentry). +Design is by Sentry's in-house creative team, +[Studio 404][studio404]. + [bsky]: https://bsky.app/profile/opensourcepledge.com +[chad]: https://chadwhitacre.com/ [contributors]: https://github.com/opensourcepledge/opensourcepledge.com/graphs/contributors [cramer-author]: https://blog.sentry.io/authors/david-cramer/ [crisis]: https://openpath.quest/2024/the-open-source-sustainability-crisis/ [fosdem-talk]: https://www.youtube.com/watch?v=UarZwUjFJpI [fosdem]: https://fosdem.org/2025/ +[greg]: https://www.kumparak.com/ [issues]: https://github.com/opensourcepledge/opensourcepledge.com/issues [join]: https://opensourcepledge.com/join/ [launch-ticket]: https://github.com/opensourcepledge/opensourcepledge.com/issues/4 @@ -51,7 +64,9 @@ the code, developer time, and gifts-in-kind that many Pledge member companies al [openpath]: https://openpath.quest/ [osp]: https://opensourcepledge.com [pledgephil]: https://vlad.website/the-philosophy-of-the-open-source-pledge/ +[selviano]: https://github.com/selviano [sentry-blog-post]: https://blog.sentry.io/join-the-pledge/ [sentry]: https://sentry.io/welcome/ [slack]: https://join.slack.com/t/opensourcepledge/shared_invite/zt-33qxp7jsz-CJeYxDBnc2Y3FuZvGCHLMw +[studio404]: https://sentry.design/ [vlad.website]: https://vlad.website/ diff --git a/REUSE.toml b/REUSE.toml index 59d7d6cd..417e9599 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -11,16 +11,13 @@ path = [ "CNAME", "REUSE.toml", "astro.config.mjs", - "members.csv", + "data/members/*", "package-lock.json", "package.json", "public/.well-known/atproto-did", - "public/images/example-members/*", "public/robots.txt", "src/cache/jobSet.json", - "src/content/members/*", "src/env.d.ts", - "src/memberRoles.json", "tsconfig.json", ] SPDX-FileCopyrightText = "NONE" diff --git a/bin/make-generated-resources b/bin/make-generated-resources index 3783fd69..3a1b29c3 100755 --- a/bin/make-generated-resources +++ b/bin/make-generated-resources @@ -46,7 +46,7 @@ done echo 'Generating files' -grand_total=$(./src/memberData/bin/getGrandTotalRaised.ts) +grand_total=$(./src/members/bin/getGrandTotalRaised.ts) sed -i "s/\\\$[^<]\\+/${grand_total}/" public/generated/templates/opengraph.svg echo 'Generated SVG file' diff --git a/contrib/ethicalads/makeAdData.ts b/contrib/ethicalads/makeAdData.ts index 4e24c7e9..fbb54129 100755 --- a/contrib/ethicalads/makeAdData.ts +++ b/contrib/ethicalads/makeAdData.ts @@ -5,22 +5,11 @@ // Must be run in repository root. -import fs from 'fs'; -import memberRoles from '../../src/memberRoles.json'; -const memberIds = Object.keys(memberRoles); +import { getMembers } from '../../src/members/common.ts'; +const members = getMembers(); console.log('id,name,image,headline,text,cta'); -for (const id of memberIds) { - const localPath = `./src/content/members/${id}.json`; - - let member = undefined; - try { - member = JSON.parse(fs.readFileSync(localPath).toString()); - } catch (e) { - console.error(`ERROR: could not load member data at ${localPath}`, e); - process.exit(1); - } - - console.log(`${id},${member["name"]},ad-${id}.png,No burnout.,Fewer vulns. Pay maintainers like ${member["name"]} does!,Join the Open Source Pledge. 💃🏻 `); +for (const member of members) { + console.log(`${member.id},${member.name},ad-${member.id}.png,No burnout.,Fewer vulns. Pay maintainers like ${member.name} does!,Join the Open Source Pledge. 💃🏻 `); } diff --git a/src/content/members/.gitkeep b/data/members/.gitkeep similarity index 100% rename from src/content/members/.gitkeep rename to data/members/.gitkeep diff --git a/src/content/members/ag-grid.json b/data/members/ag-grid.json similarity index 93% rename from src/content/members/ag-grid.json rename to data/members/ag-grid.json index cf41d686..152eae6f 100644 --- a/src/content/members/ag-grid.json +++ b/data/members/ag-grid.json @@ -1,4 +1,7 @@ { + "id": "ag-grid", + "role": "Member", + "active": true, "name": "AG Grid", "description": "AG Grid is the best JavaScript Data Grid in the world, and now AG Charts is the best JavaScript Charting Library in the world, too. Since our foundation in 2016, we've always been strong supporters of the Open Source ecosystem, and we're proud to formalise this by joining the Open Source Pledge.", "shortDescription": "Since our foundation in 2016, we've always been strong supporters of the Open Source ecosystem.", diff --git a/src/content/members/antithesis.json b/data/members/antithesis.json similarity index 93% rename from src/content/members/antithesis.json rename to data/members/antithesis.json index 7fe52b7d..213d7edc 100644 --- a/src/content/members/antithesis.json +++ b/data/members/antithesis.json @@ -1,4 +1,7 @@ { + "id": "antithesis", + "role": "Innovator", + "active": true, "name": "Antithesis", "description": "We build an autonomous testing platform. We rely heavily on the countless hours of work contributed by OSS maintainers worldwide for everything from our internal Nix tooling to core components of our product like our deterministic hypervisor, a fork of FreeBSD.", "shortDescription": "We rely heavily on the countless hours of work contributed by OSS maintainers worldwide.", diff --git a/src/content/members/astral.json b/data/members/astral.json similarity index 93% rename from src/content/members/astral.json rename to data/members/astral.json index 463eadd1..87414f3d 100644 --- a/src/content/members/astral.json +++ b/data/members/astral.json @@ -1,4 +1,7 @@ { + "id": "astral", + "role": "Innovator", + "active": true, "name": "Astral", "description": "Open source software is at the heart of Astral. We're excited to establish a precedent for giving early and often. We hope to encourage, empower, and thank open source maintainers that inspire us.", "shortDescription": "We're excited to establish a precedent for giving early and often.", diff --git a/src/content/members/bolt.json b/data/members/bolt.json similarity index 95% rename from src/content/members/bolt.json rename to data/members/bolt.json index b12ce601..8f769aa4 100644 --- a/src/content/members/bolt.json +++ b/data/members/bolt.json @@ -1,4 +1,7 @@ { + "id": "bolt", + "role": "Innovator", + "active": true, "name": "Bolt", "description": "Bolt lets you prompt, run, edit, and deploy full-stack web and mobile apps. We depend on Open Source to build our products. We also have a close relationship with Open Source teams. They are avid users of our tools and help us improve them in a close feedback loop. We have been involved in key projects, hiring maintainers of infrastructure projects like Vite and Vitest and organizing ViteConf, a free online event for the Web community. We're happy to continue giving back to the Open Source community by joining the Open Source Pledge.", "shortDescription": "We're happy to continue giving back to the Open Source community by joining the Open Source Pledge.", diff --git a/src/content/members/browserbase.json b/data/members/browserbase.json similarity index 92% rename from src/content/members/browserbase.json rename to data/members/browserbase.json index eaaef741..ff59fc2d 100644 --- a/src/content/members/browserbase.json +++ b/data/members/browserbase.json @@ -1,4 +1,7 @@ { + "id": "browserbase", + "role": "Innovator", + "active": false, "name": "Browserbase", "description": "Headless browsers that work everywhere, every time. Control fleets of stealth browsers to build reliable browser automations. We believe in the power of open source software and its impact on the growth of the tech community.", "shortDescription": "We believe in the power of open source software and its impact on the growth of the tech community.", diff --git a/src/content/members/buttondown.json b/data/members/buttondown.json similarity index 89% rename from src/content/members/buttondown.json rename to data/members/buttondown.json index 28f6b650..f2672236 100644 --- a/src/content/members/buttondown.json +++ b/data/members/buttondown.json @@ -1,4 +1,7 @@ { + "id": "buttondown", + "role": "Innovator", + "active": true, "name": "Buttondown", "description": "Buttondown is the easiest way to send emails and grow your audience.", "shortDescription": "We — and you, even if you don't know it — would be nowhere without open source software.", diff --git a/src/content/members/chieftools.json b/data/members/chieftools.json similarity index 92% rename from src/content/members/chieftools.json rename to data/members/chieftools.json index bcd4cf6e..cf012943 100644 --- a/src/content/members/chieftools.json +++ b/data/members/chieftools.json @@ -1,4 +1,7 @@ { + "id": "chieftools", + "role": "Innovator", + "active": true, "name": "Chief Tools", "description": "Building, deploying and monitoring. Our tools supporting your applications.", "shortDescription": "We're joining the Open Source Pledge because our business is built on and with open-source software.", diff --git a/src/content/members/convex.json b/data/members/convex.json similarity index 93% rename from src/content/members/convex.json rename to data/members/convex.json index d34957bc..13c18676 100644 --- a/src/content/members/convex.json +++ b/data/members/convex.json @@ -1,4 +1,7 @@ { + "id": "convex", + "role": "Member", + "active": true, "name": "Convex", "description": "Like every software company, Convex is built extensively on open source software. The developers who maintain these open source projects pour incredible amounts of time, expertise, and care into their work–usually as a labor of love. It's time for beneficiary companies to do what they can to foster more sustainable stewardship of OSS projects.", "shortDescription": "It's time for beneficiary companies to do what they can to foster more sustainable stewardship of OSS projects.", diff --git a/src/content/members/foxley-talent.json b/data/members/foxley-talent.json similarity index 94% rename from src/content/members/foxley-talent.json rename to data/members/foxley-talent.json index 42edc5d8..f96d0465 100644 --- a/src/content/members/foxley-talent.json +++ b/data/members/foxley-talent.json @@ -1,4 +1,7 @@ { + "id": "foxley-talent", + "role": "Member", + "active": true, "name": "Foxley Talent", "description": "Foxley Talent is a specialist recruiter for the Python and Django ecosystem. We operate in the United States, Europe and in the UK. We have supported companies to hire Python and Django Developers since 2008 and can help your business to find your next great hire. We are regulars at DjangoCon in the United States and in Europe providing our clients with unique access to the people we meet during the conferences. Our mission is to become THE go-to recruiter for all Python and Django Developer hiring within the regions we support giving back to the community whenever we can along the way.", "shortDescription": "Our mission is to become THE go-to recruiter for all Python and Django Developer hiring within the regions we support giving back to the community whenever we can along the way.", diff --git a/src/content/members/frontend-masters.json b/data/members/frontend-masters.json similarity index 90% rename from src/content/members/frontend-masters.json rename to data/members/frontend-masters.json index 9f5b16fb..246fb2ec 100644 --- a/src/content/members/frontend-masters.json +++ b/data/members/frontend-masters.json @@ -1,4 +1,7 @@ { + "id": "frontend-masters", + "role": "Innovator", + "active": true, "name": "Frontend Masters", "description": "Your path to senior developer and beyond! 🚀 In-depth frontend & fullstack courses.", "shortDescription": "Many maintainers have considered quitting due to burnout. We want to shine a light on this threat to open source.", diff --git a/src/content/members/gitbook.json b/data/members/gitbook.json similarity index 94% rename from src/content/members/gitbook.json rename to data/members/gitbook.json index c9510713..87552001 100644 --- a/src/content/members/gitbook.json +++ b/data/members/gitbook.json @@ -1,4 +1,7 @@ { + "id": "gitbook", + "role": "Member", + "active": true, "name": "GitBook", "description": "GitBook is a modern documentation platform that empowers teams to create and publish API and product documentation—often the first touchpoint for users exploring a feature or product. Understanding that great documentation is as vital as the product itself, GitBook transforms static pages into dynamic, AI-powered knowledge experiences that evolve alongside your product. With an intuitive interface and seamless integrations, GitBook makes it easy to keep documentation fresh, relevant, and genuinely engaging.", "shortDescription": "GitBook is a modern documentation platform that empowers teams to create and publish API and product documentation.", diff --git a/src/content/members/gitbutler.json b/data/members/gitbutler.json similarity index 94% rename from src/content/members/gitbutler.json rename to data/members/gitbutler.json index fc39b37c..0ba26e23 100644 --- a/src/content/members/gitbutler.json +++ b/data/members/gitbutler.json @@ -1,4 +1,7 @@ { + "id": "gitbutler", + "role": "Innovator", + "active": true, "name": "GitButler", "description": "GitButler is built off a vast array of amazing open source projects, such as SvelteKit, Tauri, Rails and more. We believe that it's important to do what we can to support maintainers and help make the Open Source community sustainable.", "shortDescription": "We believe that it's important to do what we can to support maintainers and help make the Open Source community sustainable.", diff --git a/src/content/members/herodevs.json b/data/members/herodevs.json similarity index 94% rename from src/content/members/herodevs.json rename to data/members/herodevs.json index e3df468b..9f1851f1 100644 --- a/src/content/members/herodevs.json +++ b/data/members/herodevs.json @@ -1,4 +1,7 @@ { + "id": "herodevs", + "role": "Innovator", + "active": true, "name": "HeroDevs", "description": "Drop-in replacements for deprecated open source Software that keep you secure, compliant, and compatible.", "shortDescription": "We believe it's essential to give back to the community that powers so much of our work.", diff --git a/src/content/members/httptoolkit.json b/data/members/httptoolkit.json similarity index 95% rename from src/content/members/httptoolkit.json rename to data/members/httptoolkit.json index 20102352..aecbcbaf 100644 --- a/src/content/members/httptoolkit.json +++ b/data/members/httptoolkit.json @@ -1,4 +1,7 @@ { + "id": "httptoolkit", + "role": "Innovator", + "active": true, "name": "HTTP Toolkit", "description": "HTTP Toolkit is an open-source tool for debugging, testing & building with HTTP. Like all modern software projects, it stands on the shoulders of a huge variety of other open-source components. It aims to return a substantial portion of revenue back to these key dependencies, to fund maintainers, ensure each project's longevity, and help grow the open-source community.", "shortDescription": "Like all modern software projects, we stand on the shoulders of a huge variety of other open-source components.", diff --git a/src/content/members/keygen.json b/data/members/keygen.json similarity index 95% rename from src/content/members/keygen.json rename to data/members/keygen.json index ab2d3b5d..692694ba 100644 --- a/src/content/members/keygen.json +++ b/data/members/keygen.json @@ -1,4 +1,7 @@ { + "id": "keygen", + "role": "Innovator", + "active": true, "name": "Keygen", "description": "Keygen is a Fair Source software licensing and distribution API. Being built on top of Open Source, we strive to give back to the community whenever possible with our own Open Source projects as well as our funds.", "shortDescription": "Being built on top of Open Source, we strive to give back to the community whenever possible with our own Open Source projects as well as our funds.", diff --git a/src/content/members/laravel.json b/data/members/laravel.json similarity index 94% rename from src/content/members/laravel.json rename to data/members/laravel.json index 59d13b42..decaa8af 100644 --- a/src/content/members/laravel.json +++ b/data/members/laravel.json @@ -1,4 +1,7 @@ { + "id": "laravel", + "role": "Innovator", + "active": false, "name": "Laravel", "description": "Laravel is the PHP framework for web artisans. Participating in the Open Source Pledge is just one of the ways we continue to invest in the future of open source. By supporting these projects—both financially and through direct contributions—we hope to ensure that the tools developers rely on remain powerful, reliable, and innovative.", "shortDescription": "Participating in the Open Source Pledge is just one of the ways we continue to invest in the future of open source.", diff --git a/src/content/members/pixee.json b/data/members/pixee.json similarity index 93% rename from src/content/members/pixee.json rename to data/members/pixee.json index 84ceef41..68f1cf3f 100644 --- a/src/content/members/pixee.json +++ b/data/members/pixee.json @@ -1,4 +1,7 @@ { + "id": "pixee", + "role": "Innovator", + "active": false, "name": "Pixee", "description": "We build an automated product security engineer for security and development teams. We automatically fix vulnerabilities, triage code scan results, and harden code to give teams more time to focus on the work that matters. And we believe in Open Source! We leverage tools from the community and strive to give back when possible. Through donations and direct contributions to OSS projects and foundations. ", "shortDescription": "We believe in Open Source! We leverage tools from the community and strive to give back when possible.", diff --git a/src/content/members/platformatic.json b/data/members/platformatic.json similarity index 94% rename from src/content/members/platformatic.json rename to data/members/platformatic.json index e5364c54..0b26228b 100644 --- a/src/content/members/platformatic.json +++ b/data/members/platformatic.json @@ -1,4 +1,7 @@ { + "id": "platformatic", + "role": "Innovator", + "active": true, "name": "Platformatic", "description": "Platformatic is the application platform for building, managing, and scaling Node.js applications. Platformatic eliminates the need for manual scaling, reduces the risk of unpredictable downtimes, and provides detailed, real-time Node-specific insights, providing engineering teams with the visibility, control, and best-practices needed to operate effortlessly.", "shortDescription": "Contributing to open source projects fosters a strong, collaborative community, benefiting everyone involved.", diff --git a/src/content/members/posit.json b/data/members/posit.json similarity index 95% rename from src/content/members/posit.json rename to data/members/posit.json index 33f3f63b..3a7bba23 100644 --- a/src/content/members/posit.json +++ b/data/members/posit.json @@ -1,4 +1,7 @@ { + "id": "posit", + "role": "Member", + "active": true, "name": "Posit", "description": "Posit creates open-source software like RStudio, Shiny, and Quarto to advance data science, research, and technical communication. We're deeply committed to sustaining open-source ecosystems by supporting projects and organizations that drive innovation. Joining the Open Source Pledge reinforces our dedication to the community and our role as stewards of open-source development.", "shortDescription": "We're deeply committed to sustaining open-source ecosystems by supporting projects and organizations that drive innovation. Joining the Open Source Pledge reinforces our dedication to the community.", diff --git a/src/content/members/prefect.json b/data/members/prefect.json similarity index 94% rename from src/content/members/prefect.json rename to data/members/prefect.json index 6a17d2fa..4ed7c97d 100644 --- a/src/content/members/prefect.json +++ b/data/members/prefect.json @@ -1,4 +1,7 @@ { + "id": "prefect", + "role": "Innovator", + "active": true, "name": "Prefect", "description": "Prefect is an open source workflow orchestration framework, supporting a wide range of workflow architectures including batch, event-driven, fully dynamic, and hybrid architecture.", "shortDescription": "By supporting these projects, we're not just giving back—we're investing in the future of open source.", diff --git a/src/content/members/private-packagist.json b/data/members/private-packagist.json similarity index 93% rename from src/content/members/private-packagist.json rename to data/members/private-packagist.json index 20b24339..911a3f10 100644 --- a/src/content/members/private-packagist.json +++ b/data/members/private-packagist.json @@ -1,4 +1,7 @@ { + "id": "private-packagist", + "role": "Innovator", + "active": true, "name": "Private Packagist", "description": "Private Packagist is a PHP Composer package repository. It's built with the help of a large number of open-source software. As open-source maintainers ourselves we wish to do our part in helping the entire ecosystem of open-source software thrive.", "shortDescription": "As open-source maintainers ourselves we wish to do our part in helping the entire ecosystem of open-source software thrive.", diff --git a/src/content/members/pydantic.json b/data/members/pydantic.json similarity index 96% rename from src/content/members/pydantic.json rename to data/members/pydantic.json index 8dda031f..c9308277 100644 --- a/src/content/members/pydantic.json +++ b/data/members/pydantic.json @@ -1,4 +1,7 @@ { + "id": "pydantic", + "role": "Innovator", + "active": true, "name": "Pydantic Logfire", "description": "

The Pydantic Stack gives developers visibility to stay in flow, from local to production, from AI to API.

Pydantic Validation is the most widely used data validation library for Python—downloaded millions of times daily by thousands of developers worldwide. It enforces type annotations, plays nicely with type checkers and IDEs, and is both fast and extensible.

Pydantic AI is a Python agent framework built to help you confidently ship production-grade applications with Generative AI. Type-safe, model-agnostic, and ready for real work.

Pydantic Logfire is an observability platform built on OpenTelemetry. With SDKs for Python, TypeScript, and Rust, Logfire provides comprehensive observability for any workload in any language—with first-class support for AI, from LLM API calls to agent frameworks.

", "shortDescription": "For too long the open source ecosystem has been taken for granted. We're proud to be part of the movement to change that.", diff --git a/src/content/members/rector.json b/data/members/rector.json similarity index 94% rename from src/content/members/rector.json rename to data/members/rector.json index f7a84490..366e0edd 100644 --- a/src/content/members/rector.json +++ b/data/members/rector.json @@ -1,4 +1,7 @@ { + "id": "rector", + "role": "Member", + "active": false, "name": "Rector", "description": "Rector is a PHP tool that you can run on any PHP project to get an instant upgrade or automated refactoring. It gives you safety and lightning speed, and helps you with PHP upgrades, framework upgrades, and improving your code quality. Rector is alive thanks to Open Source projects and the developers behind those projects.", "shortDescription": "Rector is alive thanks to Open Source projects and the developers behind those projects.", diff --git a/src/content/members/saas-pegasus.json b/data/members/saas-pegasus.json similarity index 93% rename from src/content/members/saas-pegasus.json rename to data/members/saas-pegasus.json index 716afb76..30ec139a 100644 --- a/src/content/members/saas-pegasus.json +++ b/data/members/saas-pegasus.json @@ -1,4 +1,7 @@ { + "id": "saas-pegasus", + "role": "Member", + "active": true, "name": "SaaS Pegasus", "description": "SaaS Pegasus is a starter project template that helps you create and deploy SaaS applications with Python and Django. While SaaS Pegasus is — at least for now — a closed-source, proprietary product, it is built on top of a vast Open Source ecosystem. Adopting the Open Source Pledge felt like a good way to give back to the developers whose work has made Pegasus possible.", "shortDescription": "SaaS Pegasus is built on top of a vast Open Source ecosystem. Adopting the Open Source Pledge felt like a good way to give back to the developers whose work has made Pegasus possible.", diff --git a/src/content/members/sanity.json b/data/members/sanity.json similarity index 95% rename from src/content/members/sanity.json rename to data/members/sanity.json index 265bd873..b477f3f3 100644 --- a/src/content/members/sanity.json +++ b/data/members/sanity.json @@ -1,4 +1,7 @@ { + "id": "sanity", + "role": "Member", + "active": true, "name": "Sanity", "description": "Sanity is the content operating system that lets teams structure content and deliver it through APIs to digital products. We're joining the Open Source Pledge because we believe in supporting the open source projects and maintainers that our platform is built on. As a company, we have the privilege of using and benefiting from many open source libraries, frameworks and tools. By committing to give back financially to the open source ecosystem, we hope to play a small part in making open source more sustainable.", "shortDescription": "We're joining the Open Source Pledge because we believe in supporting the open source projects and maintainers that our platform is built on.", diff --git a/src/content/members/scalar.json b/data/members/scalar.json similarity index 90% rename from src/content/members/scalar.json rename to data/members/scalar.json index 2f149980..a0a44acc 100644 --- a/src/content/members/scalar.json +++ b/data/members/scalar.json @@ -1,4 +1,7 @@ { + "id": "scalar", + "role": "Member", + "active": true, "name": "Scalar", "description": "Scalar builds open-source tools for APIs, with first class OpenAPI support.", "shortDescription": "Thanks to all the maintainers and contributors whose software we are privileged to use and build with.", diff --git a/src/content/members/sentry.json b/data/members/sentry.json similarity index 96% rename from src/content/members/sentry.json rename to data/members/sentry.json index 9ce92a28..f705136e 100644 --- a/src/content/members/sentry.json +++ b/data/members/sentry.json @@ -1,4 +1,7 @@ { + "id": "sentry", + "role": "Innovator", + "active": true, "name": "Sentry", "description": "Sentry started life as an Open Source side project in 2008. We grew that way for years before commercializing. We've always given back to the community and now we're happy to help other companies do the same through the Open Source Pledge.", "shortDescription": "We've always given back to the community and now we're happy to help other companies do the same through the Open Source Pledge.", diff --git a/src/content/members/speakeasy.json b/data/members/speakeasy.json similarity index 91% rename from src/content/members/speakeasy.json rename to data/members/speakeasy.json index 6b85c4ed..dd5b5a3b 100644 --- a/src/content/members/speakeasy.json +++ b/data/members/speakeasy.json @@ -1,4 +1,7 @@ { + "id": "speakeasy", + "role": "Innovator", + "active": true, "name": "Speakeasy", "description": "We build tools that help developers create great APIs. Since the first days of the company, we've always supported the OSS projects that are platform is built on.", "shortDescription": "We've always supported the OSS projects that our platform is built on.", diff --git a/src/content/members/tideways.json b/data/members/tideways.json similarity index 93% rename from src/content/members/tideways.json rename to data/members/tideways.json index 241eb9c4..2a586661 100644 --- a/src/content/members/tideways.json +++ b/data/members/tideways.json @@ -1,4 +1,7 @@ { + "id": "tideways", + "role": "Innovator", + "active": true, "name": "Tideways", "description": "Tideways is a PHP Profiling, Monitoring and Exception Tracking tool. It's built on top of several Open Source libraries and frameworks in multiple languages.", "shortDescription": "We want to make a public commitment on our various open source contributions.", diff --git a/src/content/members/val-town.json b/data/members/val-town.json similarity index 89% rename from src/content/members/val-town.json rename to data/members/val-town.json index 03d56d39..3d77da51 100644 --- a/src/content/members/val-town.json +++ b/data/members/val-town.json @@ -1,4 +1,7 @@ { + "id": "val-town", + "role": "Innovator", + "active": true, "name": "Val Town", "description": "Val Town is a website to build apps, APIs, and scheduled functions.", "shortDescription": "Thanks so much to all the maintainers we depend on! We are so grateful for your work.", diff --git a/src/content/members/vlt.json b/data/members/vlt.json similarity index 90% rename from src/content/members/vlt.json rename to data/members/vlt.json index 2535d097..790cf33b 100644 --- a/src/content/members/vlt.json +++ b/data/members/vlt.json @@ -1,4 +1,7 @@ { + "id": "vlt", + "role": "Innovator", + "active": true, "name": "vlt", "description": "We’re building the future of JavaScript packages", "shortDescription": "We believe open source software is at the heart of modern businesses success.", diff --git a/src/content/members/voidzero.json b/data/members/voidzero.json similarity index 92% rename from src/content/members/voidzero.json rename to data/members/voidzero.json index 3fba6000..a2614b5d 100644 --- a/src/content/members/voidzero.json +++ b/data/members/voidzero.json @@ -1,4 +1,7 @@ { + "id": "voidzero", + "role": "Member", + "active": true, "name": "VoidZero", "description": "We are an open-source first company and we maintain Vite, Vitest, Rolldown, and Oxc. We also rely on many important dependencies and we'd love to give back.", "shortDescription": "We rely on many important dependencies and we love to give back.", diff --git a/src/content/members/zerodha.json b/data/members/zerodha.json similarity index 95% rename from src/content/members/zerodha.json rename to data/members/zerodha.json index 57fb9b2d..cc975e36 100644 --- a/src/content/members/zerodha.json +++ b/data/members/zerodha.json @@ -1,4 +1,7 @@ { + "id": "zerodha", + "role": "Member", + "active": true, "name": "Zerodha", "description": "At Zerodha, we have always been passionate about contributing to a sustainable ecosystem for Free (libre) and Open Source Software (FOSS). As one of India's largest stock brokers, our journey has been deeply intertwined with FOSS, which has been instrumental in our growth. We've relied on FOSS technologies to build and scale our products, business, and services. By joining the Open Source Pledge and sharing our journey, we hope to inspire other companies to take similar steps. Supporting the open source projects we rely on is not just a responsibility; it's an opportunity to contribute to a sustainable and innovative future for the tech industry.", "shortDescription": "By joining the Open Source Pledge and sharing our journey, we hope to inspire other companies to take similar steps.", diff --git a/public/generated/output/opengraph.png b/public/generated/output/opengraph.png new file mode 100644 index 00000000..dae25646 Binary files /dev/null and b/public/generated/output/opengraph.png differ diff --git a/public/generated/templates/opengraph.svg b/public/generated/templates/opengraph.svg index e6326fdc..125be36c 100644 --- a/public/generated/templates/opengraph.svg +++ b/public/generated/templates/opengraph.svg @@ -1,2 +1,2 @@ -$2,589,432paid to maintainers +$2,460,829paid to maintainers diff --git a/src/cache/jobSet.json b/src/cache/jobSet.json index 9634fa42..69f09614 100644 --- a/src/cache/jobSet.json +++ b/src/cache/jobSet.json @@ -1 +1 @@ -{"companies":{"antithesis":[{"title":"Business Development Representative","url":"https://jobs.ashbyhq.com/Antithesis/1b4b7498-1d36-4baf-be75-6e1f231aad17"},{"title":"Customer Success Engineer","url":"https://jobs.ashbyhq.com/Antithesis/2741edbf-2312-4960-a7dd-a9f7e6760244"},{"title":"Customer Success Engineer","url":"https://jobs.ashbyhq.com/Antithesis/3db48e68-50bf-48be-9d84-68ce4498d9fc"},{"title":"Customer Success Engineer","url":"https://jobs.ashbyhq.com/Antithesis/41acb777-5f13-45e2-8dfa-19db06a186f7"},{"title":"Customer Success Manager","url":"https://jobs.ashbyhq.com/Antithesis/0ba41385-8786-4c0d-bc65-ee95150c506d"},{"title":"Executive Assistant to the CEO","url":"https://jobs.ashbyhq.com/Antithesis/d7fab903-25ef-450f-a9c0-b29ac26b6490"},{"title":"Product Marketing Manager","url":"https://jobs.ashbyhq.com/Antithesis/ae8fbd92-1eca-4437-9d16-f05e5681be98"},{"title":"Senior Account Executive","url":"https://jobs.ashbyhq.com/Antithesis/06b1a668-e52a-42f2-8409-2d8b7be2fb2b"},{"title":"Senior Software Engineer","url":"https://jobs.ashbyhq.com/Antithesis/4a547eb9-4fed-4c8d-baf1-8ad084797d96"},{"title":"Solutions Engineer","url":"https://jobs.ashbyhq.com/Antithesis/c15056b6-ed73-4a3a-bee6-d2c9f20447dd"},{"title":"Solutions Engineer","url":"https://jobs.ashbyhq.com/Antithesis/9e409d07-0c85-43f3-bd55-fa1120eb8082"},{"title":"Solutions Engineer","url":"https://jobs.ashbyhq.com/Antithesis/d5aa1458-e84c-48dd-a3c5-e7e472b0ffb5"},{"title":"UI Engineer | Frontend Developer","url":"https://jobs.ashbyhq.com/Antithesis/0f965d12-fa14-487d-a71a-d470247d61fc"}],"astral":[{"title":"Open Source Software Engineer, uv","url":"https://jobs.ashbyhq.com/Astral/dd9c07f5-7f83-4bc7-aca5-24dcedfc1bfd"},{"title":"Software Engineer, Platform","url":"https://jobs.ashbyhq.com/Astral/5a477ffb-966a-4983-8196-05089f21af1e"}],"bolt":[{"title":"General Inquiries","url":"https://jobs.lever.co/stackblitz/c397ad9e-78b9-4f10-9a92-c9f73ac65e66"}],"browserbase":[{"title":"Developer Advocate Lead","url":"https://jobs.ashbyhq.com/Browserbase/1521ecbd-7d72-4c88-ba37-0e8b3aa57373"},{"title":"Sales Engineer","url":"https://jobs.ashbyhq.com/Browserbase/9df2fe4b-0efc-40c4-9aaf-bbe3a67849c7"},{"title":"Sales Engineer, Growth","url":"https://jobs.ashbyhq.com/Browserbase/7c431367-007f-4e7c-9a85-4c33024c5aab"},{"title":"Software Engineer (Director)","url":"https://jobs.ashbyhq.com/Browserbase/887f4a45-077f-4994-a3f1-eae9a2231e57"},{"title":"Software Engineer (Distributed Systems)","url":"https://jobs.ashbyhq.com/Browserbase/bcbf0fb9-2405-497b-bbc9-e09d8f7a4963"},{"title":"Software Engineer (Stagehand)","url":"https://jobs.ashbyhq.com/Browserbase/7724fbe3-6a27-4418-9705-2dcc40751a16"}],"gitbook":[{"title":" Business Development Representative","url":"https://jobs.ashbyhq.com/GitBook/2af016cc-8884-4699-9997-04a27d8ca568"},{"title":"Product Engineer","url":"https://jobs.ashbyhq.com/GitBook/18363361-516d-4324-b9a1-203aaf8cf3bd"},{"title":"Product Marketing","url":"https://jobs.ashbyhq.com/GitBook/4ed9706e-2af2-4686-ae1f-f5e6f19bf054"},{"title":"Technical Writer","url":"https://jobs.ashbyhq.com/GitBook/a32f063a-facd-473a-a577-726f6781ad31"}],"gitbutler":[{"title":"Senior Rust Developer","url":"https://jobs.gitbutler.com/jobs/backend-rust"},{"title":"Senior TypeScript Developer","url":"https://jobs.gitbutler.com/jobs/frontend-typescript"},{"title":"Gerrit Developer","url":"https://jobs.gitbutler.com/jobs/gerrit-developer"}],"herodevs":[],"laravel":[{"title":"Senior Software Engineer, Open Source Team","url":"https://apply.workable.com/laravel/j/159D3ECAF0/"},{"title":"Technical Support Engineer (US)","url":"https://apply.workable.com/laravel/j/A0B62F74D2/"},{"title":"Technical Support Engineer (APAC)","url":"https://apply.workable.com/laravel/j/36D0ABD834/"}],"posit":[{"title":"Senior Customer Success Manager","url":"https://posit.co/job-detail/?gh_jid=6585278003"},{"title":"Principal Software Engineer, Performance","url":"https://posit.co/job-detail/?gh_jid=6623489003"},{"title":"Senior Site Reliability Engineer, Cloud Operations","url":"https://posit.co/job-detail/?gh_jid=7379533003"},{"title":"Software Engineer","url":"https://posit.co/job-detail/?gh_jid=7502578003"},{"title":"Product Marketing Manager","url":"https://posit.co/job-detail/?gh_jid=6643241003"},{"title":"VP, Product Marketing","url":"https://posit.co/job-detail/?gh_jid=6727446003"},{"title":"Senior Engineer, testing infrastructure","url":"https://posit.co/job-detail/?gh_jid=6678790003"},{"title":"Major Accounts Manager","url":"https://posit.co/job-detail/?gh_jid=7174558003"},{"title":"Major Accounts Manager, Public Sector","url":"https://posit.co/job-detail/?gh_jid=7174952003"},{"title":"Partner Sales Manager","url":"https://posit.co/job-detail/?gh_jid=7212398003"},{"title":"Renewals Associate","url":"https://posit.co/job-detail/?gh_jid=7497771003"},{"title":"Senior Account Manager, Commercial ","url":"https://posit.co/job-detail/?gh_jid=7177032003"},{"title":"Project Manager, Solutions Engineering","url":"https://posit.co/job-detail/?gh_jid=6720245003"},{"title":"Senior Solutions Advisor","url":"https://posit.co/job-detail/?gh_jid=6685813003"},{"title":"Join Our Talent Community","url":"https://posit.co/job-detail/?gh_jid=5719118003"}],"prefect":[{"title":"Account Executive (Technical Sales)","url":"https://jobs.ashbyhq.com/Prefect/89278397-d38e-414d-b8d9-a0102dde1446"}],"sanity":[{"title":"Enterprise Account Executive","url":"https://jobs.ashbyhq.com/Sanity/29d2b4fb-a14d-4769-b984-54b275d2efda"},{"title":"Enterprise Account Executive, Nordics","url":"https://jobs.ashbyhq.com/Sanity/2a1dbe5a-ba4f-4fa4-9308-e5a2148499fb"},{"title":"Enterprise Account Manager","url":"https://jobs.ashbyhq.com/Sanity/2a3c282b-21f0-475b-8115-525275a209a4"},{"title":"GTM Recruiter","url":"https://jobs.ashbyhq.com/Sanity/b1d7dda9-a8ba-46a6-9de0-67d61fa0cb47"},{"title":"Head of Data & Analytics","url":"https://jobs.ashbyhq.com/Sanity/15606aff-a39e-409b-b11d-fda43d961eee"},{"title":"Product Marketing Manager, Enterprise","url":"https://jobs.ashbyhq.com/Sanity/baaf28ec-ff25-4ab7-a1d9-1c8e999509fa"},{"title":"Sales Development Representative","url":"https://jobs.ashbyhq.com/Sanity/80ac39bd-f36b-433e-9150-1c254cb779bc"},{"title":"Sales Development Representative (Outbound)","url":"https://jobs.ashbyhq.com/Sanity/5a3194e7-ca90-4780-93d5-20fb3106ae1a"},{"title":"Senior Full Stack Engineer, AI Applications","url":"https://jobs.ashbyhq.com/Sanity/371433d5-dc01-43c3-bb82-67ce2902488e"},{"title":"Senior Sales Manager, North America","url":"https://jobs.ashbyhq.com/Sanity/ba97b5b6-a85d-42e5-9cbd-9d07668b04d2"},{"title":"Senior Software Engineer, Content Lake DX","url":"https://jobs.ashbyhq.com/Sanity/423009ac-daf1-446c-8fb8-7af1decc6b9b"},{"title":"Senior Software Engineer - Core Services","url":"https://jobs.ashbyhq.com/Sanity/3773927c-1599-4d57-a7fb-902248aeb949"},{"title":"Senior Software Engineer, SDK - Systems","url":"https://jobs.ashbyhq.com/Sanity/0815c7d3-7da7-4e26-816e-c7b4d0df4f9e"},{"title":"Senior Solution Architect","url":"https://jobs.ashbyhq.com/Sanity/2efeec05-9eb0-4864-b505-62e952d685d7"},{"title":"Senior Solution Engineer, PreSales","url":"https://jobs.ashbyhq.com/Sanity/3bc60fd9-bdd5-48f5-99e1-8b1db6601554"},{"title":"Sr. Full Stack Engineer, New Venture ","url":"https://jobs.ashbyhq.com/Sanity/f3a83b1b-def6-4b69-a4ca-dd00de2e586c"},{"title":"Staff Solutions Engineer, PreSales","url":"https://jobs.ashbyhq.com/Sanity/7bb0c4d3-10e9-4e97-91a0-cfd1d0fc980a"}],"sentry":[],"speakeasy":[],"val-town":[{"title":"Product Engineer","url":"https://val-town.notion.site/product-engineer"},{"title":"Growth Engineer","url":"https://val-town.notion.site/growth-engineer"}]},"fetchedTimestamp":1761669326485} \ No newline at end of file +{"companies":{"antithesis":[{"title":"Business Development Representative","url":"https://jobs.ashbyhq.com/Antithesis/1b4b7498-1d36-4baf-be75-6e1f231aad17"},{"title":"Customer Success Engineer","url":"https://jobs.ashbyhq.com/Antithesis/2741edbf-2312-4960-a7dd-a9f7e6760244"},{"title":"Customer Success Engineer","url":"https://jobs.ashbyhq.com/Antithesis/3db48e68-50bf-48be-9d84-68ce4498d9fc"},{"title":"Customer Success Engineer","url":"https://jobs.ashbyhq.com/Antithesis/41acb777-5f13-45e2-8dfa-19db06a186f7"},{"title":"Customer Success Manager","url":"https://jobs.ashbyhq.com/Antithesis/0ba41385-8786-4c0d-bc65-ee95150c506d"},{"title":"Executive Assistant to the CEO","url":"https://jobs.ashbyhq.com/Antithesis/d7fab903-25ef-450f-a9c0-b29ac26b6490"},{"title":"Product Marketing Manager","url":"https://jobs.ashbyhq.com/Antithesis/ae8fbd92-1eca-4437-9d16-f05e5681be98"},{"title":"Senior Account Executive","url":"https://jobs.ashbyhq.com/Antithesis/06b1a668-e52a-42f2-8409-2d8b7be2fb2b"},{"title":"Senior Software Engineer","url":"https://jobs.ashbyhq.com/Antithesis/4a547eb9-4fed-4c8d-baf1-8ad084797d96"},{"title":"Solutions Engineer","url":"https://jobs.ashbyhq.com/Antithesis/c15056b6-ed73-4a3a-bee6-d2c9f20447dd"},{"title":"Solutions Engineer","url":"https://jobs.ashbyhq.com/Antithesis/9e409d07-0c85-43f3-bd55-fa1120eb8082"},{"title":"Solutions Engineer","url":"https://jobs.ashbyhq.com/Antithesis/d5aa1458-e84c-48dd-a3c5-e7e472b0ffb5"},{"title":"UI Engineer | Frontend Developer","url":"https://jobs.ashbyhq.com/Antithesis/0f965d12-fa14-487d-a71a-d470247d61fc"}],"astral":[{"title":"Open Source Software Engineer, uv","url":"https://jobs.ashbyhq.com/Astral/dd9c07f5-7f83-4bc7-aca5-24dcedfc1bfd"},{"title":"Software Engineer, Platform","url":"https://jobs.ashbyhq.com/Astral/5a477ffb-966a-4983-8196-05089f21af1e"}],"bolt":[{"title":"General Inquiries","url":"https://jobs.lever.co/stackblitz/c397ad9e-78b9-4f10-9a92-c9f73ac65e66"}],"gitbook":[{"title":" Business Development Representative","url":"https://jobs.ashbyhq.com/GitBook/2af016cc-8884-4699-9997-04a27d8ca568"},{"title":"Product Engineer","url":"https://jobs.ashbyhq.com/GitBook/18363361-516d-4324-b9a1-203aaf8cf3bd"},{"title":"Product Marketing","url":"https://jobs.ashbyhq.com/GitBook/4ed9706e-2af2-4686-ae1f-f5e6f19bf054"},{"title":"Technical Writer","url":"https://jobs.ashbyhq.com/GitBook/a32f063a-facd-473a-a577-726f6781ad31"}],"gitbutler":[{"title":"Senior Rust Developer","url":"https://jobs.gitbutler.com/jobs/backend-rust"},{"title":"Senior TypeScript Developer","url":"https://jobs.gitbutler.com/jobs/frontend-typescript"},{"title":"Gerrit Developer","url":"https://jobs.gitbutler.com/jobs/gerrit-developer"}],"herodevs":[],"posit":[{"title":"Senior Customer Success Manager","url":"https://posit.co/job-detail/?gh_jid=6585278003"},{"title":"Principal Software Engineer, Performance","url":"https://posit.co/job-detail/?gh_jid=6623489003"},{"title":"Senior Site Reliability Engineer, Cloud Operations","url":"https://posit.co/job-detail/?gh_jid=7379533003"},{"title":"Software Engineer","url":"https://posit.co/job-detail/?gh_jid=7502578003"},{"title":"Product Marketing Manager","url":"https://posit.co/job-detail/?gh_jid=6643241003"},{"title":"VP, Product Marketing","url":"https://posit.co/job-detail/?gh_jid=6727446003"},{"title":"Python Open-Source Developer","url":"https://posit.co/job-detail/?gh_jid=7510613003"},{"title":"Senior Engineer, testing infrastructure","url":"https://posit.co/job-detail/?gh_jid=6678790003"},{"title":"Major Accounts Manager","url":"https://posit.co/job-detail/?gh_jid=7174558003"},{"title":"Major Accounts Manager, Public Sector","url":"https://posit.co/job-detail/?gh_jid=7174952003"},{"title":"Partner Sales Manager","url":"https://posit.co/job-detail/?gh_jid=7212398003"},{"title":"Renewals Associate","url":"https://posit.co/job-detail/?gh_jid=7497771003"},{"title":"Senior Account Manager, Commercial ","url":"https://posit.co/job-detail/?gh_jid=7177032003"},{"title":"Project Manager, Solutions Engineering","url":"https://posit.co/job-detail/?gh_jid=6720245003"},{"title":"Senior Solutions Advisor","url":"https://posit.co/job-detail/?gh_jid=6685813003"},{"title":"Join Our Talent Community","url":"https://posit.co/job-detail/?gh_jid=5719118003"}],"prefect":[{"title":"Account Executive (Technical Sales)","url":"https://jobs.ashbyhq.com/Prefect/89278397-d38e-414d-b8d9-a0102dde1446"}],"sanity":[{"title":"Enterprise Account Executive","url":"https://jobs.ashbyhq.com/Sanity/29d2b4fb-a14d-4769-b984-54b275d2efda"},{"title":"Enterprise Account Executive, Nordics","url":"https://jobs.ashbyhq.com/Sanity/2a1dbe5a-ba4f-4fa4-9308-e5a2148499fb"},{"title":"Enterprise Account Manager","url":"https://jobs.ashbyhq.com/Sanity/2a3c282b-21f0-475b-8115-525275a209a4"},{"title":"GTM Recruiter","url":"https://jobs.ashbyhq.com/Sanity/b1d7dda9-a8ba-46a6-9de0-67d61fa0cb47"},{"title":"Head of Data & Analytics","url":"https://jobs.ashbyhq.com/Sanity/15606aff-a39e-409b-b11d-fda43d961eee"},{"title":"Product Marketing Manager, Enterprise","url":"https://jobs.ashbyhq.com/Sanity/baaf28ec-ff25-4ab7-a1d9-1c8e999509fa"},{"title":"Sales Development Representative","url":"https://jobs.ashbyhq.com/Sanity/80ac39bd-f36b-433e-9150-1c254cb779bc"},{"title":"Sales Development Representative (Outbound)","url":"https://jobs.ashbyhq.com/Sanity/5a3194e7-ca90-4780-93d5-20fb3106ae1a"},{"title":"Senior Full Stack Engineer, AI Applications","url":"https://jobs.ashbyhq.com/Sanity/371433d5-dc01-43c3-bb82-67ce2902488e"},{"title":"Senior Sales Manager, North America","url":"https://jobs.ashbyhq.com/Sanity/ba97b5b6-a85d-42e5-9cbd-9d07668b04d2"},{"title":"Senior Software Engineer, Content Lake DX","url":"https://jobs.ashbyhq.com/Sanity/423009ac-daf1-446c-8fb8-7af1decc6b9b"},{"title":"Senior Software Engineer - Core Services","url":"https://jobs.ashbyhq.com/Sanity/3773927c-1599-4d57-a7fb-902248aeb949"},{"title":"Senior Software Engineer, SDK - Systems","url":"https://jobs.ashbyhq.com/Sanity/0815c7d3-7da7-4e26-816e-c7b4d0df4f9e"},{"title":"Senior Solution Architect","url":"https://jobs.ashbyhq.com/Sanity/2efeec05-9eb0-4864-b505-62e952d685d7"},{"title":"Senior Solution Engineer, PreSales","url":"https://jobs.ashbyhq.com/Sanity/3bc60fd9-bdd5-48f5-99e1-8b1db6601554"},{"title":"Sr. Data Engineer, New Venture ","url":"https://jobs.ashbyhq.com/Sanity/a58fe1c9-c201-4d11-bb03-b72bec5fa51a"},{"title":"Sr. Full Stack Engineer, New Venture ","url":"https://jobs.ashbyhq.com/Sanity/f3a83b1b-def6-4b69-a4ca-dd00de2e586c"},{"title":"Staff Solutions Engineer, PreSales","url":"https://jobs.ashbyhq.com/Sanity/7bb0c4d3-10e9-4e97-91a0-cfd1d0fc980a"}],"sentry":[],"speakeasy":[],"val-town":[{"title":"Product Engineer","url":"https://val-town.notion.site/product-engineer"},{"title":"Growth Engineer","url":"https://val-town.notion.site/growth-engineer"}]},"fetchedTimestamp":1761689683786} \ No newline at end of file diff --git a/src/components/GridMemberList.astro b/src/components/GridMemberList.astro index cc6e99fc..d8e67db7 100644 --- a/src/components/GridMemberList.astro +++ b/src/components/GridMemberList.astro @@ -2,12 +2,14 @@ // © 2024 Vlad-Stefan Harbuz // SPDX-License-Identifier: Apache-2.0 -import { getMembers } from '../memberCollections.ts'; -import { sortMembersByDevs, fmtCurrency, fmtDevs, getDollarsPerDev } from '../memberData/common.ts'; -import type { Map } from '../util.ts'; -import memberRoles from "../memberRoles.json" +import { sortMembersByDevs, fmtCurrency, fmtDevs, getDollarsPerDev } from '../members/common.ts'; +import type { Member } from '../members/common.ts'; -const members = await getMembers(); +interface Props { + members: Member[]; +} + +const { members } = Astro.props; ---
@@ -16,23 +18,23 @@ const members = await getMembers();
- `The + `The

-
{member.data.name}
- {(memberRoles as Map)[member.id] != "Member" && -
{(memberRoles as Map)[member.id]}
+
{member.name}
+ {member.role != "Member" && +
{member.role}
}

- “{member.data.shortDescription}” + “{member.shortDescription}”
- {fmtDevs(member.data.annualReports[0].averageNumberOfDevs)} - {fmtCurrency(getDollarsPerDev(member.data.annualReports[0]))} / dev + {fmtDevs(member.annualReports[0].averageNumberOfDevs)} + {fmtCurrency(getDollarsPerDev(member.annualReports[0]))} / dev
)} diff --git a/src/components/MiniLeaderboard.astro b/src/components/MiniLeaderboard.astro index 15cb9010..575badee 100644 --- a/src/components/MiniLeaderboard.astro +++ b/src/components/MiniLeaderboard.astro @@ -2,11 +2,10 @@ // © 2024 Vlad-Stefan Harbuz // SPDX-License-Identifier: Apache-2.0 -import { getMembers } from '../memberCollections.ts'; -import { sortMembersByDevs } from '../memberData/common.ts'; +import { getMembers, sortMembersByDevs } from '../members/common.ts'; const N_TO_CHOOSE = 5; -const members = sortMembersByDevs(await getMembers()); +const members = sortMembersByDevs(getMembers()); --- @@ -14,8 +13,8 @@ const members = sortMembersByDevs(await getMembers()); {members.map((member, idx) => = N_TO_CHOOSE}> )} @@ -37,6 +36,7 @@ const members = sortMembersByDevs(await getMembers()); img { max-width: 3.5rem; margin-right: 1rem; + border-radius: 0.5rem; } a { display: flex; diff --git a/src/components/MiniMemberList.astro b/src/components/MiniMemberList.astro new file mode 100644 index 00000000..be43f604 --- /dev/null +++ b/src/components/MiniMemberList.astro @@ -0,0 +1,87 @@ +--- +// © 2025 Functional Software, Inc. dba Sentry +// SPDX-License-Identifier: Apache-2.0 + +import { sortMembersByDevs } from '../members/common.ts'; +import type { Member } from '../members/common.ts'; + +interface Props { + members: Member[]; +} + +const { members } = Astro.props; +--- + +
+
+ {sortMembersByDevs(members).map((member) => + +
+ `The +

+
{member.name}
+ {member.role != "Member" && +
{member.role}
+ } +

+
+
+ )} +
+
+ + diff --git a/src/components/TabularMemberList.astro b/src/components/TabularMemberList.astro index 8fd8ac5c..252e4bc4 100644 --- a/src/components/TabularMemberList.astro +++ b/src/components/TabularMemberList.astro @@ -2,10 +2,14 @@ // © 2024 Vlad-Stefan Harbuz // SPDX-License-Identifier: Apache-2.0 -import { getMembers } from '../memberCollections.ts'; -import { getDollarsPerDev, fmtCurrency, sortMembersByDevs } from '../memberData/common.ts'; +import { getDollarsPerDev, fmtCurrency, sortMembersByDevs } from '../members/common.ts'; +import type { Member } from '../members/common.ts'; -const members = await getMembers(); +interface Props { + members: Member[]; +} + +const { members } = Astro.props; ---
@@ -26,24 +30,24 @@ const members = await getMembers(); {sortMembersByDevs(members).map((member) => diff --git a/src/components/TotalTally.astro b/src/components/TotalTally.astro index dd54ca75..8d50d9a7 100644 --- a/src/components/TotalTally.astro +++ b/src/components/TotalTally.astro @@ -2,13 +2,12 @@ // © 2024 Vlad-Stefan Harbuz // SPDX-License-Identifier: Apache-2.0 -import { getGrandTotalRaised, fmtCurrency } from '../memberData/common.ts'; -import { getMembers } from '../memberCollections.ts'; +import { getMembers, getGrandTotalRaised, fmtCurrency } from '../members/common.ts'; ---

Our Members have paid maintainers

-

{fmtCurrency(getGrandTotalRaised(await getMembers()))}

+

{fmtCurrency(getGrandTotalRaised(getMembers()))}

over the last year.

diff --git a/src/content/config.ts b/src/content/config.ts deleted file mode 100644 index bcdb6ab0..00000000 --- a/src/content/config.ts +++ /dev/null @@ -1,13 +0,0 @@ -// © 2024 Functional Software, Inc. dba Sentry -// © 2024 Vlad-Stefan Harbuz -// SPDX-License-Identifier: Apache-2.0 - -import { defineCollection } from "astro:content"; -import { memberSchema } from '../schemas/members'; - -export const collections = { - members: defineCollection({ - type: "data", - schema: memberSchema, - }), -}; diff --git a/src/jobs/index.ts b/src/jobs/index.ts index d208c8db..de6bab48 100644 --- a/src/jobs/index.ts +++ b/src/jobs/index.ts @@ -4,8 +4,7 @@ import fetch from "node-fetch"; import * as cheerio from 'cheerio'; -import memberRoles from "../memberRoles.json"; -import type { Member } from "../schemas/members.ts"; +import { getMembers } from "../members/common.ts"; type Job = { title: string; @@ -234,14 +233,13 @@ export async function getJobSet() { companies: {}, fetchedTimestamp: +(new Date()), }; - const memberSlugs = Object.keys(memberRoles); - for (const memberSlug of memberSlugs) { - const member: Member = await import(`../content/members/${memberSlug}.json`); + const members = getMembers(); + for (const member of members) { if (member.jobsUrl) { - console.log(`Getting jobs for ${memberSlug} from ${member.jobsUrl}`); + console.log(`Getting jobs for ${member.id} from ${member.jobsUrl}`); const jobs = await getJobsForUrl(member.jobsUrl); console.log(`Got ${jobs.length} jobs`, jobs); - jobSet.companies[memberSlug] = jobs; + jobSet.companies[member.id] = jobs; } } return jobSet; diff --git a/src/memberCollections.ts b/src/memberCollections.ts deleted file mode 100644 index faee90e5..00000000 --- a/src/memberCollections.ts +++ /dev/null @@ -1,27 +0,0 @@ -// © 2024 Vlad-Stefan Harbuz -// SPDX-License-Identifier: Apache-2.0 - -import memberRoles from "./memberRoles.json" -import type { Map } from './util.ts'; -import type { MemberWithId, MemberMap } from "./schemas/members.ts"; -import { getCollection } from 'astro:content'; -import { sortReportsForMemberWithId } from "./memberData/common.ts"; - -export async function getMembers(): Promise { - return (await getCollection('members')) - .filter((member) => member.id in (memberRoles as Map)) - .filter((member) => member.data.annualReports.length > 0) - .map(sortReportsForMemberWithId); -} - -export async function getMembersAsMap(): Promise { - let emptyMemberMap: MemberMap = {}; - return (await getCollection('members')) - .filter((member) => member.id in (memberRoles as Map)) - .filter((member) => member.data.annualReports.length > 0) - .map(sortReportsForMemberWithId) - .reduce((memberMap, member) => { - memberMap[member.id] = member.data; - return memberMap; - }, emptyMemberMap); -} diff --git a/src/memberData/bin/getGrandTotalRaised.ts b/src/memberData/bin/getGrandTotalRaised.ts deleted file mode 100755 index 84cfc921..00000000 --- a/src/memberData/bin/getGrandTotalRaised.ts +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env -S npx tsx - -// © 2025 Functional Software, Inc. dba Sentry -// SPDX-License-Identifier: Apache-2.0 - -// Must be run in repository root. - -import fs from "fs"; - -import memberRoles from "../../memberRoles.json"; -import { sortReportsForMember, fmtCurrency } from "../common.ts"; - - -async function main() { - let grandTotal = 0; - const memberSlugs = Object.keys(memberRoles); - - for (const slug of memberSlugs) { - const localPath = `./src/content/members/${slug}.json`; - - let member = undefined; - try { - member = JSON.parse(fs.readFileSync(localPath).toString()); - } catch (e) { - console.error(`ERROR: could not load member data at ${localPath}`); - process.exit(1); - } - - member = sortReportsForMember(member); - grandTotal += member.annualReports[0].usdAmountPaid; - } - - console.log(fmtCurrency(grandTotal)); -} - - -main() diff --git a/src/memberRoles.json b/src/memberRoles.json deleted file mode 100644 index b2eb9f79..00000000 --- a/src/memberRoles.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "ag-grid": "Member", - "antithesis": "Innovator", - "astral": "Innovator", - "bolt": "Innovator", - "browserbase": "Innovator", - "buttondown": "Innovator", - "chieftools": "Innovator", - "convex": "Member", - "foxley-talent": "Member", - "frontend-masters": "Innovator", - "gitbook": "Member", - "gitbutler": "Innovator", - "herodevs": "Innovator", - "httptoolkit": "Innovator", - "keygen": "Innovator", - "laravel": "Innovator", - "pixee": "Innovator", - "platformatic": "Innovator", - "posit": "Member", - "prefect": "Innovator", - "private-packagist": "Innovator", - "pydantic": "Innovator", - "saas-pegasus": "Member", - "sanity": "Member", - "scalar": "Innovator", - "sentry": "Innovator", - "speakeasy": "Innovator", - "tideways": "Innovator", - "val-town": "Innovator", - "vlt": "Innovator", - "voidzero": "Member", - "zerodha": "Member" -} diff --git a/src/memberData/bin/archiveMembers.ts b/src/members/bin/archiveMembers.ts similarity index 82% rename from src/memberData/bin/archiveMembers.ts rename to src/members/bin/archiveMembers.ts index 6368394b..69bb0101 100755 --- a/src/memberData/bin/archiveMembers.ts +++ b/src/members/bin/archiveMembers.ts @@ -7,26 +7,16 @@ import fs from "fs"; import { execSync } from "child_process"; -import memberRoles from "../../memberRoles.json"; +import { getMembers } from "../../members/common.ts"; import dayjs from 'dayjs'; function main() { - const memberSlugs = Object.keys(memberRoles); - - for (const slug of memberSlugs) { - const localPath = `./src/content/members/${slug}.json`; - - let member = undefined; - try { - member = JSON.parse(fs.readFileSync(localPath).toString()); - } catch (e) { - console.error(`ERROR: could not load member data at ${localPath}`); - process.exit(1); - } + const members = getMembers(); + for (const member of members) { for (const report of member.annualReports) { - const archiveDir = `./archives/reports/${slug}/${report.year}/`; + const archiveDir = `./archives/reports/${member.id}/${report.year}/`; const archiveFilename = `${dayjs().toISOString()}.html`; const archivePath = `${archiveDir}${archiveFilename}`; const latestArchivePath = `${archiveDir}latest.html`; diff --git a/src/memberData/bin/checkMembers.ts b/src/members/bin/checkMembers.ts similarity index 71% rename from src/memberData/bin/checkMembers.ts rename to src/members/bin/checkMembers.ts index f85440d3..a8ecf05a 100755 --- a/src/memberData/bin/checkMembers.ts +++ b/src/members/bin/checkMembers.ts @@ -5,7 +5,6 @@ // Must be run in repository root. -import fs from 'fs'; import { Octokit } from '@octokit/rest'; import { @@ -15,7 +14,7 @@ import { isReportUrlNotRetrievable, makeIssueIfNotExists, } from '../common.ts'; -import memberRoles from '../../memberRoles.json'; +import { getMembers } from '../../members/common.ts'; import { MemberException } from '../common.ts'; @@ -27,20 +26,9 @@ async function main() { const GITHUB_TOKEN = process.env.GITHUB_TOKEN; const octokit = new Octokit({ auth: GITHUB_TOKEN }); - const memberIds = Object.keys(memberRoles); - - for (const id of memberIds) { - const jsonPath = `./src/content/members/${id}.json`; - console.log(`Checking member ${id} at ${jsonPath}`); - - let member = undefined; - try { - member = JSON.parse(fs.readFileSync(jsonPath).toString()); - } catch (e) { - console.error(`ERROR: could not load member data at ${jsonPath}`, e); - process.exit(1); - } + const members = getMembers(); + for (const member of members) { if (isReportOverdue(member)) { await makeIssueIfNotExists(octokit, MemberException.ReportOverdue, member); } else if (isReportDueSoon(member)) { diff --git a/src/members/bin/getGrandTotalRaised.ts b/src/members/bin/getGrandTotalRaised.ts new file mode 100755 index 00000000..7988605a --- /dev/null +++ b/src/members/bin/getGrandTotalRaised.ts @@ -0,0 +1,14 @@ +#!/usr/bin/env -S npx tsx + +// © 2025 Functional Software, Inc. dba Sentry +// SPDX-License-Identifier: Apache-2.0 + +// Must be run in repository root. + +import { getMembers, getGrandTotalRaised, fmtCurrency } from '../common.ts'; + +async function main() { + console.log(fmtCurrency(getGrandTotalRaised(getMembers()))); +} + +main(); diff --git a/src/memberData/common.ts b/src/members/common.ts similarity index 73% rename from src/memberData/common.ts rename to src/members/common.ts index ba35cf3c..a01abd10 100644 --- a/src/memberData/common.ts +++ b/src/members/common.ts @@ -1,10 +1,43 @@ // © 2024 Vlad-Stefan Harbuz +// © 2025 Functional Software, Inc. dba Sentry // SPDX-License-Identifier: Apache-2.0 +import fs from 'fs'; +import path from 'path'; import dayjs from 'dayjs'; -import fetch from "node-fetch"; +import fetch from 'node-fetch'; +import * as z from 'zod'; -import type { Member, MemberWithId, MemberReport } from "../schemas/members.ts"; +export const memberReportSchema = z.object({ + url: z.string().url(), + year: z.string(), + reportDate: z.string().date(), + averageNumberOfDevs: z.number().nonnegative(), + usdAmountPaid: z.number().nonnegative(), +}); + +export const memberSchema = z.object({ + id: z.string(), + role: z.enum(["Innovator", "Member", "Former Innovator", "Former Member"]), + active: z.boolean(), + name: z.string(), + description: z.string(), + shortDescription: z.string(), + url: z.string().url(), + jobsUrl: z.string().url().optional(), + blogProfileSlug: z.string().optional(), + joinDate: z.string().date(), + annualReports: memberReportSchema.array().nonempty(), +}); + +export type Member = z.infer; +export type MemberReport = z.infer; +export type MemberMap = { + [id: string]: Member; +}; +export type MemberParams = { + onlyInactive?: boolean; +}; export enum MemberException { ReportDueSoon, @@ -13,6 +46,8 @@ export enum MemberException { ReportUrlNotRetrievable, } +const MEMBER_ROOT = './data/members'; + const REPO_OWNER = 'opensourcepledge'; const REPO_NAME = 'opensourcepledge.com'; const EXCEPTION_LABEL = 'member-exception'; @@ -21,10 +56,10 @@ export function getDollarsPerDev(report: MemberReport) { return report.usdAmountPaid / report.averageNumberOfDevs; } -export function getGrandTotalRaised(members: MemberWithId[]) { +export function getGrandTotalRaised(members: Member[]) { let grandTotal = 0; members.forEach((member) => { - grandTotal += member.data.annualReports[0].usdAmountPaid; + grandTotal += member.annualReports[0].usdAmountPaid; }); return grandTotal; } @@ -43,19 +78,19 @@ export function fmtDevs(num: number) { /** * Sorts members by the average number of devs in their latest annual report. */ -export function sortMembersByDevs(members: MemberWithId[]): MemberWithId[] { +export function sortMembersByDevs(members: Member[]): Member[] { return [...members].sort((m1, m2) => { - if (m1.data.annualReports.length == 0) { + if (m1.annualReports.length == 0) { return 1; } - if (m2.data.annualReports.length == 0) { + if (m2.annualReports.length == 0) { return -1; } - const devs1 = m1.data.annualReports[0].averageNumberOfDevs; - const devs2 = m2.data.annualReports[0].averageNumberOfDevs; + const devs1 = m1.annualReports[0].averageNumberOfDevs; + const devs2 = m2.annualReports[0].averageNumberOfDevs; if (devs1 == devs2) { - const dpd1 = getDollarsPerDev(m1.data.annualReports[0]); - const dpd2 = getDollarsPerDev(m2.data.annualReports[0]); + const dpd1 = getDollarsPerDev(m1.annualReports[0]); + const dpd2 = getDollarsPerDev(m2.annualReports[0]); return dpd2 - dpd1; } else { return devs2 - devs1; @@ -66,25 +101,25 @@ export function sortMembersByDevs(members: MemberWithId[]): MemberWithId[] { /** * Sorts members by join date, newest first. */ -export function sortMembersByJoinDate(members: MemberWithId[]): MemberWithId[] { +export function sortMembersByJoinDate(members: Member[]): Member[] { return [...members].sort((m1, m2) => { - return m2.data.joinDate.localeCompare(m1.data.joinDate); + return m2.joinDate.localeCompare(m1.joinDate); }); } /** * Sorts members by the dollars per dev in their latest annual report. */ -export function sortMembersByDollarsPerDev(members: MemberWithId[]): MemberWithId[] { +export function sortMembersByDollarsPerDev(members: Member[]): Member[] { return [...members].sort((m1, m2) => { - if (m1.data.annualReports.length == 0) { + if (m1.annualReports.length == 0) { return 1; } - if (m2.data.annualReports.length == 0) { + if (m2.annualReports.length == 0) { return -1; } - const dpd1 = getDollarsPerDev(m1.data.annualReports[0]); - const dpd2 = getDollarsPerDev(m2.data.annualReports[0]); + const dpd1 = getDollarsPerDev(m1.annualReports[0]); + const dpd2 = getDollarsPerDev(m2.annualReports[0]); return dpd2 - dpd1; }); } @@ -104,13 +139,6 @@ export function sortReportsForMember(member: Member): Member { } } -export function sortReportsForMemberWithId(member: MemberWithId): MemberWithId { - return { - ...member, - data: sortReportsForMember(member.data), - }; -} - export function isReportDueSoon(member: Member) { const sortedMember = sortReportsForMember(member); const latestReportEndDate = dayjs(sortedMember.annualReports[0].reportDate); @@ -285,3 +313,30 @@ export async function makeIssueIfNotExists( console.log(`Creating exception issue: “${issueTitle}”`); createExceptionIssue(octokit, issueTitle, issueBody); } + +export function getMembers(params?: MemberParams): Member[] { + const memberPaths = fs.readdirSync('./data/members') + .filter((memberPath) => path.extname(memberPath) == '.json'); + const members = memberPaths.map((memberPath) => { + return memberSchema.parse( + JSON.parse( + fs.readFileSync( + path.join(MEMBER_ROOT, memberPath) + ).toString() + ) + ); + }); + return members + .filter((member) => member.active == !(params && params.onlyInactive)) + .filter((member) => member.annualReports.length > 0) + .map(sortReportsForMember); +} + +export function getMembersAsMap(params?: MemberParams): MemberMap { + let emptyMemberMap: MemberMap = {}; + return getMembers(params) + .reduce((memberMap, member) => { + memberMap[member.id] = member; + return memberMap; + }, emptyMemberMap); +} diff --git a/src/pages/about.astro b/src/pages/about.astro index d7ca60bd..98c266ce 100644 --- a/src/pages/about.astro +++ b/src/pages/about.astro @@ -8,10 +8,10 @@ import Button from "../components/Button.astro"; import Layout from "../layouts/Layout.astro"; import MiniLeaderboard from "../components/MiniLeaderboard.astro"; import TextButton from "../components/TextButton.astro"; -import { getMembers } from '../memberCollections.ts'; +import { getMembers } from '../members/common.ts'; import TableOfContents from "../components/TableOfContents.astro"; -const members = await getMembers(); +const members = getMembers(); --- diff --git a/src/pages/jobs.astro b/src/pages/jobs.astro index c16fdb69..e6b3a9ad 100644 --- a/src/pages/jobs.astro +++ b/src/pages/jobs.astro @@ -6,10 +6,10 @@ import Blob from "../components/Blob.astro"; import Layout from "../layouts/Layout.astro"; import type { JobSet } from "../jobs/index.ts"; import jobSetRaw from "../cache/jobSet.json"; -import { getMembersAsMap } from '../memberCollections.ts'; +import { getMembersAsMap } from '../members/common.ts'; const jobSet = jobSetRaw as JobSet; -const memberMap = await getMembersAsMap(); +const memberMap = getMembersAsMap(); --- {Object.keys(jobSet.companies).map((memberId) => - jobSet.companies[memberId].length > 0 && ( + (jobSet.companies[memberId].length > 0) && (memberId in memberMap) && (
`The diff --git a/src/pages/members/[id].astro b/src/pages/members/[id].astro index 25fbba5f..b19cc603 100644 --- a/src/pages/members/[id].astro +++ b/src/pages/members/[id].astro @@ -7,11 +7,8 @@ import Blob from "../../components/Blob.astro"; import Layout from "../../layouts/Layout.astro"; import TextButton from "../../components/TextButton.astro"; -import { getDollarsPerDev, fmtCurrency } from '../../memberData/common.ts'; -import { getMembers } from '../../memberCollections.ts'; -import type { Map } from '../../util.ts'; -import memberRoles from "../../memberRoles.json" -import type { Member } from "../../schemas/members.ts"; +import { getMembers, getDollarsPerDev, fmtCurrency } from '../../members/common.ts'; +import type { Member } from '../../members/common.ts'; import type { SanityDocument } from "@sanity/client"; import { sanityClient } from "sanity:client"; import { urlForImage } from '../../sanityUtil.ts'; @@ -27,12 +24,11 @@ interface Props { } const { member } = Astro.props; -const memberRole = (memberRoles as Map)[Astro.params.id]; export async function getStaticPaths() { - const memberItems = await getMembers(); - return memberItems.map(({ id, data }: { id: string, data: Member }) => { - return { params: { id }, props: { member: data } }; + const members = getMembers(); + return members.map((member) => { + return { params: { id: member.id }, props: { member } }; }); } @@ -77,7 +73,7 @@ const memberJobs = jobSet.companies[Astro.params.id]; `The

{member.name}
-
{memberRole}
+
{member.role}

diff --git a/src/pages/members/index.astro b/src/pages/members/index.astro index 3ab7d52d..348032bf 100644 --- a/src/pages/members/index.astro +++ b/src/pages/members/index.astro @@ -8,10 +8,16 @@ import Button from "../../components/Button.astro"; import Layout from "../../layouts/Layout.astro"; import GridMemberList from "../../components/GridMemberList.astro"; import TabularMemberList from "../../components/TabularMemberList.astro"; +import MiniMemberList from "../../components/MiniMemberList.astro"; import TotalTally from "../../components/TotalTally.astro"; import svgGrid from '../../assets/icons/grid.svg?raw'; import svgList from '../../assets/icons/list.svg?raw'; + +import { getMembers } from '../../members/common.ts'; + +const activeMembers = getMembers(); +const inactiveMembers = getMembers({ onlyInactive: true }); --- @@ -57,8 +63,13 @@ import svgList from '../../assets/icons/list.svg?raw';
- - + + + + +
+

Former Member Companies

+
diff --git a/src/schemas/members.ts b/src/schemas/members.ts deleted file mode 100644 index fc270349..00000000 --- a/src/schemas/members.ts +++ /dev/null @@ -1,34 +0,0 @@ -// © 2024 Functional Software, Inc. dba Sentry -// © 2024 Vlad-Stefan Harbuz -// SPDX-License-Identifier: Apache-2.0 - -import { z } from "astro:content"; - -export const memberReportSchema = z.object({ - url: z.string().url(), - year: z.string(), - reportDate: z.string().date(), - averageNumberOfDevs: z.number().nonnegative(), - usdAmountPaid: z.number().nonnegative(), -}); - -export const memberSchema = z.object({ - name: z.string(), - description: z.string(), - shortDescription: z.string(), - url: z.string().url(), - jobsUrl: z.string().url().optional(), - blogProfileSlug: z.string().optional(), - joinDate: z.string().date(), - annualReports: memberReportSchema.array().nonempty(), -}); - -export type Member = z.infer; -export type MemberReport = z.infer; -export interface MemberWithId { - id: string, - data: Member, -}; -export type MemberMap = { - [id: string]: Member; -};
- `The - {member.data.name} + `The + {member.name} - {member.data.annualReports[0].averageNumberOfDevs} + {member.annualReports[0].averageNumberOfDevs} - {fmtCurrency(getDollarsPerDev(member.data.annualReports[0]))} + {fmtCurrency(getDollarsPerDev(member.annualReports[0]))}