Skip to content

Instantly share code, notes, and snippets.

Avatar

Christophe Marois christophemarois

  • Montreal
View GitHub Profile
@christophemarois
christophemarois / postgres-cls.md
Created April 12, 2023 16:13
Whitelist-based CLS (column level security) for Postgres
View postgres-cls.md

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
View local-caddy.md

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
View git-remove-file.md

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 / docker-cheatsheet.md
Last active March 16, 2023 17:29
Docker Cheatsheet
View docker-cheatsheet.md

Run a local Dockerfile with a local .env file

docker build \
  # Show complete log
  --progress=plain \
  # Nested dockerfile, useful for monorepos when we want to preserve CWD
  --file nested/a/Dockerfile \
  # tag can be anything
 -t nested:a \
@christophemarois
christophemarois / makeSingletonAsyncFn.ts
Last active February 25, 2023 21:50
Singleton Async Promises
View makeSingletonAsyncFn.ts
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
@christophemarois
christophemarois / z-json.ts
Last active February 14, 2023 17:50
Zod JSON Coercion
View z-json.ts
import * as z from 'zod'
function zJson <T extends z.ZodTypeAny> (schema: T) {
return z.string().transform((val, ctx) => {
try {
return JSON.parse(val)
} catch (err) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Invalid JSON',
View libs.md

TS/JS go-to libs

Updated Apr 2023. For looking up trending newer alternatives, use npmtrends. For Nodejs, nodejstoolbox a precompiled list of trends by category.

Mandatory

  • tsx esbuild-enhanced node replacement.

Universal

@christophemarois
christophemarois / sorting.ts
Created February 7, 2023 03:11
Native multiple sorting in TS
View sorting.ts
type User = {
name: string
age: number
}
const users: User[] = [
{ name: 'Adibou', age: 30 },
{ name: 'Bounda', age: 69 },
{ name: 'Adibou', age: 18 },
]
@christophemarois
christophemarois / StructuredError.ts
Last active January 18, 2023 18:16
Create errors with custom codes, values and messages with full type inference
View StructuredError.ts
export function createStructuredError<
ErrorCodes extends Record<any, (data: any) => string>
>(name: string, codes: ErrorCodes) {
return class StructuredError<
ErrorCode extends keyof ErrorCodes,
ErrorData extends Parameters<ErrorCodes[ErrorCode]>[0]
> extends Error {
constructor(
public readonly code: ErrorCode,
public readonly data: ErrorData
@christophemarois
christophemarois / EventEmitter.ts
Last active January 18, 2023 00:46
Generic EventEmitter
View EventEmitter.ts
class Emitter<Events extends Record<any, (val: any) => any>> {
private observers: Map<keyof Events, Set<(val: any) => any>> = new Map()
on<Name extends keyof Events, Fn extends Events[Name]>(name: Name, fn: Fn) {
if (!this.observers.has(name)) {
this.observers.set(name, new Set())
}
this.observers.get(name)!.add(fn)