@@ -167359,7 +167359,11 @@ define("services/login-service", function (require, exports, module) {
167359167359 require("./setup-login-service"); // this adds loginService to KernalModeTrust
167360167360 require("./promotions");
167361167361
167362+ const Metrics = require("utils/Metrics");
167363+ const LoginUtils = require("./login-utils");
167364+
167362167365 const MS_IN_DAY = 10 * 24 * 60 * 60 * 1000;
167366+ const TEN_MINUTES = 10 * 60 * 1000;
167363167367
167364167368 const KernalModeTrust = window.KernalModeTrust;
167365167369 if(!KernalModeTrust){
@@ -167375,6 +167379,25 @@ define("services/login-service", function (require, exports, module) {
167375167379 // Cached entitlements data
167376167380 let cachedEntitlements = null;
167377167381
167382+ // Last recorded state for entitlements monitoring
167383+ let lastRecordedState = null;
167384+
167385+ // Debounced trigger for entitlements changed
167386+ let entitlementsChangedTimer = null;
167387+
167388+ function _debounceEntitlementsChanged() {
167389+ if (entitlementsChangedTimer) {
167390+ // already scheduled, skip
167391+ return;
167392+ }
167393+
167394+ entitlementsChangedTimer = setTimeout(() => {
167395+ LoginService.trigger(EVENT_ENTITLEMENTS_CHANGED);
167396+ entitlementsChangedTimer = null;
167397+ }, 1000); // atmost 1 entitlement changed event will be triggered in a second
167398+ }
167399+
167400+
167378167401 /**
167379167402 * Get entitlements from API or cache
167380167403 * Returns null if user is not logged in
@@ -167428,7 +167451,7 @@ define("services/login-service", function (require, exports, module) {
167428167451
167429167452 // Trigger event if entitlements changed
167430167453 if (entitlementsChanged) {
167431- LoginService.trigger(EVENT_ENTITLEMENTS_CHANGED, result );
167454+ _debounceEntitlementsChanged( );
167432167455 }
167433167456
167434167457 return cachedEntitlements;
@@ -167448,12 +167471,43 @@ define("services/login-service", function (require, exports, module) {
167448167471 function clearEntitlements() {
167449167472 if (cachedEntitlements) {
167450167473 cachedEntitlements = null;
167474+ _debounceEntitlementsChanged();
167475+ }
167476+ }
167477+
167451167478
167452- // Trigger event when entitlements are cleared
167453- if (LoginService.trigger) {
167454- LoginService.trigger(EVENT_ENTITLEMENTS_CHANGED, null);
167479+ /**
167480+ * Start the 10-minute interval timer for monitoring entitlements
167481+ */
167482+ function startEntitlementsMonitor() {
167483+ setInterval(async () => {
167484+ try {
167485+ const current = await getEffectiveEntitlements(false); // Get effective entitlements
167486+
167487+ // Check if we need to refresh
167488+ const expiredPlanName = LoginUtils.validTillExpired(current, lastRecordedState);
167489+ const hasChanged = LoginUtils.haveEntitlementsChanged(current, lastRecordedState);
167490+
167491+ if (expiredPlanName || hasChanged) {
167492+ console.log(`Entitlements monitor detected changes, Expired: ${expiredPlanName},` +
167493+ `changed: ${hasChanged} refreshing...`);
167494+ Metrics.countEvent(Metrics.EVENT_TYPE.PRO, "entRefresh",
167495+ expiredPlanName ? "exp_"+expiredPlanName : "changed");
167496+ await getEffectiveEntitlements(true); // Force refresh
167497+ // if not logged in, the getEffectiveEntitlements will not trigger change even if some trial
167498+ // entitlements changed. so we trigger a change anyway here. The debounce will take care of
167499+ // multi fire and we are ok with multi fire 1 second apart.
167500+ _debounceEntitlementsChanged();
167501+ }
167502+
167503+ // Update last recorded state
167504+ lastRecordedState = current;
167505+ } catch (error) {
167506+ console.error('Entitlements monitor error:', error);
167455167507 }
167456- }
167508+ }, TEN_MINUTES);
167509+
167510+ console.log('Entitlements monitor started (10-minute interval)');
167457167511 }
167458167512
167459167513 /**
@@ -167529,7 +167583,8 @@ define("services/login-service", function (require, exports, module) {
167529167583 * @example
167530167584 * // Listen for entitlements changes
167531167585 * const LoginService = window.KernelModeTrust.loginService;
167532- * LoginService.on(LoginService.EVENT_ENTITLEMENTS_CHANGED, (entitlements) => {
167586+ * LoginService.on(LoginService.EVENT_ENTITLEMENTS_CHANGED, async() => {
167587+ * const entitlements = await LoginService.getEffectiveEntitlements();
167533167588 * console.log('Entitlements changed:', entitlements);
167534167589 * // Update UI based on new entitlements
167535167590 * });
@@ -167561,38 +167616,20 @@ define("services/login-service", function (require, exports, module) {
167561167616 if (serverEntitlements.plan.paidSubscriber) {
167562167617 // Already a paid subscriber, return as-is
167563167618 return serverEntitlements;
167564- } else {
167565- // Enhance entitlements for trial user
167566- return {
167567- ...serverEntitlements,
167568- plan: {
167569- ...serverEntitlements.plan,
167570- paidSubscriber: true,
167571- name: brackets.config.main_pro_plan
167572- },
167573- isInProTrial: true,
167574- trialDaysRemaining: trialDaysRemaining,
167575- entitlements: {
167576- ...serverEntitlements.entitlements,
167577- liveEdit: {
167578- activated: true,
167579- subscribeURL: brackets.config.purchase_url,
167580- upgradeToPlan: brackets.config.main_pro_plan,
167581- validTill: Date.now() + trialDaysRemaining * MS_IN_DAY
167582- }
167583- }
167584- };
167585167619 }
167586- } else {
167587- // Non-logged-in user with trial - return synthetic entitlements
167620+ // Enhance entitlements for trial user
167588167621 return {
167622+ ...serverEntitlements,
167589167623 plan: {
167624+ ...serverEntitlements.plan,
167590167625 paidSubscriber: true,
167591- name: brackets.config.main_pro_plan
167626+ name: brackets.config.main_pro_plan,
167627+ validTill: Date.now() + trialDaysRemaining * MS_IN_DAY
167592167628 },
167593167629 isInProTrial: true,
167594167630 trialDaysRemaining: trialDaysRemaining,
167595167631 entitlements: {
167632+ ...serverEntitlements.entitlements,
167596167633 liveEdit: {
167597167634 activated: true,
167598167635 subscribeURL: brackets.config.purchase_url,
@@ -167602,15 +167639,172 @@ define("services/login-service", function (require, exports, module) {
167602167639 }
167603167640 };
167604167641 }
167642+
167643+ // Non-logged-in user with trial - return synthetic entitlements
167644+ return {
167645+ plan: {
167646+ paidSubscriber: true,
167647+ name: brackets.config.main_pro_plan,
167648+ validTill: Date.now() + trialDaysRemaining * MS_IN_DAY
167649+ },
167650+ isInProTrial: true,
167651+ trialDaysRemaining: trialDaysRemaining,
167652+ entitlements: {
167653+ liveEdit: {
167654+ activated: true,
167655+ subscribeURL: brackets.config.purchase_url,
167656+ upgradeToPlan: brackets.config.main_pro_plan,
167657+ validTill: Date.now() + trialDaysRemaining * MS_IN_DAY
167658+ }
167659+ }
167660+ };
167605167661 }
167606167662
167607167663 // Add functions to secure exports
167608167664 LoginService.getEntitlements = getEntitlements;
167609167665 LoginService.getEffectiveEntitlements = getEffectiveEntitlements;
167610167666 LoginService.clearEntitlements = clearEntitlements;
167611167667 LoginService.EVENT_ENTITLEMENTS_CHANGED = EVENT_ENTITLEMENTS_CHANGED;
167668+
167669+ // Start the entitlements monitor timer
167670+ startEntitlementsMonitor();
167612167671});
167613167672
167673+ /*
167674+ * GNU AGPL-3.0 License
167675+ *
167676+ * Copyright (c) 2021 - present core.ai . All rights reserved.
167677+ *
167678+ * This program is free software: you can redistribute it and/or modify it under
167679+ * the terms of the GNU Affero General Public License as published by the Free
167680+ * Software Foundation, either version 3 of the License, or (at your option) any later version.
167681+ *
167682+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
167683+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
167684+ * See the GNU Affero General Public License for more details.
167685+ *
167686+ * You should have received a copy of the GNU Affero General Public License
167687+ * along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
167688+ *
167689+ */
167690+
167691+ /**
167692+ * Login Service Utilities
167693+ *
167694+ * This module contains utility functions for login service operations,
167695+ * including entitlements expiration checking and change detection.
167696+ */
167697+
167698+ define("services/login-utils", function (require, exports, module) {
167699+
167700+ /**
167701+ * Check if any validTill time has expired
167702+ *
167703+ * @param {Object|null} entitlements - Current entitlements object
167704+ * @param {Object|null} lastRecordedEntitlement - Previously recorded entitlements
167705+ * @returns {string|null} - Name of expired plan/entitlement or null if none expired
167706+ */
167707+ function validTillExpired(entitlements, lastRecordedEntitlement) {
167708+ if (!entitlements) {
167709+ return null;
167710+ }
167711+
167712+ const now = Date.now();
167713+
167714+ function isNewlyExpired(validTill, lastValidTill) {
167715+ return (
167716+ validTill &&
167717+ validTill < now && // expired now
167718+ (!lastValidTill || lastValidTill >= now) // but wasn't expired before
167719+ );
167720+ }
167721+
167722+ // Check plan validTill
167723+ if (entitlements.plan) {
167724+ const validTill = entitlements.plan.validTill;
167725+ const lastValidTill = (lastRecordedEntitlement && lastRecordedEntitlement.plan)
167726+ ? lastRecordedEntitlement.plan.validTill
167727+ : null;
167728+
167729+ if (isNewlyExpired(validTill, lastValidTill)) {
167730+ return entitlements.plan.name || brackets.config.main_pro_plan;
167731+ }
167732+ }
167733+
167734+ // Check entitlements validTill
167735+ if (entitlements.entitlements) {
167736+ for (const key in entitlements.entitlements) {
167737+ const entitlement = entitlements.entitlements[key];
167738+ if (!entitlement) {
167739+ continue;
167740+ }
167741+
167742+ const validTill = entitlement.validTill;
167743+ const lastValidTill = (lastRecordedEntitlement &&
167744+ lastRecordedEntitlement.entitlements &&
167745+ lastRecordedEntitlement.entitlements[key])
167746+ ? lastRecordedEntitlement.entitlements[key].validTill
167747+ : null;
167748+
167749+ if (isNewlyExpired(validTill, lastValidTill)) {
167750+ return key;
167751+ }
167752+ }
167753+ }
167754+
167755+ return null;
167756+ }
167757+
167758+ /**
167759+ * Check if entitlements have changed from last recorded state
167760+ *
167761+ * @param {Object|null} current - Current entitlements object
167762+ * @param {Object|null} last - Last recorded entitlements object
167763+ * @returns {boolean} - True if entitlements have changed, false otherwise
167764+ */
167765+ function haveEntitlementsChanged(current, last) {
167766+ if (!last && !current) {
167767+ return false;
167768+ }
167769+ if ((!last && current) || (!current && last)) {
167770+ return true;
167771+ }
167772+ if ((!last.entitlements && current.entitlements) || (!current.entitlements && last.entitlements)) {
167773+ return true;
167774+ }
167775+
167776+ // Check paidSubscriber changes
167777+ const currentPaidSub = current.plan && current.plan.paidSubscriber;
167778+ const lastPaidSub = last.plan && last.plan.paidSubscriber;
167779+ if (currentPaidSub !== lastPaidSub) {
167780+ return true;
167781+ }
167782+
167783+ // Check plan name changes
167784+ const currentPlanName = current.plan && current.plan.name;
167785+ const lastPlanName = last.plan && last.plan.name;
167786+ if (currentPlanName !== lastPlanName) {
167787+ return true;
167788+ }
167789+
167790+ // Check entitlement activations
167791+ if (current.entitlements && last.entitlements) {
167792+ for (const key of Object.keys(current.entitlements)) {
167793+ const currentActivated = current.entitlements[key] && current.entitlements[key].activated;
167794+ const lastActivated = last.entitlements[key] && last.entitlements[key].activated;
167795+ if (currentActivated !== lastActivated) {
167796+ return true;
167797+ }
167798+ }
167799+ }
167800+
167801+ return false;
167802+ }
167803+
167804+ // Export functions
167805+ exports.validTillExpired = validTillExpired;
167806+ exports.haveEntitlementsChanged = haveEntitlementsChanged;
167807+ });
167614167808/*
167615167809 * GNU AGPL-3.0 License
167616167810 *
@@ -168871,8 +169065,8 @@ define("services/promotions", function (require, exports, module) {
168871169065
168872169066 // Also trigger entitlements changed event since effective entitlements have changed
168873169067 // This allows UI components to update based on the new trial status
168874- const effectiveEntitlements = await LoginService.getEffectiveEntitlements();
168875- LoginService.trigger(LoginService.EVENT_ENTITLEMENTS_CHANGED, effectiveEntitlements );
169068+ await LoginService.getEffectiveEntitlements();
169069+ LoginService.trigger(LoginService.EVENT_ENTITLEMENTS_CHANGED);
168876169070 }
168877169071
168878169072 function _isAnyDialogsVisible() {
0 commit comments