Skip to content

Instantly share code, notes, and snippets.

View graffhyrum's full-sized avatar

Joshua Pendragon graffhyrum

View GitHub Profile
@graffhyrum
graffhyrum / Modify.ts
Created February 9, 2026 23:19
The `Modify` type is a utility type that creates a modified version of a given base type by omitting certain properties and making others optional.
declare const brand: unique symbol;
export type Brand<BaseType, TBrand extends PropertyKey> = BaseType & { [brand]: TBrand };
/**
* The `Modify` type is a utility type that creates a modified version of a given base type
* by omitting certain properties and making others optional. This is composed of three main
* operations:
*
* - Removes properties from the base type as specified in the `Omitted` parameter.
* - Makes properties optional as specified in the `Optional` parameter from the remaining keys
@graffhyrum
graffhyrum / rule-validator.ts
Created February 2, 2026 02:16
A validator script that lints for some common code smells I don't like.
// Rule Compliance Validator
// Scans code for violations of AGENTS.md rules
import path from "node:path";
import { glob } from "glob";
interface Rule {
name: string;
pattern: RegExp;
message: string;
@graffhyrum
graffhyrum / fixtures.ts
Created December 15, 2025 19:26
A Playwright fixture to catch browser console errors
import { test as base } from "@playwright/test";
export const test = base.extend({
page: async ({ baseURL, browser }, use, testInfo) => {
const context = await browser.newContext({ baseURL });
const page = await context.newPage();
const consoleErrors: string[] = [];
const pageErrors: string[] = [];
@graffhyrum
graffhyrum / range.ts
Last active December 14, 2025 22:22
TS Range
// No idea why this isn't stdlib
export function range(start: number, stop: number, step: number = 1): number[] {
if (!Number.isFinite(start) || !Number.isFinite(stop) || !Number.isFinite(step)) {
throw new TypeError("range(start, stop, step): start/stop/step must be finite numbers");
}
if (!Number.isInteger(start) || !Number.isInteger(stop) || !Number.isInteger(step)) {
throw new TypeError("range(start, stop, step): start/stop/step must be integers");
}
if (step === 0) {
throw new RangeError("range(start, stop, step): step must not be 0");
@graffhyrum
graffhyrum / 00_ComposablePageObject.md
Last active December 4, 2025 00:36
A Playwright Page (and Component) Object Model template.

Playwright Page and Component Object Model for Robust E2E Testing

This document outlines a highly effective and scalable pattern for organizing Playwright end-to-end tests using a combination of the Page Object Model (POM) and Component Object Model (COM). This approach dramatically enhances code reusability, improves test readability, and ensures maintainability as your application and test suite grow.

Introduction

In the realm of automated testing, particularly with frameworks like Playwright, managing selectors and test logic can become complex. Unstructured test suites often lead to brittle tests that are hard to understand and expensive to maintain. The POM and COM patterns address this by encapsulating the interactions and elements of your web application into dedicated objects.

  • Page Object Model (POM): Represents a distinct page within your web application. It abstracts the underlying HTML structure, providing high-level methods to interact with page elements and query their state. Thi
import {test, expect} from "bun:test";
import {getPagination, type PaginationArgs} from "./paginate.ts";
const baseArgs: PaginationArgs = {
currentPage: 7,
numberOfDisplayPages: 10,
resultsPerPage: 10,
totalResults: 175
}
@graffhyrum
graffhyrum / update_winget.ps1
Created October 13, 2025 13:52
From an admin powershell, silently update all packages installed with Winget. Also writes a log to the Desktop of the user.
# Check if winget is installed
try {
$winget = Get-Command winget -ErrorAction Stop
Write-Host "Winget is installed, proceeding with updates..." -ForegroundColor Green
} catch {
Write-Host "Winget is not installed. Please install winget first." -ForegroundColor Red
exit 1
}
# Create a log file with date stamp
@graffhyrum
graffhyrum / arktype-results.ts
Last active September 9, 2025 17:52
Wrap any arktype type in neverthrow to easily add validation to a callback chain.
import {ArkErrors, type TraversalError, type Type} from 'arktype';
import {err, ok, type Result} from 'neverthrow';
type ArkTypeOut<T> = T extends Type<infer Out, infer _Scope> ? Out : never;
type ArkTypeScope<T> = T extends Type<infer _Out, infer Scope> ? Scope : never;
export function toArkResult<
// biome-ignore lint/suspicious/noExplicitAny: generic function
ArkType extends Type<any,any>,
const Out extends ArkTypeOut<ArkType>,
@graffhyrum
graffhyrum / index.ts
Last active August 31, 2025 20:27
Validate and Parse process environment variables with Arktype.
import {validateAndParseEnv} from './schema';
const goodMockProcEnv = {
HOST: 'localhost',
PORT: '8080',
KEY: '1234567890',
SECRET: '1234567890',
EXTRAKEY: 'foo', // extra keys are ignored
};
@graffhyrum
graffhyrum / decompose-stepdown.md
Created August 29, 2025 23:34
LLM prompt to apply some of my most-used cleanups to a file.

This is a prompt I use often to improve readability of a module. With something like Claude custom slash commands you can map these to an alias for easy use.

Prompt:
Analyze this code in the context of the following rules and apply appropriate refactoring.

Summary:
The goal of these rules is to improve readability within a file. 
It is highly valuable to start at the top of the abstraction hierarchy