membership.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. /**
  2. * Light membership roles — Phase 4/5 preview.
  3. * Deep paid tiers + chain permissioning come later.
  4. */
  5. export type MemberRole =
  6. | "viewer"
  7. | "portal_member"
  8. | "wallet_member"
  9. | "operator";
  10. export type MembershipProfile = {
  11. primary: MemberRole;
  12. roles: MemberRole[];
  13. tierLabel: string;
  14. blurb: string;
  15. };
  16. const ROLE_META: Record<
  17. MemberRole,
  18. { label: string; can: string[] }
  19. > = {
  20. viewer: {
  21. label: "Viewer",
  22. can: ["Open explorer", "Read docs"],
  23. },
  24. portal_member: {
  25. label: "Portal member",
  26. can: ["Dashboard", "Save session", "Request access later"],
  27. },
  28. wallet_member: {
  29. label: "Wallet member",
  30. can: ["Show linked address", "Local anvil chain switch"],
  31. },
  32. operator: {
  33. label: "Operator",
  34. can: ["Machine keys (M2M)", "Worker APIs — later"],
  35. },
  36. };
  37. export function roleLabel(role: MemberRole): string {
  38. return ROLE_META[role].label;
  39. }
  40. export function roleCapabilities(role: MemberRole): string[] {
  41. return ROLE_META[role].can;
  42. }
  43. /**
  44. * Derive light roles from how the person signed in.
  45. * Not security-critical yet — UI honesty only.
  46. */
  47. export function membershipFor(input: {
  48. isSignedIn: boolean;
  49. source: "briven" | "demo" | "wallet" | "konnos" | null;
  50. walletAddress?: string | null;
  51. label?: string | null;
  52. }): MembershipProfile {
  53. if (!input.isSignedIn) {
  54. return {
  55. primary: "viewer",
  56. roles: ["viewer"],
  57. tierLabel: "Guest",
  58. blurb: "Sign in or connect a wallet for a personal dashboard.",
  59. };
  60. }
  61. const roles = new Set<MemberRole>(["viewer"]);
  62. if (input.source === "wallet" || input.walletAddress) {
  63. roles.add("wallet_member");
  64. }
  65. if (input.source === "briven" || input.source === "demo") {
  66. roles.add("portal_member");
  67. }
  68. // Placeholder: later map Briven project roles / M2M operator claims
  69. if (input.label?.toLowerCase().includes("operator")) {
  70. roles.add("operator");
  71. }
  72. const list = Array.from(roles);
  73. const primary: MemberRole = list.includes("operator")
  74. ? "operator"
  75. : list.includes("wallet_member") && !list.includes("portal_member")
  76. ? "wallet_member"
  77. : list.includes("portal_member")
  78. ? "portal_member"
  79. : "viewer";
  80. const tierLabel =
  81. primary === "operator"
  82. ? "Operator preview"
  83. : primary === "wallet_member"
  84. ? "Wallet member"
  85. : "Portal member";
  86. const blurb =
  87. primary === "wallet_member"
  88. ? "You connected a browser wallet. Full chain membership (validators, gated APIs) lands later."
  89. : primary === "operator"
  90. ? "Operator-style access is preview-only. M2M machine clients power background jobs."
  91. : "You have a portal session. Paid tiers and network membership roles come later.";
  92. return { primary, roles: list, tierLabel, blurb };
  93. }