@namiml/sdk-core 3.4.12-dev.202609251647 → 3.4.12-dev.202609252349
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 +243 -18
- package/dist/index.d.ts +105 -2
- package/dist/index.mjs +244 -19
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -106,7 +106,7 @@ const {
|
|
|
106
106
|
// version — stamped by scripts/version.sh
|
|
107
107
|
NAMI_SDK_VERSION: exports.NAMI_SDK_VERSION = "3.4.12",
|
|
108
108
|
// full package version including dev suffix — stamped by scripts/version.sh
|
|
109
|
-
NAMI_SDK_PACKAGE_VERSION: exports.NAMI_SDK_PACKAGE_VERSION = "3.4.12-dev.
|
|
109
|
+
NAMI_SDK_PACKAGE_VERSION: exports.NAMI_SDK_PACKAGE_VERSION = "3.4.12-dev.202609252349",
|
|
110
110
|
// environments
|
|
111
111
|
PRODUCTION: exports.PRODUCTION = "production", DEVELOPMENT: exports.DEVELOPMENT = "development",
|
|
112
112
|
// error messages
|
|
@@ -116,7 +116,19 @@ AUTH_DEVICE: exports.AUTH_DEVICE = "nami_auth_device", NAMI_CONFIGURATION: expor
|
|
|
116
116
|
// API settings
|
|
117
117
|
API_VERSION: exports.API_VERSION = "v3", BASE_URL_PATH: exports.BASE_URL_PATH = `sdk/${exports.API_VERSION}/platform`, BASE_URL: exports.BASE_URL = "https://app.namiml.com", BASE_STAGING_URL: exports.BASE_STAGING_URL = "https://app-staging.namiml.com", CUSTOM_HOST_PREFIX: exports.CUSTOM_HOST_PREFIX = "namiAPIHost=", USE_STAGING_API: exports.USE_STAGING_API = "useStagingAPI",
|
|
118
118
|
// // extended client info
|
|
119
|
-
EXTENDED_CLIENT_INFO_PREFIX: exports.EXTENDED_CLIENT_INFO_PREFIX = "extendedClientInfo", EXTENDED_CLIENT_INFO_DELIMITER: exports.EXTENDED_CLIENT_INFO_DELIMITER = ":", VALIDATE_PRODUCT_GROUPS: exports.VALIDATE_PRODUCT_GROUPS = "validateProductGroups", LOG_HTTP_REQUESTS: exports.LOG_HTTP_REQUESTS = "logHTTPRequests", LOG_HTTP_TRAFFIC: exports.LOG_HTTP_TRAFFIC = "logHTTPTraffic", API_TRAFFIC_OBSERVER: exports.API_TRAFFIC_OBSERVER = "apiTrafficObserver", STARTUP_TELEMETRY: exports.STARTUP_TELEMETRY = "startupTelemetry",
|
|
119
|
+
EXTENDED_CLIENT_INFO_PREFIX: exports.EXTENDED_CLIENT_INFO_PREFIX = "extendedClientInfo", EXTENDED_CLIENT_INFO_DELIMITER: exports.EXTENDED_CLIENT_INFO_DELIMITER = ":", VALIDATE_PRODUCT_GROUPS: exports.VALIDATE_PRODUCT_GROUPS = "validateProductGroups", LOG_HTTP_REQUESTS: exports.LOG_HTTP_REQUESTS = "logHTTPRequests", LOG_HTTP_TRAFFIC: exports.LOG_HTTP_TRAFFIC = "logHTTPTraffic", API_TRAFFIC_OBSERVER: exports.API_TRAFFIC_OBSERVER = "apiTrafficObserver", STARTUP_TELEMETRY: exports.STARTUP_TELEMETRY = "startupTelemetry",
|
|
120
|
+
// Test hook: every platform-config fetch attempt fails before the request leaves.
|
|
121
|
+
// Same namiCommand as Apple's and Android's, so the shared first-run flows drive it.
|
|
122
|
+
SIMULATE_CONFIG_FETCH_FAILURE: exports.SIMULATE_CONFIG_FETCH_FAILURE = "simulateConfigFetchFailure", EXTENDED_PLATFORM: exports.EXTENDED_PLATFORM = "extended-platform", EXTENDED_PLATFORM_VERSION: exports.EXTENDED_PLATFORM_VERSION = "extended-platform-version", API_MAX_CALLS_LIMIT: exports.API_MAX_CALLS_LIMIT = 2, API_RETRY_DELAY_SEC: exports.API_RETRY_DELAY_SEC = 2, API_TIMEOUT_LIMIT: exports.API_TIMEOUT_LIMIT = 20000, DEVICE_API_TIMEOUT_LIMIT: exports.DEVICE_API_TIMEOUT_LIMIT = 2000,
|
|
123
|
+
// Platform-config attempts when no initial config was supplied (Android's
|
|
124
|
+
// CONFIG_MAX_ATTEMPTS; Apple's retry(maxAttempts: 3)).
|
|
125
|
+
CONFIG_MAX_ATTEMPTS: exports.CONFIG_MAX_ATTEMPTS = 3,
|
|
126
|
+
// Per-attempt timeout for GET config/. Android's config client allows 5 s to connect plus 5 s
|
|
127
|
+
// to read (Apple allows 30 s); three attempts keep a total outage near 30 s.
|
|
128
|
+
CONFIG_API_TIMEOUT_LIMIT: exports.CONFIG_API_TIMEOUT_LIMIT = 10000,
|
|
129
|
+
// A foreground within this long of the last launch/resume run is skipped — Apple's
|
|
130
|
+
// NamiStartupCoordinator.shouldRunResume window.
|
|
131
|
+
RESUME_THROTTLE_MS: exports.RESUME_THROTTLE_MS = 30000,
|
|
120
132
|
// status codes
|
|
121
133
|
STATUS_SUCCESS: exports.STATUS_SUCCESS = 200, STATUS_BAD_REQUEST: exports.STATUS_BAD_REQUEST = 400, STATUS_NOT_FOUND: exports.STATUS_NOT_FOUND = 404, STATUS_CONFLICT: exports.STATUS_CONFLICT = 409, STATUS_INTERNAL_SERVER_ERROR: exports.STATUS_INTERNAL_SERVER_ERROR = 500,
|
|
122
134
|
// configuration states
|
|
@@ -58033,27 +58045,125 @@ class PaywallRepository {
|
|
|
58033
58045
|
PaywallRepository.instance = new PaywallRepository();
|
|
58034
58046
|
|
|
58035
58047
|
class ConfigRepository {
|
|
58048
|
+
/**
|
|
58049
|
+
* One `GET config/` attempt. Persists the config on success and throws on any failure —
|
|
58050
|
+
* a non-2xx or a transport error alike. `withRetry`'s own retries are turned off, so the
|
|
58051
|
+
* attempt count belongs to {@link fetchConfigForStartup} alone.
|
|
58052
|
+
*/
|
|
58036
58053
|
async fetchConfig() {
|
|
58037
|
-
|
|
58038
|
-
|
|
58039
|
-
|
|
58040
|
-
;
|
|
58041
|
-
}
|
|
58042
|
-
catch (error) {
|
|
58043
|
-
data = storageService.getAppConfig(exports.API_CONFIG)
|
|
58044
|
-
|| storageService.getAppConfig(exports.INITIAL_APP_CONFIG);
|
|
58045
|
-
if (!data) {
|
|
58046
|
-
logger.error(handleErrors(error.status, "config/"));
|
|
58047
|
-
throw error;
|
|
58048
|
-
}
|
|
58054
|
+
// Thrown from *inside* the attempt on purpose, like Apple's and Android's: the ladder then
|
|
58055
|
+
// treats it exactly as a real 500, which is the whole point of the mock.
|
|
58056
|
+
if (storageService.getNamiConfig()?.namiCommands?.includes(exports.SIMULATE_CONFIG_FETCH_FAILURE)) {
|
|
58057
|
+
throw new RetryLimitExceededError(exports.STATUS_INTERNAL_SERVER_ERROR, exports.SIMULATE_CONFIG_FETCH_FAILURE);
|
|
58049
58058
|
}
|
|
58050
|
-
|
|
58059
|
+
const platformId = storageService.getNamiConfig()?.appPlatformID;
|
|
58060
|
+
const data = await NamiAPI.instance.fetchAPI("config/", exports.CONFIG_API_TIMEOUT_LIMIT, 0);
|
|
58061
|
+
// A late answer (the startup await gave up on it) must not overwrite another platform's
|
|
58062
|
+
// config after a reset or a platform-switching reconfigure.
|
|
58063
|
+
if (storageService.getNamiConfig()?.appPlatformID === platformId) {
|
|
58051
58064
|
storageService.setAppConfig(exports.API_CONFIG, data);
|
|
58052
58065
|
}
|
|
58066
|
+
else {
|
|
58067
|
+
logger.debug("Discarding an app config response for a platform that is no longer configured");
|
|
58068
|
+
}
|
|
58053
58069
|
return data;
|
|
58054
58070
|
}
|
|
58071
|
+
/**
|
|
58072
|
+
* The startup platform-config step, converged on Apple's `NamiPlatformConfigService.fetch` and
|
|
58073
|
+
* Android's `fetchAppConfigOnce` (NAM-4470, NAM-4589):
|
|
58074
|
+
*
|
|
58075
|
+
* | case | outcome |
|
|
58076
|
+
* |---|---|
|
|
58077
|
+
* | an initial config was supplied | NOT awaited: one attempt runs in the background and stores the config when it lands; usable |
|
|
58078
|
+
* | no initial config, success, capabilities include `sdk` | usable |
|
|
58079
|
+
* | no initial config, success, capabilities lack `sdk` | not usable |
|
|
58080
|
+
* | no initial config, failure | {@link CONFIG_MAX_ATTEMPTS} attempts of up to {@link CONFIG_API_TIMEOUT_LIMIT} each, then usable only if a config is cached (and grants `sdk`) |
|
|
58081
|
+
*
|
|
58082
|
+
* The two fallbacks are consulted at two different points, deliberately. An initial config
|
|
58083
|
+
* decides the case *before* any request: startup does not wait on the network at all, since the
|
|
58084
|
+
* initial config is what makes the SDK ready without it (Apple: "Initial config is available.
|
|
58085
|
+
* Skipping retry."). The config cached by a previous launch is consulted only *after* the
|
|
58086
|
+
* retries run out. A stored `INITIAL_APP_CONFIG` is not that cache.
|
|
58087
|
+
*
|
|
58088
|
+
* One check does apply with an initial config, because it costs no wait: a cached config that
|
|
58089
|
+
* withholds `sdk` still stops startup, as Apple's cache load re-applies `updateSdkFlags`.
|
|
58090
|
+
* Without it a kill switch could never reach an app that ships an initial config — the
|
|
58091
|
+
* background fetch lands after startup has already run, so it takes effect from the next launch.
|
|
58092
|
+
*
|
|
58093
|
+
* A `404` is treated as any other 4xx, matching Apple's effective behaviour (its dedicated 404
|
|
58094
|
+
* branch is unreachable, NAM-4471) and Android's.
|
|
58095
|
+
*
|
|
58096
|
+
* @param hasInitialConfig whether the host passed an `initialConfig` that decoded to content.
|
|
58097
|
+
*/
|
|
58098
|
+
async fetchConfigForStartup(hasInitialConfig) {
|
|
58099
|
+
if (hasInitialConfig)
|
|
58100
|
+
return this.startupOnInitialConfig();
|
|
58101
|
+
const maxAttempts = exports.CONFIG_MAX_ATTEMPTS;
|
|
58102
|
+
let lastError;
|
|
58103
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
58104
|
+
if (attempt > 1) {
|
|
58105
|
+
logger.debug(`Retrying app config fetch (attempt ${attempt} of ${maxAttempts})`);
|
|
58106
|
+
}
|
|
58107
|
+
try {
|
|
58108
|
+
return outcomeOf(await this.fetchConfig());
|
|
58109
|
+
}
|
|
58110
|
+
catch (error) {
|
|
58111
|
+
lastError = error;
|
|
58112
|
+
}
|
|
58113
|
+
}
|
|
58114
|
+
const cached = storageService.getAppConfig(exports.API_CONFIG);
|
|
58115
|
+
if (cached) {
|
|
58116
|
+
// A cached kill switch still holds: Apple re-applies `updateSdkFlags` when it loads the
|
|
58117
|
+
// cached config, so one that withheld `sdk` keeps the SDK disabled through an outage.
|
|
58118
|
+
if (!grantsSdk(cached)) {
|
|
58119
|
+
return { usable: false, reason: "the cached app config does not grant the 'sdk' capability" };
|
|
58120
|
+
}
|
|
58121
|
+
logger.info(`App config fetch failed after ${maxAttempts} attempts - continuing on the cached config`);
|
|
58122
|
+
return { usable: true };
|
|
58123
|
+
}
|
|
58124
|
+
return {
|
|
58125
|
+
usable: false,
|
|
58126
|
+
reason: `app config fetch failed after ${maxAttempts} attempts (${describeFailure(lastError)}) ` +
|
|
58127
|
+
"and no initial or cached config is available",
|
|
58128
|
+
};
|
|
58129
|
+
}
|
|
58130
|
+
/**
|
|
58131
|
+
* The initial-config case: nothing is awaited. The cached kill switch is a storage read; the
|
|
58132
|
+
* config request is left running and persists its answer (`fetchConfig`) when it lands.
|
|
58133
|
+
*/
|
|
58134
|
+
startupOnInitialConfig() {
|
|
58135
|
+
// Read before the request can land, so this launch is judged on what the last one stored.
|
|
58136
|
+
const cached = storageService.getAppConfig(exports.API_CONFIG);
|
|
58137
|
+
// Issued even when the cache holds a kill switch: that is how a LIFTED kill switch reaches
|
|
58138
|
+
// the next launch.
|
|
58139
|
+
this.fetchConfig().then((config) => {
|
|
58140
|
+
if (!grantsSdk(config)) {
|
|
58141
|
+
logger.warn("App config does not grant the 'sdk' capability - it takes effect from the next launch");
|
|
58142
|
+
}
|
|
58143
|
+
}, (error) => {
|
|
58144
|
+
logger.info(`App config fetch failed (${describeFailure(error)}). Initial config is available. Skipping retry.`);
|
|
58145
|
+
});
|
|
58146
|
+
if (cached && !grantsSdk(cached)) {
|
|
58147
|
+
return { usable: false, reason: "the cached app config does not grant the 'sdk' capability" };
|
|
58148
|
+
}
|
|
58149
|
+
return { usable: true };
|
|
58150
|
+
}
|
|
58055
58151
|
}
|
|
58056
58152
|
ConfigRepository.instance = new ConfigRepository();
|
|
58153
|
+
function outcomeOf(config) {
|
|
58154
|
+
return grantsSdk(config)
|
|
58155
|
+
? { usable: true }
|
|
58156
|
+
: { usable: false, reason: "app config does not grant the 'sdk' capability" };
|
|
58157
|
+
}
|
|
58158
|
+
function grantsSdk(config) {
|
|
58159
|
+
return config.capabilities?.includes(exports.Capabilities.SDK) ?? false;
|
|
58160
|
+
}
|
|
58161
|
+
function describeFailure(error) {
|
|
58162
|
+
const status = error?.status;
|
|
58163
|
+
if (typeof status === "number")
|
|
58164
|
+
return `HTTP ${status}`;
|
|
58165
|
+
return error instanceof Error ? error.message : String(error);
|
|
58166
|
+
}
|
|
58057
58167
|
|
|
58058
58168
|
// Global reference to avoid circular imports
|
|
58059
58169
|
let namiRefsInstance = null;
|
|
@@ -61597,6 +61707,15 @@ class NamiRefs {
|
|
|
61597
61707
|
this.inMemoryAnonymousMode = false;
|
|
61598
61708
|
// Captured before device registration; true only on genuine fresh installs
|
|
61599
61709
|
this.isFirstSession = false;
|
|
61710
|
+
// Set when the platform-config step leaves no usable config (see isStartupDisabled)
|
|
61711
|
+
this.startupDisabled = false;
|
|
61712
|
+
// Whether this configure() supplied an initial config (the config ladder's early exit)
|
|
61713
|
+
this.hasInitialConfig = false;
|
|
61714
|
+
// Resume bookkeeping: the configuration startup last ran with, when it (or a resume) last
|
|
61715
|
+
// ran, and whether one is running now.
|
|
61716
|
+
this.lastConfig = null;
|
|
61717
|
+
this.lastStartupRunAt = 0;
|
|
61718
|
+
this.startupInFlight = false;
|
|
61600
61719
|
// Register this instance to avoid circular dependency issues
|
|
61601
61720
|
setNamiRefsInstance(this);
|
|
61602
61721
|
}
|
|
@@ -61614,13 +61733,101 @@ class NamiRefs {
|
|
|
61614
61733
|
getIsFirstSession() {
|
|
61615
61734
|
return this.isFirstSession;
|
|
61616
61735
|
}
|
|
61736
|
+
/**
|
|
61737
|
+
* True when the platform-config step found no usable config and startup stopped after it —
|
|
61738
|
+
* the equivalent of Apple's `sdkEnabled = false` and Android's `NamiRefs.startupDisabled`.
|
|
61739
|
+
* Like both, it gates only the startup sequence, nothing else.
|
|
61740
|
+
*/
|
|
61741
|
+
isStartupDisabled() {
|
|
61742
|
+
return this.startupDisabled;
|
|
61743
|
+
}
|
|
61617
61744
|
async init(config) {
|
|
61618
61745
|
this.isFirstSession = storageService.getDevice() === null && !storageService.getAnonymousMode();
|
|
61746
|
+
// Cleared per configure so a transient failure cannot disable a later run.
|
|
61747
|
+
this.startupDisabled = false;
|
|
61748
|
+
// `Nami.configure` has already normalised a missing or undecodable initialConfig to `{}`,
|
|
61749
|
+
// so "has content" is Apple's `initialConfig != nil`.
|
|
61750
|
+
this.hasInitialConfig = Object.keys(config.initialConfig ?? {}).length > 0;
|
|
61619
61751
|
this.setInitialValues(config);
|
|
61620
|
-
|
|
61752
|
+
this.lastConfig = config;
|
|
61753
|
+
this.lastStartupRunAt = Date.now();
|
|
61754
|
+
this.startupInFlight = true;
|
|
61755
|
+
try {
|
|
61756
|
+
if (!(await this.fetchPlatformConfig()))
|
|
61757
|
+
return;
|
|
61758
|
+
await this.runStartupAfterConfig(config);
|
|
61759
|
+
}
|
|
61760
|
+
finally {
|
|
61761
|
+
this.startupInFlight = false;
|
|
61762
|
+
}
|
|
61763
|
+
}
|
|
61764
|
+
/**
|
|
61765
|
+
* The app returned to the foreground. Re-reads the platform config, as Apple's `.appResume`
|
|
61766
|
+
* and Android's `onAppComesFromBackground` do, so a config change — the `sdk` kill switch
|
|
61767
|
+
* included — reaches a long-lived tab or app without a cold start.
|
|
61768
|
+
*
|
|
61769
|
+
* - Skipped within {@link RESUME_THROTTLE_MS} of the last launch/resume, and while one is
|
|
61770
|
+
* still running.
|
|
61771
|
+
* - When the launch had **stopped** for want of a config and one is now usable, the rest of
|
|
61772
|
+
* startup runs — the steps an Apple resume re-runs because the launch never executed them.
|
|
61773
|
+
* - Otherwise this is a config refresh only. Apple and Android also re-run device, campaigns,
|
|
61774
|
+
* profile, products and the session start on resume; core does not yet, and has no
|
|
61775
|
+
* session end on background for a second session start to pair with (NAM-4623).
|
|
61776
|
+
*/
|
|
61777
|
+
async resume() {
|
|
61778
|
+
if (!this.lastConfig || this.startupInFlight)
|
|
61779
|
+
return;
|
|
61780
|
+
if (Date.now() - this.lastStartupRunAt < exports.RESUME_THROTTLE_MS) {
|
|
61781
|
+
logger.debug("Resuming too quickly to call the service.");
|
|
61782
|
+
return;
|
|
61783
|
+
}
|
|
61784
|
+
const config = this.lastConfig;
|
|
61785
|
+
const wasStopped = this.startupDisabled;
|
|
61786
|
+
this.lastStartupRunAt = Date.now();
|
|
61787
|
+
this.startupInFlight = true;
|
|
61788
|
+
this.startupDisabled = false;
|
|
61789
|
+
try {
|
|
61790
|
+
if (!(await this.fetchPlatformConfig()) || !wasStopped)
|
|
61791
|
+
return;
|
|
61792
|
+
logger.info("Usable app config on resume - completing the startup that stopped at launch");
|
|
61793
|
+
await this.runStartupAfterConfig(config);
|
|
61794
|
+
}
|
|
61795
|
+
catch (error) {
|
|
61796
|
+
logger.warn(`Resume refresh failed: ${error}`);
|
|
61797
|
+
}
|
|
61798
|
+
finally {
|
|
61799
|
+
this.startupInFlight = false;
|
|
61800
|
+
}
|
|
61801
|
+
}
|
|
61802
|
+
/** Everything startup runs once the platform-config step has found a usable config. */
|
|
61803
|
+
async runStartupAfterConfig(config) {
|
|
61621
61804
|
await this.initAndFetchRequiredData(config);
|
|
61622
61805
|
SessionService.instance.startSession();
|
|
61623
61806
|
}
|
|
61807
|
+
/** Forgets the configuration a resume would run against; called by `Nami.reset()`. */
|
|
61808
|
+
clearResumeState() {
|
|
61809
|
+
this.lastConfig = null;
|
|
61810
|
+
this.lastStartupRunAt = 0;
|
|
61811
|
+
this.startupDisabled = false;
|
|
61812
|
+
}
|
|
61813
|
+
/**
|
|
61814
|
+
* The platform-config startup step, ahead of device registration — the position it holds on
|
|
61815
|
+
* Apple (`.fetchPlatformConfig`) and Android (`fetchAppConfigOnce`) (NAM-4464, NAM-4589).
|
|
61816
|
+
* Without an initial config it is awaited, so everything downstream — `campaign_cache_ttl`
|
|
61817
|
+
* included — is evaluated against the config it leaves in storage. With one it resolves at
|
|
61818
|
+
* once and the request finishes in the background (see `fetchConfigForStartup`).
|
|
61819
|
+
*
|
|
61820
|
+
* @returns false when there is no usable config, in which case startup must stop.
|
|
61821
|
+
*/
|
|
61822
|
+
async fetchPlatformConfig() {
|
|
61823
|
+
const outcome = await ConfigRepository.instance.fetchConfigForStartup(this.hasInitialConfig);
|
|
61824
|
+
if (!outcome.usable) {
|
|
61825
|
+
this.startupDisabled = true;
|
|
61826
|
+
logger.error(`Nami startup stopped: ${outcome.reason}`);
|
|
61827
|
+
return false;
|
|
61828
|
+
}
|
|
61829
|
+
return true;
|
|
61830
|
+
}
|
|
61624
61831
|
setInitialValues(config) {
|
|
61625
61832
|
const baseConfig = { ...config };
|
|
61626
61833
|
delete baseConfig.initialConfig;
|
|
@@ -61881,7 +62088,7 @@ class LastLaunchStore {
|
|
|
61881
62088
|
}
|
|
61882
62089
|
}
|
|
61883
62090
|
|
|
61884
|
-
var _Nami_isInitialized;
|
|
62091
|
+
var _Nami_isInitialized, _Nami_removeForegroundListener;
|
|
61885
62092
|
// NamiFlowManager is intentionally NOT imported at top level — it
|
|
61886
62093
|
// transitively imports back to this module (`Nami` for logging), and
|
|
61887
62094
|
// pulling it in here causes a load-order cycle that breaks tests
|
|
@@ -61889,6 +62096,7 @@ var _Nami_isInitialized;
|
|
|
61889
62096
|
class Nami {
|
|
61890
62097
|
constructor() {
|
|
61891
62098
|
_Nami_isInitialized.set(this, false);
|
|
62099
|
+
_Nami_removeForegroundListener.set(this, null);
|
|
61892
62100
|
this.maxLogging = false;
|
|
61893
62101
|
}
|
|
61894
62102
|
get isInitialized() {
|
|
@@ -61912,9 +62120,13 @@ class Nami {
|
|
|
61912
62120
|
* integrating app, consistent with the Apple and Android SDKs.
|
|
61913
62121
|
*/
|
|
61914
62122
|
static async reset() {
|
|
62123
|
+
var _a;
|
|
61915
62124
|
storageService.clearAll();
|
|
61916
62125
|
clearInMemoryAnonymousMode();
|
|
61917
62126
|
__classPrivateFieldSet(Nami.instance, _Nami_isInitialized, false, "f");
|
|
62127
|
+
__classPrivateFieldGet((_a = Nami.instance), _Nami_removeForegroundListener, "f")?.call(_a);
|
|
62128
|
+
__classPrivateFieldSet(Nami.instance, _Nami_removeForegroundListener, null, "f");
|
|
62129
|
+
NamiRefs.instance.clearResumeState();
|
|
61918
62130
|
// In-memory singleton state that previously survived reset and
|
|
61919
62131
|
// bled into the next configure() cycle.
|
|
61920
62132
|
NamiProfileManager$1.instance.setExternalId(undefined);
|
|
@@ -61977,6 +62189,18 @@ class Nami {
|
|
|
61977
62189
|
}
|
|
61978
62190
|
config.initialConfig = {};
|
|
61979
62191
|
}
|
|
62192
|
+
/**
|
|
62193
|
+
* Subscribes the resume config refresh to the host's foreground signal, once per process —
|
|
62194
|
+
* a reconfigure must not stack a second listener (the Android defect NAM-4541 fixed).
|
|
62195
|
+
* Registered before startup runs; `resume()` itself skips while startup is in flight.
|
|
62196
|
+
*/
|
|
62197
|
+
listenForAppForeground() {
|
|
62198
|
+
if (__classPrivateFieldGet(this, _Nami_removeForegroundListener, "f"))
|
|
62199
|
+
return;
|
|
62200
|
+
__classPrivateFieldSet(this, _Nami_removeForegroundListener, getPlatformAdapters().device.onAppForeground?.(() => {
|
|
62201
|
+
void NamiRefs.instance.resume();
|
|
62202
|
+
}) ?? null, "f");
|
|
62203
|
+
}
|
|
61980
62204
|
async initializeSDK(config) {
|
|
61981
62205
|
let fullReconfig = false;
|
|
61982
62206
|
let partialReconfig = false;
|
|
@@ -62017,6 +62241,7 @@ class Nami {
|
|
|
62017
62241
|
// configure SDK with new config
|
|
62018
62242
|
__classPrivateFieldSet(this, _Nami_isInitialized, true, "f");
|
|
62019
62243
|
NamiAPI.configure(config);
|
|
62244
|
+
this.listenForAppForeground();
|
|
62020
62245
|
await NamiRefs.instance.init(config);
|
|
62021
62246
|
logger.info("SDK successfully initialized!");
|
|
62022
62247
|
const state = partialReconfig || fullReconfig ? exports.RECONFIG_SUCCESS : exports.INITIAL_SUCCESS;
|
|
@@ -62028,7 +62253,7 @@ class Nami {
|
|
|
62028
62253
|
};
|
|
62029
62254
|
}
|
|
62030
62255
|
}
|
|
62031
|
-
_Nami_isInitialized = new WeakMap();
|
|
62256
|
+
_Nami_isInitialized = new WeakMap(), _Nami_removeForegroundListener = new WeakMap();
|
|
62032
62257
|
Nami.instance = new Nami();
|
|
62033
62258
|
|
|
62034
62259
|
const VALID_STYLES = new Set(['fullscreen', 'sheet', 'compact_sheet', 'modal']);
|
package/dist/index.d.ts
CHANGED
|
@@ -65,6 +65,13 @@ interface IDeviceAdapter {
|
|
|
65
65
|
generateUUID(): string;
|
|
66
66
|
getScreenInfo(): ScreenInfo;
|
|
67
67
|
getLanguage(): string;
|
|
68
|
+
/**
|
|
69
|
+
* Calls `listener` each time the app returns to the foreground — a browser tab becoming
|
|
70
|
+
* visible, a React Native app going `active` — and returns a function that stops listening.
|
|
71
|
+
* Drives core's resume config refresh (NAM-4589). Optional: a host with no foreground
|
|
72
|
+
* concept (the CLI) omits it and simply never resumes.
|
|
73
|
+
*/
|
|
74
|
+
onAppForeground?(listener: () => void): () => void;
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
interface FormFactor {
|
|
@@ -2122,6 +2129,12 @@ declare class Nami {
|
|
|
2122
2129
|
*/
|
|
2123
2130
|
static configure(options: NamiConfiguration): Promise<NamiConfigurationState>;
|
|
2124
2131
|
private static setInitialConfig;
|
|
2132
|
+
/**
|
|
2133
|
+
* Subscribes the resume config refresh to the host's foreground signal, once per process —
|
|
2134
|
+
* a reconfigure must not stack a second listener (the Android defect NAM-4541 fixed).
|
|
2135
|
+
* Registered before startup runs; `resume()` itself skips while startup is in flight.
|
|
2136
|
+
*/
|
|
2137
|
+
private listenForAppForeground;
|
|
2125
2138
|
private initializeSDK;
|
|
2126
2139
|
}
|
|
2127
2140
|
|
|
@@ -3183,12 +3196,16 @@ declare const LOG_HTTP_REQUESTS: string;
|
|
|
3183
3196
|
declare const LOG_HTTP_TRAFFIC: string;
|
|
3184
3197
|
declare const API_TRAFFIC_OBSERVER: string;
|
|
3185
3198
|
declare const STARTUP_TELEMETRY: string;
|
|
3199
|
+
declare const SIMULATE_CONFIG_FETCH_FAILURE: string;
|
|
3186
3200
|
declare const EXTENDED_PLATFORM: string;
|
|
3187
3201
|
declare const EXTENDED_PLATFORM_VERSION: string;
|
|
3188
3202
|
declare const API_MAX_CALLS_LIMIT: number;
|
|
3189
3203
|
declare const API_RETRY_DELAY_SEC: number;
|
|
3190
3204
|
declare const API_TIMEOUT_LIMIT: number;
|
|
3191
3205
|
declare const DEVICE_API_TIMEOUT_LIMIT: number;
|
|
3206
|
+
declare const CONFIG_MAX_ATTEMPTS: number;
|
|
3207
|
+
declare const CONFIG_API_TIMEOUT_LIMIT: number;
|
|
3208
|
+
declare const RESUME_THROTTLE_MS: number;
|
|
3192
3209
|
declare const STATUS_SUCCESS: number;
|
|
3193
3210
|
declare const STATUS_BAD_REQUEST: number;
|
|
3194
3211
|
declare const STATUS_NOT_FOUND: number;
|
|
@@ -3392,12 +3409,51 @@ declare class NamiRefs {
|
|
|
3392
3409
|
static instance: NamiRefs;
|
|
3393
3410
|
private inMemoryAnonymousMode;
|
|
3394
3411
|
private isFirstSession;
|
|
3412
|
+
private startupDisabled;
|
|
3413
|
+
private hasInitialConfig;
|
|
3414
|
+
private lastConfig;
|
|
3415
|
+
private lastStartupRunAt;
|
|
3416
|
+
private startupInFlight;
|
|
3395
3417
|
constructor();
|
|
3396
3418
|
setInMemoryAnonymousMode(enabled: boolean): void;
|
|
3397
3419
|
isInMemoryAnonymousMode(): boolean;
|
|
3398
3420
|
isAnonymousMode(): boolean;
|
|
3399
3421
|
getIsFirstSession(): boolean;
|
|
3422
|
+
/**
|
|
3423
|
+
* True when the platform-config step found no usable config and startup stopped after it —
|
|
3424
|
+
* the equivalent of Apple's `sdkEnabled = false` and Android's `NamiRefs.startupDisabled`.
|
|
3425
|
+
* Like both, it gates only the startup sequence, nothing else.
|
|
3426
|
+
*/
|
|
3427
|
+
isStartupDisabled(): boolean;
|
|
3400
3428
|
init(config: NamiConfiguration): Promise<void>;
|
|
3429
|
+
/**
|
|
3430
|
+
* The app returned to the foreground. Re-reads the platform config, as Apple's `.appResume`
|
|
3431
|
+
* and Android's `onAppComesFromBackground` do, so a config change — the `sdk` kill switch
|
|
3432
|
+
* included — reaches a long-lived tab or app without a cold start.
|
|
3433
|
+
*
|
|
3434
|
+
* - Skipped within {@link RESUME_THROTTLE_MS} of the last launch/resume, and while one is
|
|
3435
|
+
* still running.
|
|
3436
|
+
* - When the launch had **stopped** for want of a config and one is now usable, the rest of
|
|
3437
|
+
* startup runs — the steps an Apple resume re-runs because the launch never executed them.
|
|
3438
|
+
* - Otherwise this is a config refresh only. Apple and Android also re-run device, campaigns,
|
|
3439
|
+
* profile, products and the session start on resume; core does not yet, and has no
|
|
3440
|
+
* session end on background for a second session start to pair with (NAM-4623).
|
|
3441
|
+
*/
|
|
3442
|
+
resume(): Promise<void>;
|
|
3443
|
+
/** Everything startup runs once the platform-config step has found a usable config. */
|
|
3444
|
+
private runStartupAfterConfig;
|
|
3445
|
+
/** Forgets the configuration a resume would run against; called by `Nami.reset()`. */
|
|
3446
|
+
clearResumeState(): void;
|
|
3447
|
+
/**
|
|
3448
|
+
* The platform-config startup step, ahead of device registration — the position it holds on
|
|
3449
|
+
* Apple (`.fetchPlatformConfig`) and Android (`fetchAppConfigOnce`) (NAM-4464, NAM-4589).
|
|
3450
|
+
* Without an initial config it is awaited, so everything downstream — `campaign_cache_ttl`
|
|
3451
|
+
* included — is evaluated against the config it leaves in storage. With one it resolves at
|
|
3452
|
+
* once and the request finishes in the background (see `fetchConfigForStartup`).
|
|
3453
|
+
*
|
|
3454
|
+
* @returns false when there is no usable config, in which case startup must stop.
|
|
3455
|
+
*/
|
|
3456
|
+
private fetchPlatformConfig;
|
|
3401
3457
|
private setInitialValues;
|
|
3402
3458
|
private initAnonymousDevice;
|
|
3403
3459
|
private initIdentifiedDevice;
|
|
@@ -3602,9 +3658,56 @@ declare class CampaignRuleRepository {
|
|
|
3602
3658
|
private fallbackData;
|
|
3603
3659
|
}
|
|
3604
3660
|
|
|
3661
|
+
/**
|
|
3662
|
+
* Whether startup may continue past the platform-config step; `reason` explains a `false`.
|
|
3663
|
+
* A plain shape rather than a discriminated union: core does not compile with
|
|
3664
|
+
* `strictNullChecks`, so a union on `usable` would not narrow.
|
|
3665
|
+
*/
|
|
3666
|
+
type ConfigStartupOutcome = {
|
|
3667
|
+
usable: boolean;
|
|
3668
|
+
reason?: string;
|
|
3669
|
+
};
|
|
3605
3670
|
declare class ConfigRepository {
|
|
3606
3671
|
static instance: ConfigRepository;
|
|
3607
|
-
|
|
3672
|
+
/**
|
|
3673
|
+
* One `GET config/` attempt. Persists the config on success and throws on any failure —
|
|
3674
|
+
* a non-2xx or a transport error alike. `withRetry`'s own retries are turned off, so the
|
|
3675
|
+
* attempt count belongs to {@link fetchConfigForStartup} alone.
|
|
3676
|
+
*/
|
|
3677
|
+
fetchConfig(): Promise<IConfig>;
|
|
3678
|
+
/**
|
|
3679
|
+
* The startup platform-config step, converged on Apple's `NamiPlatformConfigService.fetch` and
|
|
3680
|
+
* Android's `fetchAppConfigOnce` (NAM-4470, NAM-4589):
|
|
3681
|
+
*
|
|
3682
|
+
* | case | outcome |
|
|
3683
|
+
* |---|---|
|
|
3684
|
+
* | an initial config was supplied | NOT awaited: one attempt runs in the background and stores the config when it lands; usable |
|
|
3685
|
+
* | no initial config, success, capabilities include `sdk` | usable |
|
|
3686
|
+
* | no initial config, success, capabilities lack `sdk` | not usable |
|
|
3687
|
+
* | no initial config, failure | {@link CONFIG_MAX_ATTEMPTS} attempts of up to {@link CONFIG_API_TIMEOUT_LIMIT} each, then usable only if a config is cached (and grants `sdk`) |
|
|
3688
|
+
*
|
|
3689
|
+
* The two fallbacks are consulted at two different points, deliberately. An initial config
|
|
3690
|
+
* decides the case *before* any request: startup does not wait on the network at all, since the
|
|
3691
|
+
* initial config is what makes the SDK ready without it (Apple: "Initial config is available.
|
|
3692
|
+
* Skipping retry."). The config cached by a previous launch is consulted only *after* the
|
|
3693
|
+
* retries run out. A stored `INITIAL_APP_CONFIG` is not that cache.
|
|
3694
|
+
*
|
|
3695
|
+
* One check does apply with an initial config, because it costs no wait: a cached config that
|
|
3696
|
+
* withholds `sdk` still stops startup, as Apple's cache load re-applies `updateSdkFlags`.
|
|
3697
|
+
* Without it a kill switch could never reach an app that ships an initial config — the
|
|
3698
|
+
* background fetch lands after startup has already run, so it takes effect from the next launch.
|
|
3699
|
+
*
|
|
3700
|
+
* A `404` is treated as any other 4xx, matching Apple's effective behaviour (its dedicated 404
|
|
3701
|
+
* branch is unreachable, NAM-4471) and Android's.
|
|
3702
|
+
*
|
|
3703
|
+
* @param hasInitialConfig whether the host passed an `initialConfig` that decoded to content.
|
|
3704
|
+
*/
|
|
3705
|
+
fetchConfigForStartup(hasInitialConfig: boolean): Promise<ConfigStartupOutcome>;
|
|
3706
|
+
/**
|
|
3707
|
+
* The initial-config case: nothing is awaited. The cached kill switch is a storage read; the
|
|
3708
|
+
* config request is left running and persists its answer (`fetchConfig`) when it lands.
|
|
3709
|
+
*/
|
|
3710
|
+
private startupOnInitialConfig;
|
|
3608
3711
|
}
|
|
3609
3712
|
|
|
3610
3713
|
declare class CustomerJourneyRepository {
|
|
@@ -4117,5 +4220,5 @@ declare namespace internal {
|
|
|
4117
4220
|
};
|
|
4118
4221
|
}
|
|
4119
4222
|
|
|
4120
|
-
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_TRAFFIC_OBSERVER, 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, DEFAULT_JOURNEY_SETTINGS, 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$2 as NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager$2 as NamiCustomerManager, NamiEntitlementManager$2 as NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager$1 as NamiFlowManager, NamiFlowStepType, NamiHandoffTag, NamiPaywallAction, NamiPaywallManager$2 as NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiPermissionOutcome, NamiPurchaseManager$2 as 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, STARTUP_TELEMETRY, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, TRANSPORT_FAILURE_STATUS, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, NamiProfileManager$1 as _NamiProfileManager, internal as _internal, activateEntitlementByPurchase, activeEntitlements, aggregateScreenreaderText, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, didReceiveResponse, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getJourneySettings, 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, registerAPITrafficObserver, registerPlatformAdapters, registerPurchaseAdapter, resolveLoopSource, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateForm, validateMinSDKVersion, validateTextInput };
|
|
4223
|
+
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_TRAFFIC_OBSERVER, 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, CONFIG_API_TIMEOUT_LIMIT, CONFIG_MAX_ATTEMPTS, CUSTOMER_ATTRIBUTES_KEY_PREFIX, CUSTOMER_JOURNEY_STATE_CHANGED, CUSTOM_HOST_PREFIX, CampaignNotAvailableError, CampaignRuleConversionEventType, CampaignRuleRepository, Capabilities, ClientError, ConfigRepository, ConflictError, CustomerJourneyRepository, DEFAULT_JOURNEY_SETTINGS, 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$2 as NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager$2 as NamiCustomerManager, NamiEntitlementManager$2 as NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager$1 as NamiFlowManager, NamiFlowStepType, NamiHandoffTag, NamiPaywallAction, NamiPaywallManager$2 as NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiPermissionOutcome, NamiPurchaseManager$2 as NamiPurchaseManager, NamiRefs, NamiReservedActions, NotFoundError, PAYWALL_ACTION_EVENT, PLATFORM_ID_REQUIRED, PRODUCTION, PaywallManagerEvents, PaywallRepository, PaywallState, PlacementLabelResolver, PlatformIDRequiredError, ProductRepository, RECONFIG_SUCCESS, RESUME_THROTTLE_MS, RetryLimitExceededError, SDKNotInitializedError, SDK_NOT_INITIALIZED, SERVER_NAMI_ENTITLEMENTS, SESSION_REQUIRED, SHOULD_SHOW_LOADING_INDICATOR, SIMULATE_CONFIG_FETCH_FAILURE, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STARTUP_TELEMETRY, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, TRANSPORT_FAILURE_STATUS, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, NamiProfileManager$1 as _NamiProfileManager, internal as _internal, activateEntitlementByPurchase, activeEntitlements, aggregateScreenreaderText, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, didReceiveResponse, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getJourneySettings, 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, registerAPITrafficObserver, registerPlatformAdapters, registerPurchaseAdapter, resolveLoopSource, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateForm, validateMinSDKVersion, validateTextInput };
|
|
4121
4224
|
export type { AccountStateHandler$1 as AccountStateHandler, AlignmentType, AmazonProduct, ApiResponse, AppleProduct, AvailableCampaignsResponseHandler, BorderLocationType, BorderSideType, Callback$1 as Callback, CloseHandler, CustomerJourneyState, DeepLinkUrlHandler, Device, DevicePayload, DeviceProfile, DirectionType, ExtendedPlatformInfo, FlexDirectionObject, FlowNavigationOptions, FontCollection, FontDetails, FormFactor, FormFieldValidator, GoogleProduct, IConfig, IDeviceAdapter, IEntitlements$1 as IEntitlements, IPaywall, IPlatformAdapters, IProductsWithComponents, IPurchaseAdapter, ISkuMenu, IStorageAdapter, IUIAdapter, Impression, InitialConfig, InitialConfigCompressed, InitiateStateGroup, JourneySettings, LoginResponse, NamiAPITrafficKind, NamiAPITrafficObserver, NamiAPITrafficRecord, NamiAnimation, NamiAnimationObjectSpec, NamiAnimationSpec, NamiAnonymousCampaign, NamiAppSuppliedVideoDetails, NamiCampaign, NamiCampaignManagerStatic, NamiCampaignSegment, NamiCompletionResult, NamiConfiguration, NamiConfigurationState, NamiCustomAttributeValue, NamiCustomerManagerStatic, NamiEntitlement$1 as NamiEntitlement, NamiEntitlementManagerStatic, NamiFlowAction, NamiFlowAnimation, NamiFlowCampaign, NamiFlowCompletionCondition, NamiFlowDTO, NamiFlowEventHandler, NamiFlowHandoffStepHandler, NamiFlowManagerStatic, NamiFlowObjectDTO, NamiFlowOn, NamiFlowStep, NamiFlowTransition, NamiFlowTransitionDirection, NamiFlowWithObject, NamiHandoffAttributeValue, NamiHandoffComplete, NamiHandoffOutcome, NamiHandoffTagValue, NamiInitialConfig, NamiKnownHandoffTag, NamiLanguageCodes, NamiLogLevel, NamiLoginSuccess, NamiPaywallActionHandler, NamiPaywallComponentChange, NamiPaywallEvent, NamiPaywallEventVideoMetadata, NamiPaywallLaunchContext, NamiPaywallManagerStatic, NamiPresentationStyle, NamiProductDetails, NamiProductOffer, NamiProfile, NamiPurchase, NamiPurchaseCompleteResult, NamiPurchaseDetails, NamiPurchaseManagerStatic, NamiPurchaseSuccess, NamiPurchasesState, NamiSKU, NamiSKUType, NamiSubscriptionInterval, NamiSubscriptionPeriod, NamiUserDataEnvelope, 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, TSlot, TSlotGroupSelector, TSpacerComponent, TStack, TSvgImageComponent, TSymbolComponent, TTestObject, TTextComponent, TTextInputComponent, TTextInputType, TTextLikeComponent, TTextListComponent, TTimelineRail, TToggleButtonComponent, TToggleSwitch, TVariablePattern, TVideoComponent, TVolumeButton, TimerState, TransactionRequest, UserAction, UserActionParameters, Wave, WaveSpec };
|
package/dist/index.mjs
CHANGED
|
@@ -104,7 +104,7 @@ const {
|
|
|
104
104
|
// version — stamped by scripts/version.sh
|
|
105
105
|
NAMI_SDK_VERSION = "3.4.12",
|
|
106
106
|
// full package version including dev suffix — stamped by scripts/version.sh
|
|
107
|
-
NAMI_SDK_PACKAGE_VERSION = "3.4.12-dev.
|
|
107
|
+
NAMI_SDK_PACKAGE_VERSION = "3.4.12-dev.202609252349",
|
|
108
108
|
// environments
|
|
109
109
|
PRODUCTION = "production", DEVELOPMENT = "development",
|
|
110
110
|
// error messages
|
|
@@ -114,7 +114,19 @@ AUTH_DEVICE = "nami_auth_device", NAMI_CONFIGURATION = "nami_configuration", NAM
|
|
|
114
114
|
// API settings
|
|
115
115
|
API_VERSION = "v3", BASE_URL_PATH = `sdk/${API_VERSION}/platform`, BASE_URL = "https://app.namiml.com", BASE_STAGING_URL = "https://app-staging.namiml.com", CUSTOM_HOST_PREFIX = "namiAPIHost=", USE_STAGING_API = "useStagingAPI",
|
|
116
116
|
// // extended client info
|
|
117
|
-
EXTENDED_CLIENT_INFO_PREFIX = "extendedClientInfo", EXTENDED_CLIENT_INFO_DELIMITER = ":", VALIDATE_PRODUCT_GROUPS = "validateProductGroups", LOG_HTTP_REQUESTS = "logHTTPRequests", LOG_HTTP_TRAFFIC = "logHTTPTraffic", API_TRAFFIC_OBSERVER = "apiTrafficObserver", STARTUP_TELEMETRY = "startupTelemetry",
|
|
117
|
+
EXTENDED_CLIENT_INFO_PREFIX = "extendedClientInfo", EXTENDED_CLIENT_INFO_DELIMITER = ":", VALIDATE_PRODUCT_GROUPS = "validateProductGroups", LOG_HTTP_REQUESTS = "logHTTPRequests", LOG_HTTP_TRAFFIC = "logHTTPTraffic", API_TRAFFIC_OBSERVER = "apiTrafficObserver", STARTUP_TELEMETRY = "startupTelemetry",
|
|
118
|
+
// Test hook: every platform-config fetch attempt fails before the request leaves.
|
|
119
|
+
// Same namiCommand as Apple's and Android's, so the shared first-run flows drive it.
|
|
120
|
+
SIMULATE_CONFIG_FETCH_FAILURE = "simulateConfigFetchFailure", EXTENDED_PLATFORM = "extended-platform", EXTENDED_PLATFORM_VERSION = "extended-platform-version", API_MAX_CALLS_LIMIT = 2, API_RETRY_DELAY_SEC = 2, API_TIMEOUT_LIMIT = 20000, DEVICE_API_TIMEOUT_LIMIT = 2000,
|
|
121
|
+
// Platform-config attempts when no initial config was supplied (Android's
|
|
122
|
+
// CONFIG_MAX_ATTEMPTS; Apple's retry(maxAttempts: 3)).
|
|
123
|
+
CONFIG_MAX_ATTEMPTS = 3,
|
|
124
|
+
// Per-attempt timeout for GET config/. Android's config client allows 5 s to connect plus 5 s
|
|
125
|
+
// to read (Apple allows 30 s); three attempts keep a total outage near 30 s.
|
|
126
|
+
CONFIG_API_TIMEOUT_LIMIT = 10000,
|
|
127
|
+
// A foreground within this long of the last launch/resume run is skipped — Apple's
|
|
128
|
+
// NamiStartupCoordinator.shouldRunResume window.
|
|
129
|
+
RESUME_THROTTLE_MS = 30000,
|
|
118
130
|
// status codes
|
|
119
131
|
STATUS_SUCCESS = 200, STATUS_BAD_REQUEST = 400, STATUS_NOT_FOUND = 404, STATUS_CONFLICT = 409, STATUS_INTERNAL_SERVER_ERROR = 500,
|
|
120
132
|
// configuration states
|
|
@@ -58031,27 +58043,125 @@ class PaywallRepository {
|
|
|
58031
58043
|
PaywallRepository.instance = new PaywallRepository();
|
|
58032
58044
|
|
|
58033
58045
|
class ConfigRepository {
|
|
58046
|
+
/**
|
|
58047
|
+
* One `GET config/` attempt. Persists the config on success and throws on any failure —
|
|
58048
|
+
* a non-2xx or a transport error alike. `withRetry`'s own retries are turned off, so the
|
|
58049
|
+
* attempt count belongs to {@link fetchConfigForStartup} alone.
|
|
58050
|
+
*/
|
|
58034
58051
|
async fetchConfig() {
|
|
58035
|
-
|
|
58036
|
-
|
|
58037
|
-
|
|
58038
|
-
;
|
|
58039
|
-
}
|
|
58040
|
-
catch (error) {
|
|
58041
|
-
data = storageService.getAppConfig(API_CONFIG)
|
|
58042
|
-
|| storageService.getAppConfig(INITIAL_APP_CONFIG);
|
|
58043
|
-
if (!data) {
|
|
58044
|
-
logger.error(handleErrors(error.status, "config/"));
|
|
58045
|
-
throw error;
|
|
58046
|
-
}
|
|
58052
|
+
// Thrown from *inside* the attempt on purpose, like Apple's and Android's: the ladder then
|
|
58053
|
+
// treats it exactly as a real 500, which is the whole point of the mock.
|
|
58054
|
+
if (storageService.getNamiConfig()?.namiCommands?.includes(SIMULATE_CONFIG_FETCH_FAILURE)) {
|
|
58055
|
+
throw new RetryLimitExceededError(STATUS_INTERNAL_SERVER_ERROR, SIMULATE_CONFIG_FETCH_FAILURE);
|
|
58047
58056
|
}
|
|
58048
|
-
|
|
58057
|
+
const platformId = storageService.getNamiConfig()?.appPlatformID;
|
|
58058
|
+
const data = await NamiAPI.instance.fetchAPI("config/", CONFIG_API_TIMEOUT_LIMIT, 0);
|
|
58059
|
+
// A late answer (the startup await gave up on it) must not overwrite another platform's
|
|
58060
|
+
// config after a reset or a platform-switching reconfigure.
|
|
58061
|
+
if (storageService.getNamiConfig()?.appPlatformID === platformId) {
|
|
58049
58062
|
storageService.setAppConfig(API_CONFIG, data);
|
|
58050
58063
|
}
|
|
58064
|
+
else {
|
|
58065
|
+
logger.debug("Discarding an app config response for a platform that is no longer configured");
|
|
58066
|
+
}
|
|
58051
58067
|
return data;
|
|
58052
58068
|
}
|
|
58069
|
+
/**
|
|
58070
|
+
* The startup platform-config step, converged on Apple's `NamiPlatformConfigService.fetch` and
|
|
58071
|
+
* Android's `fetchAppConfigOnce` (NAM-4470, NAM-4589):
|
|
58072
|
+
*
|
|
58073
|
+
* | case | outcome |
|
|
58074
|
+
* |---|---|
|
|
58075
|
+
* | an initial config was supplied | NOT awaited: one attempt runs in the background and stores the config when it lands; usable |
|
|
58076
|
+
* | no initial config, success, capabilities include `sdk` | usable |
|
|
58077
|
+
* | no initial config, success, capabilities lack `sdk` | not usable |
|
|
58078
|
+
* | no initial config, failure | {@link CONFIG_MAX_ATTEMPTS} attempts of up to {@link CONFIG_API_TIMEOUT_LIMIT} each, then usable only if a config is cached (and grants `sdk`) |
|
|
58079
|
+
*
|
|
58080
|
+
* The two fallbacks are consulted at two different points, deliberately. An initial config
|
|
58081
|
+
* decides the case *before* any request: startup does not wait on the network at all, since the
|
|
58082
|
+
* initial config is what makes the SDK ready without it (Apple: "Initial config is available.
|
|
58083
|
+
* Skipping retry."). The config cached by a previous launch is consulted only *after* the
|
|
58084
|
+
* retries run out. A stored `INITIAL_APP_CONFIG` is not that cache.
|
|
58085
|
+
*
|
|
58086
|
+
* One check does apply with an initial config, because it costs no wait: a cached config that
|
|
58087
|
+
* withholds `sdk` still stops startup, as Apple's cache load re-applies `updateSdkFlags`.
|
|
58088
|
+
* Without it a kill switch could never reach an app that ships an initial config — the
|
|
58089
|
+
* background fetch lands after startup has already run, so it takes effect from the next launch.
|
|
58090
|
+
*
|
|
58091
|
+
* A `404` is treated as any other 4xx, matching Apple's effective behaviour (its dedicated 404
|
|
58092
|
+
* branch is unreachable, NAM-4471) and Android's.
|
|
58093
|
+
*
|
|
58094
|
+
* @param hasInitialConfig whether the host passed an `initialConfig` that decoded to content.
|
|
58095
|
+
*/
|
|
58096
|
+
async fetchConfigForStartup(hasInitialConfig) {
|
|
58097
|
+
if (hasInitialConfig)
|
|
58098
|
+
return this.startupOnInitialConfig();
|
|
58099
|
+
const maxAttempts = CONFIG_MAX_ATTEMPTS;
|
|
58100
|
+
let lastError;
|
|
58101
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
58102
|
+
if (attempt > 1) {
|
|
58103
|
+
logger.debug(`Retrying app config fetch (attempt ${attempt} of ${maxAttempts})`);
|
|
58104
|
+
}
|
|
58105
|
+
try {
|
|
58106
|
+
return outcomeOf(await this.fetchConfig());
|
|
58107
|
+
}
|
|
58108
|
+
catch (error) {
|
|
58109
|
+
lastError = error;
|
|
58110
|
+
}
|
|
58111
|
+
}
|
|
58112
|
+
const cached = storageService.getAppConfig(API_CONFIG);
|
|
58113
|
+
if (cached) {
|
|
58114
|
+
// A cached kill switch still holds: Apple re-applies `updateSdkFlags` when it loads the
|
|
58115
|
+
// cached config, so one that withheld `sdk` keeps the SDK disabled through an outage.
|
|
58116
|
+
if (!grantsSdk(cached)) {
|
|
58117
|
+
return { usable: false, reason: "the cached app config does not grant the 'sdk' capability" };
|
|
58118
|
+
}
|
|
58119
|
+
logger.info(`App config fetch failed after ${maxAttempts} attempts - continuing on the cached config`);
|
|
58120
|
+
return { usable: true };
|
|
58121
|
+
}
|
|
58122
|
+
return {
|
|
58123
|
+
usable: false,
|
|
58124
|
+
reason: `app config fetch failed after ${maxAttempts} attempts (${describeFailure(lastError)}) ` +
|
|
58125
|
+
"and no initial or cached config is available",
|
|
58126
|
+
};
|
|
58127
|
+
}
|
|
58128
|
+
/**
|
|
58129
|
+
* The initial-config case: nothing is awaited. The cached kill switch is a storage read; the
|
|
58130
|
+
* config request is left running and persists its answer (`fetchConfig`) when it lands.
|
|
58131
|
+
*/
|
|
58132
|
+
startupOnInitialConfig() {
|
|
58133
|
+
// Read before the request can land, so this launch is judged on what the last one stored.
|
|
58134
|
+
const cached = storageService.getAppConfig(API_CONFIG);
|
|
58135
|
+
// Issued even when the cache holds a kill switch: that is how a LIFTED kill switch reaches
|
|
58136
|
+
// the next launch.
|
|
58137
|
+
this.fetchConfig().then((config) => {
|
|
58138
|
+
if (!grantsSdk(config)) {
|
|
58139
|
+
logger.warn("App config does not grant the 'sdk' capability - it takes effect from the next launch");
|
|
58140
|
+
}
|
|
58141
|
+
}, (error) => {
|
|
58142
|
+
logger.info(`App config fetch failed (${describeFailure(error)}). Initial config is available. Skipping retry.`);
|
|
58143
|
+
});
|
|
58144
|
+
if (cached && !grantsSdk(cached)) {
|
|
58145
|
+
return { usable: false, reason: "the cached app config does not grant the 'sdk' capability" };
|
|
58146
|
+
}
|
|
58147
|
+
return { usable: true };
|
|
58148
|
+
}
|
|
58053
58149
|
}
|
|
58054
58150
|
ConfigRepository.instance = new ConfigRepository();
|
|
58151
|
+
function outcomeOf(config) {
|
|
58152
|
+
return grantsSdk(config)
|
|
58153
|
+
? { usable: true }
|
|
58154
|
+
: { usable: false, reason: "app config does not grant the 'sdk' capability" };
|
|
58155
|
+
}
|
|
58156
|
+
function grantsSdk(config) {
|
|
58157
|
+
return config.capabilities?.includes(Capabilities.SDK) ?? false;
|
|
58158
|
+
}
|
|
58159
|
+
function describeFailure(error) {
|
|
58160
|
+
const status = error?.status;
|
|
58161
|
+
if (typeof status === "number")
|
|
58162
|
+
return `HTTP ${status}`;
|
|
58163
|
+
return error instanceof Error ? error.message : String(error);
|
|
58164
|
+
}
|
|
58055
58165
|
|
|
58056
58166
|
// Global reference to avoid circular imports
|
|
58057
58167
|
let namiRefsInstance = null;
|
|
@@ -61595,6 +61705,15 @@ class NamiRefs {
|
|
|
61595
61705
|
this.inMemoryAnonymousMode = false;
|
|
61596
61706
|
// Captured before device registration; true only on genuine fresh installs
|
|
61597
61707
|
this.isFirstSession = false;
|
|
61708
|
+
// Set when the platform-config step leaves no usable config (see isStartupDisabled)
|
|
61709
|
+
this.startupDisabled = false;
|
|
61710
|
+
// Whether this configure() supplied an initial config (the config ladder's early exit)
|
|
61711
|
+
this.hasInitialConfig = false;
|
|
61712
|
+
// Resume bookkeeping: the configuration startup last ran with, when it (or a resume) last
|
|
61713
|
+
// ran, and whether one is running now.
|
|
61714
|
+
this.lastConfig = null;
|
|
61715
|
+
this.lastStartupRunAt = 0;
|
|
61716
|
+
this.startupInFlight = false;
|
|
61598
61717
|
// Register this instance to avoid circular dependency issues
|
|
61599
61718
|
setNamiRefsInstance(this);
|
|
61600
61719
|
}
|
|
@@ -61612,13 +61731,101 @@ class NamiRefs {
|
|
|
61612
61731
|
getIsFirstSession() {
|
|
61613
61732
|
return this.isFirstSession;
|
|
61614
61733
|
}
|
|
61734
|
+
/**
|
|
61735
|
+
* True when the platform-config step found no usable config and startup stopped after it —
|
|
61736
|
+
* the equivalent of Apple's `sdkEnabled = false` and Android's `NamiRefs.startupDisabled`.
|
|
61737
|
+
* Like both, it gates only the startup sequence, nothing else.
|
|
61738
|
+
*/
|
|
61739
|
+
isStartupDisabled() {
|
|
61740
|
+
return this.startupDisabled;
|
|
61741
|
+
}
|
|
61615
61742
|
async init(config) {
|
|
61616
61743
|
this.isFirstSession = storageService.getDevice() === null && !storageService.getAnonymousMode();
|
|
61744
|
+
// Cleared per configure so a transient failure cannot disable a later run.
|
|
61745
|
+
this.startupDisabled = false;
|
|
61746
|
+
// `Nami.configure` has already normalised a missing or undecodable initialConfig to `{}`,
|
|
61747
|
+
// so "has content" is Apple's `initialConfig != nil`.
|
|
61748
|
+
this.hasInitialConfig = Object.keys(config.initialConfig ?? {}).length > 0;
|
|
61617
61749
|
this.setInitialValues(config);
|
|
61618
|
-
|
|
61750
|
+
this.lastConfig = config;
|
|
61751
|
+
this.lastStartupRunAt = Date.now();
|
|
61752
|
+
this.startupInFlight = true;
|
|
61753
|
+
try {
|
|
61754
|
+
if (!(await this.fetchPlatformConfig()))
|
|
61755
|
+
return;
|
|
61756
|
+
await this.runStartupAfterConfig(config);
|
|
61757
|
+
}
|
|
61758
|
+
finally {
|
|
61759
|
+
this.startupInFlight = false;
|
|
61760
|
+
}
|
|
61761
|
+
}
|
|
61762
|
+
/**
|
|
61763
|
+
* The app returned to the foreground. Re-reads the platform config, as Apple's `.appResume`
|
|
61764
|
+
* and Android's `onAppComesFromBackground` do, so a config change — the `sdk` kill switch
|
|
61765
|
+
* included — reaches a long-lived tab or app without a cold start.
|
|
61766
|
+
*
|
|
61767
|
+
* - Skipped within {@link RESUME_THROTTLE_MS} of the last launch/resume, and while one is
|
|
61768
|
+
* still running.
|
|
61769
|
+
* - When the launch had **stopped** for want of a config and one is now usable, the rest of
|
|
61770
|
+
* startup runs — the steps an Apple resume re-runs because the launch never executed them.
|
|
61771
|
+
* - Otherwise this is a config refresh only. Apple and Android also re-run device, campaigns,
|
|
61772
|
+
* profile, products and the session start on resume; core does not yet, and has no
|
|
61773
|
+
* session end on background for a second session start to pair with (NAM-4623).
|
|
61774
|
+
*/
|
|
61775
|
+
async resume() {
|
|
61776
|
+
if (!this.lastConfig || this.startupInFlight)
|
|
61777
|
+
return;
|
|
61778
|
+
if (Date.now() - this.lastStartupRunAt < RESUME_THROTTLE_MS) {
|
|
61779
|
+
logger.debug("Resuming too quickly to call the service.");
|
|
61780
|
+
return;
|
|
61781
|
+
}
|
|
61782
|
+
const config = this.lastConfig;
|
|
61783
|
+
const wasStopped = this.startupDisabled;
|
|
61784
|
+
this.lastStartupRunAt = Date.now();
|
|
61785
|
+
this.startupInFlight = true;
|
|
61786
|
+
this.startupDisabled = false;
|
|
61787
|
+
try {
|
|
61788
|
+
if (!(await this.fetchPlatformConfig()) || !wasStopped)
|
|
61789
|
+
return;
|
|
61790
|
+
logger.info("Usable app config on resume - completing the startup that stopped at launch");
|
|
61791
|
+
await this.runStartupAfterConfig(config);
|
|
61792
|
+
}
|
|
61793
|
+
catch (error) {
|
|
61794
|
+
logger.warn(`Resume refresh failed: ${error}`);
|
|
61795
|
+
}
|
|
61796
|
+
finally {
|
|
61797
|
+
this.startupInFlight = false;
|
|
61798
|
+
}
|
|
61799
|
+
}
|
|
61800
|
+
/** Everything startup runs once the platform-config step has found a usable config. */
|
|
61801
|
+
async runStartupAfterConfig(config) {
|
|
61619
61802
|
await this.initAndFetchRequiredData(config);
|
|
61620
61803
|
SessionService.instance.startSession();
|
|
61621
61804
|
}
|
|
61805
|
+
/** Forgets the configuration a resume would run against; called by `Nami.reset()`. */
|
|
61806
|
+
clearResumeState() {
|
|
61807
|
+
this.lastConfig = null;
|
|
61808
|
+
this.lastStartupRunAt = 0;
|
|
61809
|
+
this.startupDisabled = false;
|
|
61810
|
+
}
|
|
61811
|
+
/**
|
|
61812
|
+
* The platform-config startup step, ahead of device registration — the position it holds on
|
|
61813
|
+
* Apple (`.fetchPlatformConfig`) and Android (`fetchAppConfigOnce`) (NAM-4464, NAM-4589).
|
|
61814
|
+
* Without an initial config it is awaited, so everything downstream — `campaign_cache_ttl`
|
|
61815
|
+
* included — is evaluated against the config it leaves in storage. With one it resolves at
|
|
61816
|
+
* once and the request finishes in the background (see `fetchConfigForStartup`).
|
|
61817
|
+
*
|
|
61818
|
+
* @returns false when there is no usable config, in which case startup must stop.
|
|
61819
|
+
*/
|
|
61820
|
+
async fetchPlatformConfig() {
|
|
61821
|
+
const outcome = await ConfigRepository.instance.fetchConfigForStartup(this.hasInitialConfig);
|
|
61822
|
+
if (!outcome.usable) {
|
|
61823
|
+
this.startupDisabled = true;
|
|
61824
|
+
logger.error(`Nami startup stopped: ${outcome.reason}`);
|
|
61825
|
+
return false;
|
|
61826
|
+
}
|
|
61827
|
+
return true;
|
|
61828
|
+
}
|
|
61622
61829
|
setInitialValues(config) {
|
|
61623
61830
|
const baseConfig = { ...config };
|
|
61624
61831
|
delete baseConfig.initialConfig;
|
|
@@ -61879,7 +62086,7 @@ class LastLaunchStore {
|
|
|
61879
62086
|
}
|
|
61880
62087
|
}
|
|
61881
62088
|
|
|
61882
|
-
var _Nami_isInitialized;
|
|
62089
|
+
var _Nami_isInitialized, _Nami_removeForegroundListener;
|
|
61883
62090
|
// NamiFlowManager is intentionally NOT imported at top level — it
|
|
61884
62091
|
// transitively imports back to this module (`Nami` for logging), and
|
|
61885
62092
|
// pulling it in here causes a load-order cycle that breaks tests
|
|
@@ -61887,6 +62094,7 @@ var _Nami_isInitialized;
|
|
|
61887
62094
|
class Nami {
|
|
61888
62095
|
constructor() {
|
|
61889
62096
|
_Nami_isInitialized.set(this, false);
|
|
62097
|
+
_Nami_removeForegroundListener.set(this, null);
|
|
61890
62098
|
this.maxLogging = false;
|
|
61891
62099
|
}
|
|
61892
62100
|
get isInitialized() {
|
|
@@ -61910,9 +62118,13 @@ class Nami {
|
|
|
61910
62118
|
* integrating app, consistent with the Apple and Android SDKs.
|
|
61911
62119
|
*/
|
|
61912
62120
|
static async reset() {
|
|
62121
|
+
var _a;
|
|
61913
62122
|
storageService.clearAll();
|
|
61914
62123
|
clearInMemoryAnonymousMode();
|
|
61915
62124
|
__classPrivateFieldSet(Nami.instance, _Nami_isInitialized, false, "f");
|
|
62125
|
+
__classPrivateFieldGet((_a = Nami.instance), _Nami_removeForegroundListener, "f")?.call(_a);
|
|
62126
|
+
__classPrivateFieldSet(Nami.instance, _Nami_removeForegroundListener, null, "f");
|
|
62127
|
+
NamiRefs.instance.clearResumeState();
|
|
61916
62128
|
// In-memory singleton state that previously survived reset and
|
|
61917
62129
|
// bled into the next configure() cycle.
|
|
61918
62130
|
NamiProfileManager$1.instance.setExternalId(undefined);
|
|
@@ -61975,6 +62187,18 @@ class Nami {
|
|
|
61975
62187
|
}
|
|
61976
62188
|
config.initialConfig = {};
|
|
61977
62189
|
}
|
|
62190
|
+
/**
|
|
62191
|
+
* Subscribes the resume config refresh to the host's foreground signal, once per process —
|
|
62192
|
+
* a reconfigure must not stack a second listener (the Android defect NAM-4541 fixed).
|
|
62193
|
+
* Registered before startup runs; `resume()` itself skips while startup is in flight.
|
|
62194
|
+
*/
|
|
62195
|
+
listenForAppForeground() {
|
|
62196
|
+
if (__classPrivateFieldGet(this, _Nami_removeForegroundListener, "f"))
|
|
62197
|
+
return;
|
|
62198
|
+
__classPrivateFieldSet(this, _Nami_removeForegroundListener, getPlatformAdapters().device.onAppForeground?.(() => {
|
|
62199
|
+
void NamiRefs.instance.resume();
|
|
62200
|
+
}) ?? null, "f");
|
|
62201
|
+
}
|
|
61978
62202
|
async initializeSDK(config) {
|
|
61979
62203
|
let fullReconfig = false;
|
|
61980
62204
|
let partialReconfig = false;
|
|
@@ -62015,6 +62239,7 @@ class Nami {
|
|
|
62015
62239
|
// configure SDK with new config
|
|
62016
62240
|
__classPrivateFieldSet(this, _Nami_isInitialized, true, "f");
|
|
62017
62241
|
NamiAPI.configure(config);
|
|
62242
|
+
this.listenForAppForeground();
|
|
62018
62243
|
await NamiRefs.instance.init(config);
|
|
62019
62244
|
logger.info("SDK successfully initialized!");
|
|
62020
62245
|
const state = partialReconfig || fullReconfig ? RECONFIG_SUCCESS : INITIAL_SUCCESS;
|
|
@@ -62026,7 +62251,7 @@ class Nami {
|
|
|
62026
62251
|
};
|
|
62027
62252
|
}
|
|
62028
62253
|
}
|
|
62029
|
-
_Nami_isInitialized = new WeakMap();
|
|
62254
|
+
_Nami_isInitialized = new WeakMap(), _Nami_removeForegroundListener = new WeakMap();
|
|
62030
62255
|
Nami.instance = new Nami();
|
|
62031
62256
|
|
|
62032
62257
|
const VALID_STYLES = new Set(['fullscreen', 'sheet', 'compact_sheet', 'modal']);
|
|
@@ -65996,4 +66221,4 @@ var internal = /*#__PURE__*/Object.freeze({
|
|
|
65996
66221
|
NamiPurchaseManager: NamiPurchaseManager
|
|
65997
66222
|
});
|
|
65998
66223
|
|
|
65999
|
-
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_TRAFFIC_OBSERVER, 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, DEFAULT_JOURNEY_SETTINGS, 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$1 as NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager$1 as NamiCustomerManager, NamiEntitlementManager$1 as NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager$1 as NamiFlowManager, NamiFlowStepType, NamiHandoffTag, NamiPaywallAction, NamiPaywallManager$1 as NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiPermissionOutcome, NamiPurchaseManager$1 as 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, STARTUP_TELEMETRY, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, TRANSPORT_FAILURE_STATUS, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, NamiProfileManager$1 as _NamiProfileManager, internal as _internal, activateEntitlementByPurchase, activeEntitlements, aggregateScreenreaderText, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, didReceiveResponse, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getJourneySettings, 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, registerAPITrafficObserver, registerPlatformAdapters, registerPurchaseAdapter, resolveLoopSource, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateForm, validateMinSDKVersion, validateTextInput };
|
|
66224
|
+
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_TRAFFIC_OBSERVER, 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, CONFIG_API_TIMEOUT_LIMIT, CONFIG_MAX_ATTEMPTS, CUSTOMER_ATTRIBUTES_KEY_PREFIX, CUSTOMER_JOURNEY_STATE_CHANGED, CUSTOM_HOST_PREFIX, CampaignNotAvailableError, CampaignRuleConversionEventType, CampaignRuleRepository, Capabilities, ClientError, ConfigRepository, ConflictError, CustomerJourneyRepository, DEFAULT_JOURNEY_SETTINGS, 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$1 as NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager$1 as NamiCustomerManager, NamiEntitlementManager$1 as NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager$1 as NamiFlowManager, NamiFlowStepType, NamiHandoffTag, NamiPaywallAction, NamiPaywallManager$1 as NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiPermissionOutcome, NamiPurchaseManager$1 as NamiPurchaseManager, NamiRefs, NamiReservedActions, NotFoundError, PAYWALL_ACTION_EVENT, PLATFORM_ID_REQUIRED, PRODUCTION, PaywallManagerEvents, PaywallRepository, PaywallState, PlacementLabelResolver, PlatformIDRequiredError, ProductRepository, RECONFIG_SUCCESS, RESUME_THROTTLE_MS, RetryLimitExceededError, SDKNotInitializedError, SDK_NOT_INITIALIZED, SERVER_NAMI_ENTITLEMENTS, SESSION_REQUIRED, SHOULD_SHOW_LOADING_INDICATOR, SIMULATE_CONFIG_FETCH_FAILURE, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STARTUP_TELEMETRY, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, TRANSPORT_FAILURE_STATUS, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, NamiProfileManager$1 as _NamiProfileManager, internal as _internal, activateEntitlementByPurchase, activeEntitlements, aggregateScreenreaderText, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, didReceiveResponse, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getJourneySettings, 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, registerAPITrafficObserver, registerPlatformAdapters, registerPurchaseAdapter, resolveLoopSource, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateForm, validateMinSDKVersion, validateTextInput };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@namiml/sdk-core",
|
|
3
|
-
"version": "3.4.12-dev.
|
|
3
|
+
"version": "3.4.12-dev.202609252349",
|
|
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",
|