61 lines
2.1 KiB
TypeScript
61 lines
2.1 KiB
TypeScript
/**
|
|
* `feature-flags enable` / `feature-flags disable` — flip a flag's
|
|
* boolean `enabled` and write an audit row, then publish a one-shot
|
|
* invalidation on the `feature-flags:invalidate` pub/sub channel so
|
|
* every long-lived web replica drops its L1 eval cache for this flag
|
|
* immediately rather than serving stale evaluations until the per-key
|
|
* TTL expires (#39 cross-process publisher).
|
|
*/
|
|
import { defineCommand } from "citty";
|
|
import consola from "consola";
|
|
import { broadcastFlagInvalidation } from "./invalidate.js";
|
|
|
|
async function toggleFlag(name: string, target: boolean): Promise<void> {
|
|
const { getDbClient } = await import("@toolctrl/db/client.server");
|
|
const prisma = getDbClient();
|
|
const existing = await prisma.featureFlag.findFirst({ where: { name } });
|
|
if (!existing) {
|
|
consola.error(`Flag "${name}" not found.`);
|
|
process.exitCode = 1;
|
|
return;
|
|
}
|
|
if (existing.enabled === target) {
|
|
consola.info(`Flag "${name}" is already ${target ? "enabled" : "disabled"}; no-op.`);
|
|
return;
|
|
}
|
|
await prisma.$transaction([
|
|
prisma.featureFlag.update({ where: { id: existing.id }, data: { enabled: target } }),
|
|
prisma.featureFlagAudit.create({
|
|
data: {
|
|
flagId: existing.id,
|
|
actorUserId: null,
|
|
action: "toggle",
|
|
oldValue: { enabled: existing.enabled },
|
|
newValue: { enabled: target },
|
|
},
|
|
}),
|
|
]);
|
|
await broadcastFlagInvalidation(name);
|
|
consola.success(`Flag "${name}" → ${target ? "enabled" : "disabled"}.`);
|
|
}
|
|
|
|
export const featureFlagsEnableCommand = defineCommand({
|
|
meta: { name: "enable", description: "Enable a feature flag by name." },
|
|
args: {
|
|
name: { type: "positional", required: true, description: "Flag name." },
|
|
},
|
|
async run({ args }) {
|
|
await toggleFlag(String(args.name), true);
|
|
},
|
|
});
|
|
|
|
export const featureFlagsDisableCommand = defineCommand({
|
|
meta: { name: "disable", description: "Disable a feature flag by name." },
|
|
args: {
|
|
name: { type: "positional", required: true, description: "Flag name." },
|
|
},
|
|
async run({ args }) {
|
|
await toggleFlag(String(args.name), false);
|
|
},
|
|
});
|