@namiml/sdk-core 3.4.0-dev.202605190129 → 3.4.0-dev.202605191719
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +273 -22
- package/dist/index.d.ts +37 -3
- package/dist/index.mjs +273 -23
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -98,7 +98,7 @@ const {
|
|
|
98
98
|
// version — stamped by scripts/version.sh
|
|
99
99
|
NAMI_SDK_VERSION: exports.NAMI_SDK_VERSION = "3.4.0",
|
|
100
100
|
// full package version including dev suffix — stamped by scripts/version.sh
|
|
101
|
-
NAMI_SDK_PACKAGE_VERSION: exports.NAMI_SDK_PACKAGE_VERSION = "3.4.0-dev.
|
|
101
|
+
NAMI_SDK_PACKAGE_VERSION: exports.NAMI_SDK_PACKAGE_VERSION = "3.4.0-dev.202605191719",
|
|
102
102
|
// environments
|
|
103
103
|
PRODUCTION: exports.PRODUCTION = "production", DEVELOPMENT: exports.DEVELOPMENT = "development",
|
|
104
104
|
// error messages
|
|
@@ -6849,7 +6849,7 @@ async function withRetry(url, options, timeout = exports.API_TIMEOUT_LIMIT, retr
|
|
|
6849
6849
|
if (logTraffic && options?.body) {
|
|
6850
6850
|
logger.debug(`[HTTP] Request body: ${options.body}`);
|
|
6851
6851
|
}
|
|
6852
|
-
const response = await timeoutRequest(url, options, timeout);
|
|
6852
|
+
const response = await timeoutRequest$1(url, options, timeout);
|
|
6853
6853
|
if (!response.ok) {
|
|
6854
6854
|
if (logTraffic) {
|
|
6855
6855
|
const errorBody = await response.clone().text();
|
|
@@ -6879,7 +6879,7 @@ async function withRetry(url, options, timeout = exports.API_TIMEOUT_LIMIT, retr
|
|
|
6879
6879
|
const response = await fetchWithRetry();
|
|
6880
6880
|
return response.json();
|
|
6881
6881
|
}
|
|
6882
|
-
async function timeoutRequest(url, options = {}, timeout) {
|
|
6882
|
+
async function timeoutRequest$1(url, options = {}, timeout) {
|
|
6883
6883
|
const controller = new AbortController();
|
|
6884
6884
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
6885
6885
|
options["signal"] = controller.signal;
|
|
@@ -8309,6 +8309,161 @@ class ProductRepository {
|
|
|
8309
8309
|
}
|
|
8310
8310
|
ProductRepository.instance = new ProductRepository();
|
|
8311
8311
|
|
|
8312
|
+
const DEFAULTS = {
|
|
8313
|
+
maxAttempts: 3,
|
|
8314
|
+
baseDelayMs: 200,
|
|
8315
|
+
maxDelayMs: 2000,
|
|
8316
|
+
budgetMs: 5000,
|
|
8317
|
+
};
|
|
8318
|
+
const TRANSIENT_STATUSES = new Set([408, 425, 429]);
|
|
8319
|
+
const TRANSIENT_ERROR_CODES = new Set(["ECONNRESET", "ECONNREFUSED", "ENOTFOUND", "ETIMEDOUT"]);
|
|
8320
|
+
class NonRetryableHttpError extends Error {
|
|
8321
|
+
constructor(status, url) {
|
|
8322
|
+
super(`CDN fetch failed with non-retryable status ${status} for ${url}`);
|
|
8323
|
+
this.name = "NonRetryableHttpError";
|
|
8324
|
+
this.status = status;
|
|
8325
|
+
this.url = url;
|
|
8326
|
+
}
|
|
8327
|
+
}
|
|
8328
|
+
class RetryExhaustedError extends Error {
|
|
8329
|
+
constructor(url, attempts, lastStatus) {
|
|
8330
|
+
super(`CDN fetch exhausted ${attempts} attempts for ${url}${lastStatus !== undefined ? ` (last status ${lastStatus})` : ""}`);
|
|
8331
|
+
this.name = "RetryExhaustedError";
|
|
8332
|
+
this.url = url;
|
|
8333
|
+
this.attempts = attempts;
|
|
8334
|
+
this.lastStatus = lastStatus;
|
|
8335
|
+
}
|
|
8336
|
+
}
|
|
8337
|
+
/**
|
|
8338
|
+
* Classify a failure as transient (worth retrying) or not.
|
|
8339
|
+
*
|
|
8340
|
+
* Transient:
|
|
8341
|
+
* - HTTP 408 / 425 / 429 / any 5xx (504 Gateway Timeout from the CDN edge
|
|
8342
|
+
* is the motivating production case)
|
|
8343
|
+
* - AbortError from the request timeout
|
|
8344
|
+
* - Any TypeError thrown by fetch — covers Safari's "Load failed",
|
|
8345
|
+
* Chrome's "Failed to fetch", Firefox's "NetworkError ...", and
|
|
8346
|
+
* CORS-blocked responses from the CDN edge (a 504 without
|
|
8347
|
+
* Access-Control-Allow-Origin surfaces as a TypeError, not a Response)
|
|
8348
|
+
* - ECONNRESET/ECONNREFUSED/ENOTFOUND/ETIMEDOUT in Node test envs
|
|
8349
|
+
*
|
|
8350
|
+
* Non-transient: every other status (including 404 — a missing paywall is
|
|
8351
|
+
* genuinely gone; retrying just delays the inevitable failure).
|
|
8352
|
+
*/
|
|
8353
|
+
function isTransientFailure(err, status) {
|
|
8354
|
+
if (typeof status === "number") {
|
|
8355
|
+
return TRANSIENT_STATUSES.has(status) || status >= 500;
|
|
8356
|
+
}
|
|
8357
|
+
if (err && typeof err === "object") {
|
|
8358
|
+
const e = err;
|
|
8359
|
+
if (e.name === "AbortError")
|
|
8360
|
+
return true;
|
|
8361
|
+
if (e.code && TRANSIENT_ERROR_CODES.has(e.code))
|
|
8362
|
+
return true;
|
|
8363
|
+
// fetch() throws TypeError for network-layer failures (DNS/TCP/TLS,
|
|
8364
|
+
// CORS-blocked, edge dropped). HTTP status failures arrive as fulfilled
|
|
8365
|
+
// responses with !response.ok — never as a thrown TypeError. So any
|
|
8366
|
+
// TypeError reaching this catch is by definition transient. Don't filter
|
|
8367
|
+
// on message text: Safari ("Load failed"), Chrome ("Failed to fetch"),
|
|
8368
|
+
// and Firefox ("NetworkError ...") all differ, and any future variation
|
|
8369
|
+
// would silently be classified non-transient.
|
|
8370
|
+
if (err instanceof TypeError)
|
|
8371
|
+
return true;
|
|
8372
|
+
}
|
|
8373
|
+
return false;
|
|
8374
|
+
}
|
|
8375
|
+
async function timeoutRequest(url, timeout) {
|
|
8376
|
+
const controller = new AbortController();
|
|
8377
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
8378
|
+
try {
|
|
8379
|
+
return await fetch(url, { signal: controller.signal });
|
|
8380
|
+
}
|
|
8381
|
+
finally {
|
|
8382
|
+
clearTimeout(timeoutId);
|
|
8383
|
+
}
|
|
8384
|
+
}
|
|
8385
|
+
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
8386
|
+
/**
|
|
8387
|
+
* Fetch a single CDN-hosted paywall with bounded retries on transient
|
|
8388
|
+
* failures. Exponential backoff (baseDelayMs * 2^n, capped at maxDelayMs)
|
|
8389
|
+
* with ±50% jitter; the whole loop is clamped to budgetMs wall-clock so
|
|
8390
|
+
* the parallel-fetch window can't balloon when one edge is degraded.
|
|
8391
|
+
*
|
|
8392
|
+
* Throws NonRetryableHttpError for hard 4xx (including 404), or
|
|
8393
|
+
* RetryExhaustedError when all attempts and/or the budget are spent.
|
|
8394
|
+
*/
|
|
8395
|
+
async function fetchWithCdnRetry(url, opts = {}) {
|
|
8396
|
+
const maxAttempts = opts.maxAttempts ?? DEFAULTS.maxAttempts;
|
|
8397
|
+
const baseDelayMs = opts.baseDelayMs ?? DEFAULTS.baseDelayMs;
|
|
8398
|
+
const maxDelayMs = opts.maxDelayMs ?? DEFAULTS.maxDelayMs;
|
|
8399
|
+
const budgetMs = opts.budgetMs ?? DEFAULTS.budgetMs;
|
|
8400
|
+
const timeoutMs = opts.timeoutMs ?? exports.API_TIMEOUT_LIMIT;
|
|
8401
|
+
const randomFn = opts.randomFn ?? Math.random;
|
|
8402
|
+
const sleepFn = opts.sleepFn ?? defaultSleep;
|
|
8403
|
+
const logRequests = shouldLogHTTPRequests();
|
|
8404
|
+
const logTraffic = shouldLogHTTPTraffic();
|
|
8405
|
+
const start = Date.now();
|
|
8406
|
+
let attempt = 0;
|
|
8407
|
+
let lastStatus;
|
|
8408
|
+
let lastError;
|
|
8409
|
+
while (attempt < maxAttempts) {
|
|
8410
|
+
if (attempt > 0) {
|
|
8411
|
+
const elapsed = Date.now() - start;
|
|
8412
|
+
if (elapsed >= budgetMs)
|
|
8413
|
+
break;
|
|
8414
|
+
const baseDelay = Math.min(baseDelayMs * Math.pow(2, attempt - 1), maxDelayMs);
|
|
8415
|
+
// ±50% jitter: random in [0,1) → factor in [-0.5, 0.5)
|
|
8416
|
+
const jitter = baseDelay * (randomFn() - 0.5);
|
|
8417
|
+
const remaining = Math.max(0, budgetMs - elapsed);
|
|
8418
|
+
const sleepFor = Math.min(Math.max(0, baseDelay + jitter), remaining);
|
|
8419
|
+
await sleepFn(sleepFor);
|
|
8420
|
+
// If the sleep consumed the rest of the budget, don't fire another fetch.
|
|
8421
|
+
if (Date.now() - start >= budgetMs)
|
|
8422
|
+
break;
|
|
8423
|
+
}
|
|
8424
|
+
try {
|
|
8425
|
+
if (logRequests || logTraffic) {
|
|
8426
|
+
logger.debug(`[HTTP] GET ${url} (cdn attempt ${attempt + 1}/${maxAttempts})`);
|
|
8427
|
+
}
|
|
8428
|
+
const response = await timeoutRequest(url, timeoutMs);
|
|
8429
|
+
if (!response.ok) {
|
|
8430
|
+
if (logTraffic) {
|
|
8431
|
+
const errorBody = await response.clone().text();
|
|
8432
|
+
logger.debug(`[HTTP] Response ${response.status}: ${errorBody}`);
|
|
8433
|
+
}
|
|
8434
|
+
lastStatus = response.status;
|
|
8435
|
+
if (isTransientFailure(undefined, response.status)) {
|
|
8436
|
+
attempt++;
|
|
8437
|
+
continue;
|
|
8438
|
+
}
|
|
8439
|
+
throw new NonRetryableHttpError(response.status, url);
|
|
8440
|
+
}
|
|
8441
|
+
if (logTraffic) {
|
|
8442
|
+
const responseBody = await response.clone().text();
|
|
8443
|
+
logger.debug(`[HTTP] Response ${response.status}: ${responseBody}`);
|
|
8444
|
+
}
|
|
8445
|
+
const data = (await response.json());
|
|
8446
|
+
return { data, retryCount: attempt };
|
|
8447
|
+
}
|
|
8448
|
+
catch (err) {
|
|
8449
|
+
if (err instanceof NonRetryableHttpError)
|
|
8450
|
+
throw err;
|
|
8451
|
+
if (isTransientFailure(err)) {
|
|
8452
|
+
lastError = err;
|
|
8453
|
+
attempt++;
|
|
8454
|
+
continue;
|
|
8455
|
+
}
|
|
8456
|
+
throw err;
|
|
8457
|
+
}
|
|
8458
|
+
}
|
|
8459
|
+
// If we exited the loop without success but never saw a transient response,
|
|
8460
|
+
// fall back to surfacing the last raw error. Otherwise raise the canonical
|
|
8461
|
+
// exhausted-attempts signal with the last seen status for telemetry.
|
|
8462
|
+
if (lastStatus === undefined && lastError !== undefined)
|
|
8463
|
+
throw lastError;
|
|
8464
|
+
throw new RetryExhaustedError(url, attempt, lastStatus);
|
|
8465
|
+
}
|
|
8466
|
+
|
|
8312
8467
|
class PaywallRepository {
|
|
8313
8468
|
async fetchPaywalls() {
|
|
8314
8469
|
const authDevice = storageService.getDevice();
|
|
@@ -8359,17 +8514,22 @@ class PaywallRepository {
|
|
|
8359
8514
|
return data?.results || [];
|
|
8360
8515
|
}
|
|
8361
8516
|
async fetchPaywallByUrl(url) {
|
|
8362
|
-
return
|
|
8517
|
+
return fetchWithCdnRetry(url);
|
|
8363
8518
|
}
|
|
8364
8519
|
async fetchPaywallsByUrls(urls) {
|
|
8365
8520
|
const uniqueUrls = [...new Set(urls)];
|
|
8366
8521
|
const results = await Promise.allSettled(uniqueUrls.map((url) => this.fetchPaywallByUrl(url)));
|
|
8367
8522
|
const paywalls = [];
|
|
8523
|
+
let retryCount = 0;
|
|
8368
8524
|
for (const result of results) {
|
|
8369
8525
|
if (result.status === "fulfilled") {
|
|
8370
|
-
paywalls.push(result.value);
|
|
8526
|
+
paywalls.push(result.value.data);
|
|
8527
|
+
retryCount += result.value.retryCount;
|
|
8371
8528
|
}
|
|
8372
8529
|
else {
|
|
8530
|
+
// Note: an exhausted-failure path contributed retries we can't see
|
|
8531
|
+
// from the rejected promise, so retryCount under-reports failures.
|
|
8532
|
+
// Acceptable for the telemetry signal; revisit if full accounting matters.
|
|
8373
8533
|
logger.error(`Failed to fetch individual paywall: ${result.reason}`);
|
|
8374
8534
|
}
|
|
8375
8535
|
}
|
|
@@ -8378,9 +8538,9 @@ class PaywallRepository {
|
|
|
8378
8538
|
if (valid.length > 0) {
|
|
8379
8539
|
storageService.setPaywalls(exports.API_PAYWALLS, valid);
|
|
8380
8540
|
}
|
|
8381
|
-
return valid;
|
|
8541
|
+
return { paywalls: valid, retryCount };
|
|
8382
8542
|
}
|
|
8383
|
-
return paywalls;
|
|
8543
|
+
return { paywalls, retryCount };
|
|
8384
8544
|
}
|
|
8385
8545
|
validatePaywalls(paywalls) {
|
|
8386
8546
|
return paywalls.filter((paywall) => {
|
|
@@ -11856,10 +12016,13 @@ class NamiRefs {
|
|
|
11856
12016
|
const useIndividual = !campaignRepo.useLegacyPaywallFetch &&
|
|
11857
12017
|
CampaignRuleRepository.hasPaywallUrls(rawCampaigns);
|
|
11858
12018
|
let paywalls;
|
|
12019
|
+
let retryCount = 0;
|
|
11859
12020
|
const paywallsStartTime = Date.now();
|
|
11860
12021
|
if (useIndividual) {
|
|
11861
12022
|
const urls = CampaignRuleRepository.extractPaywallUrls(rawCampaigns);
|
|
11862
|
-
|
|
12023
|
+
const result = await paywallRepo.fetchPaywallsByUrls(urls);
|
|
12024
|
+
paywalls = result.paywalls;
|
|
12025
|
+
retryCount = result.retryCount;
|
|
11863
12026
|
}
|
|
11864
12027
|
else {
|
|
11865
12028
|
paywalls = await paywallRepo.fetchPaywalls();
|
|
@@ -11868,7 +12031,8 @@ class NamiRefs {
|
|
|
11868
12031
|
const totalDuration = Date.now() - startTime;
|
|
11869
12032
|
logger.info(`Paywall fetch telemetry: strategy=${useIndividual ? "individual" : "bulk"}, ` +
|
|
11870
12033
|
`count=${paywalls.length}, total=${totalDuration}ms, ` +
|
|
11871
|
-
`campaigns=${campaignRulesDuration}ms, paywalls=${paywallsDuration}ms`
|
|
12034
|
+
`campaigns=${campaignRulesDuration}ms, paywalls=${paywallsDuration}ms` +
|
|
12035
|
+
`${useIndividual ? `, retry_count=${retryCount}` : ""}`);
|
|
11872
12036
|
return campaignRepo.finalizeCampaignRules(rawCampaigns, paywalls);
|
|
11873
12037
|
}
|
|
11874
12038
|
reRenderPaywall() {
|
|
@@ -12831,21 +12995,46 @@ class NamiProfileManager {
|
|
|
12831
12995
|
}
|
|
12832
12996
|
PaywallState.setIsLoggedIn(!!this.externalId);
|
|
12833
12997
|
}
|
|
12834
|
-
|
|
12998
|
+
/**
|
|
12999
|
+
* Update local profile state for login: persist the external id and flip the
|
|
13000
|
+
* logged-in flag in PaywallState. No network I/O — pair with `loginRemote`
|
|
13001
|
+
* to perform the actual API call. Used by the async login/logout fast path.
|
|
13002
|
+
*/
|
|
13003
|
+
loginLocal(externalId) {
|
|
13004
|
+
this.setExternalId(externalId).save();
|
|
13005
|
+
PaywallState.setIsLoggedIn(true);
|
|
13006
|
+
}
|
|
13007
|
+
/**
|
|
13008
|
+
* Perform the login network call only. If the server returns a different
|
|
13009
|
+
* `external_id` than the locally stored value, reconcile it into local state.
|
|
13010
|
+
*/
|
|
13011
|
+
async loginRemote(externalId) {
|
|
12835
13012
|
const loginData = await NamiAPI.instance.login(externalId);
|
|
12836
|
-
if (loginData.external_id) {
|
|
12837
|
-
|
|
13013
|
+
if (loginData.external_id && loginData.external_id !== this.externalId) {
|
|
13014
|
+
this.setExternalId(loginData.external_id).save();
|
|
12838
13015
|
}
|
|
12839
|
-
this
|
|
12840
|
-
.setExternalId(loginData.external_id)
|
|
12841
|
-
.save();
|
|
12842
13016
|
}
|
|
12843
|
-
|
|
12844
|
-
|
|
13017
|
+
/**
|
|
13018
|
+
* Update local profile state for logout: clear the external id, persist,
|
|
13019
|
+
* and flip the logged-in flag in PaywallState. Used by the async fast path.
|
|
13020
|
+
*/
|
|
13021
|
+
logoutLocal() {
|
|
13022
|
+
this.setExternalId(undefined).save();
|
|
12845
13023
|
PaywallState.setIsLoggedIn(false);
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
13024
|
+
}
|
|
13025
|
+
/**
|
|
13026
|
+
* Perform the logout network call only.
|
|
13027
|
+
*/
|
|
13028
|
+
async logoutRemote() {
|
|
13029
|
+
await NamiAPI.instance.logout();
|
|
13030
|
+
}
|
|
13031
|
+
async login(externalId) {
|
|
13032
|
+
this.loginLocal(externalId);
|
|
13033
|
+
await this.loginRemote(externalId);
|
|
13034
|
+
}
|
|
13035
|
+
async logout() {
|
|
13036
|
+
this.logoutLocal();
|
|
13037
|
+
await this.logoutRemote();
|
|
12849
13038
|
}
|
|
12850
13039
|
}
|
|
12851
13040
|
NamiProfileManager.instance = new NamiProfileManager();
|
|
@@ -13165,6 +13354,21 @@ class NamiFlowManager {
|
|
|
13165
13354
|
}
|
|
13166
13355
|
}
|
|
13167
13356
|
|
|
13357
|
+
const SHOULD_SHOW_LOADING_INDICATOR = false;
|
|
13358
|
+
const DISABLE_ASYNC_LOGIN_LOGOUT = "disableAsyncLoginLogout";
|
|
13359
|
+
/**
|
|
13360
|
+
* Returns true when `disableAsyncLoginLogout` appears in the active `namiCommands` list,
|
|
13361
|
+
* meaning login/logout should await the underlying API call before notifying handlers.
|
|
13362
|
+
*
|
|
13363
|
+
* Default behavior (flag absent) is the async/fast path: update local state and notify
|
|
13364
|
+
* handlers immediately while dispatching the API call as fire-and-forget.
|
|
13365
|
+
*
|
|
13366
|
+
* Internal feature flag — opt-out via `Nami.configure({ namiCommands: [...] })`.
|
|
13367
|
+
*/
|
|
13368
|
+
const isAsyncLoginLogoutDisabled = () => {
|
|
13369
|
+
return storageService.getNamiConfig()?.namiCommands?.includes(DISABLE_ASYNC_LOGIN_LOGOUT) ?? false;
|
|
13370
|
+
};
|
|
13371
|
+
|
|
13168
13372
|
/**
|
|
13169
13373
|
* @class NamiCustomerManager
|
|
13170
13374
|
* Provides methods for managing customer-related functionality.
|
|
@@ -13226,6 +13430,30 @@ class NamiCustomerManager {
|
|
|
13226
13430
|
this.invokeStateHandler(exports.AccountStateAction.LOGIN, false, error);
|
|
13227
13431
|
throw error;
|
|
13228
13432
|
}
|
|
13433
|
+
if (!isAsyncLoginLogoutDisabled()) {
|
|
13434
|
+
// Default sync/fast path: update local state, notify handlers, and fire
|
|
13435
|
+
// lifecycle immediately. Dispatch the underlying API call and entitlement
|
|
13436
|
+
// refresh fire-and-forget — failures are logged but do not surface.
|
|
13437
|
+
NamiProfileManager.instance.loginLocal(externalId);
|
|
13438
|
+
this.invokeStateHandler(exports.AccountStateAction.LOGIN, true);
|
|
13439
|
+
if (NamiFlowManager.instance.flowOpen) {
|
|
13440
|
+
const currentFlow = NamiFlowManager.instance.currentFlow;
|
|
13441
|
+
const currentStep = currentFlow?.currentFlowStep;
|
|
13442
|
+
if (currentStep) {
|
|
13443
|
+
if (Nami.instance.maxLogging) {
|
|
13444
|
+
logger.debug(`[NamiCustomerManager] async login success — triggering __login_success__ on step ${currentStep.id}`);
|
|
13445
|
+
}
|
|
13446
|
+
currentFlow.executeLifecycle(currentStep, NamiReservedActions.LOGIN_SUCCESS);
|
|
13447
|
+
}
|
|
13448
|
+
}
|
|
13449
|
+
void NamiProfileManager.instance.loginRemote(externalId).catch((err) => {
|
|
13450
|
+
logger.error('[NamiCustomerManager] async login API failed', err);
|
|
13451
|
+
});
|
|
13452
|
+
void EntitlementRepository.instance.fetchActiveEntitlements().catch((err) => {
|
|
13453
|
+
logger.error('[NamiCustomerManager] async login entitlement refresh failed', err);
|
|
13454
|
+
});
|
|
13455
|
+
return;
|
|
13456
|
+
}
|
|
13229
13457
|
try {
|
|
13230
13458
|
await NamiProfileManager.instance.login(externalId);
|
|
13231
13459
|
await EntitlementRepository.instance.fetchActiveEntitlements();
|
|
@@ -13271,6 +13499,30 @@ class NamiCustomerManager {
|
|
|
13271
13499
|
this.invokeStateHandler(exports.AccountStateAction.LOGOUT, false, error);
|
|
13272
13500
|
throw error;
|
|
13273
13501
|
}
|
|
13502
|
+
if (!isAsyncLoginLogoutDisabled()) {
|
|
13503
|
+
// Default sync/fast path: clear local state, notify handlers, and fire
|
|
13504
|
+
// lifecycle immediately. Dispatch the underlying API call and entitlement
|
|
13505
|
+
// refresh fire-and-forget — failures are logged but do not surface.
|
|
13506
|
+
NamiProfileManager.instance.logoutLocal();
|
|
13507
|
+
this.invokeStateHandler(exports.AccountStateAction.LOGOUT, true);
|
|
13508
|
+
if (NamiFlowManager.instance.flowOpen) {
|
|
13509
|
+
const currentFlow = NamiFlowManager.instance.currentFlow;
|
|
13510
|
+
const currentStep = currentFlow?.currentFlowStep;
|
|
13511
|
+
if (currentStep) {
|
|
13512
|
+
if (Nami.instance.maxLogging) {
|
|
13513
|
+
logger.debug(`[NamiCustomerManager] async logout success — triggering __logout_success__ on step ${currentStep.id}`);
|
|
13514
|
+
}
|
|
13515
|
+
currentFlow.executeLifecycle(currentStep, NamiReservedActions.LOGOUT_SUCCESS);
|
|
13516
|
+
}
|
|
13517
|
+
}
|
|
13518
|
+
void NamiProfileManager.instance.logoutRemote().catch((err) => {
|
|
13519
|
+
logger.error('[NamiCustomerManager] async logout API failed', err);
|
|
13520
|
+
});
|
|
13521
|
+
void EntitlementRepository.instance.fetchActiveEntitlements().catch((err) => {
|
|
13522
|
+
logger.error('[NamiCustomerManager] async logout entitlement refresh failed', err);
|
|
13523
|
+
});
|
|
13524
|
+
return;
|
|
13525
|
+
}
|
|
13274
13526
|
try {
|
|
13275
13527
|
await NamiProfileManager.instance.logout();
|
|
13276
13528
|
await EntitlementRepository.instance.fetchActiveEntitlements();
|
|
@@ -14705,8 +14957,6 @@ exports.CampaignRuleConversionEventType = void 0;
|
|
|
14705
14957
|
CampaignRuleConversionEventType["IN_APP"] = "in_app";
|
|
14706
14958
|
})(exports.CampaignRuleConversionEventType || (exports.CampaignRuleConversionEventType = {}));
|
|
14707
14959
|
|
|
14708
|
-
const SHOULD_SHOW_LOADING_INDICATOR = false;
|
|
14709
|
-
|
|
14710
14960
|
function parseToSemver(versionString) {
|
|
14711
14961
|
const [semVer, major, minor, patch, prerelease, buildmetadata] = versionString.match(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/) ?? [];
|
|
14712
14962
|
return {
|
|
@@ -64181,6 +64431,7 @@ exports.ClientError = ClientError;
|
|
|
64181
64431
|
exports.ConfigRepository = ConfigRepository;
|
|
64182
64432
|
exports.ConflictError = ConflictError;
|
|
64183
64433
|
exports.CustomerJourneyRepository = CustomerJourneyRepository;
|
|
64434
|
+
exports.DISABLE_ASYNC_LOGIN_LOGOUT = DISABLE_ASYNC_LOGIN_LOGOUT;
|
|
64184
64435
|
exports.DeviceIDRequiredError = DeviceIDRequiredError;
|
|
64185
64436
|
exports.DeviceRepository = DeviceRepository;
|
|
64186
64437
|
exports.EntitlementRepository = EntitlementRepository;
|
package/dist/index.d.ts
CHANGED
|
@@ -2133,6 +2133,26 @@ declare class NamiProfileManager {
|
|
|
2133
2133
|
isLoggedIn(): boolean;
|
|
2134
2134
|
private save;
|
|
2135
2135
|
private load;
|
|
2136
|
+
/**
|
|
2137
|
+
* Update local profile state for login: persist the external id and flip the
|
|
2138
|
+
* logged-in flag in PaywallState. No network I/O — pair with `loginRemote`
|
|
2139
|
+
* to perform the actual API call. Used by the async login/logout fast path.
|
|
2140
|
+
*/
|
|
2141
|
+
loginLocal(externalId: string): void;
|
|
2142
|
+
/**
|
|
2143
|
+
* Perform the login network call only. If the server returns a different
|
|
2144
|
+
* `external_id` than the locally stored value, reconcile it into local state.
|
|
2145
|
+
*/
|
|
2146
|
+
loginRemote(externalId: string): Promise<void>;
|
|
2147
|
+
/**
|
|
2148
|
+
* Update local profile state for logout: clear the external id, persist,
|
|
2149
|
+
* and flip the logged-in flag in PaywallState. Used by the async fast path.
|
|
2150
|
+
*/
|
|
2151
|
+
logoutLocal(): void;
|
|
2152
|
+
/**
|
|
2153
|
+
* Perform the logout network call only.
|
|
2154
|
+
*/
|
|
2155
|
+
logoutRemote(): Promise<void>;
|
|
2136
2156
|
login(externalId: string): Promise<void>;
|
|
2137
2157
|
logout(): Promise<void>;
|
|
2138
2158
|
}
|
|
@@ -2607,6 +2627,7 @@ declare const LIQUID_VARIABLE_REGEX: RegExp;
|
|
|
2607
2627
|
declare const NAMI_STORAGE_KEYS: readonly string[];
|
|
2608
2628
|
|
|
2609
2629
|
declare const SHOULD_SHOW_LOADING_INDICATOR = false;
|
|
2630
|
+
declare const DISABLE_ASYNC_LOGIN_LOGOUT = "disableAsyncLoginLogout";
|
|
2610
2631
|
|
|
2611
2632
|
declare const getBaseUrl: (namiCommands?: string[]) => string;
|
|
2612
2633
|
declare const getExtendedClientInfo: (namiCommands: string[]) => ExtendedPlatformInfo;
|
|
@@ -2974,13 +2995,26 @@ declare class EntitlementRepository {
|
|
|
2974
2995
|
private invokeActiveEntitlementsHandler;
|
|
2975
2996
|
}
|
|
2976
2997
|
|
|
2998
|
+
/**
|
|
2999
|
+
* Result of a single per-paywall CDN fetch — carries the parsed body
|
|
3000
|
+
* alongside the number of retries it took to land. Aggregated upstream
|
|
3001
|
+
* by PaywallRepository.fetchPaywallsByUrls and surfaced in telemetry.
|
|
3002
|
+
*/
|
|
3003
|
+
type CdnFetchResult<T> = {
|
|
3004
|
+
data: T;
|
|
3005
|
+
retryCount: number;
|
|
3006
|
+
};
|
|
3007
|
+
|
|
2977
3008
|
declare class PaywallRepository {
|
|
2978
3009
|
static instance: PaywallRepository;
|
|
2979
3010
|
fetchPaywalls(): Promise<IPaywall[]>;
|
|
2980
3011
|
private getAnonymousPaywalls;
|
|
2981
3012
|
private getPaywalls;
|
|
2982
|
-
fetchPaywallByUrl(url: string): Promise<IPaywall
|
|
2983
|
-
fetchPaywallsByUrls(urls: string[]): Promise<
|
|
3013
|
+
fetchPaywallByUrl(url: string): Promise<CdnFetchResult<IPaywall>>;
|
|
3014
|
+
fetchPaywallsByUrls(urls: string[]): Promise<{
|
|
3015
|
+
paywalls: IPaywall[];
|
|
3016
|
+
retryCount: number;
|
|
3017
|
+
}>;
|
|
2984
3018
|
private validatePaywalls;
|
|
2985
3019
|
private fallbackData;
|
|
2986
3020
|
}
|
|
@@ -3069,5 +3103,5 @@ declare const getBillingPeriodNumber: (billingPeriod: string) => number;
|
|
|
3069
3103
|
declare const formattedPrice: (price: number) => number;
|
|
3070
3104
|
declare function toDouble(num: number): number;
|
|
3071
3105
|
|
|
3072
|
-
export { ALREADY_CONFIGURED, ANONYMOUS_MODE, ANONYMOUS_MODE_ALREADY_OFF, ANONYMOUS_MODE_ALREADY_ON, ANONYMOUS_MODE_LOGIN_NOT_ALLOWED, ANONYMOUS_UUID, APIError, API_ACTIVE_ENTITLEMENTS, API_CAMPAIGN_RULES, API_CAMPAIGN_SESSION_TIMESTAMP, API_CONFIG, API_MAX_CALLS_LIMIT, API_PAYWALLS, API_PRODUCTS, API_RETRY_DELAY_SEC, API_TIMEOUT_LIMIT, API_VERSION, AUTH_DEVICE, AVAILABLE_ACTIVE_ENTITLEMENTS_CHANGED, AVAILABLE_CAMPAIGNS_CHANGED, AccountStateAction, AnonymousCDPError, AnonymousLoginError, AnonymousModeAlreadyOffError, AnonymousModeAlreadyOnError, BASE_STAGING_URL, BASE_URL, BASE_URL_PATH, BadRequestError, BasicNamiFlow, BorderMap, BorderSideMap, CAMPAIGN_NOT_AVAILABLE, CUSTOMER_ATTRIBUTES_KEY_PREFIX, CUSTOMER_JOURNEY_STATE_CHANGED, CUSTOM_HOST_PREFIX, CampaignNotAvailableError, CampaignRuleConversionEventType, CampaignRuleRepository, Capabilities, ClientError, ConfigRepository, ConflictError, CustomerJourneyRepository, DEVELOPMENT, DEVICE_API_TIMEOUT_LIMIT, DEVICE_ID_NOT_SET, DEVICE_ID_REQUIRED, DeviceIDRequiredError, DeviceRepository, EXTENDED_CLIENT_INFO_DELIMITER, EXTENDED_CLIENT_INFO_PREFIX, EXTENDED_PLATFORM, EXTENDED_PLATFORM_VERSION, EXTERNAL_ID_REQUIRED, EntitlementRepository, EntitlementUtils, ExternalIDRequiredError, FLOW_SCREENS_NOT_AVAILABLE, FlowScreensNotAvailableError, HTML_REGEX, INITIAL_APP_CONFIG, INITIAL_CAMPAIGN_RULES, INITIAL_PAYWALLS, INITIAL_PRODUCTS, INITIAL_SESSION_COUNTER_VALUE, INITIAL_SUCCESS, InternalServerError, KEY_SESSION_COUNTER, LIQUID_VARIABLE_REGEX, LOCAL_NAMI_ENTITLEMENTS, LOG_HTTP_REQUESTS, LOG_HTTP_TRAFFIC, LaunchCampaignError, LaunchContextResolver, LogLevel, NAMI_CONFIGURATION, NAMI_CUSTOMER_JOURNEY_STATE, NAMI_LANGUAGE_CODE, NAMI_LAST_IMPRESSION_ID, NAMI_LAUNCH_ID, NAMI_PROFILE, NAMI_PURCHASE_CHANNEL, NAMI_PURCHASE_IMPRESSION_ID, NAMI_SDK_PACKAGE_VERSION, NAMI_SDK_VERSION, NAMI_SESSION_ID, NAMI_STORAGE_KEYS, Nami, NamiAPI, NamiAnimationType, NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager, NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager, NamiFlowStepType, NamiPaywallAction, NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiProfileManager, NamiPurchaseManager, NamiRefs, NamiReservedActions, NotFoundError, PAYWALL_ACTION_EVENT, PLATFORM_ID_REQUIRED, PRODUCTION, PaywallManagerEvents, PaywallRepository, PaywallState, PlacementLabelResolver, PlatformIDRequiredError, ProductRepository, RECONFIG_SUCCESS, RetryLimitExceededError, SDKNotInitializedError, SDK_NOT_INITIALIZED, SERVER_NAMI_ENTITLEMENTS, SESSION_REQUIRED, SHOULD_SHOW_LOADING_INDICATOR, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, activateEntitlementByPurchase, activeEntitlements, aggregateScreenreaderText, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getPaywall, getPaywallDataFromLabel, getPercentagePriceDifference, getPeriodNumberInDays, getPeriodNumberInWeeks, getPlatformAdapters, getPriceDifference, getPricePerMonth, getProductDetail, getPurchaseAdapter, getReferenceSku, getSkuProductDetailKeys, getSkuSmartTextValue, getSlideSmartTextValue, getStandardBillingPeriod, getTranslate, getUrlParams, handleErrors, hasAllPaywalls, hasCapability, hasPurchaseManagement, initialState, invokeHandler, isAnonymousMode, isInitialConfigCompressed, isNamiFlowCampaign, isSubscription, isValidISODate, isValidUrl, logger, mapAnonymousCampaigns, namiBuySKU, normalizeLaunchContext, parseToSemver, postConversion, productDetail, registerPlatformAdapters, registerPurchaseAdapter, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateMinSDKVersion };
|
|
3106
|
+
export { ALREADY_CONFIGURED, ANONYMOUS_MODE, ANONYMOUS_MODE_ALREADY_OFF, ANONYMOUS_MODE_ALREADY_ON, ANONYMOUS_MODE_LOGIN_NOT_ALLOWED, ANONYMOUS_UUID, APIError, API_ACTIVE_ENTITLEMENTS, API_CAMPAIGN_RULES, API_CAMPAIGN_SESSION_TIMESTAMP, API_CONFIG, API_MAX_CALLS_LIMIT, API_PAYWALLS, API_PRODUCTS, API_RETRY_DELAY_SEC, API_TIMEOUT_LIMIT, API_VERSION, AUTH_DEVICE, AVAILABLE_ACTIVE_ENTITLEMENTS_CHANGED, AVAILABLE_CAMPAIGNS_CHANGED, AccountStateAction, AnonymousCDPError, AnonymousLoginError, AnonymousModeAlreadyOffError, AnonymousModeAlreadyOnError, BASE_STAGING_URL, BASE_URL, BASE_URL_PATH, BadRequestError, BasicNamiFlow, BorderMap, BorderSideMap, CAMPAIGN_NOT_AVAILABLE, CUSTOMER_ATTRIBUTES_KEY_PREFIX, CUSTOMER_JOURNEY_STATE_CHANGED, CUSTOM_HOST_PREFIX, CampaignNotAvailableError, CampaignRuleConversionEventType, CampaignRuleRepository, Capabilities, ClientError, ConfigRepository, ConflictError, CustomerJourneyRepository, DEVELOPMENT, DEVICE_API_TIMEOUT_LIMIT, DEVICE_ID_NOT_SET, DEVICE_ID_REQUIRED, DISABLE_ASYNC_LOGIN_LOGOUT, DeviceIDRequiredError, DeviceRepository, EXTENDED_CLIENT_INFO_DELIMITER, EXTENDED_CLIENT_INFO_PREFIX, EXTENDED_PLATFORM, EXTENDED_PLATFORM_VERSION, EXTERNAL_ID_REQUIRED, EntitlementRepository, EntitlementUtils, ExternalIDRequiredError, FLOW_SCREENS_NOT_AVAILABLE, FlowScreensNotAvailableError, HTML_REGEX, INITIAL_APP_CONFIG, INITIAL_CAMPAIGN_RULES, INITIAL_PAYWALLS, INITIAL_PRODUCTS, INITIAL_SESSION_COUNTER_VALUE, INITIAL_SUCCESS, InternalServerError, KEY_SESSION_COUNTER, LIQUID_VARIABLE_REGEX, LOCAL_NAMI_ENTITLEMENTS, LOG_HTTP_REQUESTS, LOG_HTTP_TRAFFIC, LaunchCampaignError, LaunchContextResolver, LogLevel, NAMI_CONFIGURATION, NAMI_CUSTOMER_JOURNEY_STATE, NAMI_LANGUAGE_CODE, NAMI_LAST_IMPRESSION_ID, NAMI_LAUNCH_ID, NAMI_PROFILE, NAMI_PURCHASE_CHANNEL, NAMI_PURCHASE_IMPRESSION_ID, NAMI_SDK_PACKAGE_VERSION, NAMI_SDK_VERSION, NAMI_SESSION_ID, NAMI_STORAGE_KEYS, Nami, NamiAPI, NamiAnimationType, NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager, NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager, NamiFlowStepType, NamiPaywallAction, NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiProfileManager, NamiPurchaseManager, NamiRefs, NamiReservedActions, NotFoundError, PAYWALL_ACTION_EVENT, PLATFORM_ID_REQUIRED, PRODUCTION, PaywallManagerEvents, PaywallRepository, PaywallState, PlacementLabelResolver, PlatformIDRequiredError, ProductRepository, RECONFIG_SUCCESS, RetryLimitExceededError, SDKNotInitializedError, SDK_NOT_INITIALIZED, SERVER_NAMI_ENTITLEMENTS, SESSION_REQUIRED, SHOULD_SHOW_LOADING_INDICATOR, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, activateEntitlementByPurchase, activeEntitlements, aggregateScreenreaderText, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getPaywall, getPaywallDataFromLabel, getPercentagePriceDifference, getPeriodNumberInDays, getPeriodNumberInWeeks, getPlatformAdapters, getPriceDifference, getPricePerMonth, getProductDetail, getPurchaseAdapter, getReferenceSku, getSkuProductDetailKeys, getSkuSmartTextValue, getSlideSmartTextValue, getStandardBillingPeriod, getTranslate, getUrlParams, handleErrors, hasAllPaywalls, hasCapability, hasPurchaseManagement, initialState, invokeHandler, isAnonymousMode, isInitialConfigCompressed, isNamiFlowCampaign, isSubscription, isValidISODate, isValidUrl, logger, mapAnonymousCampaigns, namiBuySKU, normalizeLaunchContext, parseToSemver, postConversion, productDetail, registerPlatformAdapters, registerPurchaseAdapter, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateMinSDKVersion };
|
|
3073
3107
|
export type { AlignmentType, AmazonProduct, ApiResponse, AppleProduct, AvailableCampaignsResponseHandler, BorderLocationType, BorderSideType, Callback$1 as Callback, CloseHandler, CustomerJourneyState, DeepLinkUrlHandler, Device, DevicePayload, DeviceProfile, DirectionType, ExtendedPlatformInfo, FlexDirectionObject, FlowNavigationOptions, FontCollection, FontDetails, FormFactor, GoogleProduct, IConfig, IDeviceAdapter, IEntitlements$1 as IEntitlements, IPaywall, IPlatformAdapters, IProductsWithComponents, IPurchaseAdapter, ISkuMenu, IStorageAdapter, IUIAdapter, Impression, InitialConfig, InitialConfigCompressed, InitiateStateGroup, LoginResponse, NamiAnimation, NamiAnimationObjectSpec, NamiAnimationSpec, NamiAnonymousCampaign, NamiAppSuppliedVideoDetails, NamiCampaign, NamiCampaignSegment, NamiConfiguration, NamiConfigurationState, NamiEntitlement$1 as NamiEntitlement, NamiFlowAction, NamiFlowAnimation, NamiFlowCampaign, NamiFlowDTO, NamiFlowEventHandler, NamiFlowHandoffStepHandler, NamiFlowObjectDTO, NamiFlowOn, NamiFlowStep, NamiFlowTransition, NamiFlowTransitionDirection, NamiFlowWithObject, NamiInitialConfig, NamiLanguageCodes, NamiLogLevel, NamiPaywallActionHandler, NamiPaywallComponentChange, NamiPaywallEvent, NamiPaywallEventVideoMetadata, NamiPaywallLaunchContext, NamiPresentationStyle, NamiProductDetails, NamiProductOffer, NamiProfile, NamiPurchase, NamiPurchaseCompleteResult, NamiPurchaseDetails, NamiPurchasesState, NamiSKU, NamiSKUType, NamiSubscriptionInterval, NamiSubscriptionPeriod, None, NoneSpec, PaywallActionEvent, PaywallHandle, PaywallResultHandler, PaywallSKU, PricingPhase, ProductGroup, Pulse, PulseSpec, PurchaseContext, PurchaseResult, PurchaseValidationRequest, SKU, SKUActionHandler, ScreenInfo, Session, TBaseComponent, TButtonContainer, TCarouselContainer, TCarouselSlide, TCarouselSlidesState, TCollapseContainer, TComponent, TConditionalAttributes, TConditionalComponent, TContainer, TContainerPosition, TCountdownTimerTextComponent, TDevice, TDisabledButton, TField, TFieldSettings, TFlexProductContainer, THeaderFooter, TImageComponent, TInitialState, TMediaTypes, TOffer, TPages, TPaywallContext, TPaywallLaunchContext, TPaywallMedia, TPaywallTemplate, TPlayPauseButton, TProductContainer, TProductGroup, TProgressBarComponent, TProgressIndicatorComponent, TQRCodeComponent, TRadioButton, TRepeatingGrid, TResponsiveGrid, TSegmentPicker, TSegmentPickerItem, TSemverObj, TSpacerComponent, TStack, TSvgImageComponent, TSymbolComponent, TTestObject, TTextComponent, TTextLikeComponent, TTextListComponent, TToggleButtonComponent, TToggleSwitch, TVariablePattern, TVideoComponent, TVolumeButton, TimerState, TransactionRequest, UserAction, UserActionParameters, Wave, WaveSpec };
|
package/dist/index.mjs
CHANGED
|
@@ -96,7 +96,7 @@ const {
|
|
|
96
96
|
// version — stamped by scripts/version.sh
|
|
97
97
|
NAMI_SDK_VERSION = "3.4.0",
|
|
98
98
|
// full package version including dev suffix — stamped by scripts/version.sh
|
|
99
|
-
NAMI_SDK_PACKAGE_VERSION = "3.4.0-dev.
|
|
99
|
+
NAMI_SDK_PACKAGE_VERSION = "3.4.0-dev.202605191719",
|
|
100
100
|
// environments
|
|
101
101
|
PRODUCTION = "production", DEVELOPMENT = "development",
|
|
102
102
|
// error messages
|
|
@@ -6847,7 +6847,7 @@ async function withRetry(url, options, timeout = API_TIMEOUT_LIMIT, retries = AP
|
|
|
6847
6847
|
if (logTraffic && options?.body) {
|
|
6848
6848
|
logger.debug(`[HTTP] Request body: ${options.body}`);
|
|
6849
6849
|
}
|
|
6850
|
-
const response = await timeoutRequest(url, options, timeout);
|
|
6850
|
+
const response = await timeoutRequest$1(url, options, timeout);
|
|
6851
6851
|
if (!response.ok) {
|
|
6852
6852
|
if (logTraffic) {
|
|
6853
6853
|
const errorBody = await response.clone().text();
|
|
@@ -6877,7 +6877,7 @@ async function withRetry(url, options, timeout = API_TIMEOUT_LIMIT, retries = AP
|
|
|
6877
6877
|
const response = await fetchWithRetry();
|
|
6878
6878
|
return response.json();
|
|
6879
6879
|
}
|
|
6880
|
-
async function timeoutRequest(url, options = {}, timeout) {
|
|
6880
|
+
async function timeoutRequest$1(url, options = {}, timeout) {
|
|
6881
6881
|
const controller = new AbortController();
|
|
6882
6882
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
6883
6883
|
options["signal"] = controller.signal;
|
|
@@ -8307,6 +8307,161 @@ class ProductRepository {
|
|
|
8307
8307
|
}
|
|
8308
8308
|
ProductRepository.instance = new ProductRepository();
|
|
8309
8309
|
|
|
8310
|
+
const DEFAULTS = {
|
|
8311
|
+
maxAttempts: 3,
|
|
8312
|
+
baseDelayMs: 200,
|
|
8313
|
+
maxDelayMs: 2000,
|
|
8314
|
+
budgetMs: 5000,
|
|
8315
|
+
};
|
|
8316
|
+
const TRANSIENT_STATUSES = new Set([408, 425, 429]);
|
|
8317
|
+
const TRANSIENT_ERROR_CODES = new Set(["ECONNRESET", "ECONNREFUSED", "ENOTFOUND", "ETIMEDOUT"]);
|
|
8318
|
+
class NonRetryableHttpError extends Error {
|
|
8319
|
+
constructor(status, url) {
|
|
8320
|
+
super(`CDN fetch failed with non-retryable status ${status} for ${url}`);
|
|
8321
|
+
this.name = "NonRetryableHttpError";
|
|
8322
|
+
this.status = status;
|
|
8323
|
+
this.url = url;
|
|
8324
|
+
}
|
|
8325
|
+
}
|
|
8326
|
+
class RetryExhaustedError extends Error {
|
|
8327
|
+
constructor(url, attempts, lastStatus) {
|
|
8328
|
+
super(`CDN fetch exhausted ${attempts} attempts for ${url}${lastStatus !== undefined ? ` (last status ${lastStatus})` : ""}`);
|
|
8329
|
+
this.name = "RetryExhaustedError";
|
|
8330
|
+
this.url = url;
|
|
8331
|
+
this.attempts = attempts;
|
|
8332
|
+
this.lastStatus = lastStatus;
|
|
8333
|
+
}
|
|
8334
|
+
}
|
|
8335
|
+
/**
|
|
8336
|
+
* Classify a failure as transient (worth retrying) or not.
|
|
8337
|
+
*
|
|
8338
|
+
* Transient:
|
|
8339
|
+
* - HTTP 408 / 425 / 429 / any 5xx (504 Gateway Timeout from the CDN edge
|
|
8340
|
+
* is the motivating production case)
|
|
8341
|
+
* - AbortError from the request timeout
|
|
8342
|
+
* - Any TypeError thrown by fetch — covers Safari's "Load failed",
|
|
8343
|
+
* Chrome's "Failed to fetch", Firefox's "NetworkError ...", and
|
|
8344
|
+
* CORS-blocked responses from the CDN edge (a 504 without
|
|
8345
|
+
* Access-Control-Allow-Origin surfaces as a TypeError, not a Response)
|
|
8346
|
+
* - ECONNRESET/ECONNREFUSED/ENOTFOUND/ETIMEDOUT in Node test envs
|
|
8347
|
+
*
|
|
8348
|
+
* Non-transient: every other status (including 404 — a missing paywall is
|
|
8349
|
+
* genuinely gone; retrying just delays the inevitable failure).
|
|
8350
|
+
*/
|
|
8351
|
+
function isTransientFailure(err, status) {
|
|
8352
|
+
if (typeof status === "number") {
|
|
8353
|
+
return TRANSIENT_STATUSES.has(status) || status >= 500;
|
|
8354
|
+
}
|
|
8355
|
+
if (err && typeof err === "object") {
|
|
8356
|
+
const e = err;
|
|
8357
|
+
if (e.name === "AbortError")
|
|
8358
|
+
return true;
|
|
8359
|
+
if (e.code && TRANSIENT_ERROR_CODES.has(e.code))
|
|
8360
|
+
return true;
|
|
8361
|
+
// fetch() throws TypeError for network-layer failures (DNS/TCP/TLS,
|
|
8362
|
+
// CORS-blocked, edge dropped). HTTP status failures arrive as fulfilled
|
|
8363
|
+
// responses with !response.ok — never as a thrown TypeError. So any
|
|
8364
|
+
// TypeError reaching this catch is by definition transient. Don't filter
|
|
8365
|
+
// on message text: Safari ("Load failed"), Chrome ("Failed to fetch"),
|
|
8366
|
+
// and Firefox ("NetworkError ...") all differ, and any future variation
|
|
8367
|
+
// would silently be classified non-transient.
|
|
8368
|
+
if (err instanceof TypeError)
|
|
8369
|
+
return true;
|
|
8370
|
+
}
|
|
8371
|
+
return false;
|
|
8372
|
+
}
|
|
8373
|
+
async function timeoutRequest(url, timeout) {
|
|
8374
|
+
const controller = new AbortController();
|
|
8375
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
8376
|
+
try {
|
|
8377
|
+
return await fetch(url, { signal: controller.signal });
|
|
8378
|
+
}
|
|
8379
|
+
finally {
|
|
8380
|
+
clearTimeout(timeoutId);
|
|
8381
|
+
}
|
|
8382
|
+
}
|
|
8383
|
+
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
8384
|
+
/**
|
|
8385
|
+
* Fetch a single CDN-hosted paywall with bounded retries on transient
|
|
8386
|
+
* failures. Exponential backoff (baseDelayMs * 2^n, capped at maxDelayMs)
|
|
8387
|
+
* with ±50% jitter; the whole loop is clamped to budgetMs wall-clock so
|
|
8388
|
+
* the parallel-fetch window can't balloon when one edge is degraded.
|
|
8389
|
+
*
|
|
8390
|
+
* Throws NonRetryableHttpError for hard 4xx (including 404), or
|
|
8391
|
+
* RetryExhaustedError when all attempts and/or the budget are spent.
|
|
8392
|
+
*/
|
|
8393
|
+
async function fetchWithCdnRetry(url, opts = {}) {
|
|
8394
|
+
const maxAttempts = opts.maxAttempts ?? DEFAULTS.maxAttempts;
|
|
8395
|
+
const baseDelayMs = opts.baseDelayMs ?? DEFAULTS.baseDelayMs;
|
|
8396
|
+
const maxDelayMs = opts.maxDelayMs ?? DEFAULTS.maxDelayMs;
|
|
8397
|
+
const budgetMs = opts.budgetMs ?? DEFAULTS.budgetMs;
|
|
8398
|
+
const timeoutMs = opts.timeoutMs ?? API_TIMEOUT_LIMIT;
|
|
8399
|
+
const randomFn = opts.randomFn ?? Math.random;
|
|
8400
|
+
const sleepFn = opts.sleepFn ?? defaultSleep;
|
|
8401
|
+
const logRequests = shouldLogHTTPRequests();
|
|
8402
|
+
const logTraffic = shouldLogHTTPTraffic();
|
|
8403
|
+
const start = Date.now();
|
|
8404
|
+
let attempt = 0;
|
|
8405
|
+
let lastStatus;
|
|
8406
|
+
let lastError;
|
|
8407
|
+
while (attempt < maxAttempts) {
|
|
8408
|
+
if (attempt > 0) {
|
|
8409
|
+
const elapsed = Date.now() - start;
|
|
8410
|
+
if (elapsed >= budgetMs)
|
|
8411
|
+
break;
|
|
8412
|
+
const baseDelay = Math.min(baseDelayMs * Math.pow(2, attempt - 1), maxDelayMs);
|
|
8413
|
+
// ±50% jitter: random in [0,1) → factor in [-0.5, 0.5)
|
|
8414
|
+
const jitter = baseDelay * (randomFn() - 0.5);
|
|
8415
|
+
const remaining = Math.max(0, budgetMs - elapsed);
|
|
8416
|
+
const sleepFor = Math.min(Math.max(0, baseDelay + jitter), remaining);
|
|
8417
|
+
await sleepFn(sleepFor);
|
|
8418
|
+
// If the sleep consumed the rest of the budget, don't fire another fetch.
|
|
8419
|
+
if (Date.now() - start >= budgetMs)
|
|
8420
|
+
break;
|
|
8421
|
+
}
|
|
8422
|
+
try {
|
|
8423
|
+
if (logRequests || logTraffic) {
|
|
8424
|
+
logger.debug(`[HTTP] GET ${url} (cdn attempt ${attempt + 1}/${maxAttempts})`);
|
|
8425
|
+
}
|
|
8426
|
+
const response = await timeoutRequest(url, timeoutMs);
|
|
8427
|
+
if (!response.ok) {
|
|
8428
|
+
if (logTraffic) {
|
|
8429
|
+
const errorBody = await response.clone().text();
|
|
8430
|
+
logger.debug(`[HTTP] Response ${response.status}: ${errorBody}`);
|
|
8431
|
+
}
|
|
8432
|
+
lastStatus = response.status;
|
|
8433
|
+
if (isTransientFailure(undefined, response.status)) {
|
|
8434
|
+
attempt++;
|
|
8435
|
+
continue;
|
|
8436
|
+
}
|
|
8437
|
+
throw new NonRetryableHttpError(response.status, url);
|
|
8438
|
+
}
|
|
8439
|
+
if (logTraffic) {
|
|
8440
|
+
const responseBody = await response.clone().text();
|
|
8441
|
+
logger.debug(`[HTTP] Response ${response.status}: ${responseBody}`);
|
|
8442
|
+
}
|
|
8443
|
+
const data = (await response.json());
|
|
8444
|
+
return { data, retryCount: attempt };
|
|
8445
|
+
}
|
|
8446
|
+
catch (err) {
|
|
8447
|
+
if (err instanceof NonRetryableHttpError)
|
|
8448
|
+
throw err;
|
|
8449
|
+
if (isTransientFailure(err)) {
|
|
8450
|
+
lastError = err;
|
|
8451
|
+
attempt++;
|
|
8452
|
+
continue;
|
|
8453
|
+
}
|
|
8454
|
+
throw err;
|
|
8455
|
+
}
|
|
8456
|
+
}
|
|
8457
|
+
// If we exited the loop without success but never saw a transient response,
|
|
8458
|
+
// fall back to surfacing the last raw error. Otherwise raise the canonical
|
|
8459
|
+
// exhausted-attempts signal with the last seen status for telemetry.
|
|
8460
|
+
if (lastStatus === undefined && lastError !== undefined)
|
|
8461
|
+
throw lastError;
|
|
8462
|
+
throw new RetryExhaustedError(url, attempt, lastStatus);
|
|
8463
|
+
}
|
|
8464
|
+
|
|
8310
8465
|
class PaywallRepository {
|
|
8311
8466
|
async fetchPaywalls() {
|
|
8312
8467
|
const authDevice = storageService.getDevice();
|
|
@@ -8357,17 +8512,22 @@ class PaywallRepository {
|
|
|
8357
8512
|
return data?.results || [];
|
|
8358
8513
|
}
|
|
8359
8514
|
async fetchPaywallByUrl(url) {
|
|
8360
|
-
return
|
|
8515
|
+
return fetchWithCdnRetry(url);
|
|
8361
8516
|
}
|
|
8362
8517
|
async fetchPaywallsByUrls(urls) {
|
|
8363
8518
|
const uniqueUrls = [...new Set(urls)];
|
|
8364
8519
|
const results = await Promise.allSettled(uniqueUrls.map((url) => this.fetchPaywallByUrl(url)));
|
|
8365
8520
|
const paywalls = [];
|
|
8521
|
+
let retryCount = 0;
|
|
8366
8522
|
for (const result of results) {
|
|
8367
8523
|
if (result.status === "fulfilled") {
|
|
8368
|
-
paywalls.push(result.value);
|
|
8524
|
+
paywalls.push(result.value.data);
|
|
8525
|
+
retryCount += result.value.retryCount;
|
|
8369
8526
|
}
|
|
8370
8527
|
else {
|
|
8528
|
+
// Note: an exhausted-failure path contributed retries we can't see
|
|
8529
|
+
// from the rejected promise, so retryCount under-reports failures.
|
|
8530
|
+
// Acceptable for the telemetry signal; revisit if full accounting matters.
|
|
8371
8531
|
logger.error(`Failed to fetch individual paywall: ${result.reason}`);
|
|
8372
8532
|
}
|
|
8373
8533
|
}
|
|
@@ -8376,9 +8536,9 @@ class PaywallRepository {
|
|
|
8376
8536
|
if (valid.length > 0) {
|
|
8377
8537
|
storageService.setPaywalls(API_PAYWALLS, valid);
|
|
8378
8538
|
}
|
|
8379
|
-
return valid;
|
|
8539
|
+
return { paywalls: valid, retryCount };
|
|
8380
8540
|
}
|
|
8381
|
-
return paywalls;
|
|
8541
|
+
return { paywalls, retryCount };
|
|
8382
8542
|
}
|
|
8383
8543
|
validatePaywalls(paywalls) {
|
|
8384
8544
|
return paywalls.filter((paywall) => {
|
|
@@ -11854,10 +12014,13 @@ class NamiRefs {
|
|
|
11854
12014
|
const useIndividual = !campaignRepo.useLegacyPaywallFetch &&
|
|
11855
12015
|
CampaignRuleRepository.hasPaywallUrls(rawCampaigns);
|
|
11856
12016
|
let paywalls;
|
|
12017
|
+
let retryCount = 0;
|
|
11857
12018
|
const paywallsStartTime = Date.now();
|
|
11858
12019
|
if (useIndividual) {
|
|
11859
12020
|
const urls = CampaignRuleRepository.extractPaywallUrls(rawCampaigns);
|
|
11860
|
-
|
|
12021
|
+
const result = await paywallRepo.fetchPaywallsByUrls(urls);
|
|
12022
|
+
paywalls = result.paywalls;
|
|
12023
|
+
retryCount = result.retryCount;
|
|
11861
12024
|
}
|
|
11862
12025
|
else {
|
|
11863
12026
|
paywalls = await paywallRepo.fetchPaywalls();
|
|
@@ -11866,7 +12029,8 @@ class NamiRefs {
|
|
|
11866
12029
|
const totalDuration = Date.now() - startTime;
|
|
11867
12030
|
logger.info(`Paywall fetch telemetry: strategy=${useIndividual ? "individual" : "bulk"}, ` +
|
|
11868
12031
|
`count=${paywalls.length}, total=${totalDuration}ms, ` +
|
|
11869
|
-
`campaigns=${campaignRulesDuration}ms, paywalls=${paywallsDuration}ms`
|
|
12032
|
+
`campaigns=${campaignRulesDuration}ms, paywalls=${paywallsDuration}ms` +
|
|
12033
|
+
`${useIndividual ? `, retry_count=${retryCount}` : ""}`);
|
|
11870
12034
|
return campaignRepo.finalizeCampaignRules(rawCampaigns, paywalls);
|
|
11871
12035
|
}
|
|
11872
12036
|
reRenderPaywall() {
|
|
@@ -12829,21 +12993,46 @@ class NamiProfileManager {
|
|
|
12829
12993
|
}
|
|
12830
12994
|
PaywallState.setIsLoggedIn(!!this.externalId);
|
|
12831
12995
|
}
|
|
12832
|
-
|
|
12996
|
+
/**
|
|
12997
|
+
* Update local profile state for login: persist the external id and flip the
|
|
12998
|
+
* logged-in flag in PaywallState. No network I/O — pair with `loginRemote`
|
|
12999
|
+
* to perform the actual API call. Used by the async login/logout fast path.
|
|
13000
|
+
*/
|
|
13001
|
+
loginLocal(externalId) {
|
|
13002
|
+
this.setExternalId(externalId).save();
|
|
13003
|
+
PaywallState.setIsLoggedIn(true);
|
|
13004
|
+
}
|
|
13005
|
+
/**
|
|
13006
|
+
* Perform the login network call only. If the server returns a different
|
|
13007
|
+
* `external_id` than the locally stored value, reconcile it into local state.
|
|
13008
|
+
*/
|
|
13009
|
+
async loginRemote(externalId) {
|
|
12833
13010
|
const loginData = await NamiAPI.instance.login(externalId);
|
|
12834
|
-
if (loginData.external_id) {
|
|
12835
|
-
|
|
13011
|
+
if (loginData.external_id && loginData.external_id !== this.externalId) {
|
|
13012
|
+
this.setExternalId(loginData.external_id).save();
|
|
12836
13013
|
}
|
|
12837
|
-
this
|
|
12838
|
-
.setExternalId(loginData.external_id)
|
|
12839
|
-
.save();
|
|
12840
13014
|
}
|
|
12841
|
-
|
|
12842
|
-
|
|
13015
|
+
/**
|
|
13016
|
+
* Update local profile state for logout: clear the external id, persist,
|
|
13017
|
+
* and flip the logged-in flag in PaywallState. Used by the async fast path.
|
|
13018
|
+
*/
|
|
13019
|
+
logoutLocal() {
|
|
13020
|
+
this.setExternalId(undefined).save();
|
|
12843
13021
|
PaywallState.setIsLoggedIn(false);
|
|
12844
|
-
|
|
12845
|
-
|
|
12846
|
-
|
|
13022
|
+
}
|
|
13023
|
+
/**
|
|
13024
|
+
* Perform the logout network call only.
|
|
13025
|
+
*/
|
|
13026
|
+
async logoutRemote() {
|
|
13027
|
+
await NamiAPI.instance.logout();
|
|
13028
|
+
}
|
|
13029
|
+
async login(externalId) {
|
|
13030
|
+
this.loginLocal(externalId);
|
|
13031
|
+
await this.loginRemote(externalId);
|
|
13032
|
+
}
|
|
13033
|
+
async logout() {
|
|
13034
|
+
this.logoutLocal();
|
|
13035
|
+
await this.logoutRemote();
|
|
12847
13036
|
}
|
|
12848
13037
|
}
|
|
12849
13038
|
NamiProfileManager.instance = new NamiProfileManager();
|
|
@@ -13163,6 +13352,21 @@ class NamiFlowManager {
|
|
|
13163
13352
|
}
|
|
13164
13353
|
}
|
|
13165
13354
|
|
|
13355
|
+
const SHOULD_SHOW_LOADING_INDICATOR = false;
|
|
13356
|
+
const DISABLE_ASYNC_LOGIN_LOGOUT = "disableAsyncLoginLogout";
|
|
13357
|
+
/**
|
|
13358
|
+
* Returns true when `disableAsyncLoginLogout` appears in the active `namiCommands` list,
|
|
13359
|
+
* meaning login/logout should await the underlying API call before notifying handlers.
|
|
13360
|
+
*
|
|
13361
|
+
* Default behavior (flag absent) is the async/fast path: update local state and notify
|
|
13362
|
+
* handlers immediately while dispatching the API call as fire-and-forget.
|
|
13363
|
+
*
|
|
13364
|
+
* Internal feature flag — opt-out via `Nami.configure({ namiCommands: [...] })`.
|
|
13365
|
+
*/
|
|
13366
|
+
const isAsyncLoginLogoutDisabled = () => {
|
|
13367
|
+
return storageService.getNamiConfig()?.namiCommands?.includes(DISABLE_ASYNC_LOGIN_LOGOUT) ?? false;
|
|
13368
|
+
};
|
|
13369
|
+
|
|
13166
13370
|
/**
|
|
13167
13371
|
* @class NamiCustomerManager
|
|
13168
13372
|
* Provides methods for managing customer-related functionality.
|
|
@@ -13224,6 +13428,30 @@ class NamiCustomerManager {
|
|
|
13224
13428
|
this.invokeStateHandler(AccountStateAction.LOGIN, false, error);
|
|
13225
13429
|
throw error;
|
|
13226
13430
|
}
|
|
13431
|
+
if (!isAsyncLoginLogoutDisabled()) {
|
|
13432
|
+
// Default sync/fast path: update local state, notify handlers, and fire
|
|
13433
|
+
// lifecycle immediately. Dispatch the underlying API call and entitlement
|
|
13434
|
+
// refresh fire-and-forget — failures are logged but do not surface.
|
|
13435
|
+
NamiProfileManager.instance.loginLocal(externalId);
|
|
13436
|
+
this.invokeStateHandler(AccountStateAction.LOGIN, true);
|
|
13437
|
+
if (NamiFlowManager.instance.flowOpen) {
|
|
13438
|
+
const currentFlow = NamiFlowManager.instance.currentFlow;
|
|
13439
|
+
const currentStep = currentFlow?.currentFlowStep;
|
|
13440
|
+
if (currentStep) {
|
|
13441
|
+
if (Nami.instance.maxLogging) {
|
|
13442
|
+
logger.debug(`[NamiCustomerManager] async login success — triggering __login_success__ on step ${currentStep.id}`);
|
|
13443
|
+
}
|
|
13444
|
+
currentFlow.executeLifecycle(currentStep, NamiReservedActions.LOGIN_SUCCESS);
|
|
13445
|
+
}
|
|
13446
|
+
}
|
|
13447
|
+
void NamiProfileManager.instance.loginRemote(externalId).catch((err) => {
|
|
13448
|
+
logger.error('[NamiCustomerManager] async login API failed', err);
|
|
13449
|
+
});
|
|
13450
|
+
void EntitlementRepository.instance.fetchActiveEntitlements().catch((err) => {
|
|
13451
|
+
logger.error('[NamiCustomerManager] async login entitlement refresh failed', err);
|
|
13452
|
+
});
|
|
13453
|
+
return;
|
|
13454
|
+
}
|
|
13227
13455
|
try {
|
|
13228
13456
|
await NamiProfileManager.instance.login(externalId);
|
|
13229
13457
|
await EntitlementRepository.instance.fetchActiveEntitlements();
|
|
@@ -13269,6 +13497,30 @@ class NamiCustomerManager {
|
|
|
13269
13497
|
this.invokeStateHandler(AccountStateAction.LOGOUT, false, error);
|
|
13270
13498
|
throw error;
|
|
13271
13499
|
}
|
|
13500
|
+
if (!isAsyncLoginLogoutDisabled()) {
|
|
13501
|
+
// Default sync/fast path: clear local state, notify handlers, and fire
|
|
13502
|
+
// lifecycle immediately. Dispatch the underlying API call and entitlement
|
|
13503
|
+
// refresh fire-and-forget — failures are logged but do not surface.
|
|
13504
|
+
NamiProfileManager.instance.logoutLocal();
|
|
13505
|
+
this.invokeStateHandler(AccountStateAction.LOGOUT, true);
|
|
13506
|
+
if (NamiFlowManager.instance.flowOpen) {
|
|
13507
|
+
const currentFlow = NamiFlowManager.instance.currentFlow;
|
|
13508
|
+
const currentStep = currentFlow?.currentFlowStep;
|
|
13509
|
+
if (currentStep) {
|
|
13510
|
+
if (Nami.instance.maxLogging) {
|
|
13511
|
+
logger.debug(`[NamiCustomerManager] async logout success — triggering __logout_success__ on step ${currentStep.id}`);
|
|
13512
|
+
}
|
|
13513
|
+
currentFlow.executeLifecycle(currentStep, NamiReservedActions.LOGOUT_SUCCESS);
|
|
13514
|
+
}
|
|
13515
|
+
}
|
|
13516
|
+
void NamiProfileManager.instance.logoutRemote().catch((err) => {
|
|
13517
|
+
logger.error('[NamiCustomerManager] async logout API failed', err);
|
|
13518
|
+
});
|
|
13519
|
+
void EntitlementRepository.instance.fetchActiveEntitlements().catch((err) => {
|
|
13520
|
+
logger.error('[NamiCustomerManager] async logout entitlement refresh failed', err);
|
|
13521
|
+
});
|
|
13522
|
+
return;
|
|
13523
|
+
}
|
|
13272
13524
|
try {
|
|
13273
13525
|
await NamiProfileManager.instance.logout();
|
|
13274
13526
|
await EntitlementRepository.instance.fetchActiveEntitlements();
|
|
@@ -14703,8 +14955,6 @@ var CampaignRuleConversionEventType;
|
|
|
14703
14955
|
CampaignRuleConversionEventType["IN_APP"] = "in_app";
|
|
14704
14956
|
})(CampaignRuleConversionEventType || (CampaignRuleConversionEventType = {}));
|
|
14705
14957
|
|
|
14706
|
-
const SHOULD_SHOW_LOADING_INDICATOR = false;
|
|
14707
|
-
|
|
14708
14958
|
function parseToSemver(versionString) {
|
|
14709
14959
|
const [semVer, major, minor, patch, prerelease, buildmetadata] = versionString.match(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/) ?? [];
|
|
14710
14960
|
return {
|
|
@@ -64164,4 +64414,4 @@ function namiBuySKU(skuRefId) {
|
|
|
64164
64414
|
return result;
|
|
64165
64415
|
}
|
|
64166
64416
|
|
|
64167
|
-
export { ALREADY_CONFIGURED, ANONYMOUS_MODE, ANONYMOUS_MODE_ALREADY_OFF, ANONYMOUS_MODE_ALREADY_ON, ANONYMOUS_MODE_LOGIN_NOT_ALLOWED, ANONYMOUS_UUID, APIError, API_ACTIVE_ENTITLEMENTS, API_CAMPAIGN_RULES, API_CAMPAIGN_SESSION_TIMESTAMP, API_CONFIG, API_MAX_CALLS_LIMIT, API_PAYWALLS, API_PRODUCTS, API_RETRY_DELAY_SEC, API_TIMEOUT_LIMIT, API_VERSION, AUTH_DEVICE, AVAILABLE_ACTIVE_ENTITLEMENTS_CHANGED, AVAILABLE_CAMPAIGNS_CHANGED, AccountStateAction, AnonymousCDPError, AnonymousLoginError, AnonymousModeAlreadyOffError, AnonymousModeAlreadyOnError, BASE_STAGING_URL, BASE_URL, BASE_URL_PATH, BadRequestError, BasicNamiFlow, BorderMap, BorderSideMap, CAMPAIGN_NOT_AVAILABLE, CUSTOMER_ATTRIBUTES_KEY_PREFIX, CUSTOMER_JOURNEY_STATE_CHANGED, CUSTOM_HOST_PREFIX, CampaignNotAvailableError, CampaignRuleConversionEventType, CampaignRuleRepository, Capabilities, ClientError, ConfigRepository, ConflictError, CustomerJourneyRepository, DEVELOPMENT, DEVICE_API_TIMEOUT_LIMIT, DEVICE_ID_NOT_SET, DEVICE_ID_REQUIRED, DeviceIDRequiredError, DeviceRepository, EXTENDED_CLIENT_INFO_DELIMITER, EXTENDED_CLIENT_INFO_PREFIX, EXTENDED_PLATFORM, EXTENDED_PLATFORM_VERSION, EXTERNAL_ID_REQUIRED, EntitlementRepository, EntitlementUtils, ExternalIDRequiredError, FLOW_SCREENS_NOT_AVAILABLE, FlowScreensNotAvailableError, HTML_REGEX, INITIAL_APP_CONFIG, INITIAL_CAMPAIGN_RULES, INITIAL_PAYWALLS, INITIAL_PRODUCTS, INITIAL_SESSION_COUNTER_VALUE, INITIAL_SUCCESS, InternalServerError, KEY_SESSION_COUNTER, LIQUID_VARIABLE_REGEX, LOCAL_NAMI_ENTITLEMENTS, LOG_HTTP_REQUESTS, LOG_HTTP_TRAFFIC, LaunchCampaignError, LaunchContextResolver, LogLevel, NAMI_CONFIGURATION, NAMI_CUSTOMER_JOURNEY_STATE, NAMI_LANGUAGE_CODE, NAMI_LAST_IMPRESSION_ID, NAMI_LAUNCH_ID, NAMI_PROFILE, NAMI_PURCHASE_CHANNEL, NAMI_PURCHASE_IMPRESSION_ID, NAMI_SDK_PACKAGE_VERSION, NAMI_SDK_VERSION, NAMI_SESSION_ID, NAMI_STORAGE_KEYS, Nami, NamiAPI, NamiAnimationType, NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager, NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager, NamiFlowStepType, NamiPaywallAction, NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiProfileManager, NamiPurchaseManager, NamiRefs, NamiReservedActions, NotFoundError, PAYWALL_ACTION_EVENT, PLATFORM_ID_REQUIRED, PRODUCTION, PaywallManagerEvents, PaywallRepository, PaywallState, PlacementLabelResolver, PlatformIDRequiredError, ProductRepository, RECONFIG_SUCCESS, RetryLimitExceededError, SDKNotInitializedError, SDK_NOT_INITIALIZED, SERVER_NAMI_ENTITLEMENTS, SESSION_REQUIRED, SHOULD_SHOW_LOADING_INDICATOR, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, activateEntitlementByPurchase, activeEntitlements, aggregateScreenreaderText, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getPaywall, getPaywallDataFromLabel, getPercentagePriceDifference, getPeriodNumberInDays, getPeriodNumberInWeeks, getPlatformAdapters, getPriceDifference, getPricePerMonth, getProductDetail, getPurchaseAdapter, getReferenceSku, getSkuProductDetailKeys, getSkuSmartTextValue, getSlideSmartTextValue, getStandardBillingPeriod, getTranslate, getUrlParams, handleErrors, hasAllPaywalls, hasCapability, hasPurchaseManagement, initialState, invokeHandler, isAnonymousMode, isInitialConfigCompressed, isNamiFlowCampaign, isSubscription, isValidISODate, isValidUrl, logger, mapAnonymousCampaigns, namiBuySKU, normalizeLaunchContext, parseToSemver, postConversion, productDetail, registerPlatformAdapters, registerPurchaseAdapter, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateMinSDKVersion };
|
|
64417
|
+
export { ALREADY_CONFIGURED, ANONYMOUS_MODE, ANONYMOUS_MODE_ALREADY_OFF, ANONYMOUS_MODE_ALREADY_ON, ANONYMOUS_MODE_LOGIN_NOT_ALLOWED, ANONYMOUS_UUID, APIError, API_ACTIVE_ENTITLEMENTS, API_CAMPAIGN_RULES, API_CAMPAIGN_SESSION_TIMESTAMP, API_CONFIG, API_MAX_CALLS_LIMIT, API_PAYWALLS, API_PRODUCTS, API_RETRY_DELAY_SEC, API_TIMEOUT_LIMIT, API_VERSION, AUTH_DEVICE, AVAILABLE_ACTIVE_ENTITLEMENTS_CHANGED, AVAILABLE_CAMPAIGNS_CHANGED, AccountStateAction, AnonymousCDPError, AnonymousLoginError, AnonymousModeAlreadyOffError, AnonymousModeAlreadyOnError, BASE_STAGING_URL, BASE_URL, BASE_URL_PATH, BadRequestError, BasicNamiFlow, BorderMap, BorderSideMap, CAMPAIGN_NOT_AVAILABLE, CUSTOMER_ATTRIBUTES_KEY_PREFIX, CUSTOMER_JOURNEY_STATE_CHANGED, CUSTOM_HOST_PREFIX, CampaignNotAvailableError, CampaignRuleConversionEventType, CampaignRuleRepository, Capabilities, ClientError, ConfigRepository, ConflictError, CustomerJourneyRepository, DEVELOPMENT, DEVICE_API_TIMEOUT_LIMIT, DEVICE_ID_NOT_SET, DEVICE_ID_REQUIRED, DISABLE_ASYNC_LOGIN_LOGOUT, DeviceIDRequiredError, DeviceRepository, EXTENDED_CLIENT_INFO_DELIMITER, EXTENDED_CLIENT_INFO_PREFIX, EXTENDED_PLATFORM, EXTENDED_PLATFORM_VERSION, EXTERNAL_ID_REQUIRED, EntitlementRepository, EntitlementUtils, ExternalIDRequiredError, FLOW_SCREENS_NOT_AVAILABLE, FlowScreensNotAvailableError, HTML_REGEX, INITIAL_APP_CONFIG, INITIAL_CAMPAIGN_RULES, INITIAL_PAYWALLS, INITIAL_PRODUCTS, INITIAL_SESSION_COUNTER_VALUE, INITIAL_SUCCESS, InternalServerError, KEY_SESSION_COUNTER, LIQUID_VARIABLE_REGEX, LOCAL_NAMI_ENTITLEMENTS, LOG_HTTP_REQUESTS, LOG_HTTP_TRAFFIC, LaunchCampaignError, LaunchContextResolver, LogLevel, NAMI_CONFIGURATION, NAMI_CUSTOMER_JOURNEY_STATE, NAMI_LANGUAGE_CODE, NAMI_LAST_IMPRESSION_ID, NAMI_LAUNCH_ID, NAMI_PROFILE, NAMI_PURCHASE_CHANNEL, NAMI_PURCHASE_IMPRESSION_ID, NAMI_SDK_PACKAGE_VERSION, NAMI_SDK_VERSION, NAMI_SESSION_ID, NAMI_STORAGE_KEYS, Nami, NamiAPI, NamiAnimationType, NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager, NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager, NamiFlowStepType, NamiPaywallAction, NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiProfileManager, NamiPurchaseManager, NamiRefs, NamiReservedActions, NotFoundError, PAYWALL_ACTION_EVENT, PLATFORM_ID_REQUIRED, PRODUCTION, PaywallManagerEvents, PaywallRepository, PaywallState, PlacementLabelResolver, PlatformIDRequiredError, ProductRepository, RECONFIG_SUCCESS, RetryLimitExceededError, SDKNotInitializedError, SDK_NOT_INITIALIZED, SERVER_NAMI_ENTITLEMENTS, SESSION_REQUIRED, SHOULD_SHOW_LOADING_INDICATOR, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, activateEntitlementByPurchase, activeEntitlements, aggregateScreenreaderText, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getPaywall, getPaywallDataFromLabel, getPercentagePriceDifference, getPeriodNumberInDays, getPeriodNumberInWeeks, getPlatformAdapters, getPriceDifference, getPricePerMonth, getProductDetail, getPurchaseAdapter, getReferenceSku, getSkuProductDetailKeys, getSkuSmartTextValue, getSlideSmartTextValue, getStandardBillingPeriod, getTranslate, getUrlParams, handleErrors, hasAllPaywalls, hasCapability, hasPurchaseManagement, initialState, invokeHandler, isAnonymousMode, isInitialConfigCompressed, isNamiFlowCampaign, isSubscription, isValidISODate, isValidUrl, logger, mapAnonymousCampaigns, namiBuySKU, normalizeLaunchContext, parseToSemver, postConversion, productDetail, registerPlatformAdapters, registerPurchaseAdapter, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateMinSDKVersion };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@namiml/sdk-core",
|
|
3
|
-
"version": "3.4.0-dev.
|
|
3
|
+
"version": "3.4.0-dev.202605191719",
|
|
4
4
|
"description": "Platform-agnostic core for the Nami SDK — business logic, API, types, and state management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|