Skip to content

Instantly share code, notes, and snippets.

View christophemarois's full-sized avatar

Christophe Marois christophemarois

  • Pathway Medical
  • Montreal
View GitHub Profile
@christophemarois
christophemarois / git_list_branch_changes.sh
Created September 15, 2023 12:57
List branch changes in git
# list changed files in a branch compared to its base branch
# usage: git_list_branch_changes [base_branch?] | xargs echo
function git_list_branch_changes {
local BASE_BRANCH="${1:-main}"
(git diff --diff-filter=MA --name-only $BASE_BRANCH... && git status --short --porcelain | awk '{print $2}') | sort -u
}
@christophemarois
christophemarois / chunk.ts
Last active August 25, 2023 16:43
Chunking arrays and strings in typescript
function chunk<T>(arr: T[], size: number): T[][] {
const count = Math.ceil(arr.length / size)
let out: T[][] = []
for (let i = 0; i < count; i++) {
out.push(arr.slice(count * i, count * (i + 1)))
}
return out
}
function chunkString(str: string, size: number): string[] {
@christophemarois
christophemarois / node-cli-args-parse.ts
Created August 9, 2023 14:15
Native parsing for CLI args in node
import { parseArgs } from 'node:util'
const args = parseArgs({
options: { env: { type: 'string' }, dryRun: { type: 'boolean' } },
})
@christophemarois
christophemarois / node-inspector.ts
Last active July 12, 2023 18:24
Boot up a node inspector anywhere
// will act --inspect-brk, but only when evaluated
// works inside the main thread, a worker thread
// also works with typescript sources, built-in support in tsx
require('node:inspector').open(undefined, undefined, true)
@christophemarois
christophemarois / streamWretchResponseToFile.ts
Last active July 5, 2023 19:31
Stream a wretch response directly to a file in node.js
import wretch, { WretchResponse } from 'wretch'
import fs from 'node:fs'
async function streamWretchResponseToFile(
res: WretchResponse,
writable: fs.WriteStream,
) {
if (!res.body) {
throw new Error('Response body is null')
}
@christophemarois
christophemarois / zMapKeys.ts
Last active June 16, 2023 21:26
zMapKeys.ts
// Type inference does not work yet
export function zMapKeys<
Shape extends z.ZodRawShape,
T extends z.ZodObject<Shape>,
Key extends keyof Shape,
MapFn extends <T extends Key>(v: Shape[T], k: T) => PropertyKey,
>(schema: T, renameMap: Record<Key, PropertyKey | MapFn>) {
const keys = Object.keys(schema.shape) as Key[]
return schema.transform((input) => {
let output: Record<PropertyKey, any> = {}
@christophemarois
christophemarois / postgres-cls.md
Created April 12, 2023 16:13
Whitelist-based CLS (column level security) for Postgres

With the following generic trigger function:

CREATE OR REPLACE FUNCTION allow_updating_only()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
DECLARE
  whitelist TEXT[] := TG_ARGV::TEXT[];
  schema_table TEXT;
@christophemarois
christophemarois / local-caddy.md
Created March 23, 2023 17:04
Run a local HTTPS server

Use Caddy (brew install caddy) to run localhost sites with custom addresses and https certificates.

  1. Run caddy reverse-proxy --from site.localhost --to localhost:3000
  2. Visit https://site.localhost/

For a more stable config, create a Caddyfile

site.localhost {
 reverse_proxy localhost:3000
@christophemarois
christophemarois / git-remove-file.md
Last active March 18, 2023 19:39
Remove a file from git

Remove a file from git

Accidentally committed a 4GB file and GitHub (rightly) doesn't let you push? Commited a .env with your social insurance number?

  1. Back up the entire repo
  2. Run bfg --delete-files [filenames...] (brew install bfg)
  3. Follow instructions
@christophemarois
christophemarois / makeSingletonAsyncFn.ts
Last active February 25, 2023 21:50
Singleton Async Promises
export type SingletonAsyncFn<Args extends any[], Result> = {
getCurrent(): Promise<Result> | null
} & ((...args: Args) => Promise<Result>)
/** Wrap an async function so only one instance runs at the time
* and concurrent calls await the running instance
*
* @example
* const getRand = makeSingletonAsyncFn(() => new Promise<number>(resolve => setTimeout(() => resolve(Math.random()), 1000)))
* console.log(await Promise.all([getRand(), getRand()])) // e.g. [0.6214563565449123, 0.6214563565449123] after 1sec