Skip to content

Instantly share code, notes, and snippets.

View michaelbromley's full-sized avatar
👨‍💻
Writing colourful text files

Michael Bromley michaelbromley

👨‍💻
Writing colourful text files
View GitHub Profile
@michaelbromley
michaelbromley / hunt-shai-hulud.js
Created November 25, 2025 08:20
Script to detect packages affected by Shai Hulud's Return - Nov 2025
#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Source of this list:
@michaelbromley
michaelbromley / nigel-code-critic.md
Created November 10, 2025 14:50
Nigel, the code review agent
name description tools model color
nigel-code-critic
Use this agent when you need rigorous code review that prioritizes maintainability, proper abstractions, and system-wide consistency.
Glob, Grep, Read, WebFetch, TodoWrite, WebSearch, BashOutput, KillShell, NotebookEdit, AskUserQuestion
sonnet
cyan

You are Nigel, a grumpy but highly skilled senior developer with 20 years of battle-tested experience. You've seen every flavor of bad code, survived multiple rewrites, and developed a finely-tuned bullshit detector. You value craftsmanship, thoughtful abstractions, and code that will still make sense at 2am when something breaks in production.

@michaelbromley
michaelbromley / convert.js
Created November 13, 2023 13:50
Insomnia GraphQL export to Hoppscotch
const fs = require('node:fs');
const insomniaFilePath = './insomnia.json';
const outputFilePath = './hoppscotch.json';
const insomniaGraphqlExport = require(insomniaFilePath);
const folders = [];
const insomniaFolders = insomniaGraphqlExport.resources.filter(
@michaelbromley
michaelbromley / typesense-types.ts
Created April 21, 2021 18:50
Typesense TS type defs
export interface Typesense {
Client: TypesenseClientCtor;
}
export interface TypesenseDocument {
id: string;
}
export type TypesenseClientCtor = new (options: TypesenseClientOptions) => TypesenseClient;
@michaelbromley
michaelbromley / example-mysql-migration.ts
Created March 8, 2021 14:10
Vendure v1.0.0-beta.1 migration
import {MigrationInterface, QueryRunner} from "typeorm";
import { addToDefaultChannel, migratePaymentMethods } from '../migration-utils';
export class v100Beta11614933697219 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query("CREATE TABLE `tag` (`createdAt` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updatedAt` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `value` varchar(255) NOT NULL, `id` int NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`)) ENGINE=InnoDB", undefined);
await queryRunner.query("CREATE TABLE `asset_tags_tag` (`assetId` int NOT NULL, `tagId` int NOT NULL, INDEX `IDX_9e412b00d4c6cee1a4b3d92071` (`assetId`), INDEX `IDX_fb5e800171ffbe9823f2cc727f` (`tagId`), PRIMARY KEY (`assetId`, `tagId`)) ENGINE=InnoDB", undefined);
await queryRunner.query("CREATE TABLE `asset_channels_channel` (`assetId` int NOT NULL, `channelId` int NOT NULL, INDEX `IDX_dc4e7435f9f5e9e6436bebd33
@michaelbromley
michaelbromley / example-mysql-migration.ts
Created December 29, 2020 16:22
Vendure 0.18.0 Migration
import {MigrationInterface, QueryRunner} from "typeorm";
import { addProductVariantsToProductChannels,
migrateDefaultShippingCalculatorArgs,
migrateOrderAdjustmentsToSurcharges,
migrateOrderItemPromotionsAndTaxes, migrateOrderShippingToShippingLines } from '../migration-utils';
export class v18011609156046047 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query("ALTER TABLE `order_item` DROP FOREIGN KEY `FK_eed51be48640c21e1c76d3e9fbe`", undefined);
@michaelbromley
michaelbromley / vendure-0.14.0-migration.ts
Last active July 20, 2020 10:56
A TypeORM migration script for updating to Vendure v0.14.0
import { MigrationInterface, QueryRunner } from 'typeorm';
export class v01401594033611805 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(
'CREATE TABLE `authentication_method` (`createdAt` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updatedAt` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `identifier` varchar(255) NULL, `passwordHash` varchar(255) NULL, `verificationToken` varchar(255) NULL, `passwordResetToken` varchar(255) NULL, `identifierChangeToken` varchar(255) NULL, `pendingIdentifier` varchar(255) NULL, `strategy` varchar(255) NULL, `externalIdentifier` varchar(255) NULL, `metadata` text NULL, `id` int NOT NULL AUTO_INCREMENT, `type` varchar(255) NOT NULL, `userId` int NULL, INDEX `IDX_a23445b2c942d8dfcae15b8de2` (`type`), PRIMARY KEY (`id`)) ENGINE=InnoDB',
undefined,
);
await queryRunner.query(
`INSERT INTO authentication_method (identifier, passwordHash,
@michaelbromley
michaelbromley / index.html
Created July 3, 2020 09:22
Floating webcam script
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Display Webcam Stream</title>
<style>
</style>
</head>
@michaelbromley
michaelbromley / jest-config.js
Created June 18, 2020 14:19
MeiliSearch Issue Jest tests
module.exports = {
moduleFileExtensions: ['js', 'json', 'ts'],
rootDir: __dirname,
testRegex: '.spec.ts$',
transform: {
'^.+\\.ts$': 'ts-jest',
},
testEnvironment: 'node',
globals: {
'ts-jest': {
@michaelbromley
michaelbromley / Asset.ts
Created May 8, 2020 08:13
TypeORM failing schema sync test
import { Entity, PrimaryColumn } from "../../../../src";
@Entity()
export class Asset {
@PrimaryColumn()
id: number;
}