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 / z-json.ts
Last active February 14, 2023 17:50
Zod JSON Coercion
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',
@christophemarois
christophemarois / sorting.ts
Created February 7, 2023 03:11
Native multiple sorting in 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
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
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)
@christophemarois
christophemarois / chain.ts
Last active January 9, 2023 19:58
Polymorphic sync/async typed chain explorations
import { orderBy } from 'lodash'
function chain<OriginalValue>(originalValue?: OriginalValue) {
let fns: Array<(val: any) => any> = []
function step<CurrentValue>(currentValue: CurrentValue) {
const methods = {
do<NextValue>(fn: (val: CurrentValue) => NextValue) {
fns.push(fn)
return step<Awaited<NextValue>>(
@christophemarois
christophemarois / ssl.sh
Created November 16, 2016 19:16
Configure a Let's Encrypt SSL Certificate for a static domain with Nginx on Ubuntu 16.04
# Info: https://certbot.eff.org/#ubuntuxenial-nginx
sudo apt-get update
sudo apt-get install letsencrypt
# Generate certificate
letsencrypt certonly --standalone -d example.com -d www.example.com
# Verify that four file have been created
sudo ls -l /etc/letsencrypt/live/example.com
# Install brew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install nvm
brew install nvm
# Install node 18
nvm install node 18
nvm alias default node 18
@christophemarois
christophemarois / custom-error.ts
Last active November 25, 2022 14:14
Extend built-in error
export class ResponseNotOkError extends Error {
constructor(public response: Response) {
super(`HTTP ${response.status}`)
// These lines can be added to further mimic Error magic properties.
// Never needed them, though.
this.name = this.constructor.name
Object.setPrototypeOf(this, ResponseNotOkError.prototype)
}
}
interface Serializable {
toString: () => string;
}
function groupBy<T>(els: T[], fn: (el: T) => Serializable) {
let out: Record<string, T[]> = {};
for (const el of els) {
const k = fn(el).toString();
@christophemarois
christophemarois / get_indefinite_article.rb
Created December 17, 2015 18:43
Programatically determine whether to use the “a” or “an” articles
def get_indefinite_article (noun_phrase)
chars = 'aedhilmnorsx'.split('')
regexp1 = /(?!FJO|[HLMNS]Y.|RY[EO]|SQU|(F[LR]?|[HL]|MN?|N|RH?|S[CHKLMNPTVW]?|X(YL)?)[AEIOU])[FHLMNRSX][A-Z]/
regexp2 = [/^e[uw]/, /^onc?e\b/, /^uni([^nmd]|mo)/, /^u[bcfhjkqrst][aeiou]/]
regexp3 = /^U[NK][AIEO]/
regexp4 = /^y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt)/
m = /\w+/.match noun_phrase
return "an" unless m