-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Initial Pass at Runtime Flag Architecture #579
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { describe, it, expect } from "vitest"; | ||
import { FlagController } from "../controller"; | ||
import { PlayerFlags } from "../types"; | ||
|
||
describe("Controller Functionality", () => { | ||
const mockFlags: PlayerFlags = { | ||
duplicateIDLogLevel: "error", | ||
}; | ||
|
||
it("Basic Functionality", () => { | ||
const controller = new FlagController(mockFlags); | ||
|
||
expect(controller.getFlag("duplicateIDLogLevel")).toBe("error"); | ||
|
||
controller.updateFlags({ duplicateIDLogLevel: "debug" }); | ||
|
||
expect(controller.getFlag("duplicateIDLogLevel")).toBe("debug"); | ||
}); | ||
|
||
it("Hooks", () => { | ||
const controller = new FlagController(mockFlags); | ||
|
||
controller.hooks.overrideFlag.tap("test", (value, key) => { | ||
expect(value).toBe("error"); | ||
expect(key).toBe("duplicateIDLogLevel"); | ||
|
||
return "warning"; | ||
}); | ||
|
||
expect(controller.getFlag("duplicateIDLogLevel")).toBe("warning"); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import dlv from "dlv"; | ||
|
||
import { PlayerFlags, Flag, DefaultFlags } from "./types"; | ||
import { SyncWaterfallHook } from "tapable-ts"; | ||
|
||
export class FlagController { | ||
private flags: PlayerFlags; | ||
|
||
constructor(flags?: PlayerFlags) { | ||
this.flags = { ...DefaultFlags, ...flags }; | ||
} | ||
|
||
/** Hooks for the FlagsController */ | ||
public readonly hooks: { | ||
/** Allow a plugin or integration to dynamically change a flag without setting it globally */ | ||
overrideFlag: SyncWaterfallHook<[any, string]>; | ||
} = { | ||
overrideFlag: new SyncWaterfallHook(), | ||
}; | ||
|
||
public updateFlags(newFlags: Partial<PlayerFlags>): void { | ||
this.flags = { | ||
...this.flags, | ||
...newFlags, | ||
}; | ||
} | ||
|
||
public getFlag<T>(flag: Flag): T { | ||
const configuredFlag = dlv(this.flags, flag) as PlayerFlags[Flag]; | ||
return this.hooks.overrideFlag.call(configuredFlag, flag); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./controller"; | ||
export * from "./types"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Severity } from "../../logger"; | ||
|
||
/** Configuration for runtime Player behavior */ | ||
export interface PlayerFlags { | ||
/** What log level duplicate ID errors during view resolution should be raised as */ | ||
duplicateIDLogLevel: Severity; | ||
|
||
/** Log level for when there are cache conflicts in view resolution (usually related to duplicate IDs) */ | ||
cacheConflictLogLevel: Severity; | ||
} | ||
Comment on lines
+4
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Open to feedback on the flag format. For simplicity this initial pass was built to use flat flags primarily because nested flags would take more logic to merge and operate on and pulling in another dependency would be preferable to writing that logic custom but something like that would add a few kb to Player's size There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have the logs.level.duplicateId There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was considering using that but it seemed like overkill complexity-wise and potentially adding some overhead to get a flag if we need to go through binding parsing (even if its cached). I can write some benchmarks for it though to see what the impact would be. |
||
|
||
export const DefaultFlags: PlayerFlags = { | ||
duplicateIDLogLevel: "error", | ||
cacheConflictLogLevel: "info", | ||
}; | ||
|
||
export type Flag = keyof PlayerFlags; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: Technically this doesn't need to use
dlv
but will clean up when a flag format is agreed on.