import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose' import { HydratedDocument, Types } from 'mongoose' export type FlagDocument = HydratedDocument export type FlagState = 'off' | 'targeted' | 'rollout' | 'on' // Embedded change log on the flag itself. Capped to the last 20 entries via a // guard in FlagsService — keeps the doc small and the side-panel render fast. export interface FlagHistoryEntry { at: Date byUserId?: Types.ObjectId byEmail?: string action: string // e.g. 'created', 'state: off → rollout', 'pct: 25 → 50' note?: string } @Schema({ collection: 'flags', timestamps: true }) export class Flag { // snake_case identifier used in code paths. Same regex as TenantPlan/slug-ish // — keeps it greppable and unambiguous in audit messages. @Prop({ required: true, unique: true, index: true, lowercase: true, trim: true }) key!: string // Human label shown in the UI. Optional but recommended. @Prop({ default: '' }) description!: string @Prop({ enum: ['off', 'targeted', 'rollout', 'on'], default: 'off', index: true }) state!: FlagState // Only consulted when state === 'rollout'. Hash-based assignment to a bucket // (see FlagsService.evaluate) means a given tenant either is or isn't in the // rollout — flipping the pct only changes the new slice, not existing ones. @Prop({ default: 0, min: 0, max: 100 }) pct!: number // Targeting axes. All optional; empty/missing = "no restriction on this axis". // For state === 'targeted' / 'rollout', a tenant must match every non-empty // axis to be eligible (intersection, not union). @Prop({ type: { plans: { type: [String], default: [] }, tenantSlugs: { type: [String], default: [] }, partnerSlugs: { type: [String], default: [] }, environments: { type: [String], default: [] }, }, default: () => ({ plans: [], tenantSlugs: [], partnerSlugs: [], environments: [] }), }) scope!: { plans: string[] tenantSlugs: string[] partnerSlugs: string[] environments: string[] } @Prop({ type: Types.ObjectId, ref: 'User' }) createdBy?: Types.ObjectId // Capped at last 20 in FlagsService.recordHistory. @Prop({ type: [ { at: { type: Date, default: () => new Date() }, byUserId: { type: Types.ObjectId, ref: 'User' }, byEmail: String, action: { type: String, required: true }, note: String, }, ], default: [], }) history!: FlagHistoryEntry[] } export const FlagSchema = SchemaFactory.createForClass(Flag)