{"_id":"@brandtg/flapjack","_rev":"9-52ba544f41ab076f29c5d368f33db535","name":"@brandtg/flapjack","dist-tags":{"latest":"1.5.0"},"versions":{"0.1.0":{"name":"@brandtg/flapjack","version":"0.1.0","keywords":["feature-flags","postgres","database","configuration"],"author":{"name":"Greg Brandt"},"license":"BSD-3-Clause","_id":"@brandtg/flapjack@0.1.0","maintainers":[{"name":"brandtg","email":"brandt.greg@gmail.com"}],"homepage":"https://github.com/brandtg/flapjack#readme","bugs":{"url":"https://github.com/brandtg/flapjack/issues"},"bin":{"flapjack":"dist/cli.js"},"dist":{"shasum":"fb37a7576a698f5b4bb14625c6d66e58c960d5ec","tarball":"https://registry.npmjs.org/@brandtg/flapjack/-/flapjack-0.1.0.tgz","fileCount":19,"integrity":"sha512-OzqIUOC137Hydq41SlPfKnUxPrVuy1K8NxTGHmyf6fI3/c58faJM3btQrQQFgRlWisem3W8QD21N7RU1mDM5gw==","signatures":[{"sig":"MEUCIQCK80Ei9ZMman6rMsDUuG/cN3/mNRzui3j1p/6zsaV8EgIgCAICk0bhKmVZEO9HP0DG4gZvy/6FrxxMGCl+XD211fE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":57109},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"9ac01fe9cb191273b1078e2d2b2fa38ce2ffa870","scripts":{"dev":"npx tsc --watch","lint":"eslint .","test":"npm run build && vitest run","build":"npm run clean && npx tsc","clean":"rm -rf dist","smoke":"npm run build && node dist/cli.js --help && node dist/cli.js hash-user smoke","format":"prettier --write .","dev:env":"bash bin/dev/env.sh","pack:dev":"npm run build && npm pack","typecheck":"tsc --noEmit","dev:migrate":"npx dotenv-cli -e env -- node-pg-migrate up","dev:docker:up":"docker compose -f docker-compose.yml up -d","prepublishOnly":"npm run lint && npm run test && npm run build","dev:docker:down":"docker compose -f docker-compose.yml down -v","create-migration":"npx node-pg-migrate create --"},"_npmUser":{"name":"brandtg","email":"brandt.greg@gmail.com"},"repository":{"url":"git+https://github.com/brandtg/flapjack.git","type":"git"},"_npmVersion":"10.9.3","description":"A simple feature flags library with PostgreSQL integration","directories":{},"_nodeVersion":"22.20.0","dependencies":{"pg":"^8.12.0","yargs":"^18.0.0","dotenv":"^16.4.5","imurmurhash":"^0.1.4","node-pg-migrate":"^8.0.3"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.37.0","vitest":"^3.2.4","prettier":"^3.6.2","@types/pg":"^8.11.10","@eslint/js":"^9.37.0","dotenv-cli":"^10.0.0","typescript":"~5.9.3","@types/yargs":"^17.0.33","typescript-eslint":"^8.45.0","@types/imurmurhash":"^0.1.4"},"_npmOperationalInternal":{"tmp":"tmp/flapjack_0.1.0_1759863386829_0.8014919657909392","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@brandtg/flapjack","version":"0.1.2","keywords":["feature-flags","postgres","database","configuration"],"author":{"name":"Greg Brandt"},"license":"BSD-3-Clause","_id":"@brandtg/flapjack@0.1.2","maintainers":[{"name":"brandtg","email":"brandt.greg@gmail.com"}],"homepage":"https://github.com/brandtg/flapjack#readme","bugs":{"url":"https://github.com/brandtg/flapjack/issues"},"bin":{"flapjack":"dist/cli.js"},"dist":{"shasum":"48427dd0b8fae215730a7b75d87ed64181b9b4aa","tarball":"https://registry.npmjs.org/@brandtg/flapjack/-/flapjack-0.1.2.tgz","fileCount":23,"integrity":"sha512-n37yvXCOk7TLqDISepmIIDNBbKvDQFi5AXZu174noG+yXSA7nQWloJm12nY3l7x2lGWHSDIpsukYO20Wm0elnw==","signatures":[{"sig":"MEYCIQC/B66mX3srQv8izmskxwnycS9jDXgSv+reM/UUz6mSYwIhAJAxhXkS5Q/7FFMHJ4F+BjWrbYbGl0MCIUbE2dcoz77f","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":67117},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"ceef2276ffbfc684f7d5f518784e7fcb0e88ba5a","scripts":{"dev":"npx tsc --watch","lint":"eslint .","test":"npm run build && vitest run","build":"npm run clean && npx tsc","clean":"rm -rf dist","smoke":"npm run build && node dist/cli.js --help && node dist/cli.js hash-user smoke","format":"prettier --write .","dev:env":"bash bin/dev/env.sh","pack:dev":"npm run build && npm pack","typecheck":"tsc --noEmit","dev:migrate":"npx dotenv-cli -e env -- node-pg-migrate up","dev:docker:up":"docker compose -f docker-compose.yml up -d","prepublishOnly":"npm run lint && npm run test && npm run build","dev:docker:down":"docker compose -f docker-compose.yml down -v","create-migration":"npx node-pg-migrate create --"},"_npmUser":{"name":"brandtg","email":"brandt.greg@gmail.com"},"repository":{"url":"git+https://github.com/brandtg/flapjack.git","type":"git"},"_npmVersion":"10.9.3","description":"A simple feature flags library with PostgreSQL integration","directories":{},"_nodeVersion":"22.20.0","dependencies":{"pg":"^8.12.0","yargs":"^18.0.0","dotenv":"^17.2.3","imurmurhash":"^0.1.4","node-pg-migrate":"^8.0.3"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.37.0","vitest":"^3.2.4","prettier":"^3.6.2","@types/pg":"^8.11.10","@eslint/js":"^9.37.0","dotenv-cli":"^10.0.0","typescript":"~5.9.3","@types/yargs":"^17.0.33","typescript-eslint":"^8.45.0","@types/imurmurhash":"^0.1.4"},"_npmOperationalInternal":{"tmp":"tmp/flapjack_0.1.2_1760505074150_0.7771946323966734","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@brandtg/flapjack","version":"0.2.0","keywords":["feature-flags","postgres","database","configuration"],"author":{"name":"Greg Brandt"},"license":"BSD-3-Clause","_id":"@brandtg/flapjack@0.2.0","maintainers":[{"name":"brandtg","email":"brandt.greg@gmail.com"}],"homepage":"https://github.com/brandtg/flapjack#readme","bugs":{"url":"https://github.com/brandtg/flapjack/issues"},"bin":{"flapjack":"dist/cli.js"},"dist":{"shasum":"ced5c3a4221f537089ab2f884cdc8f3e200bd8d4","tarball":"https://registry.npmjs.org/@brandtg/flapjack/-/flapjack-0.2.0.tgz","fileCount":24,"integrity":"sha512-Y3VrUIxiagSlkTPRDyirf18gGXLiWLfY7YHQOjayR3LxFraVLOuByQROG8ZlbV/nSQuVmYHgM3BSwU08QEx3dA==","signatures":[{"sig":"MEQCIAfX9CV7Owqn2Rfe7r5pCpGd5P/wsiT3XtV+4XH6UzpkAiBoZ3eiLqSGV+64JuHVNZDkqt+vmoy56+IpU8eFQjIRaw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":89264},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"8644231c4d7a75bb387b277196988f07e8c64ccf","scripts":{"dev":"npx tsc --watch","lint":"eslint .","test":"npm run build && vitest run","build":"npm run clean && npx tsc","clean":"rm -rf dist","smoke":"npm run build && node dist/cli.js --help && node dist/cli.js hash-user smoke","format":"prettier --write .","dev:env":"bash bin/dev/env.sh","pack:dev":"npm run build && npm pack","typecheck":"tsc --noEmit","dev:migrate":"npx dotenv-cli -e env -- node-pg-migrate up","dev:docker:up":"docker compose -f docker-compose.yml up -d","prepublishOnly":"npm run lint && npm run test && npm run build","dev:docker:down":"docker compose -f docker-compose.yml down -v","create-migration":"npx node-pg-migrate create --"},"_npmUser":{"name":"brandtg","email":"brandt.greg@gmail.com"},"repository":{"url":"git+https://github.com/brandtg/flapjack.git","type":"git"},"_npmVersion":"10.9.4","description":"A simple feature flags library with PostgreSQL integration","directories":{},"_nodeVersion":"22.21.1","dependencies":{"pg":"^8.12.0","yargs":"^18.0.0","dotenv":"^17.2.3","imurmurhash":"^0.1.4","node-pg-migrate":"^8.0.3"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.37.0","vitest":"^3.2.4","prettier":"^3.6.2","@types/pg":"^8.11.10","@eslint/js":"^9.37.0","dotenv-cli":"^10.0.0","typescript":"~5.9.3","@types/yargs":"^17.0.33","typescript-eslint":"^8.45.0","@types/imurmurhash":"^0.1.4"},"_npmOperationalInternal":{"tmp":"tmp/flapjack_0.2.0_1764564064127_0.4900839947487423","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@brandtg/flapjack","version":"1.0.0","keywords":["feature-flags","postgres","database","configuration"],"author":{"name":"Greg Brandt"},"license":"BSD-3-Clause","_id":"@brandtg/flapjack@1.0.0","maintainers":[{"name":"brandtg","email":"brandt.greg@gmail.com"}],"homepage":"https://github.com/brandtg/flapjack#readme","bugs":{"url":"https://github.com/brandtg/flapjack/issues"},"bin":{"flapjack":"dist/cli.js"},"dist":{"shasum":"1bf48cf23abd27a95fefef704e4f69f1266ed112","tarball":"https://registry.npmjs.org/@brandtg/flapjack/-/flapjack-1.0.0.tgz","fileCount":24,"integrity":"sha512-R3WHOeRfcmU8yuEcJZWNdkckL6DfP/8FY0gsVcst3/eiY0PwjtQwJMVDwymumNy8wh4rwnz7MOXZTdFYeCzheg==","signatures":[{"sig":"MEYCIQD+cfgYd+cdnoNRJLcmqBQ3/K1rcWlsmAZYwuPBLlOOnQIhAInn4PiYXOlVV2lnuk3LtXBKV7/5Aj10xWJYXZukzAVz","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":88709},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"6097ffd4c4151d444aafdeac1ee44cd0c67d8d36","scripts":{"dev":"npx tsc --watch","lint":"eslint .","test":"npm run build && vitest run","build":"npm run clean && npx tsc","clean":"rm -rf dist","smoke":"npm run build && node dist/cli.js --help && node dist/cli.js hash-user smoke","format":"prettier --write .","dev:env":"bash bin/dev/env.sh","pack:dev":"npm run build && npm pack","typecheck":"tsc --noEmit","dev:migrate":"npx dotenv-cli -e env -- node-pg-migrate up","dev:docker:up":"docker compose -f docker-compose.yml up -d","prepublishOnly":"npm run lint && npm run test && npm run build","dev:docker:down":"docker compose -f docker-compose.yml down -v","create-migration":"npx node-pg-migrate create --"},"_npmUser":{"name":"brandtg","email":"brandt.greg@gmail.com"},"repository":{"url":"git+https://github.com/brandtg/flapjack.git","type":"git"},"_npmVersion":"10.9.4","description":"A simple feature flags library with PostgreSQL integration","directories":{},"_nodeVersion":"22.21.1","dependencies":{"pg":"^8.12.0","yargs":"^18.0.0","dotenv":"^17.2.3","imurmurhash":"^0.1.4","node-pg-migrate":"^8.0.3"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.37.0","vitest":"^3.2.4","prettier":"^3.6.2","@types/pg":"^8.11.10","@eslint/js":"^9.37.0","dotenv-cli":"^10.0.0","typescript":"~5.9.3","@types/yargs":"^17.0.33","typescript-eslint":"^8.45.0","@types/imurmurhash":"^0.1.4"},"_npmOperationalInternal":{"tmp":"tmp/flapjack_1.0.0_1765937440731_0.5962003560246658","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@brandtg/flapjack","version":"1.1.0","keywords":["feature-flags","postgres","database","configuration"],"author":{"name":"Greg Brandt"},"license":"BSD-3-Clause","_id":"@brandtg/flapjack@1.1.0","maintainers":[{"name":"brandtg","email":"brandt.greg@gmail.com"}],"homepage":"https://github.com/brandtg/flapjack#readme","bugs":{"url":"https://github.com/brandtg/flapjack/issues"},"bin":{"flapjack":"dist/cli.js"},"dist":{"shasum":"9999ca4f17c298c91390b1737c78f8976ac12f89","tarball":"https://registry.npmjs.org/@brandtg/flapjack/-/flapjack-1.1.0.tgz","fileCount":24,"integrity":"sha512-/a1svUjKk6Dw/3QSxF7Rd2Ds2r//XDDx+IHjrzdXkCPMWYKbk70HouuQhKdozu8ZFVkFaN4N/ujvlX8tJQTMpg==","signatures":[{"sig":"MEUCIQD/yhk82D24MHGdKVgC50a+AiM/NSt50doJoEqKZZTvWQIgFi1VcxBatPZbReNVfWP2LcreTzDx6yBdWQq9c4Lejmk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":88877},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"a2c4bd2fc63747247abdb978750b6e76adde1adf","scripts":{"dev":"npx tsc --watch","lint":"eslint .","test":"npm run build && vitest run","build":"npm run clean && npx tsc","clean":"rm -rf dist","smoke":"npm run build && node dist/cli.js --help && node dist/cli.js hash-user smoke","format":"prettier --write .","dev:env":"bash bin/dev/env.sh","pack:dev":"npm run build && npm pack","typecheck":"tsc --noEmit","dev:migrate":"npx dotenv-cli -e env -- node-pg-migrate up","dev:docker:up":"docker compose -f docker-compose.yml up -d","prepublishOnly":"npm run lint && npm run test && npm run build","dev:docker:down":"docker compose -f docker-compose.yml down -v","create-migration":"npx node-pg-migrate create --"},"_npmUser":{"name":"brandtg","email":"brandt.greg@gmail.com"},"repository":{"url":"git+https://github.com/brandtg/flapjack.git","type":"git"},"_npmVersion":"10.9.4","description":"A simple feature flags library with PostgreSQL integration","directories":{},"_nodeVersion":"22.21.1","dependencies":{"pg":"^8.12.0","yargs":"^18.0.0","dotenv":"^17.2.3","imurmurhash":"^0.1.4","node-pg-migrate":"^8.0.3"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.37.0","vitest":"^3.2.4","prettier":"^3.6.2","@types/pg":"^8.11.10","@eslint/js":"^9.37.0","dotenv-cli":"^10.0.0","typescript":"~5.9.3","@types/yargs":"^17.0.33","typescript-eslint":"^8.45.0","@types/imurmurhash":"^0.1.4"},"_npmOperationalInternal":{"tmp":"tmp/flapjack_1.1.0_1765938643847_0.4439775627647895","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@brandtg/flapjack","version":"1.2.0","keywords":["feature-flags","postgres","database","configuration"],"author":{"name":"Greg Brandt"},"license":"BSD-3-Clause","_id":"@brandtg/flapjack@1.2.0","maintainers":[{"name":"brandtg","email":"brandt.greg@gmail.com"}],"homepage":"https://github.com/brandtg/flapjack#readme","bugs":{"url":"https://github.com/brandtg/flapjack/issues"},"bin":{"flapjack":"dist/cli.js"},"dist":{"shasum":"830f12ddc2cdce7341c262a232f5e5dfce87c0e7","tarball":"https://registry.npmjs.org/@brandtg/flapjack/-/flapjack-1.2.0.tgz","fileCount":24,"integrity":"sha512-yTzD/25MOKdqZxTdIzjsKoOwLFXFGx6OFeGu/dSyZM88pAtG7A3tv4lBGwv6C8f116LHFLjX6XkB+LanbcYflg==","signatures":[{"sig":"MEUCIAL50k98gCkh5WVWPpDifzNxHf6U0ttdcWMexgbi28BkAiEAzyiyM63nDZj88V48LlNPK7q/8YbvZjhPAQB/lyXT2Us=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":96579},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"5ff6eb38b406fa78fb69b5b30709901654cfe01c","scripts":{"dev":"npx tsc --watch","lint":"eslint .","test":"npm run build && vitest run","build":"npm run clean && npx tsc","clean":"rm -rf dist","smoke":"npm run build && node dist/cli.js --help && node dist/cli.js hash-user smoke","format":"prettier --write .","dev:env":"bash bin/dev/env.sh","pack:dev":"npm run build && npm pack","typecheck":"tsc --noEmit","dev:migrate":"npx dotenv-cli -e env -- node-pg-migrate up","dev:docker:up":"docker compose -f docker-compose.yml up -d","prepublishOnly":"npm run lint && npm run test && npm run build","dev:docker:down":"docker compose -f docker-compose.yml down -v","create-migration":"npx node-pg-migrate create --"},"_npmUser":{"name":"brandtg","email":"brandt.greg@gmail.com"},"repository":{"url":"git+https://github.com/brandtg/flapjack.git","type":"git"},"_npmVersion":"10.9.4","description":"A simple feature flags library with PostgreSQL integration","directories":{},"_nodeVersion":"22.21.1","dependencies":{"pg":"^8.12.0","yargs":"^18.0.0","dotenv":"^17.2.3","imurmurhash":"^0.1.4","node-pg-migrate":"^8.0.3"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.37.0","vitest":"^3.2.4","prettier":"^3.6.2","@types/pg":"^8.11.10","@eslint/js":"^9.37.0","dotenv-cli":"^10.0.0","typescript":"~5.9.3","@types/yargs":"^17.0.33","typescript-eslint":"^8.45.0","@types/imurmurhash":"^0.1.4"},"_npmOperationalInternal":{"tmp":"tmp/flapjack_1.2.0_1765945393715_0.591652687094846","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@brandtg/flapjack","version":"1.3.0","keywords":["feature-flags","postgres","database","configuration"],"author":{"name":"Greg Brandt"},"license":"BSD-3-Clause","_id":"@brandtg/flapjack@1.3.0","maintainers":[{"name":"brandtg","email":"brandt.greg@gmail.com"}],"homepage":"https://github.com/brandtg/flapjack#readme","bugs":{"url":"https://github.com/brandtg/flapjack/issues"},"bin":{"flapjack":"dist/cli.js"},"dist":{"shasum":"16701ffca1b1da3a3cef31245d71d02e41b8cae3","tarball":"https://registry.npmjs.org/@brandtg/flapjack/-/flapjack-1.3.0.tgz","fileCount":24,"integrity":"sha512-z9S0NWWkxDoLm+g4U299osjzwuTti8czGKKwvXhz0o9VBOVS9Zfj0jr1/05NvwITC0BXWBUxGaFFtiOw3JrkhQ==","signatures":[{"sig":"MEUCIAeBzVA9bhDOuAsb/ObB7Q1mazhuwBoppDjnZKC8V0iGAiEAhjXSAmyqETU5VeJU82nC530PqO+KyAmkeDfDIGCy4BY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":97918},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"a0a46278ce5999a2a66594fefc82b40d7d216762","scripts":{"dev":"npx tsc --watch","lint":"eslint .","test":"npm run build && vitest run","build":"npm run clean && npx tsc","clean":"rm -rf dist","smoke":"npm run build && node dist/cli.js --help && node dist/cli.js hash-user smoke","format":"prettier --write .","dev:env":"bash bin/dev/env.sh","pack:dev":"npm run build && npm pack","typecheck":"tsc --noEmit","dev:migrate":"npx dotenv-cli -e env -- node-pg-migrate up","dev:docker:up":"docker compose -f docker-compose.yml up -d","prepublishOnly":"npm run lint && npm run test && npm run build","dev:docker:down":"docker compose -f docker-compose.yml down -v","create-migration":"npx node-pg-migrate create --"},"_npmUser":{"name":"brandtg","email":"brandt.greg@gmail.com"},"repository":{"url":"git+https://github.com/brandtg/flapjack.git","type":"git"},"_npmVersion":"10.9.4","description":"A simple feature flags library with PostgreSQL integration","directories":{},"_nodeVersion":"22.21.1","dependencies":{"pg":"^8.12.0","yargs":"^18.0.0","dotenv":"^17.2.3","imurmurhash":"^0.1.4","node-pg-migrate":"^8.0.3"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.37.0","vitest":"^3.2.4","prettier":"^3.6.2","@types/pg":"^8.11.10","@eslint/js":"^9.37.0","dotenv-cli":"^10.0.0","typescript":"~5.9.3","@types/yargs":"^17.0.33","typescript-eslint":"^8.45.0","@types/imurmurhash":"^0.1.4"},"_npmOperationalInternal":{"tmp":"tmp/flapjack_1.3.0_1765946210783_0.04944007343527823","host":"s3://npm-registry-packages-npm-production"}},"1.4.0":{"name":"@brandtg/flapjack","version":"1.4.0","keywords":["feature-flags","postgres","database","configuration"],"author":{"name":"Greg Brandt"},"license":"BSD-3-Clause","_id":"@brandtg/flapjack@1.4.0","maintainers":[{"name":"brandtg","email":"brandt.greg@gmail.com"}],"homepage":"https://github.com/brandtg/flapjack#readme","bugs":{"url":"https://github.com/brandtg/flapjack/issues"},"bin":{"flapjack":"dist/cli.js"},"dist":{"shasum":"bdecffce9d9217083b8cc998a9bd48898fb3fcd1","tarball":"https://registry.npmjs.org/@brandtg/flapjack/-/flapjack-1.4.0.tgz","fileCount":25,"integrity":"sha512-GXCNYcGTteetqfYMDVeRmvVHPZaghwZ5jzsWt20gwIcrRFxpzDP2Sy1tUKJdSBG5tDVtGpDRrdYxtHxlJ5NAKw==","signatures":[{"sig":"MEYCIQDClfADmjU9+d6JpmpFpMixmbPb1W5jLvN1cZXCMcBgOwIhAKdKzsLkqmnhtM6EXNrsJ154Inz28kcBP4oU29QdxujL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":134862},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"db3b4d98fe37244620cd539ce7d87249664e3a82","scripts":{"dev":"npx tsc --watch","lint":"eslint .","test":"npm run build && vitest run","build":"npm run clean && npx tsc","clean":"rm -rf dist","smoke":"npm run build && node dist/cli.js --help && node dist/cli.js hash-user smoke","format":"prettier --write .","dev:env":"bash bin/dev/env.sh","pack:dev":"npm run build && npm pack","typecheck":"tsc --noEmit","dev:migrate":"npx dotenv-cli -e .env -- node-pg-migrate up -s flapjack --create-schema -t pgmigrations","dev:docker:up":"docker compose -f docker-compose.yml up -d","prepublishOnly":"npm run lint && npm run test && npm run build","dev:docker:down":"docker compose -f docker-compose.yml down -v","create-migration":"npx node-pg-migrate create --"},"_npmUser":{"name":"brandtg","email":"brandt.greg@gmail.com"},"repository":{"url":"git+https://github.com/brandtg/flapjack.git","type":"git"},"_npmVersion":"10.9.4","description":"A simple feature flags library with PostgreSQL integration","directories":{},"_nodeVersion":"22.22.0","dependencies":{"pg":"^8.12.0","yargs":"^18.0.0","dotenv":"^17.2.3","imurmurhash":"^0.1.4","node-pg-migrate":"^8.0.3"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.37.0","vitest":"^3.2.4","prettier":"^3.6.2","@types/pg":"^8.11.10","@eslint/js":"^9.37.0","dotenv-cli":"^10.0.0","typescript":"~5.9.3","@types/yargs":"^17.0.33","typescript-eslint":"^8.45.0","@types/imurmurhash":"^0.1.4"},"_npmOperationalInternal":{"tmp":"tmp/flapjack_1.4.0_1779213059961_0.07516804536865118","host":"s3://npm-registry-packages-npm-production"}},"1.5.0":{"name":"@brandtg/flapjack","version":"1.5.0","description":"A simple feature flags library with PostgreSQL integration","type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","bin":{"flapjack":"dist/cli.js"},"exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"scripts":{"build":"npm run clean && npx tsc","clean":"rm -rf dist","dev":"npx tsc --watch","dev:env":"bash bin/dev/env.sh","dev:migrate":"npx dotenv-cli -e .env -- node-pg-migrate up -s flapjack --create-schema -t pgmigrations","dev:docker:up":"docker compose -f docker-compose.yml up -d","dev:docker:down":"docker compose -f docker-compose.yml down -v","create-migration":"npx node-pg-migrate create --","test":"npm run build && vitest run","typecheck":"tsc --noEmit","lint":"eslint .","format":"prettier --write .","prepublishOnly":"npm run lint && npm run test && npm run build","pack:dev":"npm run build && npm pack","smoke":"npm run build && node dist/cli.js --help && node dist/cli.js hash-user smoke"},"dependencies":{"dotenv":"^17.2.3","imurmurhash":"^0.1.4","node-pg-migrate":"^8.0.3","pg":"^8.12.0","yargs":"^18.0.0"},"devDependencies":{"@eslint/js":"^9.37.0","@types/imurmurhash":"^0.1.4","@types/pg":"^8.11.10","@types/yargs":"^17.0.33","dotenv-cli":"^10.0.0","eslint":"^9.37.0","prettier":"^3.6.2","typescript":"~5.9.3","typescript-eslint":"^8.45.0","vitest":"^3.2.4"},"keywords":["feature-flags","postgres","database","configuration"],"repository":{"type":"git","url":"git+https://github.com/brandtg/flapjack.git"},"bugs":{"url":"https://github.com/brandtg/flapjack/issues"},"homepage":"https://github.com/brandtg/flapjack#readme","license":"BSD-3-Clause","author":{"name":"Greg Brandt"},"engines":{"node":">=18.0.0"},"_id":"@brandtg/flapjack@1.5.0","gitHead":"1794b2cd5b780ab3e7cdda263ad38bf493092a7c","_nodeVersion":"22.21.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-JhRWPTx6m8Y0g/EgXHgAfXgiKPrrjsrMEo8Y49EsMLEHq7f4fcD60SLe5pZ3K68epwjTWD/VKegFMpDFus/AIw==","shasum":"4e4bd099a38217bc51392c2205138ffd23554ab9","tarball":"https://registry.npmjs.org/@brandtg/flapjack/-/flapjack-1.5.0.tgz","fileCount":27,"unpackedSize":155760,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDjUDK1WnvUQ4UApvRxOOwTXSxYaKYy8RFxMexXIjiv6AiAe3kZGTuV6o85aRXpze1HpsNsMtKv3HcQI1DAKKf+JYA=="}]},"_npmUser":{"name":"brandtg","email":"brandt.greg@gmail.com"},"directories":{},"maintainers":[{"name":"brandtg","email":"brandt.greg@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/flapjack_1.5.0_1781111950819_0.3342266983713522"},"_hasShrinkwrap":false}},"time":{"created":"2025-10-07T18:56:26.672Z","modified":"2026-06-10T17:19:11.135Z","0.1.0":"2025-10-07T18:56:26.999Z","0.1.2":"2025-10-15T05:11:14.353Z","0.2.0":"2025-12-01T04:41:04.328Z","1.0.0":"2025-12-17T02:10:40.911Z","1.1.0":"2025-12-17T02:30:43.986Z","1.2.0":"2025-12-17T04:23:13.868Z","1.3.0":"2025-12-17T04:36:50.934Z","1.4.0":"2026-05-19T17:51:00.168Z","1.5.0":"2026-06-10T17:19:11.017Z"},"bugs":{"url":"https://github.com/brandtg/flapjack/issues"},"author":{"name":"Greg Brandt"},"license":"BSD-3-Clause","homepage":"https://github.com/brandtg/flapjack#readme","keywords":["feature-flags","postgres","database","configuration"],"repository":{"type":"git","url":"git+https://github.com/brandtg/flapjack.git"},"description":"A simple feature flags library with PostgreSQL integration","maintainers":[{"name":"brandtg","email":"brandt.greg@gmail.com"}],"readme":"# Flapjack\n\n[![CI](https://github.com/brandtg/flapjack/actions/workflows/ci.yml/badge.svg)](https://github.com/brandtg/flapjack/actions/workflows/ci.yml)\n[![npm version](https://badge.fury.io/js/@brandtg%2Fflapjack.svg)](https://www.npmjs.com/package/@brandtg/flapjack)\n[![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)\n\nA simple feature flags library with PostgreSQL integration, inspired by [django-waffle](https://github.com/django-waffle/django-waffle).\n\n## Features\n\n- **Multiple targeting strategies**: Enable features for specific users, roles, groups, or percentage rollouts\n- **External subject targeting**: Assign flags (or flag groups) to external subjects like tenants or org IDs\n- **Consistent hashing**: Deterministic user bucketing for A/B testing and experimentation\n- **PostgreSQL-backed**: Reliable, transactional flag storage with your existing database\n- **CLI included**: Manage feature flags from the command line\n- **TypeScript-first**: Full type safety with comprehensive TypeScript definitions\n- **Battle-tested**: Comprehensive test suite with 31+ tests\n\n## Requirements\n\n- Node.js >= 18.0.0\n- PostgreSQL >= 10.0\n\n## Installation\n\nInstall the library\n\n```bash\nnpm install @brandtg/flapjack\n```\n\nApply the migrations\n\n```typescript\nimport { runMigrations } from \"@brandtg/flapjack\";\n\nawait runMigrations({\n  // Connect to your PostgreSQL database\n  databaseUrl: process.env.DATABASE_URL,\n  // Use the same migrations table as your application\n  migrationsTable: \"pgmigrations\",\n});\n```\n\n## Usage\n\n```typescript\nimport { Pool } from \"pg\";\nimport { FeatureFlagModel } from \"@brandtg/flapjack\";\n\n// Connect to the database\nconst pool: Pool = getDatabasePool();\nconst featureFlags = new FeatureFlagModel(pool);\n\n// Create a feature flag\n// N.b. use a meaningful naming scheme like <feature>_<date>_<owner>\nconst flag = await featureFlags.create({\n  name: \"enable_new_checkout_20250101_gbrandt\",\n});\n\n// Check if a feature flag is active for a user\nconst active = await featureFlag.isActiveForUser({\n  name: \"enable_new_checkout_20250101_gbrandt\",\n  user: \"1234\",\n});\n\n// Assign a flag to an external subject ID (for example, a tenant)\nawait featureFlags.addSubject(flag.id, \"tenant:acme\");\n\n// Check if active for a broader context that includes external subject IDs\nconst activeForTenant = await featureFlags.isActiveForContext({\n  name: \"enable_new_checkout_20250101_gbrandt\",\n  user: \"1234\",\n  subjects: [\"tenant:acme\"],\n});\n\n// Enable the feature flag for certain roles\nawait featureFlags.update(flag.id, {\n  roles: [\"admin\", \"staff\"],\n});\n\n// Enable the feature flag for certain user groups\nawait featureFlags.update(flag.id, {\n  groups: [\"early_adopters\"],\n});\n\n// Launch the flag to a certain percentage of users\nawait featureFlags.update(flag.id, {\n  percent: 25,\n});\n\n// Launch the flag to everyone\nawait featureFlags.update(flag.id, {\n  everyone: true,\n});\n\n// Disable the flag for everyone\nawait featureFlags.update(flag.id, {\n  everyone: false,\n});\n\n// Move the flag back into normal state (other rules then apply)\nawait featureFlags.update(flag.id, {\n  everyone: null,\n});\n```\n\n## How Feature Flag Evaluation Works\n\nWhen checking if a feature flag is active for a user, Flapjack evaluates rules in the following order:\n\n1. **Everyone Override** (`everyone: true/false`): If set, immediately returns this value, ignoring all other rules\n2. **User List** (`users: [...]`): If the user ID is in this list, returns `true`\n3. **Group Membership** (`groups: [...]`): If the user belongs to any specified group, returns `true`\n4. **External Subject Match** (`subjects: [...]`): If any provided subject matches a direct flag subject or a flag-group subject, returns `true`\n5. **Role Membership** (`roles: [...]`): If the user has any specified role, returns `true`\n6. **Percentage Rollout** (`percent: 0-99.9`): Uses consistent hashing to deterministically bucket users\n7. **Default**: Returns `false` if no conditions are met\n\n## External Subject Targeting\n\nUse subjects when identity/group membership is managed outside Flapjack (for example, tenant IDs from another system).\n\n```typescript\nconst groupModel = new FeatureFlagGroupModel(pool);\nconst flagModel = new FeatureFlagModel(pool);\n\nconst rolloutGroup = await groupModel.create({\n  name: \"checkout_rollout\",\n});\n\nconst checkoutFlag = await flagModel.create({\n  name: \"enable_checkout_v2\",\n});\n\nawait groupModel.addFeatureFlag(rolloutGroup.id, checkoutFlag.id);\n\n// Attach external subject to all flags in this feature flag group\nawait groupModel.addSubject(rolloutGroup.id, \"tenant:acme\");\n\n// You can also attach a subject directly to a single flag\nawait flagModel.addSubject(checkoutFlag.id, \"tenant:beta\");\n\nconst isActive = await flagModel.isActiveForContext({\n  name: \"enable_checkout_v2\",\n  user: \"user_123\",\n  subjects: [\"tenant:acme\"],\n});\n```\n\n### Example Evaluation\n\n```typescript\n// Flag configured with multiple rules\nawait featureFlags.create({\n  name: \"new_feature\",\n  roles: [\"admin\"],\n  groups: [\"beta_testers\"],\n  percent: 25,\n});\n\n// Admin user: ✓ enabled (matches role)\nawait featureFlags.isActiveForUser({\n  name: \"new_feature\",\n  user: \"user_123\",\n  roles: [\"admin\"],\n});\n\n// Beta tester: ✓ enabled (matches group)\nawait featureFlags.isActiveForUser({\n  name: \"new_feature\",\n  user: \"user_456\",\n  groups: [\"beta_testers\"],\n});\n\n// Regular user: ? maybe (depends on hash bucket)\nawait featureFlags.isActiveForUser({\n  name: \"new_feature\",\n  user: \"user_789\",\n  roles: [\"user\"],\n});\n```\n\n## Organizing Flags with Tags\n\nTags are freeform labels for organizing flags without encoding that information in\nthe flag name. They do **not** affect evaluation — they are purely organizational\nmetadata. A common use is associating a release version with a flag, which can then\nbe bumped via a database update without any code change.\n\n```typescript\n// Tag a flag with a release version (and any other labels)\nawait featureFlags.create({\n  name: \"checkout_v2\",\n  tags: [\"release:1.5.0\", \"checkout\"],\n});\n\n// Find every flag shipping in a given release\nconst flags = await featureFlags.listByTag(\"release:1.5.0\");\n\n// Bump the release version later — no code change required\nawait featureFlags.update(flag.id, { tags: [\"release:1.6.0\", \"checkout\"] });\n```\n\nThe equivalent CLI commands:\n\n```bash\nflapjack create --name checkout_v2 --tags release:1.5.0 checkout\nflapjack list-by-tag release:1.5.0\nflapjack update <id> --tags release:1.6.0 checkout\nflapjack update <id> --clear-tags\n```\n\n## Performance Considerations\n\n⚠️ **Important**: Without caching, Flapjack queries the database on every `isActiveForUser()` call. For high-traffic applications, use the built-in caching layer:\n\n### Built-in Caching Layer\n\nFlapjack includes a high-performance caching layer with TTL support:\n\n```typescript\nimport { Pool } from \"pg\";\nimport {\n  FeatureFlagModel,\n  FeatureFlagCache,\n  InMemoryCache,\n} from \"@brandtg/flapjack\";\n\n// Set up the database model\nconst pool = new Pool({ connectionString: process.env.DATABASE_URL });\n\n// Create cached feature flags instance\nconst featureFlags = new FeatureFlagCache({\n  model: new FeatureFlagModel(pool),\n  cache: new InMemoryCache<boolean>(),\n  ttl: 300, // Set TTL to 5 minutes (300 seconds)\n});\n\n// Use exactly the same as the model, but with automatic caching\nconst isActive = await featureFlags.isActiveForUser({\n  name: \"new_feature\",\n  user: \"user_123\",\n  roles: [\"admin\"],\n  groups: [\"beta_testers\"],\n});\n```\n\n### Cache Key Generation\n\nThe cache automatically generates deterministic keys using MurmurHash3 of the flag name and user parameters:\n\n```typescript\n// These calls generate the same cache key:\nawait featureFlags.isActiveForUser({\n  name: \"test\",\n  roles: [\"admin\", \"user\"],\n  groups: [\"beta\", \"alpha\"],\n});\n\nawait featureFlags.isActiveForUser({\n  name: \"test\",\n  roles: [\"user\", \"admin\"], // Different order\n  groups: [\"alpha\", \"beta\"], // Different order\n});\n```\n\n### Custom Cache Implementation\n\nYou can implement your own cache (Redis, Memcached, etc.) by implementing the `Cache` interface:\n\n```typescript\nimport type { Cache } from \"@brandtg/flapjack\";\n\nclass RedisCache implements Cache {\n  private redis: RedisClient;\n\n  constructor(redis: RedisClient) {\n    this.redis = redis;\n  }\n\n  get(key: string): any {\n    const value = this.redis.get(key);\n    return value ? JSON.parse(value) : undefined;\n  }\n\n  set(key: string, value: any, ttl?: number): void {\n    const serialized = JSON.stringify(value);\n    if (ttl) {\n      this.redis.setex(key, ttl, serialized);\n    } else {\n      this.redis.set(key, serialized);\n    }\n  }\n\n  delete(key: string): void {\n    this.redis.del(key);\n  }\n}\n\n// Use your custom cache\nconst featureFlags = new FeatureFlagCache({\n  model,\n  cache: new RedisCache(redisClient),\n  ttl: 300,\n});\n```\n\n### Database Connection Pooling\n\nAlways use connection pooling in production:\n\n```typescript\nimport { Pool } from \"pg\";\n\nconst pool = new Pool({\n  connectionString: process.env.DATABASE_URL,\n  max: 20, // Maximum pool size\n  idleTimeoutMillis: 30000,\n  connectionTimeoutMillis: 2000,\n});\n\nconst featureFlags = new FeatureFlagModel(pool);\n```\n\n### Performance Tips\n\n- **Cache flag configurations** at the application level with a reasonable TTL (30-60 seconds)\n- **Batch flag checks** when possible to reduce round trips\n- **Use database indexes**: The default migration includes an index on `name` field\n- **Monitor query performance**: Feature flag checks should be <10ms in most cases\n- **Consider read replicas** for extremely high-traffic scenarios\n\n## Experimentation and A/B Testing\n\nFlapjack's percentage rollout feature enables experimentation and A/B testing:\n\n```typescript\n// Create an experiment: 50% of users see the new feature\nawait featureFlags.create({\n  name: \"checkout_redesign_experiment\",\n  percent: 50,\n  note: \"A/B test: new checkout flow vs. old\",\n});\n\n// Users are consistently bucketed - same result every time\nconst isInExperiment = await featureFlags.isActiveForUser({\n  name: \"checkout_redesign_experiment\",\n  user: \"user_123\",\n});\n\n// Gradual rollout: Start at 5%, increase to 100% over time\nawait featureFlags.update(flagId, { percent: 5 });\n// ... monitor metrics ...\nawait featureFlags.update(flagId, { percent: 25 });\n// ... monitor metrics ...\nawait featureFlags.update(flagId, { percent: 100 });\n```\n\n### How User Bucketing Works\n\n- Uses **MurmurHash3** for consistent, deterministic hashing\n- Same user ID always maps to the same bucket (0-99)\n- Changing the user ID will change bucket assignment\n- Distribution is uniform across the user base\n\n## API Reference\n\n### FeatureFlagModel\n\n#### `create(input: CreateInput): Promise<FeatureFlag>`\n\nCreates a new feature flag.\n\n#### `getById(id: number, options?: { includeArchived?: boolean }): Promise<FeatureFlag | null>`\n\nRetrieves a feature flag by its ID. Archived flags are excluded unless `includeArchived` is `true`.\n\n#### `getByName(name: string, options?: { includeArchived?: boolean }): Promise<FeatureFlag | null>`\n\nRetrieves a feature flag by its name. Archived flags are excluded unless `includeArchived` is `true`.\n\n#### `list(options?: { includeArchived?: boolean }): Promise<FeatureFlag[]>`\n\nLists all feature flags, ordered by ID. Archived flags are excluded unless `includeArchived` is `true`.\n\n#### `listByTag(tag: string, options?: { includeArchived?: boolean }): Promise<FeatureFlag[]>`\n\nLists all feature flags carrying the given tag (exact match), ordered by ID. Archived flags are excluded unless `includeArchived` is `true`.\n\n#### `update(id: number, changes: UpdateChanges): Promise<FeatureFlag | null>`\n\nUpdates a feature flag. Returns the updated flag or null if not found.\n\n#### `delete(id: number): Promise<boolean>`\n\nPermanently deletes a feature flag and all its relationships. Returns true if deleted, false if not found. To hide a flag while keeping its history, use `archive` instead.\n\n#### `archive(id: number): Promise<FeatureFlag | null>`\n\nArchives (hides) a feature flag while keeping the row for historical/audit purposes. Archived flags are excluded from normal reads and evaluate as inactive, but remain retrievable via `{ includeArchived: true }`. Returns the archived flag, or null if it does not exist or is already archived.\n\nArchiving is **irreversible** (there is no `unarchive`) and the `archived` timestamp is immutable once set. The flag's name stays reserved permanently — a new flag cannot reuse it; create a new flag with a different name instead. The same `archive(id)` / `includeArchived` semantics apply to `FeatureFlagGroupModel`.\n\n#### `isActiveForUser(params): Promise<boolean>`\n\nChecks if a feature flag is active for a user based on the evaluation rules.\n\n#### `hashUserId(userId: string): Promise<number>`\n\nReturns the hash value used for percentage bucketing. Useful for debugging rollout distributions.\n\n## CLI Usage\n\nFlapjack includes a CLI for managing feature flags:\n\n```bash\n# Set your database URL\nexport DATABASE_URL=\"postgresql://user:pass@localhost/dbname\"\n\n# Create a flag\nflapjack create --name my_feature --roles admin --note \"Admin-only feature\"\n\n# List all flags\nflapjack list\n\n# Include archived (hidden) flags in any read command\nflapjack list --include-archived\nflapjack get-by-name my_feature --include-archived\n\n# List flags carrying a tag (e.g. a release version)\nflapjack list-by-tag release:1.5.0\n\n# Get a specific flag\nflapjack get-by-name my_feature\n\n# Check if active for a user\nflapjack is-active my_feature --user user123 --roles admin\n\n# Check if multiple flags are active for a user\nflapjack are-active --names my_feature other_feature --user user123 --roles admin\n\n# Check if active for context (with external subject IDs)\nflapjack is-active-context my_feature --user user123 --subjects tenant:acme\n\n# Check multiple flags for context\nflapjack are-active-context --names my_feature other_feature --subjects tenant:acme\n\n# Update a flag\nflapjack update 1 --percent 50 --everyone false\n\n# Clear specific fields\nflapjack update 1 --clear-roles --clear-percent\n\n# Delete a flag (permanent — destroys all metadata)\nflapjack delete 1\n\n# Archive a flag (hide it but keep its history; irreversible)\nflapjack archive 1\n\n# Add/remove/list subject mappings on a flag\nflapjack add-subject 1 tenant:acme\nflapjack remove-subject 1 tenant:acme\nflapjack list-subjects 1\nflapjack list-by-subject tenant:acme\n\n# Feature flag groups\nflapjack group-create --name checkout_rollout --note \"Checkout launch cohort\"\nflapjack group-list\nflapjack group-get 1\nflapjack group-get-by-name checkout_rollout\nflapjack group-update 1 --note \"Updated\"\nflapjack group-delete 1\nflapjack group-archive 1\n\n# Group membership\nflapjack group-add-flag 1 10\nflapjack group-remove-flag 1 10\nflapjack group-list-flags 1\nflapjack group-list-for-flag 10\n\n# Bulk update all flags in a group\nflapjack group-update-all 1 --percent 25 --roles admin\n\n# Group-level subject mappings\nflapjack group-add-subject 1 tenant:acme\nflapjack group-remove-subject 1 tenant:acme\nflapjack group-list-subjects 1\nflapjack group-list-by-subject tenant:acme\n\n# Debug user bucketing\nflapjack hash-user user123\n```\n\n## Best Practices\n\n### Naming Conventions\n\nUse descriptive names that include context:\n\n```typescript\n// Good: Includes feature, date, and owner\n\"enable_new_checkout_20250101_gbrandt\";\n\"experiment_ai_suggestions_20250115_team_growth\";\n\n// Avoid: Too generic\n\"new_feature\";\n\"test_flag\";\n```\n\n### Gradual Rollouts\n\nAlways roll out features gradually:\n\n1. Start with internal users/roles (e.g., `roles: [\"admin\", \"staff\"]`)\n2. Expand to beta testers (e.g., `groups: [\"beta_testers\"]`)\n3. Percentage rollout (5% → 25% → 50% → 100%)\n4. Enable for everyone (e.g., `everyone: true`)\n5. After stable, remove the flag from code and database\n\n### Flag Lifecycle Management\n\n```typescript\n// 1. Development: Admin only\nawait featureFlags.create({\n  name: \"new_feature_20250101\",\n  roles: [\"admin\"],\n  note: \"New feature in development\",\n});\n\n// 2. Beta Testing\nawait featureFlags.update(flagId, {\n  groups: [\"beta_testers\"],\n});\n\n// 3. Gradual Rollout\nawait featureFlags.update(flagId, { percent: 10 });\n// Monitor, then increase...\n\n// 4. Full Launch\nawait featureFlags.update(flagId, { everyone: true });\n\n// 5. Cleanup (after feature is stable)\n// Remove feature flag checks from code, then either:\n//   - delete to remove it entirely, or\n//   - archive to hide it while keeping its history for audit\nawait featureFlags.archive(flagId);\n// (archive is irreversible and the name stays reserved permanently)\n```\n\n### Error Handling\n\n```typescript\ntry {\n  const isActive = await featureFlags.isActiveForUser({\n    name: \"my_feature\",\n    user: \"user_123\",\n  });\n\n  if (isActive) {\n    // Show new feature\n  } else {\n    // Show old feature\n  }\n} catch (error) {\n  // On error, fail closed (disable feature) or open (enable feature)\n  // depending on your risk tolerance\n  console.error(\"Feature flag check failed:\", error);\n  const isActive = false; // Fail closed - safer default\n}\n```\n\n## Troubleshooting\n\n### Database Connection Issues\n\n```typescript\n// Verify connection before using feature flags\nimport { Pool } from \"pg\";\n\nconst pool = new Pool({\n  connectionString: process.env.DATABASE_URL,\n});\n\ntry {\n  await pool.query(\"SELECT 1\");\n  console.log(\"Database connection successful\");\n} catch (error) {\n  console.error(\"Database connection failed:\", error);\n}\n```\n\n### Flag Not Found\n\nIf `isActiveForUser()` returns false unexpectedly:\n\n1. Verify the flag exists: `flapjack get-by-name your_flag_name`\n2. Check the evaluation rules with `flapjack is-active`\n3. Verify user/role/group matching is case-sensitive\n\n### Percentage Rollout Not Working\n\nDebug user bucketing:\n\n```bash\n# Check which bucket a user falls into\nflapjack hash-user user_123\n# Output: { userId: \"user_123\", hash: 1234567, bucket: 67 }\n\n# If bucket is 67 and percent is 50, user is NOT in rollout\n# If bucket is 67 and percent is 75, user IS in rollout\n```\n\n## Development\n\n### Setup\n\nInstall dependencies\n\n```bash\nnpm install\n```\n\nCreate an environment file:\n\n```bash\nnpm run dev:env\n```\n\nStart the PostgreSQL database:\n\n```bash\nnpm run dev:docker:up\n```\n\nRun database migrations:\n\n```bash\nnpm run dev:migrate\n```\n\n### Running Tests\n\n```bash\nnpm run test\n```\n\n### Database Management\n\nCreate a new migration:\n\n```bash\nnpm run create-migration -- migration_name\n```\n\nReset the database:\n\n```bash\nnpm run dev:docker:down\n```\n\n## Building and Publishing\n\n### Development Build\n\nTo build the project for development and testing:\n\n```bash\nnpm run build\n```\n\nThis compiles TypeScript to JavaScript and generates type declaration files in the `dist/` directory.\n\n### Creating a Development Package\n\nTo create a tarball package that can be installed manually in other projects:\n\n```bash\nnpm run pack:dev\n```\n\nThis will create a `flapjack-0.1.0.tgz` file that you can install in another project using:\n\n```bash\nnpm install /path/to/flapjack-0.1.0.tgz\n```\n\n### Publishing to npm\n\nBefore publishing to npm, make sure your package is ready:\n\n1. **Login to npm** (one-time setup):\n\n   ```bash\n   npm login\n   ```\n\n   This will prompt for your username, password, email, and 2FA code.\n\n2. **Verify your login**:\n\n   ```bash\n   npm whoami\n   ```\n\n3. **Update the version** in `package.json`:\n\n   ```bash\n   # For a patch release (0.1.0 -> 0.1.1)\n   npm version patch\n\n   # For a minor release (0.1.0 -> 0.2.0)\n   npm version minor\n\n   # For a major release (0.1.0 -> 1.0.0)\n   npm version major\n   ```\n\n   This automatically creates a git commit and tag.\n\n4. **Run pre-publish checks** (linting, tests, and build):\n\n   ```bash\n   npm run prepublishOnly\n   ```\n\n5. **Publish to npm**:\n\n   ```bash\n   npm publish --access public\n   ```\n\n   You'll be prompted for your 2FA code. For a dry run to see what would be published:\n\n   ```bash\n   npm publish --access public --dry-run\n   ```\n\n6. **Push the version tag to GitHub**:\n\n   ```bash\n   git push && git push --tags\n   ```\n\n7. **Optional: Create a GitHub release** for the new version at https://github.com/brandtg/flapjack/releases/new\n\n#### Publishing a Beta Version\n\nFor pre-release versions:\n\n```bash\n# Update to a pre-release version\nnpm version prerelease --preid=beta\n\n# Publish with beta tag\nnpm publish --access public --tag beta\n```\n\nUsers can install beta versions with:\n\n```bash\nnpm install @brandtg/flapjack@beta\n```\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nThis project is licensed under the BSD 3-Clause License - see the [LICENSE](LICENSE) file for details.\n\nThis project is inspired by [django-waffle](https://github.com/django-waffle/django-waffle).\n","readmeFilename":"README.md"}