AccessCommandUtils
Cmdr types and readout formatting for AccessDataService.
The formatting lives here, apart from the command that prints it, because the readout is the thing this package exists to make good -- somebody is complaining, and this is what tells you why. Pure functions over an AccessFactReport, so the exact text is unit-tested rather than eyeballed once.
Properties
OverrideValues
AccessCommandUtils.OverrideValues: {TRUE: string,FALSE: string,UNRESOLVED: string}
The three things an override can be set to. unresolved is spelled out rather than left as an empty
argument because forcing a fact to "nobody knows" is a deliberate thing to do, not a missing value.
RealmValues
AccessCommandUtils.RealmValues: {SERVER: string,CLIENT: string,BOTH: string}
Which realm a readout should come from. both is the one worth reaching for: the server and the
client disagreeing about a fact is the failure this package exists to make visible, and it is
invisible from either side alone.
Functions
describeValue
AccessCommandUtils.describeValue(value: boolean?) → stringHow a tri-state reads to a human. Never blank -- an empty column in a readout looks like a bug in the readout rather than the answer it actually is.
parseOverrideValue
AccessCommandUtils.parseOverrideValue(text: string) → boolean?Turns the override argument back into the value it names.
describeMetadata
AccessCommandUtils.describeMetadata(metadata: any?) → stringAttribution, rendered inline. Empty when a layer attached none, so the common case stays uncluttered.
Answers the question a bare "true" cannot: which friend granted this, which gamepass was owned.
formatFactReport
One fact and every layer under it, highest priority first, with the winner marked.
Losing layers are printed rather than hidden. "Denied and the allowlist said false" and "denied and the allowlist never answered" are different problems, and only the full list tells them apart.
formatFeatureReport
AccessCommandUtils.formatFeatureReport(subject: any--
what it was evaluated against, named on the verdict line
) → stringA feature's verdict followed by every fact it read. The whole answer to a complaint in one block.
formatContext
AccessCommandUtils.formatContext(context: {[string]: any}?) → {string}A feature's non-fact inputs, one per line, sorted. Empty when the feature declared none -- a feature with no context should read exactly as it did before there was such a thing.
formatFeatureInputs
The verdicts this feature inherited from other features, one per line, sorted.
Named and reasoned rather than reduced to true/false: "refused because bought access is switched off" and "refused because they do not own it" are different answers to the person complaining, and a boolean loses both.
formatPolicies
AccessCommandUtils.formatPolicies(policies: {{policyName: string,enabled: boolean,facts: {string}}}) → stringEvery policy and whether it is running. A disabled policy is listed rather than hidden -- knowing a consequence exists but is switched off is most of the answer to "why did nothing happen".
formatPlayerState
AccessCommandUtils.formatPlayerState(policies: {any}?,featureNamesNeedingSubject: {string}?) → stringEverything known about one player, in the order you actually read it: what the game concluded, then the facts those conclusions rest on. One block to paste into a bug report.
describeNameList
AccessCommandUtils.describeNameList(names: {string},noun: string--
plural, for the count and empty forms: "facts", "policies"
) → string
How a list of names reads back in a confirmation. Names them while there are few enough to check at a
glance, and counts them once naming them would be a wall -- which is exactly what * produces.
Sorted, because Cmdr builds a list argument by iterating its dedupe table: the order it hands back is hash order, not the order anybody typed.
parseSubject
AccessCommandUtils.parseSubject(text: string?) → anyTurns what somebody typed into the subject a per-thing feature is evaluated against.
Numeric text becomes a number, because the subjects that actually get typed are ids -- a world index, a
chapter -- and a feature comparing subject == 3 would silently deny for "3". Anything else is
passed through as the string it was. Empty means no subject.
describeSubject
AccessCommandUtils.describeSubject(subject: any) → stringHow a subject reads on a verdict line. Empty when there is none, because most features never take one and "(no subject)" on every line would read as a missing input rather than as a feature that has no such input. Where the absence does matter -- a whole-player dump, where a per-thing gate is being shown in the one form nobody needs -- AccessCommandUtils.formatPlayerState says so once instead.
collectPlayerState
AccessCommandUtils.collectPlayerState() → {policies: {any}}Everything a readout needs about one player, gathered from whichever realm this runs on.
Shared rather than written twice, because the whole value of showing the client's view next to the server's is that a difference between them is a real difference and not two formatters disagreeing.
The result is plain tables so it can be sent over remoting for exactly that comparison.
formatCollectedPlayerState
AccessCommandUtils.formatCollectedPlayerState(collected: any) → stringRenders what AccessCommandUtils.collectPlayerState gathered.
registerTypes
AccessCommandUtils.registerTypes() → ()Registers the fact-name and override-value argument types. Called on both realms -- the client needs them for autocomplete, the server to validate.
getPolicyNames is optional only as a fallback. Both realms register policies, so both realms should
pass it -- without it the argument accepts whatever is typed and suggests nothing.