@namiml/sdk-core 3.4.12-dev.202609281859 → 3.4.12-dev.202609290143

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 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.202609281859",
109
+ NAMI_SDK_PACKAGE_VERSION: exports.NAMI_SDK_PACKAGE_VERSION = "3.4.12-dev.202609290143",
110
110
  // environments
111
111
  PRODUCTION: exports.PRODUCTION = "production", DEVELOPMENT: exports.DEVELOPMENT = "development",
112
112
  // error messages
@@ -125,7 +125,12 @@ SIMULATE_CONFIG_FETCH_FAILURE: exports.SIMULATE_CONFIG_FETCH_FAILURE = "simulate
125
125
  SIMULATE_PAYWALL_FETCH_FAILURE: exports.SIMULATE_PAYWALL_FETCH_FAILURE = "simulatePaywallFetchFailure",
126
126
  // `campaignCacheTTL=<seconds>` overrides the config's campaign_cache_ttl — Apple's command name.
127
127
  // It lets the paywall-cache e2e suite pin TTL 3600 / 1 / 0 per load (NAM-4357).
128
- CAMPAIGN_CACHE_TTL_PREFIX: exports.CAMPAIGN_CACHE_TTL_PREFIX = "campaignCacheTTL=", 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,
128
+ CAMPAIGN_CACHE_TTL_PREFIX: exports.CAMPAIGN_CACHE_TTL_PREFIX = "campaignCacheTTL=",
129
+ // `timeOverride=<ISO-8601 with offset>` pins the ${system.time.*} snapshot instant, so e2e suites
130
+ // and the builder preview can evaluate time conditions at any moment (NAM-4635).
131
+ TIME_OVERRIDE_PREFIX: exports.TIME_OVERRIDE_PREFIX = "timeOverride=",
132
+ // Page condition values that resolve against the per-presentation time snapshot (NAM-4635).
133
+ SYSTEM_TIME_TOKEN_PREFIX: exports.SYSTEM_TIME_TOKEN_PREFIX = "${system.time.", 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,
129
134
  // Platform-config attempts when no initial config was supplied (Android's
130
135
  // CONFIG_MAX_ATTEMPTS; Apple's retry(maxAttempts: 3)).
131
136
  CONFIG_MAX_ATTEMPTS: exports.CONFIG_MAX_ATTEMPTS = 3,
@@ -57086,7 +57091,16 @@ const selectSegment = (segments, splitPosition) => {
57086
57091
  const mapAnonymousCampaigns = (campaigns, splitPosition, formFactor) => {
57087
57092
  if (!campaigns)
57088
57093
  return [];
57094
+ // A rule without a `segments` array is not in the anonymous shape — the backend serves the
57095
+ // legacy resolved shape (a flat `segment`) when a request carries no usable sdk_version. Skip
57096
+ // it rather than throw: throwing here escapes configure() and stops startup (NAM-4653).
57097
+ const unsegmented = campaigns.filter((campaign) => !Array.isArray(campaign.segments)).length;
57098
+ if (unsegmented > 0) {
57099
+ logger.warn(`Skipping ${unsegmented} of ${campaigns.length} campaign rules with no segments array`);
57100
+ }
57089
57101
  return campaigns.flatMap((campaign) => {
57102
+ if (!Array.isArray(campaign.segments))
57103
+ return [];
57090
57104
  let segments;
57091
57105
  if (typeof formFactor === 'string') {
57092
57106
  segments = campaign.segments.filter(seg => seg.form_factors.some(f => f.form_factor === formFactor));
@@ -66402,6 +66416,154 @@ function resolveLoopSource(loopSource) {
66402
66416
  return [];
66403
66417
  }
66404
66418
 
66419
+ /**
66420
+ * `${system.time.*}` Page conditions — the reference implementation of the cross-SDK contract in
66421
+ * `shared/time_conditions/README.md`, verified against `shared/time_conditions/vectors.json`.
66422
+ *
66423
+ * Every token resolves to the per-presentation snapshot instant; the zone lives on `expected`
66424
+ * (`Sunday@America/New_York`), so the instant is projected into that zone at comparison time and
66425
+ * the device zone and locale never matter. Invalid input fails closed: the assertion is `false`
66426
+ * for every operator, `notEquals` included.
66427
+ */
66428
+ const DAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
66429
+ const TIME_OF_DAY = /^([01]?\d|2[0-3]):([0-5]\d)$/;
66430
+ const INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/;
66431
+ const ORDERING = {
66432
+ equals: (a, b) => a === b,
66433
+ gt: (a, b) => a > b,
66434
+ gte: (a, b) => a >= b,
66435
+ lt: (a, b) => a < b,
66436
+ lte: (a, b) => a <= b,
66437
+ };
66438
+ const isOrderingOperator = (operator) => Object.prototype.hasOwnProperty.call(ORDERING, operator);
66439
+ /** True when `value` is a `${system.time.<leaf>}` token (any leaf; unknown leaves evaluate `false`). */
66440
+ const isSystemTimeAssertion = (assertion) => typeof assertion.value === "string" &&
66441
+ assertion.value.startsWith(exports.SYSTEM_TIME_TOKEN_PREFIX) &&
66442
+ assertion.value.endsWith("}");
66443
+ /** An ISO-8601 instant with a required offset, or `null`. */
66444
+ const parseInstant = (value) => {
66445
+ if (typeof value !== "string" || !INSTANT.test(value))
66446
+ return null;
66447
+ const date = new Date(value);
66448
+ return Number.isNaN(date.getTime()) ? null : date;
66449
+ };
66450
+ const isValidZone = (zone) => {
66451
+ if (!zone)
66452
+ return false;
66453
+ try {
66454
+ new Intl.DateTimeFormat("en-US", { timeZone: zone });
66455
+ return true;
66456
+ }
66457
+ catch {
66458
+ return false;
66459
+ }
66460
+ };
66461
+ /** Splits `<value>@<zone>` into its parts when there is exactly one `@` and the zone resolves. */
66462
+ const splitZoned = (expected) => {
66463
+ if (typeof expected !== "string")
66464
+ return null;
66465
+ const parts = expected.split("@");
66466
+ if (parts.length !== 2 || !parts[0] || !isValidZone(parts[1]))
66467
+ return null;
66468
+ return { value: parts[0], zone: parts[1] };
66469
+ };
66470
+ /** The snapshot seen in `zone`: English day name and minutes since local midnight. */
66471
+ const projectInZone = (now, zone) => {
66472
+ try {
66473
+ const [dayOfWeek, time] = formatInTimeZone(now, zone, "EEEE|HH:mm", { locale: enUS }).split("|");
66474
+ const [hours, minutes] = time.split(":").map(Number);
66475
+ return { dayOfWeek, minutes: hours * 60 + minutes };
66476
+ }
66477
+ catch {
66478
+ // Runtimes without Intl time-zone support (e.g. Hermes) fail closed.
66479
+ return null;
66480
+ }
66481
+ };
66482
+ const parseDay = (expected) => {
66483
+ const zoned = splitZoned(expected);
66484
+ return zoned && DAYS.includes(zoned.value) ? { day: zoned.value, zone: zoned.zone } : null;
66485
+ };
66486
+ const evaluateDayOfWeek = (operator, expected, now) => {
66487
+ const isDay = ({ day, zone }) => projectInZone(now, zone)?.dayOfWeek === day;
66488
+ if (operator === "in") {
66489
+ if (!Array.isArray(expected) || expected.length === 0)
66490
+ return false;
66491
+ const days = expected.map(parseDay);
66492
+ // Any invalid element invalidates the whole assertion, even when another element matches.
66493
+ if (days.some((day) => day === null))
66494
+ return false;
66495
+ return days.some((day) => isDay(day));
66496
+ }
66497
+ if (operator !== "equals" && operator !== "notEquals")
66498
+ return false;
66499
+ const day = parseDay(expected);
66500
+ if (!day)
66501
+ return false;
66502
+ const projected = projectInZone(now, day.zone);
66503
+ if (!projected)
66504
+ return false;
66505
+ return operator === "equals" ? projected.dayOfWeek === day.day : projected.dayOfWeek !== day.day;
66506
+ };
66507
+ const evaluateTimeOfDay = (operator, expected, now) => {
66508
+ if (!isOrderingOperator(operator))
66509
+ return false;
66510
+ const zoned = splitZoned(expected);
66511
+ const match = zoned && TIME_OF_DAY.exec(zoned.value);
66512
+ if (!zoned || !match)
66513
+ return false;
66514
+ const projected = projectInZone(now, zoned.zone);
66515
+ if (!projected)
66516
+ return false;
66517
+ return ORDERING[operator](projected.minutes, Number(match[1]) * 60 + Number(match[2]));
66518
+ };
66519
+ const evaluateNow = (operator, expected, now) => {
66520
+ if (!isOrderingOperator(operator))
66521
+ return false;
66522
+ const instant = parseInstant(expected);
66523
+ return instant !== null && ORDERING[operator](now.getTime(), instant.getTime());
66524
+ };
66525
+ /**
66526
+ * Evaluates a `${system.time.*}` assertion against the snapshot instant `now`.
66527
+ * Returns `null` when the assertion is not a time assertion, so callers fall through to their
66528
+ * existing evaluator; otherwise `true`/`false`, with every invalid input `false`.
66529
+ */
66530
+ const evaluateSystemTimeAssertion = (assertion, now) => {
66531
+ if (!isSystemTimeAssertion(assertion))
66532
+ return null;
66533
+ if (Number.isNaN(now.getTime()))
66534
+ return false;
66535
+ const leaf = assertion.value.slice(exports.SYSTEM_TIME_TOKEN_PREFIX.length, -1);
66536
+ switch (leaf) {
66537
+ case "dayOfWeek":
66538
+ return evaluateDayOfWeek(assertion.operator, assertion.expected, now);
66539
+ case "timeOfDay":
66540
+ return evaluateTimeOfDay(assertion.operator, assertion.expected, now);
66541
+ case "now":
66542
+ return evaluateNow(assertion.operator, assertion.expected, now);
66543
+ default:
66544
+ return false;
66545
+ }
66546
+ };
66547
+ /** Parses a `timeOverride=<ISO-8601 with offset>` namiCommand; `null` when it is not a valid one. */
66548
+ const parseTimeOverride = (command) => command.startsWith(exports.TIME_OVERRIDE_PREFIX) ? parseInstant(command.slice(exports.TIME_OVERRIDE_PREFIX.length)) : null;
66549
+ /**
66550
+ * The instant to snapshot for a Page or flow-screen presentation: the `timeOverride=` namiCommand
66551
+ * when present and valid, otherwise the device clock. Call it once per presentation.
66552
+ */
66553
+ const getSystemTimeNow = () => {
66554
+ const command = storageService
66555
+ .getNamiConfig()
66556
+ ?.namiCommands?.find((cmd) => cmd.startsWith(exports.TIME_OVERRIDE_PREFIX));
66557
+ if (command === undefined)
66558
+ return new Date();
66559
+ const override = parseTimeOverride(command);
66560
+ if (override === null) {
66561
+ logger.warn(`Ignoring invalid ${command}: expected ISO-8601 with an offset, e.g. ${exports.TIME_OVERRIDE_PREFIX}2026-09-13T13:00:00-04:00`);
66562
+ return new Date();
66563
+ }
66564
+ return override;
66565
+ };
66566
+
66405
66567
  /**
66406
66568
  * Components that are skipped entirely (they and their subtree do not
66407
66569
  * contribute text to the page-level announcement).
@@ -66764,6 +66926,7 @@ exports.createNamiEntitlements = createNamiEntitlements;
66764
66926
  exports.currentSku = currentSku;
66765
66927
  exports.didReceiveResponse = didReceiveResponse;
66766
66928
  exports.empty = empty;
66929
+ exports.evaluateSystemTimeAssertion = evaluateSystemTimeAssertion;
66767
66930
  exports.extractStandardPricingPhases = extractStandardPricingPhases;
66768
66931
  exports.formatDate = formatDate;
66769
66932
  exports.formattedPrice = formattedPrice;
@@ -66798,6 +66961,7 @@ exports.getSkuProductDetailKeys = getSkuProductDetailKeys;
66798
66961
  exports.getSkuSmartTextValue = getSkuSmartTextValue;
66799
66962
  exports.getSlideSmartTextValue = getSlideSmartTextValue;
66800
66963
  exports.getStandardBillingPeriod = getStandardBillingPeriod;
66964
+ exports.getSystemTimeNow = getSystemTimeNow;
66801
66965
  exports.getTranslate = getTranslate;
66802
66966
  exports.getUrlParams = getUrlParams;
66803
66967
  exports.handleErrors = handleErrors;
@@ -66810,12 +66974,14 @@ exports.isAnonymousMode = isAnonymousMode;
66810
66974
  exports.isInitialConfigCompressed = isInitialConfigCompressed;
66811
66975
  exports.isNamiFlowCampaign = isNamiFlowCampaign;
66812
66976
  exports.isSubscription = isSubscription;
66977
+ exports.isSystemTimeAssertion = isSystemTimeAssertion;
66813
66978
  exports.isValidISODate = isValidISODate;
66814
66979
  exports.isValidUrl = isValidUrl;
66815
66980
  exports.logger = logger;
66816
66981
  exports.mapAnonymousCampaigns = mapAnonymousCampaigns;
66817
66982
  exports.namiBuySKU = namiBuySKU;
66818
66983
  exports.normalizeLaunchContext = normalizeLaunchContext;
66984
+ exports.parseTimeOverride = parseTimeOverride;
66819
66985
  exports.parseToSemver = parseToSemver;
66820
66986
  exports.postConversion = postConversion;
66821
66987
  exports.productDetail = productDetail;
package/dist/index.d.ts CHANGED
@@ -1737,7 +1737,7 @@ declare const BorderSideMap: Record<BorderSideType, string>;
1737
1737
  type TTestObject = {
1738
1738
  value: any;
1739
1739
  expected: any;
1740
- operator: "equals" | "contains" | "notEquals" | "set" | "notSet" | "notContains" | 'gt' | 'lt' | 'gte' | 'lte';
1740
+ operator: "equals" | "contains" | "notEquals" | "set" | "notSet" | "notContains" | 'gt' | 'lt' | 'gte' | 'lte' | 'in';
1741
1741
  };
1742
1742
  type TransformToString<T> = {
1743
1743
  [P in keyof T]: T[P] extends number | boolean ? T[P] | string : T[P];
@@ -3226,6 +3226,8 @@ declare const STARTUP_TELEMETRY: string;
3226
3226
  declare const SIMULATE_CONFIG_FETCH_FAILURE: string;
3227
3227
  declare const SIMULATE_PAYWALL_FETCH_FAILURE: string;
3228
3228
  declare const CAMPAIGN_CACHE_TTL_PREFIX: string;
3229
+ declare const TIME_OVERRIDE_PREFIX: string;
3230
+ declare const SYSTEM_TIME_TOKEN_PREFIX: string;
3229
3231
  declare const EXTENDED_PLATFORM: string;
3230
3232
  declare const EXTENDED_PLATFORM_VERSION: string;
3231
3233
  declare const API_MAX_CALLS_LIMIT: number;
@@ -3317,6 +3319,22 @@ declare const formatDate: (text: string, dateFormat: string) => string;
3317
3319
  declare const isValidISODate: (isoDate: string) => boolean;
3318
3320
  declare const convertLocale: (locale: string) => string;
3319
3321
 
3322
+ /** True when `value` is a `${system.time.<leaf>}` token (any leaf; unknown leaves evaluate `false`). */
3323
+ declare const isSystemTimeAssertion: (assertion: Pick<TTestObject, "value">) => boolean;
3324
+ /**
3325
+ * Evaluates a `${system.time.*}` assertion against the snapshot instant `now`.
3326
+ * Returns `null` when the assertion is not a time assertion, so callers fall through to their
3327
+ * existing evaluator; otherwise `true`/`false`, with every invalid input `false`.
3328
+ */
3329
+ declare const evaluateSystemTimeAssertion: (assertion: TTestObject, now: Date) => boolean | null;
3330
+ /** Parses a `timeOverride=<ISO-8601 with offset>` namiCommand; `null` when it is not a valid one. */
3331
+ declare const parseTimeOverride: (command: string) => Date | null;
3332
+ /**
3333
+ * The instant to snapshot for a Page or flow-screen presentation: the `timeOverride=` namiCommand
3334
+ * when present and valid, otherwise the device clock. Call it once per presentation.
3335
+ */
3336
+ declare const getSystemTimeNow: () => Date;
3337
+
3320
3338
  declare const getUrlParams: () => Record<string, string>;
3321
3339
 
3322
3340
  /**
@@ -4365,5 +4383,5 @@ declare namespace internal {
4365
4383
  };
4366
4384
  }
4367
4385
 
4368
- 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_CACHE_TTL_PREFIX, 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, SIMULATE_PAYWALL_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 };
4386
+ 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_CACHE_TTL_PREFIX, 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, SIMULATE_PAYWALL_FETCH_FAILURE, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STARTUP_TELEMETRY, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SYSTEM_TIME_TOKEN_PREFIX, SessionService, SimpleEventTarget, StorageService, TIME_OVERRIDE_PREFIX, 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, evaluateSystemTimeAssertion, 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, getSystemTimeNow, getTranslate, getUrlParams, handleErrors, hasAllPaywalls, hasCapability, hasPurchaseManagement, initialState, invokeHandler, isAnonymousMode, isInitialConfigCompressed, isNamiFlowCampaign, isSubscription, isSystemTimeAssertion, isValidISODate, isValidUrl, logger, mapAnonymousCampaigns, namiBuySKU, normalizeLaunchContext, parseTimeOverride, parseToSemver, postConversion, productDetail, registerAPITrafficObserver, registerPlatformAdapters, registerPurchaseAdapter, resolveLoopSource, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateForm, validateMinSDKVersion, validateTextInput };
4369
4387
  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.202609281859",
107
+ NAMI_SDK_PACKAGE_VERSION = "3.4.12-dev.202609290143",
108
108
  // environments
109
109
  PRODUCTION = "production", DEVELOPMENT = "development",
110
110
  // error messages
@@ -123,7 +123,12 @@ SIMULATE_CONFIG_FETCH_FAILURE = "simulateConfigFetchFailure",
123
123
  SIMULATE_PAYWALL_FETCH_FAILURE = "simulatePaywallFetchFailure",
124
124
  // `campaignCacheTTL=<seconds>` overrides the config's campaign_cache_ttl — Apple's command name.
125
125
  // It lets the paywall-cache e2e suite pin TTL 3600 / 1 / 0 per load (NAM-4357).
126
- CAMPAIGN_CACHE_TTL_PREFIX = "campaignCacheTTL=", 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,
126
+ CAMPAIGN_CACHE_TTL_PREFIX = "campaignCacheTTL=",
127
+ // `timeOverride=<ISO-8601 with offset>` pins the ${system.time.*} snapshot instant, so e2e suites
128
+ // and the builder preview can evaluate time conditions at any moment (NAM-4635).
129
+ TIME_OVERRIDE_PREFIX = "timeOverride=",
130
+ // Page condition values that resolve against the per-presentation time snapshot (NAM-4635).
131
+ SYSTEM_TIME_TOKEN_PREFIX = "${system.time.", 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,
127
132
  // Platform-config attempts when no initial config was supplied (Android's
128
133
  // CONFIG_MAX_ATTEMPTS; Apple's retry(maxAttempts: 3)).
129
134
  CONFIG_MAX_ATTEMPTS = 3,
@@ -57084,7 +57089,16 @@ const selectSegment = (segments, splitPosition) => {
57084
57089
  const mapAnonymousCampaigns = (campaigns, splitPosition, formFactor) => {
57085
57090
  if (!campaigns)
57086
57091
  return [];
57092
+ // A rule without a `segments` array is not in the anonymous shape — the backend serves the
57093
+ // legacy resolved shape (a flat `segment`) when a request carries no usable sdk_version. Skip
57094
+ // it rather than throw: throwing here escapes configure() and stops startup (NAM-4653).
57095
+ const unsegmented = campaigns.filter((campaign) => !Array.isArray(campaign.segments)).length;
57096
+ if (unsegmented > 0) {
57097
+ logger.warn(`Skipping ${unsegmented} of ${campaigns.length} campaign rules with no segments array`);
57098
+ }
57087
57099
  return campaigns.flatMap((campaign) => {
57100
+ if (!Array.isArray(campaign.segments))
57101
+ return [];
57088
57102
  let segments;
57089
57103
  if (typeof formFactor === 'string') {
57090
57104
  segments = campaign.segments.filter(seg => seg.form_factors.some(f => f.form_factor === formFactor));
@@ -66400,6 +66414,154 @@ function resolveLoopSource(loopSource) {
66400
66414
  return [];
66401
66415
  }
66402
66416
 
66417
+ /**
66418
+ * `${system.time.*}` Page conditions — the reference implementation of the cross-SDK contract in
66419
+ * `shared/time_conditions/README.md`, verified against `shared/time_conditions/vectors.json`.
66420
+ *
66421
+ * Every token resolves to the per-presentation snapshot instant; the zone lives on `expected`
66422
+ * (`Sunday@America/New_York`), so the instant is projected into that zone at comparison time and
66423
+ * the device zone and locale never matter. Invalid input fails closed: the assertion is `false`
66424
+ * for every operator, `notEquals` included.
66425
+ */
66426
+ const DAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
66427
+ const TIME_OF_DAY = /^([01]?\d|2[0-3]):([0-5]\d)$/;
66428
+ const INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/;
66429
+ const ORDERING = {
66430
+ equals: (a, b) => a === b,
66431
+ gt: (a, b) => a > b,
66432
+ gte: (a, b) => a >= b,
66433
+ lt: (a, b) => a < b,
66434
+ lte: (a, b) => a <= b,
66435
+ };
66436
+ const isOrderingOperator = (operator) => Object.prototype.hasOwnProperty.call(ORDERING, operator);
66437
+ /** True when `value` is a `${system.time.<leaf>}` token (any leaf; unknown leaves evaluate `false`). */
66438
+ const isSystemTimeAssertion = (assertion) => typeof assertion.value === "string" &&
66439
+ assertion.value.startsWith(SYSTEM_TIME_TOKEN_PREFIX) &&
66440
+ assertion.value.endsWith("}");
66441
+ /** An ISO-8601 instant with a required offset, or `null`. */
66442
+ const parseInstant = (value) => {
66443
+ if (typeof value !== "string" || !INSTANT.test(value))
66444
+ return null;
66445
+ const date = new Date(value);
66446
+ return Number.isNaN(date.getTime()) ? null : date;
66447
+ };
66448
+ const isValidZone = (zone) => {
66449
+ if (!zone)
66450
+ return false;
66451
+ try {
66452
+ new Intl.DateTimeFormat("en-US", { timeZone: zone });
66453
+ return true;
66454
+ }
66455
+ catch {
66456
+ return false;
66457
+ }
66458
+ };
66459
+ /** Splits `<value>@<zone>` into its parts when there is exactly one `@` and the zone resolves. */
66460
+ const splitZoned = (expected) => {
66461
+ if (typeof expected !== "string")
66462
+ return null;
66463
+ const parts = expected.split("@");
66464
+ if (parts.length !== 2 || !parts[0] || !isValidZone(parts[1]))
66465
+ return null;
66466
+ return { value: parts[0], zone: parts[1] };
66467
+ };
66468
+ /** The snapshot seen in `zone`: English day name and minutes since local midnight. */
66469
+ const projectInZone = (now, zone) => {
66470
+ try {
66471
+ const [dayOfWeek, time] = formatInTimeZone(now, zone, "EEEE|HH:mm", { locale: enUS }).split("|");
66472
+ const [hours, minutes] = time.split(":").map(Number);
66473
+ return { dayOfWeek, minutes: hours * 60 + minutes };
66474
+ }
66475
+ catch {
66476
+ // Runtimes without Intl time-zone support (e.g. Hermes) fail closed.
66477
+ return null;
66478
+ }
66479
+ };
66480
+ const parseDay = (expected) => {
66481
+ const zoned = splitZoned(expected);
66482
+ return zoned && DAYS.includes(zoned.value) ? { day: zoned.value, zone: zoned.zone } : null;
66483
+ };
66484
+ const evaluateDayOfWeek = (operator, expected, now) => {
66485
+ const isDay = ({ day, zone }) => projectInZone(now, zone)?.dayOfWeek === day;
66486
+ if (operator === "in") {
66487
+ if (!Array.isArray(expected) || expected.length === 0)
66488
+ return false;
66489
+ const days = expected.map(parseDay);
66490
+ // Any invalid element invalidates the whole assertion, even when another element matches.
66491
+ if (days.some((day) => day === null))
66492
+ return false;
66493
+ return days.some((day) => isDay(day));
66494
+ }
66495
+ if (operator !== "equals" && operator !== "notEquals")
66496
+ return false;
66497
+ const day = parseDay(expected);
66498
+ if (!day)
66499
+ return false;
66500
+ const projected = projectInZone(now, day.zone);
66501
+ if (!projected)
66502
+ return false;
66503
+ return operator === "equals" ? projected.dayOfWeek === day.day : projected.dayOfWeek !== day.day;
66504
+ };
66505
+ const evaluateTimeOfDay = (operator, expected, now) => {
66506
+ if (!isOrderingOperator(operator))
66507
+ return false;
66508
+ const zoned = splitZoned(expected);
66509
+ const match = zoned && TIME_OF_DAY.exec(zoned.value);
66510
+ if (!zoned || !match)
66511
+ return false;
66512
+ const projected = projectInZone(now, zoned.zone);
66513
+ if (!projected)
66514
+ return false;
66515
+ return ORDERING[operator](projected.minutes, Number(match[1]) * 60 + Number(match[2]));
66516
+ };
66517
+ const evaluateNow = (operator, expected, now) => {
66518
+ if (!isOrderingOperator(operator))
66519
+ return false;
66520
+ const instant = parseInstant(expected);
66521
+ return instant !== null && ORDERING[operator](now.getTime(), instant.getTime());
66522
+ };
66523
+ /**
66524
+ * Evaluates a `${system.time.*}` assertion against the snapshot instant `now`.
66525
+ * Returns `null` when the assertion is not a time assertion, so callers fall through to their
66526
+ * existing evaluator; otherwise `true`/`false`, with every invalid input `false`.
66527
+ */
66528
+ const evaluateSystemTimeAssertion = (assertion, now) => {
66529
+ if (!isSystemTimeAssertion(assertion))
66530
+ return null;
66531
+ if (Number.isNaN(now.getTime()))
66532
+ return false;
66533
+ const leaf = assertion.value.slice(SYSTEM_TIME_TOKEN_PREFIX.length, -1);
66534
+ switch (leaf) {
66535
+ case "dayOfWeek":
66536
+ return evaluateDayOfWeek(assertion.operator, assertion.expected, now);
66537
+ case "timeOfDay":
66538
+ return evaluateTimeOfDay(assertion.operator, assertion.expected, now);
66539
+ case "now":
66540
+ return evaluateNow(assertion.operator, assertion.expected, now);
66541
+ default:
66542
+ return false;
66543
+ }
66544
+ };
66545
+ /** Parses a `timeOverride=<ISO-8601 with offset>` namiCommand; `null` when it is not a valid one. */
66546
+ const parseTimeOverride = (command) => command.startsWith(TIME_OVERRIDE_PREFIX) ? parseInstant(command.slice(TIME_OVERRIDE_PREFIX.length)) : null;
66547
+ /**
66548
+ * The instant to snapshot for a Page or flow-screen presentation: the `timeOverride=` namiCommand
66549
+ * when present and valid, otherwise the device clock. Call it once per presentation.
66550
+ */
66551
+ const getSystemTimeNow = () => {
66552
+ const command = storageService
66553
+ .getNamiConfig()
66554
+ ?.namiCommands?.find((cmd) => cmd.startsWith(TIME_OVERRIDE_PREFIX));
66555
+ if (command === undefined)
66556
+ return new Date();
66557
+ const override = parseTimeOverride(command);
66558
+ if (override === null) {
66559
+ logger.warn(`Ignoring invalid ${command}: expected ISO-8601 with an offset, e.g. ${TIME_OVERRIDE_PREFIX}2026-09-13T13:00:00-04:00`);
66560
+ return new Date();
66561
+ }
66562
+ return override;
66563
+ };
66564
+
66403
66565
  /**
66404
66566
  * Components that are skipped entirely (they and their subtree do not
66405
66567
  * contribute text to the page-level announcement).
@@ -66685,4 +66847,4 @@ var internal = /*#__PURE__*/Object.freeze({
66685
66847
  NamiPurchaseManager: NamiPurchaseManager
66686
66848
  });
66687
66849
 
66688
- 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_CACHE_TTL_PREFIX, 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, SIMULATE_PAYWALL_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 };
66850
+ 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_CACHE_TTL_PREFIX, 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, SIMULATE_PAYWALL_FETCH_FAILURE, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STARTUP_TELEMETRY, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SYSTEM_TIME_TOKEN_PREFIX, SessionService, SimpleEventTarget, StorageService, TIME_OVERRIDE_PREFIX, 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, evaluateSystemTimeAssertion, 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, getSystemTimeNow, getTranslate, getUrlParams, handleErrors, hasAllPaywalls, hasCapability, hasPurchaseManagement, initialState, invokeHandler, isAnonymousMode, isInitialConfigCompressed, isNamiFlowCampaign, isSubscription, isSystemTimeAssertion, isValidISODate, isValidUrl, logger, mapAnonymousCampaigns, namiBuySKU, normalizeLaunchContext, parseTimeOverride, 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.202609281859",
3
+ "version": "3.4.12-dev.202609290143",
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",