Skip to content

Instantly share code, notes, and snippets.

OpenAPI TypeScript Generator

First we start off with a very generic module for executing OpenAPI operations:

export interface ApiRequest {
  path: string;
  method: string;
  params?: Record<string, string>;
  queryParams?: Record<string, string>;

Multiple GitHub accounts (Work vs Personal)

This setup uses some tricks to ensure that the right email/name/ssh-key is used for the right repos without having to think about it ever again.

  • First generate two SSH keys, ~/.ssh/id_ed25519 and ~/.ssh/id_ed25519_work
  • Add one key to your personal account and the other to your work account

.ssh/config

Idea: Flat file system for file-based routing

Personally I've never liked how tools like Remix or NextJS have mapped a nested file system to routes. Simple things like "I want to put this component in its own file" become annoying tasks.

I've always been a fan of "flatter" file systems, my files often look like this:

/App/
async function validatePassword(password: string): string[] {
let errors = []
// 1. Don't regex for things you can trivially express in code
// -----------------------------------------------------------
// For example, we could have written this as `/^.{0,7}$/` but that's not
// nearly as clear as checking the length of the string.
if (password.length < 8) {
errors.push("Password must be at least 8 characters long")
/**
* If you have a volume input (0 to 1), such as a slider, use this to convert
* it to a logarithmic volume that is closer to human perception (0 to 1).
*
* A more robust approach would use a Fletcher-Munson curve, however this is a
* close enough approximation for most use cases.
*
* You can customize the curve to your liking, generally 3-4 is a good value.
*
* The inverse of this function is {@link convertVolumeToInput}.
@jamiebuilds
jamiebuilds / main.js
Created June 16, 2023 19:20
dialog.showSaveDialog force normalized extension
let { app, dialog } = require('electron')
let path = require("path")
let pathCompleteExtname = require("path-complete-extname")
app.whenReady().then(async () => {
for (let defaultPath of ['change-me.txt', 'change-me.tar.gz', 'change-me.jpeg']) {
let defaultExtname = pathCompleteExtname(defaultPath)
let defaultBasename = path.basename(defaultPath, defaultExtname)
let { canceled, filePath: selectedPath } = await dialog.showSaveDialog({
@jamiebuilds
jamiebuilds / main.js
Last active June 14, 2023 17:49
showSaveDialog without filters bug
let { app, dialog } = require('electron')
app.whenReady().then(async () => {
// case 1: without filters (BUG)
{
let { canceled, filePath } = await dialog.showSaveDialog({
defaultPath: "change-me.txt",
})
if (canceled) return
await dialog.showMessageBox({
@jamiebuilds
jamiebuilds / index.html
Last active June 14, 2023 17:10
Save Dialog
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
@jamiebuilds
jamiebuilds / gulpfile.js
Created October 5, 2015 20:45
Example multi-file gulp setup
export lint from './task-lint';
export test from './task-test';
export build from './task-build';
export dev from './task-dev';
export default dev;
export type QueryTemplateParam = string | number | undefined;
export type QueryFragmentValue = QueryFragment | QueryTemplateParam;
export type QueryFragment = [
{ fragment: string },
ReadonlyArray<QueryTemplateParam>
];
/**
* You can use tagged template literals to build "fragments" of SQL queries