Skip to main content

FeatureFlags

Types

ActivationContext

interface ActivationContext {
userIdnumber?--

The ID of the user

groups{[string]true}?--

A set of groups

systemStates{[string]true}?--

A set of system states

abSegments{[string]true}?--

A set of AB segments

}

The ActivationContext for a feature' activation.

Represents user ID, groups, system states, and AB segments that inform feature activation. These parameters allow features to operate under different rules based on their specific context.

For instance, features may activate for specific user groups, AB segments, or under certain system states.

Default behavior is applied if a context parameter is not provided.

local userContext = {
	userId = 12345, -- Replace with actual user ID
	groups = { betaTesters = true }, -- User is in the 'betaTesters' group
	systemStates = { lowLoad = true }, -- System is currently under low load
	abSegments = { testA = true }, -- User is in the 'testA' AB segment
}

if isActive("ourFeature", userContext) then
	-- Our feature is active for this particular context
end

ActivationConfig

interface ActivationConfig {
defaultboolean?--

Default activation status if the feature doesn't exist

allowRetireboolean?--

Flag to allow retirement of the feature; if not set and the flag is retired, this notifies in the console

warnExistsboolean?--

Flag to warn rather than error when a feature doesn't exist

warnRetireboolean?--

Flag to warn rather than error when a feature is retired

}

The configuration parameters for a feature's activation.

This determines the default state and warning behavior of a feature. This can assist with development of features behind flags, such as throwing errors when flags are being used that are no longer meant to be used.

if
	isActive("newUI", userContext, {
		default = true, -- Assume the feature is active if not found (do not notify)
		allowRetire = false, -- Notify if the flag has been retired
		warnRetire = false, -- Error if the flag is retired
	})
then
	-- Load the new user interface
else
	-- Load the old user interface
end

InitializationOptions

interface InitializationOptions {
store?DataStore--

The name of the DataStore to use for serialization

synchronizationInterval?number--

The interval in seconds to synchronize flags

}

Options to initialize the serialization process with.

RuleSet

interface RuleSet {
activation?((
context?{[unknown]unknown},
ruleSet?{[unknown]unknown}
) → boolean)--

A custom activation function to evaluate

allowedUsers?{[number]true}--

A set of user IDs that must match

forbiddenUsers?{[number]true}--

A set of user IDs that may not match

allowedGroups?{[string]true}--

A set of groups that must match

forbiddenGroups?{[string]true}--

A set of groups that may not much

allowedSystemStates?{[string]true}--

A set of system states that must match

forbiddenSystemStates?{[string]true}--

A set of system states that may not match

abSegments?{[string]number}--

A map of AB testing segments

}

A rule set to determine if a feature should be active for a given context.

When a rule set is evaluated against a context, all parts of the rule set must be true for this rule set to pass. If no rule sets match then this rule set will not apply. See isActive for a more detailed explanation.

AB testing segments are provided as a map of segment names to segment proportions. All testing segment proportions are added together when selecting a segment to determine the total proportion rather than using a percentage. This enables more specific and configurable group proportions than strict percentages may allow.

create("abTestFlag", {
	active: true,
	ruleSets: {
		{ -- Our AB testing rule set
			abSegments = {
				-- Our total proportion is 8, so we're selecting out of 8 total.
				segment1 = 5, -- This segment accounts for 5/8ths of the population.
				segment2 = 2, -- This segment accounts for 2/8ths, or 1/4th.
				segment3 = 1, -- This segment accounts for 1/8th.
			},
		},
	},
})

Most rules accept a boolean value for each member. This creates a set of members, but also allows for these to be defined as predicates. Predicates are functions that accept some semantic context and return a boolean value. This allows for more complex contexts to be provided without needing to create specialized logic for each case.

local userId = 1234567890

isActive("predicateFlag", {
	userId = userId,
	groups = {
		beta = isBetaMember(userId),
		campaign = isCampaignMember(userId),
		criticalGeoRegion = isCriticalGeoRegion(userId),
	},
})
tip

Prefer using predicates over activation functions. Predicates are easier to use and understand. The predicate logic is clear to the developer placing a feature behind a flag, and the predicate logic is clear to the developer configuring the flag.

Activation functions are less clear and can introduce magic behavior. While the activation function may be clear to the developer configuring the flag, it may not be clear to the developer placing a feature behind a flag, and may even be confusing. Activation functions should be used sparingly and only when predicates are insufficient.

FlagConfig

interface FlagConfig {
activeboolean--

Whether the flag is active

retiredboolean--

Whether this flag is retired

ruleSets{RuleSet}--

Rule sets to evaluate for this configuration

}

The configuration of a flag.

Rule sets will be evaluated one at a time to determine if an activation context should have this feature active. See isActive for a more detailed explanation.

info

The active property controls whether this flag can be active for anyone. If this is false, no one will have this feature active. If a flag should be active in some circumstances, use rule sets.

PartialFlagConfig

interface PartialFlagConfig {
active?boolean--

Whether the flag is active

retired?boolean--

Whether this flag is retired

ruleSets?{RuleSet}--

Rule sets to evaluate for this configuration

}

A partial flag configuration.

This is used to update a flag. Any properties that are nil will not be updated.

ChangeRecord

interface ChangeRecord {
old?FlagConfig--

The old flag, or nil if the flag was just created

new?FlagConfig--

The new flag, or nil if the flag no longer exists

}

A record of how a flag has changed.

UpdateOptions

interface UpdateOptions {
serializeboolean--

Whether this change should be serialized

}

Options for updating a flag.

These are options for how a flag should be updated. Here you can specify whether this change should be serialized. The default is false.

PartialUpdateOptions

interface PartialUpdateOptions {
serialize?boolean--

Whether this change should be serialized

}

Partial update options.

This is used to configure a flag update. Any properties that are nil will be given default values.

Properties

Changed

FeatureFlags.Changed: Event

The flag changed event.

This fires every time a flag changes. It provides the name, a ChangeRecord, and UpdateOptions.

Changed:Connect(function(name: string, record: ChangeRecord, options: UpdateOptions)
	print(string.format("Flag '%s' changed.", name))
	print("Old flag:", record.old)
	print("New flag:", record.new)
	if options.serialize then
		print("This change will be serialized.")
	end
end)

Functions

isActive

FeatureFlags.isActive(
namestring,--

The name of the feature flag to check

context?ActivationContext,--

The user context for this feature activation

config?ActivationConfig--

The configuration parameters for this feature activation

) → boolean--

Whether the feature should be active

Checks if a feature flag is active based on provided context and configuration.

The isActive function evaluates whether a feature should be active based on the provided user context and configuration. It normalizes these inputs, using default values for any missing context or configuration properties.

if isActive("uiUpdate", {
	userId = 1000,
	groups = { beta = true },
	abSegments = { newInventory = true },
}) then
	-- The user with this context should have this feature active.
end

The feature flag's existence and retirement status are then checked:

  • If the feature doesn't exist, the behavior depends on the warnExists and default configuration properties.

    1. If warnExists is true, a warning is logged.
    2. If default is provided, the default value is returned.
    3. If neither a warning is logged nor a default value is provided, an error is thrown.
    4. If nothing else causes the function to terminate a default value of false is returned.
    if isActive("missingFlag", activationContext, {
    	default = true,
    	warnExists = true,
    }) then
    	-- If the flag no longer exists we still execute this code.
    	-- A warning is logged rather than an error.
    else
    	-- The flag exists, but is set to false.
    end
    
  • If the feature is retired, the behavior depends on the allowRetire and warnRetire configuration properties.

    1. If allowRetire is false, an error is thrown.
    2. If allowRetire is true but warnRetire is true as well, a warning is logged.
    if isActive("oldFlag", activationContext, {
    	allowRetire = true,
    	warnRetire = true,
    }) then
    	-- A retired flag can still be checked, but a warning is logged.
    end
    

The flag's active status is then checked. If the flag isn't active, then false is returned immediately.

info

An inactive flag indicates it should not be active for anyone or under any circumstances. To activate a feature conditionally rule sets should be used.

If the flag is active, each rule set in the feature flag's configuration is evaluated using the provided context:

  • If a rule set evaluates to true, the feature is active.

  • If no rule set evaluates to true, but at least one rule set was evaluated (i.e., it matched the context even though it evaluated to false), the feature is not active.

  • If no rule set matches the context (i.e., none of the rule sets were evaluated), this means that no rules apply to disable a feature for the provided context. The feature is considered active.

    caution

    This is sometimes unintuitive for users unfamiliar with it.

    Consider the case where a rule set to activate a feature for a specific user ID is configured but the feature is being activated without a user context, such as on the server. In such a case, the user restriction should not be considered matching and the feature should be considered active if no other rules apply.

init

FeatureFlags.init(
options?InitializationOptions--

Options to initialize the serialization process

) → Connection--

A connection that can be used to disconnect the serialization process

Initializes the serialization process.

Begins synchronizing flags with the data store. This will listen for local changes to flags that are marked for serialization and update the data store accordingly. Local changes are listened for continuously, but only sent to the data store in specified intervals to help avoid rate limits.

info

This should be started as soon as possible in a game's lifecycle to ensure that flags are synchronized as soon as possible. Any features that depend on serialized flags will be unavailable until this is started and synchronized.

init({
	store = "MyFeatureFlagStore",
	synchronizationInterval = 300, -- 5 minutes
})

The initial synchronization happens immediately as this is called. This ensures that flags are available as soon as possible. Later synchronizations will happen in the specified interval.

Flag changes can easily be marked for serialization by updating them while passing the serialize option of UpdateOptions. This will mark the flag as needing serialization and will be synchronized with the data store during the next synchronization interval.

-- This flag will not be serialized
update("localFlag", {
	-- Desired flag changes
})

-- Mark this flag for serialization
update("serializedFlag", {
	-- Desired flag changes
}, {
	serialize = true,
})

This is a convenience function that takes care of gathering flag changes and synchronizing them with a data store. If you need more control over the serialization process, you can create your own bespoke serialization process instead using the Changed event and the serialize option of UpdateOptions.

create

FeatureFlags.create(
namestring,--

The name to use for the flag

configPartialFlagConfig?,--

The configuration of this flag

) → ()

Creates a new flag with the provided name and configuration.

note

This only needs to be used when introducing new flags, such as when you want to introduce a new flag for a feature ahead of time.

This is typically done through some configuration interface, such as the built in one.

tip

If updating a flag that already exists, use the update function instead.

Errors

TypeDescription
"Flag '%s' already exists."Thrown when a flag with this name already exists.

exists

FeatureFlags.exists(namestring) → boolean

Checks if a flag with this name currently exists.

read

FeatureFlags.read(namestring) → FlagConfig

Reads the data of this flag.

This is primarily useful to display or manipulate flag information.

caution

This shouldn't be used to determine flag activation. Use the isActive function instead.

While this contains all flag information, it doesn't include any complex activation evaluation logic that isActive does.

Errors

TypeDescription
"Flag '%s' doesn't exist."Thrown when a flag with this name doesn't exist.

update

FeatureFlags.update(
namestring,
) → ()

Updates the configuration of this flag.

Errors

TypeDescription
"Flag '%s' doesn't exist."Thrown when a flag with this name doesn't exist.

retire

FeatureFlags.retire(
namestring,
retired?boolean,--

The retired status of the flag

) → ()

Sets the retired status of a flag.

If a retired status isn't provided it defaults to true, assuming you intend to retire a flag.

Errors

TypeDescription
"Flag '%s' doesn't exist."Thrown when a flag with this name doesn't exist.

destroy

FeatureFlags.destroy(
namestring,
) → ()

Removes a flag entirely.

Errors

TypeDescription
"Flag '%s' doesn't exist."Thrown when a flag with this name doesn't exist.

reset

FeatureFlags.reset(
notify?boolean--

Whether to notify listeners of this change

) → ()

Resets all registered flags.

After this operation, there will be no registered flags and flags will need to be registered again before they can be used. This is primarily used to re-initialize all feature flags.

Notification will inform all listeners about the removal of all flags. Performing no notification is faster, but may break features currently listening. The default is not to notify, assuming that features listening to flags have already been handled or are not currently listening.

get

FeatureFlags.get(
namestring--

The name of the flag

) → Promise<Flag>--

A Promise of the Flag requested

Gets a flag asynchronously.

get("newActivity"):andThen(function(flag: Flag)
	-- The flag is available to use.
	if flag.isActive() then
		-- The flag is active.
	end
end)
Show raw api
{
    "functions": [
        {
            "name": "isActive",
            "desc": "Checks if a feature flag is active based on provided context and\nconfiguration.\n\nThe `isActive` function evaluates whether a feature should be active based on\nthe provided user context and configuration. It normalizes these inputs, using\ndefault values for any missing context or configuration properties.\n\n```lua\nif isActive(\"uiUpdate\", {\n\tuserId = 1000,\n\tgroups = { beta = true },\n\tabSegments = { newInventory = true },\n}) then\n\t-- The user with this context should have this feature active.\nend\n```\n\nThe feature flag's existence and retirement status are then checked:\n\n- If the feature doesn't exist, the behavior depends on the `warnExists` and\n  `default` configuration properties.\n\n  1. If `warnExists` is true, a warning is logged.\n  2. If `default` is provided, the `default` value is returned.\n  3. If neither a warning is logged nor a `default` value is provided, an\n     error is thrown.\n  4. If nothing else causes the function to terminate a default value of false\n     is returned.\n\n  ```lua\n  if isActive(\"missingFlag\", activationContext, {\n  \tdefault = true,\n  \twarnExists = true,\n  }) then\n  \t-- If the flag no longer exists we still execute this code.\n  \t-- A warning is logged rather than an error.\n  else\n  \t-- The flag exists, but is set to false.\n  end\n  ```\n\n- If the feature is retired, the behavior depends on the `allowRetire` and\n  `warnRetire` configuration properties.\n\n  1. If `allowRetire` is false, an error is thrown.\n  2. If `allowRetire` is true but `warnRetire` is true as well, a warning is\n     logged.\n\n  ```lua\n  if isActive(\"oldFlag\", activationContext, {\n  \tallowRetire = true,\n  \twarnRetire = true,\n  }) then\n  \t-- A retired flag can still be checked, but a warning is logged.\n  end\n  ```\n\nThe flag's active status is then checked. If the flag isn't active, then false\nis returned immediately.\n\n:::info\nAn inactive flag indicates it should not be active for anyone or under any\ncircumstances. To activate a feature conditionally rule sets should be used.\n:::\n\nIf the flag is active, each rule set in the feature flag's configuration is\nevaluated using the provided context:\n\n- If a rule set evaluates to true, the feature is active.\n- If no rule set evaluates to true, but at least one rule set was evaluated\n  (i.e., it matched the context even though it evaluated to false), the\n  feature is not active.\n- If no rule set matches the context (i.e., none of the rule sets were\n  evaluated), this means that no rules apply to disable a feature for the\n  provided context. The feature is considered active.\n\n\t:::caution\n  This is sometimes unintuitive for users unfamiliar with it.\n\n  Consider the case where a rule set to activate a feature for a specific user\n  ID is configured but the feature is being activated without a user context,\n  such as on the server. In such a case, the user restriction should not be\n  considered matching and the feature should be considered active if no other\n  rules apply.\n\t:::",
            "params": [
                {
                    "name": "name",
                    "desc": "The name of the feature flag to check",
                    "lua_type": "string"
                },
                {
                    "name": "context?",
                    "desc": "The user context for this feature activation",
                    "lua_type": "ActivationContext"
                },
                {
                    "name": "config?",
                    "desc": "The configuration parameters for this feature activation",
                    "lua_type": "ActivationConfig"
                }
            ],
            "returns": [
                {
                    "desc": "Whether the feature should be active",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 386,
                "path": "lib/isActive.lua"
            }
        },
        {
            "name": "init",
            "desc": "Initializes the serialization process.\n\nBegins synchronizing flags with the data store. This will listen for local\nchanges to flags that are marked for serialization and update the data store\naccordingly. Local changes are listened for continuously, but only sent to\nthe data store in specified intervals to help avoid rate limits.\n\n:::info\nThis should be started as soon as possible in a game's lifecycle to ensure\nthat flags are synchronized as soon as possible. Any features that depend on\nserialized flags will be unavailable until this is started and synchronized.\n:::\n\n```lua\ninit({\n\tstore = \"MyFeatureFlagStore\",\n\tsynchronizationInterval = 300, -- 5 minutes\n})\n```\n\nThe initial synchronization happens immediately as this is called. This\nensures that flags are available as soon as possible. Later synchronizations\nwill happen in the specified interval.\n\nFlag changes can easily be marked for serialization by updating them while\npassing the `serialize` option of [UpdateOptions]. This will mark the flag as\nneeding serialization and will be synchronized with the data store during the\nnext synchronization interval.\n\n```lua\n-- This flag will not be serialized\nupdate(\"localFlag\", {\n\t-- Desired flag changes\n})\n\n-- Mark this flag for serialization\nupdate(\"serializedFlag\", {\n\t-- Desired flag changes\n}, {\n\tserialize = true,\n})\n```\n\nThis is a convenience function that takes care of gathering flag changes and\nsynchronizing them with a data store. If you need more control over the\nserialization process, you can create your own bespoke serialization process\ninstead using the [Changed](#Changed) event and the `serialize` option of\n[UpdateOptions].",
            "params": [
                {
                    "name": "options?",
                    "desc": "Options to initialize the serialization process",
                    "lua_type": "InitializationOptions"
                }
            ],
            "returns": [
                {
                    "desc": "A connection that can be used to disconnect the serialization process",
                    "lua_type": "Connection"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 194,
                "path": "lib/serialization/init.lua"
            }
        },
        {
            "name": "create",
            "desc": "Creates a new flag with the provided name and configuration.\n\n:::note\nThis only needs to be used when introducing new flags, such as when you want\nto introduce a new flag for a feature ahead of time.\n\nThis is typically done through some configuration interface, such as the built\nin one.\n:::\n\n:::tip\nIf updating a flag that already exists, use the `update` function instead.\n:::",
            "params": [
                {
                    "name": "name",
                    "desc": "The name to use for the flag",
                    "lua_type": "string"
                },
                {
                    "name": "config",
                    "desc": "The configuration of this flag",
                    "lua_type": "PartialFlagConfig?"
                },
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "PartialUpdateOptions?"
                }
            ],
            "returns": [],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "\"Flag '%s' already exists.\"",
                    "desc": "Thrown when a flag with this name already exists."
                }
            ],
            "source": {
                "line": 286,
                "path": "lib/Flags.lua"
            }
        },
        {
            "name": "exists",
            "desc": "Checks if a flag with this name currently exists.",
            "params": [
                {
                    "name": "name",
                    "desc": "",
                    "lua_type": "string"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "boolean\n"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 302,
                "path": "lib/Flags.lua"
            }
        },
        {
            "name": "read",
            "desc": "Reads the data of this flag.\n\nThis is primarily useful to display or manipulate flag information.\n\n:::caution\nThis shouldn't be used to determine flag activation. Use the `isActive`\nfunction instead.\n\nWhile this contains all flag information, it doesn't include any complex\nactivation evaluation logic that `isActive` does.\n:::",
            "params": [
                {
                    "name": "name",
                    "desc": "",
                    "lua_type": "string"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "FlagConfig\n"
                }
            ],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "\"Flag '%s' doesn't exist.\"",
                    "desc": "Thrown when a flag with this name doesn't exist."
                }
            ],
            "source": {
                "line": 323,
                "path": "lib/Flags.lua"
            }
        },
        {
            "name": "update",
            "desc": "Updates the configuration of this flag.",
            "params": [
                {
                    "name": "name",
                    "desc": "",
                    "lua_type": "string"
                },
                {
                    "name": "config",
                    "desc": "",
                    "lua_type": "PartialFlagConfig"
                },
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "PartialUpdateOptions?"
                }
            ],
            "returns": [],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "\"Flag '%s' doesn't exist.\"",
                    "desc": "Thrown when a flag with this name doesn't exist."
                }
            ],
            "source": {
                "line": 338,
                "path": "lib/Flags.lua"
            }
        },
        {
            "name": "retire",
            "desc": "Sets the retired status of a flag.\n\nIf a retired status isn't provided it defaults to true, assuming you intend to\nretire a flag.",
            "params": [
                {
                    "name": "name",
                    "desc": "",
                    "lua_type": "string"
                },
                {
                    "name": "retired?",
                    "desc": "The retired status of the flag",
                    "lua_type": "boolean"
                },
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "PartialUpdateOptions?"
                }
            ],
            "returns": [],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "\"Flag '%s' doesn't exist.\"",
                    "desc": "Thrown when a flag with this name doesn't exist."
                }
            ],
            "source": {
                "line": 358,
                "path": "lib/Flags.lua"
            }
        },
        {
            "name": "destroy",
            "desc": "Removes a flag entirely.",
            "params": [
                {
                    "name": "name",
                    "desc": "",
                    "lua_type": "string"
                },
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "PartialUpdateOptions?"
                }
            ],
            "returns": [],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "\"Flag '%s' doesn't exist.\"",
                    "desc": "Thrown when a flag with this name doesn't exist."
                }
            ],
            "source": {
                "line": 374,
                "path": "lib/Flags.lua"
            }
        },
        {
            "name": "reset",
            "desc": "Resets all registered flags.\n\nAfter this operation, there will be no registered flags and flags will need to\nbe registered again before they can be used. This is primarily used to\nre-initialize all feature flags.\n\nNotification will inform all listeners about the removal of all flags.\nPerforming no notification is faster, but may break features currently\nlistening. The default is not to notify, assuming that features listening to\nflags have already been handled or are not currently listening.",
            "params": [
                {
                    "name": "notify?",
                    "desc": "Whether to notify listeners of this change",
                    "lua_type": "boolean"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 397,
                "path": "lib/Flags.lua"
            }
        },
        {
            "name": "get",
            "desc": "Gets a flag asynchronously.\n\n```lua\nget(\"newActivity\"):andThen(function(flag: Flag)\n\t-- The flag is available to use.\n\tif flag.isActive() then\n\t\t-- The flag is active.\n\tend\nend)\n```",
            "params": [
                {
                    "name": "name",
                    "desc": "The name of the flag",
                    "lua_type": "string"
                }
            ],
            "returns": [
                {
                    "desc": "A Promise of the Flag requested",
                    "lua_type": "Promise<Flag>"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 29,
                "path": "lib/get.lua"
            }
        }
    ],
    "properties": [
        {
            "name": "Changed",
            "desc": "The flag changed event.\n\nThis fires every time a flag changes. It provides the name, a [ChangeRecord],\nand [UpdateOptions].\n\n```lua\nChanged:Connect(function(name: string, record: ChangeRecord, options: UpdateOptions)\n\tprint(string.format(\"Flag '%s' changed.\", name))\n\tprint(\"Old flag:\", record.old)\n\tprint(\"New flag:\", record.new)\n\tif options.serialize then\n\t\tprint(\"This change will be serialized.\")\n\tend\nend)\n```",
            "lua_type": "Event",
            "source": {
                "line": 205,
                "path": "lib/Flags.lua"
            }
        }
    ],
    "types": [
        {
            "name": "ActivationContext",
            "desc": "The ActivationContext for a feature' activation.\n\nRepresents user ID, groups, system states, and AB segments that inform feature\nactivation. These parameters allow features to operate under different rules\nbased on their specific context.\n\nFor instance, features may activate for specific user groups, AB segments, or\nunder certain system states.\n\nDefault behavior is applied if a context parameter is not provided.\n\n```lua\nlocal userContext = {\n\tuserId = 12345, -- Replace with actual user ID\n\tgroups = { betaTesters = true }, -- User is in the 'betaTesters' group\n\tsystemStates = { lowLoad = true }, -- System is currently under low load\n\tabSegments = { testA = true }, -- User is in the 'testA' AB segment\n}\n\nif isActive(\"ourFeature\", userContext) then\n\t-- Our feature is active for this particular context\nend\n```",
            "fields": [
                {
                    "name": "userId",
                    "lua_type": "number?",
                    "desc": "The ID of the user"
                },
                {
                    "name": "groups",
                    "lua_type": "{ [string]: true }?",
                    "desc": "A set of groups"
                },
                {
                    "name": "systemStates",
                    "lua_type": "{ [string]: true }?",
                    "desc": "A set of system states"
                },
                {
                    "name": "abSegments",
                    "lua_type": "{ [string]: true }?",
                    "desc": "A set of AB segments"
                }
            ],
            "source": {
                "line": 45,
                "path": "lib/isActive.lua"
            }
        },
        {
            "name": "ActivationConfig",
            "desc": "The configuration parameters for a feature's activation.\n\nThis determines the default state and warning behavior of a feature. This can\nassist with development of features behind flags, such as throwing errors when\nflags are being used that are no longer meant to be used.\n\n```lua\nif\n\tisActive(\"newUI\", userContext, {\n\t\tdefault = true, -- Assume the feature is active if not found (do not notify)\n\t\tallowRetire = false, -- Notify if the flag has been retired\n\t\twarnRetire = false, -- Error if the flag is retired\n\t})\nthen\n\t-- Load the new user interface\nelse\n\t-- Load the old user interface\nend\n```",
            "fields": [
                {
                    "name": "default",
                    "lua_type": "boolean?",
                    "desc": "Default activation status if the feature doesn't exist"
                },
                {
                    "name": "allowRetire",
                    "lua_type": "boolean?",
                    "desc": "Flag to allow retirement of the feature; if not set and the flag is retired, this notifies in the console"
                },
                {
                    "name": "warnExists",
                    "lua_type": "boolean?",
                    "desc": "Flag to warn rather than error when a feature doesn't exist"
                },
                {
                    "name": "warnRetire",
                    "lua_type": "boolean?",
                    "desc": "Flag to warn rather than error when a feature is retired"
                }
            ],
            "source": {
                "line": 81,
                "path": "lib/isActive.lua"
            }
        },
        {
            "name": "InitializationOptions",
            "desc": "Options to initialize the serialization process with.",
            "fields": [
                {
                    "name": "store?",
                    "lua_type": "DataStore",
                    "desc": "The name of the DataStore to use for serialization"
                },
                {
                    "name": "synchronizationInterval?",
                    "lua_type": "number",
                    "desc": "The interval in seconds to synchronize flags"
                }
            ],
            "source": {
                "line": 10,
                "path": "lib/serialization/normalize.lua"
            }
        },
        {
            "name": "RuleSet",
            "desc": "A rule set to determine if a feature should be active for a given context.\n\nWhen a rule set is evaluated against a context, all parts of the rule set must\nbe true for this rule set to pass. If no rule sets match then this rule set\nwill not apply. See [isActive](#isActive) for a more detailed explanation.\n\nAB testing segments are provided as a map of segment names to segment\nproportions. All testing segment proportions are added together when selecting\na segment to determine the total proportion rather than using a percentage.\nThis enables more specific and configurable group proportions than strict\npercentages may allow.\n\n```lua\ncreate(\"abTestFlag\", {\n\tactive: true,\n\truleSets: {\n\t\t{ -- Our AB testing rule set\n\t\t\tabSegments = {\n\t\t\t\t-- Our total proportion is 8, so we're selecting out of 8 total.\n\t\t\t\tsegment1 = 5, -- This segment accounts for 5/8ths of the population.\n\t\t\t\tsegment2 = 2, -- This segment accounts for 2/8ths, or 1/4th.\n\t\t\t\tsegment3 = 1, -- This segment accounts for 1/8th.\n\t\t\t},\n\t\t},\n\t},\n})\n```\n\nMost rules accept a boolean value for each member. This creates a set of\nmembers, but also allows for these to be defined as predicates. Predicates are\nfunctions that accept some semantic context and return a boolean value. This\nallows for more complex contexts to be provided without needing to create\nspecialized logic for each case.\n\n```lua\nlocal userId = 1234567890\n\nisActive(\"predicateFlag\", {\n\tuserId = userId,\n\tgroups = {\n\t\tbeta = isBetaMember(userId),\n\t\tcampaign = isCampaignMember(userId),\n\t\tcriticalGeoRegion = isCriticalGeoRegion(userId),\n\t},\n})\n```\n\n:::tip\nPrefer using predicates over activation functions. Predicates are easier to\nuse and understand. The predicate logic is clear to the developer placing a\nfeature behind a flag, and the predicate logic is clear to the developer\nconfiguring the flag.\n\nActivation functions are less clear and can introduce magic behavior. While\nthe activation function may be clear to the developer configuring the flag, it\nmay not be clear to the developer placing a feature behind a flag, and may\neven be confusing. Activation functions should be used sparingly and only when\npredicates are insufficient.\n:::",
            "fields": [
                {
                    "name": "activation?",
                    "lua_type": "((context?: { [unknown]: unknown }, ruleSet?: { [unknown]: unknown }) -> boolean)",
                    "desc": "A custom activation function to evaluate"
                },
                {
                    "name": "allowedUsers?",
                    "lua_type": "{ [number]: true }",
                    "desc": "A set of user IDs that must match"
                },
                {
                    "name": "forbiddenUsers?",
                    "lua_type": "{ [number]: true }",
                    "desc": "A set of user IDs that may not match"
                },
                {
                    "name": "allowedGroups?",
                    "lua_type": "{ [string]: true }",
                    "desc": "A set of groups that must match"
                },
                {
                    "name": "forbiddenGroups?",
                    "lua_type": "{ [string]: true }",
                    "desc": "A set of groups that may not much"
                },
                {
                    "name": "allowedSystemStates?",
                    "lua_type": "{ [string]: true }",
                    "desc": "A set of system states that must match"
                },
                {
                    "name": "forbiddenSystemStates?",
                    "lua_type": "{ [string]: true }",
                    "desc": "A set of system states that may not match"
                },
                {
                    "name": "abSegments?",
                    "lua_type": "{ [string]: number }",
                    "desc": "A map of AB testing segments"
                }
            ],
            "source": {
                "line": 77,
                "path": "lib/Flags.lua"
            }
        },
        {
            "name": "FlagConfig",
            "desc": "The configuration of a flag.\n\nRule sets will be evaluated one at a time to determine if an activation\ncontext should have this feature active. See [isActive](#isActive) for a more\ndetailed explanation.\n\n:::info\nThe active property controls whether this flag can be active for anyone. If\nthis is false, no one will have this feature active. If a flag should be\nactive in some circumstances, use rule sets.\n:::",
            "fields": [
                {
                    "name": "active",
                    "lua_type": "boolean",
                    "desc": "Whether the flag is active"
                },
                {
                    "name": "retired",
                    "lua_type": "boolean",
                    "desc": "Whether this flag is retired"
                },
                {
                    "name": "ruleSets",
                    "lua_type": "{ RuleSet }",
                    "desc": "Rule sets to evaluate for this configuration"
                }
            ],
            "source": {
                "line": 108,
                "path": "lib/Flags.lua"
            }
        },
        {
            "name": "PartialFlagConfig",
            "desc": "A partial flag configuration.\n\nThis is used to update a flag. Any properties that are nil will not be\nupdated.",
            "fields": [
                {
                    "name": "active?",
                    "lua_type": "boolean",
                    "desc": "Whether the flag is active"
                },
                {
                    "name": "retired?",
                    "lua_type": "boolean",
                    "desc": "Whether this flag is retired"
                },
                {
                    "name": "ruleSets?",
                    "lua_type": "{ RuleSet }",
                    "desc": "Rule sets to evaluate for this configuration"
                }
            ],
            "source": {
                "line": 127,
                "path": "lib/Flags.lua"
            }
        },
        {
            "name": "ChangeRecord",
            "desc": "A record of how a flag has changed.",
            "fields": [
                {
                    "name": "old?",
                    "lua_type": "FlagConfig",
                    "desc": "The old flag, or nil if the flag was just created"
                },
                {
                    "name": "new?",
                    "lua_type": "FlagConfig",
                    "desc": "The new flag, or nil if the flag no longer exists"
                }
            ],
            "source": {
                "line": 142,
                "path": "lib/Flags.lua"
            }
        },
        {
            "name": "UpdateOptions",
            "desc": "Options for updating a flag.\n\nThese are options for how a flag should be updated. Here you can specify\nwhether this change should be serialized. The default is false.",
            "fields": [
                {
                    "name": "serialize",
                    "lua_type": "boolean",
                    "desc": "Whether this change should be serialized"
                }
            ],
            "source": {
                "line": 158,
                "path": "lib/Flags.lua"
            }
        },
        {
            "name": "PartialUpdateOptions",
            "desc": "Partial update options.\n\nThis is used to configure a flag update. Any properties that are nil will be\ngiven default values.",
            "fields": [
                {
                    "name": "serialize?",
                    "lua_type": "boolean",
                    "desc": "Whether this change should be serialized"
                }
            ],
            "source": {
                "line": 173,
                "path": "lib/Flags.lua"
            }
        }
    ],
    "name": "FeatureFlags",
    "desc": "",
    "source": {
        "line": 9,
        "path": "lib/init.lua"
    }
}