PageSourceSearch

https://fruiti.org/js/app/services/current-user.js?v=20260725-map30

js fruiti.org collected 2026-10-02 15:44:29 UTC 2,991 bytes, 96 lines download raw bytes

1(function (global) {
2  "use strict";
3
4  var cache = { profile: null, entitlements: null };
5
6  /**
7   * Prefer /app/profile over /app/profiles/me.
8   * WHY: some API builds treat "me" as :id and return "Invalid profile ID".
9   */
10  async function fetchProfile(signal) {
11    var primary = await FruitiAPI.apiRequest("/app/profile", { signal: signal });
12    if (!primary.ok) {
13      var secondary = await FruitiAPI.apiRequest("/app/profiles/me", { signal: signal });
14      if (
15        !secondary.ok &&
16        FruitiDiscovery &&
17        FruitiDiscovery.isInvalidProfileIdError(secondary)
18      ) {
19        throw Object.assign(new Error(primary.message || secondary.message), {
20          status: primary.status || secondary.status,
21          result: primary,
22          code: "PROFILE_LOOKUP_FAILED",
23        });
24      }
25      primary = secondary;
26    }
27    if (!primary.ok) {
28      throw Object.assign(new Error(primary.message), {
29        status: primary.status,
30        result: primary,
31        code: "PROFILE_LOOKUP_FAILED",
32      });
33    }
34    cache.profile = primary.data;
35    return primary.data;
36  }
37
38  async function fetchEntitlements(signal) {
39    var res = await FruitiAPI.apiRequest("/app/me/entitlements", { signal: signal });
40    if (!res.ok) {
41      throw Object.assign(new Error(res.message), { status: res.status, result: res });
42    }
43    cache.entitlements = res.data;
44    return res.data;
45  }
46
47  function mapEntitlement(data) {
48    var root = data || {};
49    // Live API returns { fruitiPlus: { isActive, source, ... } }; tests/legacy may be flat.
50    var d =
51      root.fruitiPlus && typeof root.fruitiPlus === "object"
52        ? root.fruitiPlus
53        : root;
54    var active =
55      d.isActive === true ||
56      d.active === true ||
57      d.hasFruitiPlus === true ||
58      d.fruitiPlusActive === true ||
59      String(d.status || "").toLowerCase() === "active" ||
60      String(d.entitlement || "").toLowerCase() === "fruiti_plus" ||
61      String(root.status || "").toLowerCase() === "active";
62    var trial =
63      d.isTrial === true ||
64      String(d.status || "").toLowerCase() === "trial" ||
65      String(d.period || "").toLowerCase() === "trial";
66    var expired =
67      d.isExpired === true ||
68      String(d.status || "").toLowerCase() === "expired";
69    var status = "none";
70    if (active && trial) status = "trial";
71    else if (active) status = "active";
72    else if (expired) status = "expired";
73    return {
74      status: status,
75      expiresAt: d.expiresAt || d.expiryDate || d.currentPeriodEnd || root.expiresAt || null,
76      raw: root,
77    };
78  }
79
80  function getCachedProfile() {
81    return cache.profile;
82  }
83
84  function setCachedProfile(profile) {
85    cache.profile = profile || null;
86    return cache.profile;
87  }
88
89  global.FruitiCurrentUser = {
90    fetchProfile: fetchProfile,
91    fetchEntitlements: fetchEntitlements,
92    mapEntitlement: mapEntitlement,
93    getCachedProfile: getCachedProfile,
94    setCachedProfile: setCachedProfile,
95  };
96})(window);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.