73 lines
2.4 KiB
JavaScript
73 lines
2.4 KiB
JavaScript
/**
|
|
* Tier-based response gating.
|
|
*
|
|
* Free-tier users see the grade letter + confidence + edge (the hook)
|
|
* but the explanation stays locked. Paid tiers see everything. The
|
|
* frontend already has a `tier-locked` CSS class and `BlurredText`
|
|
* component — we just need to mark the locked fields with
|
|
* `locked: true` so the UI knows what to blur.
|
|
*
|
|
* This module is import-once / pure-function — it never reaches into
|
|
* Supabase or Redis. Callers pass the user's tier string; the function
|
|
* returns a new object (does not mutate input).
|
|
*/
|
|
|
|
const { canAccess } = require('../config/tiers');
|
|
|
|
const LOCKED_REASONING_SUMMARY = 'Upgrade to see full analysis.';
|
|
const LOCKED_KILL_REASON = 'Upgrade to see details.';
|
|
|
|
function lockReasoning(reasoning) {
|
|
if (!reasoning || typeof reasoning !== 'object') {
|
|
return { summary: LOCKED_REASONING_SUMMARY, steps: null, locked: true };
|
|
}
|
|
return { summary: LOCKED_REASONING_SUMMARY, steps: null, locked: true };
|
|
}
|
|
|
|
function lockKillConditions(list) {
|
|
if (!Array.isArray(list)) return [];
|
|
return list.map((kc) => ({
|
|
code: kc?.code ?? 'LOCKED',
|
|
reason: LOCKED_KILL_REASON,
|
|
locked: true,
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* applyTierGating — translate a fully-shaped analyzer result into the
|
|
* gated shape appropriate for the caller's tier. The legacy fields
|
|
* (grade, confidence, edge_pct, player, stat_type, line, direction,
|
|
* book) survive untouched on every tier. Only the explanation surface
|
|
* is redacted for free.
|
|
*
|
|
* @param {Object} result — legacy-shaped analyzer output
|
|
* @param {string} tierName — 'free' | 'analyst' | 'desk' | undefined
|
|
* @returns {Object} gated result (new object — input not mutated)
|
|
*/
|
|
function applyTierGating(result, tierName) {
|
|
if (!result || typeof result !== 'object') return result;
|
|
if (canAccess(tierName, 'reasoning_visible')) {
|
|
// Paid tier — pass through unchanged.
|
|
return result;
|
|
}
|
|
// Free tier: keep grade + confidence + edge_pct (the hook), redact
|
|
// reasoning + kill condition explanations.
|
|
return {
|
|
...result,
|
|
reasoning: lockReasoning(result.reasoning),
|
|
kill_conditions_triggered: lockKillConditions(result.kill_conditions_triggered),
|
|
tier_gated: true,
|
|
upgrade_hint: 'Upgrade to see full analysis + kill condition details.',
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
applyTierGating,
|
|
__internals: {
|
|
lockReasoning,
|
|
lockKillConditions,
|
|
LOCKED_REASONING_SUMMARY,
|
|
LOCKED_KILL_REASON,
|
|
},
|
|
};
|