Skip to content

Instantly share code, notes, and snippets.

@andrewchilds
andrewchilds / README.md
Last active June 27, 2023 13:03
How to configure HAProxy to proxy h2 (encrypted http2) to h2c (plain text http2) connections

How to configure HAProxy to support end-to-end (e2e) connections that switch from h2 to h2c

This was tested to work using HAProxy 2.8 on Ubuntu 20.04.

Use case

This lets you run a cluster of API servers configured to use HTTP/2 (for example, Fastify using { http2: true }), behind a HAProxy LB, so that you can serve typical REST API requests as well as SSE (Server Side Events) streaming connections, so that you can do fancy real-time page updates in your app.

Using HTTP/2 resolves the "6 concurrent HTTP connections" limit imposed by Chrome/Firefox/etc, which means the web app would become unresponsive if the user has 6+ tabs open with the app simultaneously.

@andrewchilds
andrewchilds / saveGPT.bookmarklet.js
Last active January 11, 2024 13:21
Download ChatGPT conversations as markdown files.
javascript:function parseChatGPTData(data) { const mapping = data.mapping; const conversationTitle = data.title; const createDate = new Date(data.create_time * 1000).toISOString().slice(0, 10); const messagesArray = Object.values(mapping) .filter(node => node.message) .map(node => { const message = node.message; const sender = message.author.role === 'user' ? 'You' : 'Assistant'; const content = message.content.parts.join(''); const createTime = message.create_time; return { sender: sender, content: content, createTime: createTime, }; }); messagesArray.sort((a, b) => a.createTime - b.createTime); return { date: createDate, title: conversationTitle, messages: messagesArray.map(({ sender, content }) => ({ sender, content })), }; } function download(filename, text) { const element = document.createElement('a'); element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); element.setAttribute('download', filename); element.style.display = 'none'; document.body.appendChild(element); e
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@andrewchilds
andrewchilds / clientCache.js
Last active June 22, 2022 18:12
Proposal: A client-side key-value store interface
const tableName = 'crayons';
const crayons = [
{ id: 1, color: 'blue', updated_at: new Date('2022-02-10T11:31:25-0500') },
{ id: 2, color: 'black', updated_at: new Date('2022-02-10T11:31:25-0500') },
{ id: 3, color: 'brown', updated_at: new Date('2022-02-10T11:31:25-0500') },
{ id: 4, color: 'green', updated_at: new Date('2022-02-10T11:31:25-0500') },
];
// Upsert multiple entries to a table, returning number of rows changed
@andrewchilds
andrewchilds / getTimezoneOffset.js
Created May 3, 2022 18:37
Convert a minute-based timezone offset like `240` to an offset like `"-04:00"`.
// Converts a getTimezoneOffset() offset to one that can be used in new Date().
// Examples:
// Eastern Standard Time: 240 -> '-04:00'
// India Standard Time: -330 -> '+05:30'
// Australian Central Western Standard Time: -525 -> '+08:45'
export function getTimezoneOffset(d) {
return convertTimezoneOffset(d.getTimezoneOffset());
}
export function convertTimezoneOffset(offset) {
@andrewchilds
andrewchilds / deepClone.js
Created April 28, 2022 01:07
Simple, standalone, vanilla implementation of lodash.cloneDeep
// Simple implementation of lodash.cloneDeep
// Does not clone functions or handle recursive references.
export function deepClone(original) {
if (original instanceof RegExp) {
return new RegExp(original);
} else if (original instanceof Date) {
return new Date(original.getTime());
} else if (Array.isArray(original)) {
return original.map(deepClone);
} else if (typeof original === 'object' && original !== null) {
@andrewchilds
andrewchilds / deepGet.js
Last active January 11, 2023 22:38 — forked from harish2704/lodash.get.js
Simple, standalone, vanilla implementation of lodash.get
// Simple implementation of lodash.get
// Handles arrays, objects, and any nested combination of the two.
// Also handles undefined as a valid value - see test case for details.
// Based on: https://gist.github.com/harish2704/d0ee530e6ee75bad6fd30c98e5ad9dab
export function deepGet(obj, query, defaultVal) {
query = Array.isArray(query) ? query : query.replace(/(\[(\d)\])/g, '.$2').replace(/^\./, '').split('.');
if (!(query[0] in obj)) {
return defaultVal;
}
obj = obj[query[0]];
@andrewchilds
andrewchilds / google-meet-kindergarten-edition.css
Last active September 25, 2020 02:25
Google Meet: Kindergarten Edition
/*
This is a quick attempt to make Google Meet work better for someone in Kindergarten
(one in particular - it may or may not work for yours).
Requirements:
- I used the "Stylebot" extension to make this CSS work, but any other related extension should also work.
Changes:
- Hides the "who's here" rotating carousel which is distracting.
@andrewchilds
andrewchilds / redirect.js
Created November 1, 2017 19:47
How long does it take to stop JS execution during a location redirect?
for (var i = 0; i < 1000; i++) {
logAndRedirect(i);
}
window.location.href = window.location.href;
function logAndRedirect(i) {
setTimeout(function () {
window.console.log(i);
}, i);
@andrewchilds
andrewchilds / getAgeOfCodebase.sh
Created July 3, 2017 14:59
Creates a histogram CSV file of the age of every line of code in a directory
#!/bin/bash
set -e
APP_DIR="$1"
TEMP_FILE="codebase-age-histogram.txt"
OUTPUT_FILE="codebase-age-histogram.csv"
pushd $APP_DIR