@namiml/sdk-core 3.4.0-dev.202605141520 → 3.4.0-dev.202605180118

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
@@ -98,7 +98,7 @@ const {
98
98
  // version — stamped by scripts/version.sh
99
99
  NAMI_SDK_VERSION: exports.NAMI_SDK_VERSION = "3.4.0",
100
100
  // full package version including dev suffix — stamped by scripts/version.sh
101
- NAMI_SDK_PACKAGE_VERSION: exports.NAMI_SDK_PACKAGE_VERSION = "3.4.0-dev.202605141520",
101
+ NAMI_SDK_PACKAGE_VERSION: exports.NAMI_SDK_PACKAGE_VERSION = "3.4.0-dev.202605180118",
102
102
  // environments
103
103
  PRODUCTION: exports.PRODUCTION = "production", DEVELOPMENT: exports.DEVELOPMENT = "development",
104
104
  // error messages
@@ -63913,6 +63913,230 @@ const convertLocale = (locale) => {
63913
63913
  return intlLocale.language + (intlLocale.region ?? intlLocale.script ?? '');
63914
63914
  };
63915
63915
 
63916
+ /**
63917
+ * Components that are skipped entirely (they and their subtree do not
63918
+ * contribute text to the page-level announcement).
63919
+ *
63920
+ * - Buttons / interactive controls: their label is appended as the
63921
+ * focused-button suffix, not collected mid-tree. Recursing into them
63922
+ * would double-count the label and pull in non-spoken decorative text.
63923
+ * - Image-like leaves: explicitly skipped per spec.
63924
+ * - Form pickers + QR codes + raw video: not informational text.
63925
+ */
63926
+ const SKIP_COMPONENTS = new Set([
63927
+ 'button',
63928
+ 'playPauseButton',
63929
+ 'volumeButton',
63930
+ 'toggleButton',
63931
+ 'toggleSwitch',
63932
+ 'radio',
63933
+ 'image',
63934
+ 'svgImage',
63935
+ 'symbol',
63936
+ 'qrCode',
63937
+ 'videoUrl',
63938
+ 'segmentPicker',
63939
+ 'segmentPickerItem',
63940
+ ]);
63941
+ /** Component values in {@link SKIP_COMPONENTS} that are interactive controls
63942
+ * (as opposed to images / media / non-text leaves). When a text component
63943
+ * shares a container with one of these, the text is treated as that
63944
+ * button's accessible label and excluded from the page composite. */
63945
+ const BUTTON_COMPONENTS = new Set([
63946
+ 'button',
63947
+ 'playPauseButton',
63948
+ 'volumeButton',
63949
+ 'toggleButton',
63950
+ 'toggleSwitch',
63951
+ 'radio',
63952
+ ]);
63953
+ /** `namiComponentType` values that are always included in the page
63954
+ * composite — even when their structural position (e.g. a sibling of a
63955
+ * utility button) would otherwise mark them for exclusion. Driven by the
63956
+ * spec's schema table:
63957
+ *
63958
+ * | `namiComponentType: "legalText"` | Legal/T&C content (included in tree-walk) |
63959
+ */
63960
+ const ALWAYS_INCLUDE_NAMI_TYPES = new Set(['legalText']);
63961
+ /**
63962
+ * Whether a text leaf inside `parent` should be skipped because it acts as
63963
+ * the accessible label for a sibling button.
63964
+ *
63965
+ * Schema pattern this catches: a container whose direct children include
63966
+ * BOTH a text/text-list AND an interactive button (e.g. Page 3's "Login
63967
+ * Group" — `[ text "Already an NFL+ subscriber?", loginButton "Sign In" ]`).
63968
+ * The text describes the button and is announced when the button is
63969
+ * focused, NOT as page-level content. Without this rule, the composite
63970
+ * leaks "Already an NFL+ subscriber? Subscribe to NFL plus button" instead
63971
+ * of "Subscribe to NFL plus button".
63972
+ *
63973
+ * Texts marked `namiComponentType: "legalText"` are exempt — the spec
63974
+ * explicitly opts legal copy in regardless of its container's other
63975
+ * children (e.g. Page 5's "Bottom Wrapper" mixes a body text, a restore
63976
+ * button, and the legal text — the legal text must still be spoken).
63977
+ */
63978
+ function isButtonSiblingLabel(node, parent) {
63979
+ if (!parent || !Array.isArray(parent.components))
63980
+ return false;
63981
+ if (typeof node.namiComponentType === 'string' &&
63982
+ ALWAYS_INCLUDE_NAMI_TYPES.has(node.namiComponentType)) {
63983
+ return false;
63984
+ }
63985
+ for (const sibling of parent.components) {
63986
+ if (sibling && typeof sibling === 'object') {
63987
+ const s = sibling;
63988
+ if (typeof s.component === 'string' && BUTTON_COMPONENTS.has(s.component)) {
63989
+ return true;
63990
+ }
63991
+ }
63992
+ }
63993
+ return false;
63994
+ }
63995
+ /**
63996
+ * Recursively collect spoken text from a component subtree following the
63997
+ * TV Full-Page Announcement contract.
63998
+ *
63999
+ * Rules (in order):
64000
+ * 1. `null` / `undefined` → contribute nothing.
64001
+ * 2. `hidden: true` → skip the node AND its subtree.
64002
+ * 3. `component` in {@link SKIP_COMPONENTS} → skip the node AND its subtree.
64003
+ * 4. `screenreaderText` set on this container → take that string verbatim
64004
+ * and do NOT recurse (server-supplied override).
64005
+ * 5. `component: "text"` or `"text-list"` → collect, UNLESS the text is a
64006
+ * sibling label for an adjacent button (see
64007
+ * {@link isButtonSiblingLabel}). `namiComponentType: "legalText"` is
64008
+ * exempted from the sibling-label exclusion.
64009
+ * 6. Otherwise → recurse into `components`.
64010
+ */
64011
+ function collectText(node, parent = null) {
64012
+ if (!node || typeof node !== 'object')
64013
+ return [];
64014
+ const n = node;
64015
+ if (n.hidden === true)
64016
+ return [];
64017
+ if (typeof n.component === 'string' && SKIP_COMPONENTS.has(n.component))
64018
+ return [];
64019
+ // Container-level override: when an enclosing container pre-supplies its own
64020
+ // screenreader text, take it verbatim and stop descending — the customer
64021
+ // has authored a polished announcement for this subtree.
64022
+ if (typeof n.screenreaderText === 'string' && n.screenreaderText.trim().length > 0) {
64023
+ return [n.screenreaderText.trim()];
64024
+ }
64025
+ if (n.component === 'text') {
64026
+ if (isButtonSiblingLabel(n, parent))
64027
+ return [];
64028
+ return typeof n.text === 'string' && n.text.length > 0 ? [n.text] : [];
64029
+ }
64030
+ if (n.component === 'text-list') {
64031
+ if (isButtonSiblingLabel(n, parent))
64032
+ return [];
64033
+ if (!Array.isArray(n.texts))
64034
+ return [];
64035
+ return n.texts.filter((t) => typeof t === 'string' && t.length > 0);
64036
+ }
64037
+ if (Array.isArray(n.components)) {
64038
+ const out = [];
64039
+ for (const child of n.components) {
64040
+ out.push(...collectText(child, n));
64041
+ }
64042
+ return out;
64043
+ }
64044
+ return [];
64045
+ }
64046
+ function collectHeaderFooter(slot) {
64047
+ if (!Array.isArray(slot))
64048
+ return [];
64049
+ // The slot itself acts as the synthetic parent so a top-level text inside
64050
+ // a header/footer that sits alongside a top-level button is treated as a
64051
+ // button label and excluded.
64052
+ const syntheticParent = { components: slot };
64053
+ const out = [];
64054
+ for (const node of slot) {
64055
+ out.push(...collectText(node, syntheticParent));
64056
+ }
64057
+ return out;
64058
+ }
64059
+ /**
64060
+ * Join an ordered list of collected text fragments into a single speakable
64061
+ * string. Each fragment is trimmed; empty fragments are dropped. Author
64062
+ * terminators (`.`, `!`, `?`) are preserved so emphasis carries through to
64063
+ * the screenreader; only fragments that lack a terminator get `". "`
64064
+ * inserted before the next fragment. The result is a string that reads
64065
+ * naturally without double-punctuation like `"...REACH!. Watch..."`.
64066
+ */
64067
+ function joinFragments(fragments) {
64068
+ let out = '';
64069
+ for (const raw of fragments) {
64070
+ const trimmed = raw.trim();
64071
+ if (!trimmed)
64072
+ continue;
64073
+ if (!out) {
64074
+ out = trimmed;
64075
+ continue;
64076
+ }
64077
+ const last = out[out.length - 1];
64078
+ const endsWithTerminator = last === '.' || last === '!' || last === '?';
64079
+ out += (endsWithTerminator ? ' ' : '. ') + trimmed;
64080
+ }
64081
+ return out;
64082
+ }
64083
+ /**
64084
+ * Build the full-screen announcement string for a paywall page per the
64085
+ * TV Full-Page Announcement contract
64086
+ * (https://linear.app/nami-product-development/document/tv-full-page-announcement-980f61b96e7c).
64087
+ *
64088
+ * Behaviour:
64089
+ * 1. **Override path.** If `page.screenreaderText` is set, that string is
64090
+ * returned verbatim (followed by `focusedButtonLabel` if provided and
64091
+ * not already present at the tail). Server-authored polished
64092
+ * announcements win over client-side aggregation.
64093
+ *
64094
+ * 2. **Tree-walk fallback.** Otherwise, walk the page's containers in
64095
+ * reading order — `header` → `backgroundContainer` → `contentContainer`
64096
+ * → `footer` — collecting visible `text` / `text-list` descendants in
64097
+ * source order. `image`, `hidden: true` subtrees, interactive controls
64098
+ * (buttons / toggles), and non-text leaves are skipped. The
64099
+ * `focusedButtonLabel` is appended as the final sentence.
64100
+ *
64101
+ * SDKs invoke this once per page on the first focus of the default CTA, and
64102
+ * speak the result via their platform TTS API. On subsequent focuses within
64103
+ * the same page, the SDK announces only the focused element's own label —
64104
+ * this helper is NOT re-called.
64105
+ *
64106
+ * @param page The paywall page to announce.
64107
+ * @param focusedButtonLabel The resolved label of the button that
64108
+ * triggered the announcement (typically the
64109
+ * page's default-focused CTA). For
64110
+ * subscription-plan CTAs this is the dynamic
64111
+ * `"{price} {period} subscription plan button"`
64112
+ * string the backend already resolved.
64113
+ * @returns A single string ready to pass to a platform TTS API. Empty when
64114
+ * the page has no spoken content and no focused button label.
64115
+ */
64116
+ function aggregateScreenreaderText(page, focusedButtonLabel = '') {
64117
+ const trimmedLabel = focusedButtonLabel.trim();
64118
+ // Override path
64119
+ if (typeof page.screenreaderText === 'string' && page.screenreaderText.trim().length > 0) {
64120
+ const override = page.screenreaderText.trim();
64121
+ if (!trimmedLabel)
64122
+ return override;
64123
+ // If the server-authored override already ends with the focused button
64124
+ // label, don't append it again.
64125
+ if (override.toLowerCase().endsWith(trimmedLabel.toLowerCase()))
64126
+ return override;
64127
+ return joinFragments([override, trimmedLabel]);
64128
+ }
64129
+ // Tree-walk path
64130
+ const fragments = [];
64131
+ fragments.push(...collectHeaderFooter(page.header));
64132
+ fragments.push(...collectText(page.backgroundContainer));
64133
+ fragments.push(...collectText(page.contentContainer));
64134
+ fragments.push(...collectHeaderFooter(page.footer));
64135
+ if (trimmedLabel)
64136
+ fragments.push(trimmedLabel);
64137
+ return joinFragments(fragments);
64138
+ }
64139
+
63916
64140
  function namiBuySKU(skuRefId) {
63917
64141
  if (!hasPurchaseManagement("NamiPurchaseManager.buySKU")) {
63918
64142
  return;
@@ -63982,6 +64206,7 @@ exports.SimpleEventTarget = SimpleEventTarget;
63982
64206
  exports.StorageService = StorageService;
63983
64207
  exports.activateEntitlementByPurchase = activateEntitlementByPurchase;
63984
64208
  exports.activeEntitlements = activeEntitlements;
64209
+ exports.aggregateScreenreaderText = aggregateScreenreaderText;
63985
64210
  exports.allCampaigns = allCampaigns;
63986
64211
  exports.allPaywalls = allPaywalls;
63987
64212
  exports.applyEntitlementActivation = applyEntitlementActivation;
package/dist/index.d.ts CHANGED
@@ -772,6 +772,31 @@ interface TBaseComponent {
772
772
  context?: {
773
773
  [key: string]: any;
774
774
  };
775
+ /**
776
+ * Text spoken by the platform screenreader. Plays two roles depending on
777
+ * where it is set, per the TV Full-Page Announcement contract
778
+ * (https://linear.app/nami-product-development/document/tv-full-page-announcement-980f61b96e7c):
779
+ *
780
+ * - **On focusable elements** (buttons, toggles, etc.): the element's own
781
+ * label, spoken when the element receives focus. For subscription-plan
782
+ * CTAs the backend resolves this dynamically to
783
+ * `"{price} {period} [{tier}] subscription plan button"`.
784
+ *
785
+ * - **On containers** (any TBaseComponent-derived container, and TPages):
786
+ * an optional *server-supplied composite override*. When set, SDKs
787
+ * announce this verbatim on first focus of the page's default CTA
788
+ * instead of tree-walking the page's text. When omitted, SDKs derive
789
+ * the composite by walking visible `text` / `text-list` descendants in
790
+ * source order (skipping `image`, `hidden: true`, and non-text leaves)
791
+ * and appending the focused button's label.
792
+ */
793
+ screenreaderText?: string;
794
+ /**
795
+ * Supplementary hint announced after `screenreaderText` on the element it
796
+ * is set on (e.g. "Double-tap to subscribe"). Optional; SDKs that lack a
797
+ * hint affordance may omit it.
798
+ */
799
+ screenreaderHint?: string;
775
800
  moveX?: number | string;
776
801
  moveY?: string | number;
777
802
  direction?: DirectionType;
@@ -1701,6 +1726,22 @@ type TPages = {
1701
1726
  contentContainer: TContainer | null;
1702
1727
  footer: THeaderFooter;
1703
1728
  backgroundContainer: TContainer | null;
1729
+ /**
1730
+ * Optional server-supplied composite announcement for the page. When set,
1731
+ * TV SDKs speak this verbatim on first focus of the default CTA instead of
1732
+ * tree-walking the page's text content. When omitted, SDKs derive the
1733
+ * composite client-side from visible `text` / `text-list` descendants (in
1734
+ * source order, skipping `image`, `hidden: true`, and non-text leaves) and
1735
+ * append the focused button's label.
1736
+ *
1737
+ * Video pages are detected by the presence of `component: "videoUrl"` in
1738
+ * `backgroundContainer` with `autoplayVideo: true` and `loopVideo: false`
1739
+ * — TTS is deferred until playback completes. No additional schema flag is
1740
+ * required for that suppression.
1741
+ *
1742
+ * Contract: https://linear.app/nami-product-development/document/tv-full-page-announcement-980f61b96e7c
1743
+ */
1744
+ screenreaderText?: string;
1704
1745
  };
1705
1746
  type TInitialState = {
1706
1747
  slides?: TCarouselSlidesState;
@@ -2623,6 +2664,41 @@ interface INamiRefsInstance {
2623
2664
  */
2624
2665
  declare function isAnonymousMode(): boolean;
2625
2666
 
2667
+ /**
2668
+ * Build the full-screen announcement string for a paywall page per the
2669
+ * TV Full-Page Announcement contract
2670
+ * (https://linear.app/nami-product-development/document/tv-full-page-announcement-980f61b96e7c).
2671
+ *
2672
+ * Behaviour:
2673
+ * 1. **Override path.** If `page.screenreaderText` is set, that string is
2674
+ * returned verbatim (followed by `focusedButtonLabel` if provided and
2675
+ * not already present at the tail). Server-authored polished
2676
+ * announcements win over client-side aggregation.
2677
+ *
2678
+ * 2. **Tree-walk fallback.** Otherwise, walk the page's containers in
2679
+ * reading order — `header` → `backgroundContainer` → `contentContainer`
2680
+ * → `footer` — collecting visible `text` / `text-list` descendants in
2681
+ * source order. `image`, `hidden: true` subtrees, interactive controls
2682
+ * (buttons / toggles), and non-text leaves are skipped. The
2683
+ * `focusedButtonLabel` is appended as the final sentence.
2684
+ *
2685
+ * SDKs invoke this once per page on the first focus of the default CTA, and
2686
+ * speak the result via their platform TTS API. On subsequent focuses within
2687
+ * the same page, the SDK announces only the focused element's own label —
2688
+ * this helper is NOT re-called.
2689
+ *
2690
+ * @param page The paywall page to announce.
2691
+ * @param focusedButtonLabel The resolved label of the button that
2692
+ * triggered the announcement (typically the
2693
+ * page's default-focused CTA). For
2694
+ * subscription-plan CTAs this is the dynamic
2695
+ * `"{price} {period} subscription plan button"`
2696
+ * string the backend already resolved.
2697
+ * @returns A single string ready to pass to a platform TTS API. Empty when
2698
+ * the page has no spoken content and no focused button label.
2699
+ */
2700
+ declare function aggregateScreenreaderText(page: TPages, focusedButtonLabel?: string): string;
2701
+
2626
2702
  /**
2627
2703
  * Returns the translated string for the given key in the current SDK language.
2628
2704
  * Uses Nami language code from storage (set via Nami.configure({ namiLanguageCode })).
@@ -2985,5 +3061,5 @@ declare const getBillingPeriodNumber: (billingPeriod: string) => number;
2985
3061
  declare const formattedPrice: (price: number) => number;
2986
3062
  declare function toDouble(num: number): number;
2987
3063
 
2988
- export { ALREADY_CONFIGURED, ANONYMOUS_MODE, ANONYMOUS_MODE_ALREADY_OFF, ANONYMOUS_MODE_ALREADY_ON, ANONYMOUS_MODE_LOGIN_NOT_ALLOWED, ANONYMOUS_UUID, APIError, API_ACTIVE_ENTITLEMENTS, API_CAMPAIGN_RULES, API_CAMPAIGN_SESSION_TIMESTAMP, API_CONFIG, API_MAX_CALLS_LIMIT, API_PAYWALLS, API_PRODUCTS, API_RETRY_DELAY_SEC, API_TIMEOUT_LIMIT, API_VERSION, AUTH_DEVICE, AVAILABLE_ACTIVE_ENTITLEMENTS_CHANGED, AVAILABLE_CAMPAIGNS_CHANGED, AccountStateAction, AnonymousCDPError, AnonymousLoginError, AnonymousModeAlreadyOffError, AnonymousModeAlreadyOnError, BASE_STAGING_URL, BASE_URL, BASE_URL_PATH, BadRequestError, BasicNamiFlow, BorderMap, BorderSideMap, CAMPAIGN_NOT_AVAILABLE, CUSTOMER_ATTRIBUTES_KEY_PREFIX, CUSTOMER_JOURNEY_STATE_CHANGED, CUSTOM_HOST_PREFIX, CampaignNotAvailableError, CampaignRuleConversionEventType, CampaignRuleRepository, Capabilities, ClientError, ConfigRepository, ConflictError, CustomerJourneyRepository, DEVELOPMENT, DEVICE_API_TIMEOUT_LIMIT, DEVICE_ID_NOT_SET, DEVICE_ID_REQUIRED, DeviceIDRequiredError, DeviceRepository, EXTENDED_CLIENT_INFO_DELIMITER, EXTENDED_CLIENT_INFO_PREFIX, EXTENDED_PLATFORM, EXTENDED_PLATFORM_VERSION, EXTERNAL_ID_REQUIRED, EntitlementRepository, EntitlementUtils, ExternalIDRequiredError, FLOW_SCREENS_NOT_AVAILABLE, FlowScreensNotAvailableError, HTML_REGEX, INITIAL_APP_CONFIG, INITIAL_CAMPAIGN_RULES, INITIAL_PAYWALLS, INITIAL_PRODUCTS, INITIAL_SESSION_COUNTER_VALUE, INITIAL_SUCCESS, InternalServerError, KEY_SESSION_COUNTER, LIQUID_VARIABLE_REGEX, LOCAL_NAMI_ENTITLEMENTS, LOG_HTTP_REQUESTS, LOG_HTTP_TRAFFIC, LaunchCampaignError, LaunchContextResolver, LogLevel, NAMI_CONFIGURATION, NAMI_CUSTOMER_JOURNEY_STATE, NAMI_LANGUAGE_CODE, NAMI_LAST_IMPRESSION_ID, NAMI_LAUNCH_ID, NAMI_PROFILE, NAMI_PURCHASE_CHANNEL, NAMI_PURCHASE_IMPRESSION_ID, NAMI_SDK_PACKAGE_VERSION, NAMI_SDK_VERSION, NAMI_SESSION_ID, NAMI_STORAGE_KEYS, Nami, NamiAPI, NamiAnimationType, NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager, NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager, NamiFlowStepType, NamiPaywallAction, NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiProfileManager, NamiPurchaseManager, NamiRefs, NamiReservedActions, NotFoundError, PAYWALL_ACTION_EVENT, PLATFORM_ID_REQUIRED, PRODUCTION, PaywallManagerEvents, PaywallRepository, PaywallState, PlacementLabelResolver, PlatformIDRequiredError, ProductRepository, RECONFIG_SUCCESS, RetryLimitExceededError, SDKNotInitializedError, SDK_NOT_INITIALIZED, SERVER_NAMI_ENTITLEMENTS, SESSION_REQUIRED, SHOULD_SHOW_LOADING_INDICATOR, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, activateEntitlementByPurchase, activeEntitlements, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getPaywall, getPaywallDataFromLabel, getPercentagePriceDifference, getPeriodNumberInDays, getPeriodNumberInWeeks, getPlatformAdapters, getPriceDifference, getPricePerMonth, getProductDetail, getPurchaseAdapter, getReferenceSku, getSkuProductDetailKeys, getSkuSmartTextValue, getSlideSmartTextValue, getStandardBillingPeriod, getTranslate, getUrlParams, handleErrors, hasAllPaywalls, hasCapability, hasPurchaseManagement, initialState, invokeHandler, isAnonymousMode, isInitialConfigCompressed, isNamiFlowCampaign, isSubscription, isValidISODate, isValidUrl, logger, mapAnonymousCampaigns, namiBuySKU, normalizeLaunchContext, parseToSemver, postConversion, productDetail, registerPlatformAdapters, registerPurchaseAdapter, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateMinSDKVersion };
3064
+ export { ALREADY_CONFIGURED, ANONYMOUS_MODE, ANONYMOUS_MODE_ALREADY_OFF, ANONYMOUS_MODE_ALREADY_ON, ANONYMOUS_MODE_LOGIN_NOT_ALLOWED, ANONYMOUS_UUID, APIError, API_ACTIVE_ENTITLEMENTS, API_CAMPAIGN_RULES, API_CAMPAIGN_SESSION_TIMESTAMP, API_CONFIG, API_MAX_CALLS_LIMIT, API_PAYWALLS, API_PRODUCTS, API_RETRY_DELAY_SEC, API_TIMEOUT_LIMIT, API_VERSION, AUTH_DEVICE, AVAILABLE_ACTIVE_ENTITLEMENTS_CHANGED, AVAILABLE_CAMPAIGNS_CHANGED, AccountStateAction, AnonymousCDPError, AnonymousLoginError, AnonymousModeAlreadyOffError, AnonymousModeAlreadyOnError, BASE_STAGING_URL, BASE_URL, BASE_URL_PATH, BadRequestError, BasicNamiFlow, BorderMap, BorderSideMap, CAMPAIGN_NOT_AVAILABLE, CUSTOMER_ATTRIBUTES_KEY_PREFIX, CUSTOMER_JOURNEY_STATE_CHANGED, CUSTOM_HOST_PREFIX, CampaignNotAvailableError, CampaignRuleConversionEventType, CampaignRuleRepository, Capabilities, ClientError, ConfigRepository, ConflictError, CustomerJourneyRepository, DEVELOPMENT, DEVICE_API_TIMEOUT_LIMIT, DEVICE_ID_NOT_SET, DEVICE_ID_REQUIRED, DeviceIDRequiredError, DeviceRepository, EXTENDED_CLIENT_INFO_DELIMITER, EXTENDED_CLIENT_INFO_PREFIX, EXTENDED_PLATFORM, EXTENDED_PLATFORM_VERSION, EXTERNAL_ID_REQUIRED, EntitlementRepository, EntitlementUtils, ExternalIDRequiredError, FLOW_SCREENS_NOT_AVAILABLE, FlowScreensNotAvailableError, HTML_REGEX, INITIAL_APP_CONFIG, INITIAL_CAMPAIGN_RULES, INITIAL_PAYWALLS, INITIAL_PRODUCTS, INITIAL_SESSION_COUNTER_VALUE, INITIAL_SUCCESS, InternalServerError, KEY_SESSION_COUNTER, LIQUID_VARIABLE_REGEX, LOCAL_NAMI_ENTITLEMENTS, LOG_HTTP_REQUESTS, LOG_HTTP_TRAFFIC, LaunchCampaignError, LaunchContextResolver, LogLevel, NAMI_CONFIGURATION, NAMI_CUSTOMER_JOURNEY_STATE, NAMI_LANGUAGE_CODE, NAMI_LAST_IMPRESSION_ID, NAMI_LAUNCH_ID, NAMI_PROFILE, NAMI_PURCHASE_CHANNEL, NAMI_PURCHASE_IMPRESSION_ID, NAMI_SDK_PACKAGE_VERSION, NAMI_SDK_VERSION, NAMI_SESSION_ID, NAMI_STORAGE_KEYS, Nami, NamiAPI, NamiAnimationType, NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager, NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager, NamiFlowStepType, NamiPaywallAction, NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiProfileManager, NamiPurchaseManager, NamiRefs, NamiReservedActions, NotFoundError, PAYWALL_ACTION_EVENT, PLATFORM_ID_REQUIRED, PRODUCTION, PaywallManagerEvents, PaywallRepository, PaywallState, PlacementLabelResolver, PlatformIDRequiredError, ProductRepository, RECONFIG_SUCCESS, RetryLimitExceededError, SDKNotInitializedError, SDK_NOT_INITIALIZED, SERVER_NAMI_ENTITLEMENTS, SESSION_REQUIRED, SHOULD_SHOW_LOADING_INDICATOR, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, activateEntitlementByPurchase, activeEntitlements, aggregateScreenreaderText, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getPaywall, getPaywallDataFromLabel, getPercentagePriceDifference, getPeriodNumberInDays, getPeriodNumberInWeeks, getPlatformAdapters, getPriceDifference, getPricePerMonth, getProductDetail, getPurchaseAdapter, getReferenceSku, getSkuProductDetailKeys, getSkuSmartTextValue, getSlideSmartTextValue, getStandardBillingPeriod, getTranslate, getUrlParams, handleErrors, hasAllPaywalls, hasCapability, hasPurchaseManagement, initialState, invokeHandler, isAnonymousMode, isInitialConfigCompressed, isNamiFlowCampaign, isSubscription, isValidISODate, isValidUrl, logger, mapAnonymousCampaigns, namiBuySKU, normalizeLaunchContext, parseToSemver, postConversion, productDetail, registerPlatformAdapters, registerPurchaseAdapter, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateMinSDKVersion };
2989
3065
  export type { AlignmentType, AmazonProduct, ApiResponse, AppleProduct, AvailableCampaignsResponseHandler, BorderLocationType, BorderSideType, Callback$1 as Callback, CloseHandler, CustomerJourneyState, DeepLinkUrlHandler, Device, DevicePayload, DeviceProfile, DirectionType, ExtendedPlatformInfo, FlexDirectionObject, FlowNavigationOptions, FontCollection, FontDetails, FormFactor, GoogleProduct, IConfig, IDeviceAdapter, IEntitlements$1 as IEntitlements, IPaywall, IPlatformAdapters, IProductsWithComponents, IPurchaseAdapter, ISkuMenu, IStorageAdapter, IUIAdapter, Impression, InitialConfig, InitialConfigCompressed, InitiateStateGroup, LoginResponse, NamiAnimation, NamiAnimationObjectSpec, NamiAnimationSpec, NamiAnonymousCampaign, NamiAppSuppliedVideoDetails, NamiCampaign, NamiCampaignSegment, NamiConfiguration, NamiConfigurationState, NamiEntitlement$1 as NamiEntitlement, NamiFlowAction, NamiFlowAnimation, NamiFlowCampaign, NamiFlowDTO, NamiFlowEventHandler, NamiFlowHandoffStepHandler, NamiFlowObjectDTO, NamiFlowOn, NamiFlowStep, NamiFlowTransition, NamiFlowTransitionDirection, NamiFlowWithObject, NamiInitialConfig, NamiLanguageCodes, NamiLogLevel, NamiPaywallActionHandler, NamiPaywallComponentChange, NamiPaywallEvent, NamiPaywallEventVideoMetadata, NamiPaywallLaunchContext, NamiPresentationStyle, NamiProductDetails, NamiProductOffer, NamiProfile, NamiPurchase, NamiPurchaseCompleteResult, NamiPurchaseDetails, NamiPurchasesState, NamiSKU, NamiSKUType, NamiSubscriptionInterval, NamiSubscriptionPeriod, None, NoneSpec, PaywallActionEvent, PaywallHandle, PaywallResultHandler, PaywallSKU, PricingPhase, ProductGroup, Pulse, PulseSpec, PurchaseContext, PurchaseResult, PurchaseValidationRequest, SKU, SKUActionHandler, ScreenInfo, Session, TBaseComponent, TButtonContainer, TCarouselContainer, TCarouselSlide, TCarouselSlidesState, TCollapseContainer, TComponent, TConditionalAttributes, TConditionalComponent, TContainer, TContainerPosition, TCountdownTimerTextComponent, TDevice, TDisabledButton, TField, TFieldSettings, TFlexProductContainer, THeaderFooter, TImageComponent, TInitialState, TMediaTypes, TOffer, TPages, TPaywallContext, TPaywallLaunchContext, TPaywallMedia, TPaywallTemplate, TPlayPauseButton, TProductContainer, TProductGroup, TProgressBarComponent, TProgressIndicatorComponent, TQRCodeComponent, TRadioButton, TRepeatingGrid, TResponsiveGrid, TSegmentPicker, TSegmentPickerItem, TSemverObj, TSpacerComponent, TStack, TSvgImageComponent, TSymbolComponent, TTestObject, TTextComponent, TTextLikeComponent, TTextListComponent, TToggleButtonComponent, TToggleSwitch, TVariablePattern, TVideoComponent, TVolumeButton, TimerState, TransactionRequest, UserAction, UserActionParameters, Wave, WaveSpec };
package/dist/index.mjs CHANGED
@@ -96,7 +96,7 @@ const {
96
96
  // version — stamped by scripts/version.sh
97
97
  NAMI_SDK_VERSION = "3.4.0",
98
98
  // full package version including dev suffix — stamped by scripts/version.sh
99
- NAMI_SDK_PACKAGE_VERSION = "3.4.0-dev.202605141520",
99
+ NAMI_SDK_PACKAGE_VERSION = "3.4.0-dev.202605180118",
100
100
  // environments
101
101
  PRODUCTION = "production", DEVELOPMENT = "development",
102
102
  // error messages
@@ -63911,6 +63911,230 @@ const convertLocale = (locale) => {
63911
63911
  return intlLocale.language + (intlLocale.region ?? intlLocale.script ?? '');
63912
63912
  };
63913
63913
 
63914
+ /**
63915
+ * Components that are skipped entirely (they and their subtree do not
63916
+ * contribute text to the page-level announcement).
63917
+ *
63918
+ * - Buttons / interactive controls: their label is appended as the
63919
+ * focused-button suffix, not collected mid-tree. Recursing into them
63920
+ * would double-count the label and pull in non-spoken decorative text.
63921
+ * - Image-like leaves: explicitly skipped per spec.
63922
+ * - Form pickers + QR codes + raw video: not informational text.
63923
+ */
63924
+ const SKIP_COMPONENTS = new Set([
63925
+ 'button',
63926
+ 'playPauseButton',
63927
+ 'volumeButton',
63928
+ 'toggleButton',
63929
+ 'toggleSwitch',
63930
+ 'radio',
63931
+ 'image',
63932
+ 'svgImage',
63933
+ 'symbol',
63934
+ 'qrCode',
63935
+ 'videoUrl',
63936
+ 'segmentPicker',
63937
+ 'segmentPickerItem',
63938
+ ]);
63939
+ /** Component values in {@link SKIP_COMPONENTS} that are interactive controls
63940
+ * (as opposed to images / media / non-text leaves). When a text component
63941
+ * shares a container with one of these, the text is treated as that
63942
+ * button's accessible label and excluded from the page composite. */
63943
+ const BUTTON_COMPONENTS = new Set([
63944
+ 'button',
63945
+ 'playPauseButton',
63946
+ 'volumeButton',
63947
+ 'toggleButton',
63948
+ 'toggleSwitch',
63949
+ 'radio',
63950
+ ]);
63951
+ /** `namiComponentType` values that are always included in the page
63952
+ * composite — even when their structural position (e.g. a sibling of a
63953
+ * utility button) would otherwise mark them for exclusion. Driven by the
63954
+ * spec's schema table:
63955
+ *
63956
+ * | `namiComponentType: "legalText"` | Legal/T&C content (included in tree-walk) |
63957
+ */
63958
+ const ALWAYS_INCLUDE_NAMI_TYPES = new Set(['legalText']);
63959
+ /**
63960
+ * Whether a text leaf inside `parent` should be skipped because it acts as
63961
+ * the accessible label for a sibling button.
63962
+ *
63963
+ * Schema pattern this catches: a container whose direct children include
63964
+ * BOTH a text/text-list AND an interactive button (e.g. Page 3's "Login
63965
+ * Group" — `[ text "Already an NFL+ subscriber?", loginButton "Sign In" ]`).
63966
+ * The text describes the button and is announced when the button is
63967
+ * focused, NOT as page-level content. Without this rule, the composite
63968
+ * leaks "Already an NFL+ subscriber? Subscribe to NFL plus button" instead
63969
+ * of "Subscribe to NFL plus button".
63970
+ *
63971
+ * Texts marked `namiComponentType: "legalText"` are exempt — the spec
63972
+ * explicitly opts legal copy in regardless of its container's other
63973
+ * children (e.g. Page 5's "Bottom Wrapper" mixes a body text, a restore
63974
+ * button, and the legal text — the legal text must still be spoken).
63975
+ */
63976
+ function isButtonSiblingLabel(node, parent) {
63977
+ if (!parent || !Array.isArray(parent.components))
63978
+ return false;
63979
+ if (typeof node.namiComponentType === 'string' &&
63980
+ ALWAYS_INCLUDE_NAMI_TYPES.has(node.namiComponentType)) {
63981
+ return false;
63982
+ }
63983
+ for (const sibling of parent.components) {
63984
+ if (sibling && typeof sibling === 'object') {
63985
+ const s = sibling;
63986
+ if (typeof s.component === 'string' && BUTTON_COMPONENTS.has(s.component)) {
63987
+ return true;
63988
+ }
63989
+ }
63990
+ }
63991
+ return false;
63992
+ }
63993
+ /**
63994
+ * Recursively collect spoken text from a component subtree following the
63995
+ * TV Full-Page Announcement contract.
63996
+ *
63997
+ * Rules (in order):
63998
+ * 1. `null` / `undefined` → contribute nothing.
63999
+ * 2. `hidden: true` → skip the node AND its subtree.
64000
+ * 3. `component` in {@link SKIP_COMPONENTS} → skip the node AND its subtree.
64001
+ * 4. `screenreaderText` set on this container → take that string verbatim
64002
+ * and do NOT recurse (server-supplied override).
64003
+ * 5. `component: "text"` or `"text-list"` → collect, UNLESS the text is a
64004
+ * sibling label for an adjacent button (see
64005
+ * {@link isButtonSiblingLabel}). `namiComponentType: "legalText"` is
64006
+ * exempted from the sibling-label exclusion.
64007
+ * 6. Otherwise → recurse into `components`.
64008
+ */
64009
+ function collectText(node, parent = null) {
64010
+ if (!node || typeof node !== 'object')
64011
+ return [];
64012
+ const n = node;
64013
+ if (n.hidden === true)
64014
+ return [];
64015
+ if (typeof n.component === 'string' && SKIP_COMPONENTS.has(n.component))
64016
+ return [];
64017
+ // Container-level override: when an enclosing container pre-supplies its own
64018
+ // screenreader text, take it verbatim and stop descending — the customer
64019
+ // has authored a polished announcement for this subtree.
64020
+ if (typeof n.screenreaderText === 'string' && n.screenreaderText.trim().length > 0) {
64021
+ return [n.screenreaderText.trim()];
64022
+ }
64023
+ if (n.component === 'text') {
64024
+ if (isButtonSiblingLabel(n, parent))
64025
+ return [];
64026
+ return typeof n.text === 'string' && n.text.length > 0 ? [n.text] : [];
64027
+ }
64028
+ if (n.component === 'text-list') {
64029
+ if (isButtonSiblingLabel(n, parent))
64030
+ return [];
64031
+ if (!Array.isArray(n.texts))
64032
+ return [];
64033
+ return n.texts.filter((t) => typeof t === 'string' && t.length > 0);
64034
+ }
64035
+ if (Array.isArray(n.components)) {
64036
+ const out = [];
64037
+ for (const child of n.components) {
64038
+ out.push(...collectText(child, n));
64039
+ }
64040
+ return out;
64041
+ }
64042
+ return [];
64043
+ }
64044
+ function collectHeaderFooter(slot) {
64045
+ if (!Array.isArray(slot))
64046
+ return [];
64047
+ // The slot itself acts as the synthetic parent so a top-level text inside
64048
+ // a header/footer that sits alongside a top-level button is treated as a
64049
+ // button label and excluded.
64050
+ const syntheticParent = { components: slot };
64051
+ const out = [];
64052
+ for (const node of slot) {
64053
+ out.push(...collectText(node, syntheticParent));
64054
+ }
64055
+ return out;
64056
+ }
64057
+ /**
64058
+ * Join an ordered list of collected text fragments into a single speakable
64059
+ * string. Each fragment is trimmed; empty fragments are dropped. Author
64060
+ * terminators (`.`, `!`, `?`) are preserved so emphasis carries through to
64061
+ * the screenreader; only fragments that lack a terminator get `". "`
64062
+ * inserted before the next fragment. The result is a string that reads
64063
+ * naturally without double-punctuation like `"...REACH!. Watch..."`.
64064
+ */
64065
+ function joinFragments(fragments) {
64066
+ let out = '';
64067
+ for (const raw of fragments) {
64068
+ const trimmed = raw.trim();
64069
+ if (!trimmed)
64070
+ continue;
64071
+ if (!out) {
64072
+ out = trimmed;
64073
+ continue;
64074
+ }
64075
+ const last = out[out.length - 1];
64076
+ const endsWithTerminator = last === '.' || last === '!' || last === '?';
64077
+ out += (endsWithTerminator ? ' ' : '. ') + trimmed;
64078
+ }
64079
+ return out;
64080
+ }
64081
+ /**
64082
+ * Build the full-screen announcement string for a paywall page per the
64083
+ * TV Full-Page Announcement contract
64084
+ * (https://linear.app/nami-product-development/document/tv-full-page-announcement-980f61b96e7c).
64085
+ *
64086
+ * Behaviour:
64087
+ * 1. **Override path.** If `page.screenreaderText` is set, that string is
64088
+ * returned verbatim (followed by `focusedButtonLabel` if provided and
64089
+ * not already present at the tail). Server-authored polished
64090
+ * announcements win over client-side aggregation.
64091
+ *
64092
+ * 2. **Tree-walk fallback.** Otherwise, walk the page's containers in
64093
+ * reading order — `header` → `backgroundContainer` → `contentContainer`
64094
+ * → `footer` — collecting visible `text` / `text-list` descendants in
64095
+ * source order. `image`, `hidden: true` subtrees, interactive controls
64096
+ * (buttons / toggles), and non-text leaves are skipped. The
64097
+ * `focusedButtonLabel` is appended as the final sentence.
64098
+ *
64099
+ * SDKs invoke this once per page on the first focus of the default CTA, and
64100
+ * speak the result via their platform TTS API. On subsequent focuses within
64101
+ * the same page, the SDK announces only the focused element's own label —
64102
+ * this helper is NOT re-called.
64103
+ *
64104
+ * @param page The paywall page to announce.
64105
+ * @param focusedButtonLabel The resolved label of the button that
64106
+ * triggered the announcement (typically the
64107
+ * page's default-focused CTA). For
64108
+ * subscription-plan CTAs this is the dynamic
64109
+ * `"{price} {period} subscription plan button"`
64110
+ * string the backend already resolved.
64111
+ * @returns A single string ready to pass to a platform TTS API. Empty when
64112
+ * the page has no spoken content and no focused button label.
64113
+ */
64114
+ function aggregateScreenreaderText(page, focusedButtonLabel = '') {
64115
+ const trimmedLabel = focusedButtonLabel.trim();
64116
+ // Override path
64117
+ if (typeof page.screenreaderText === 'string' && page.screenreaderText.trim().length > 0) {
64118
+ const override = page.screenreaderText.trim();
64119
+ if (!trimmedLabel)
64120
+ return override;
64121
+ // If the server-authored override already ends with the focused button
64122
+ // label, don't append it again.
64123
+ if (override.toLowerCase().endsWith(trimmedLabel.toLowerCase()))
64124
+ return override;
64125
+ return joinFragments([override, trimmedLabel]);
64126
+ }
64127
+ // Tree-walk path
64128
+ const fragments = [];
64129
+ fragments.push(...collectHeaderFooter(page.header));
64130
+ fragments.push(...collectText(page.backgroundContainer));
64131
+ fragments.push(...collectText(page.contentContainer));
64132
+ fragments.push(...collectHeaderFooter(page.footer));
64133
+ if (trimmedLabel)
64134
+ fragments.push(trimmedLabel);
64135
+ return joinFragments(fragments);
64136
+ }
64137
+
63914
64138
  function namiBuySKU(skuRefId) {
63915
64139
  if (!hasPurchaseManagement("NamiPurchaseManager.buySKU")) {
63916
64140
  return;
@@ -63925,4 +64149,4 @@ function namiBuySKU(skuRefId) {
63925
64149
  return result;
63926
64150
  }
63927
64151
 
63928
- export { ALREADY_CONFIGURED, ANONYMOUS_MODE, ANONYMOUS_MODE_ALREADY_OFF, ANONYMOUS_MODE_ALREADY_ON, ANONYMOUS_MODE_LOGIN_NOT_ALLOWED, ANONYMOUS_UUID, APIError, API_ACTIVE_ENTITLEMENTS, API_CAMPAIGN_RULES, API_CAMPAIGN_SESSION_TIMESTAMP, API_CONFIG, API_MAX_CALLS_LIMIT, API_PAYWALLS, API_PRODUCTS, API_RETRY_DELAY_SEC, API_TIMEOUT_LIMIT, API_VERSION, AUTH_DEVICE, AVAILABLE_ACTIVE_ENTITLEMENTS_CHANGED, AVAILABLE_CAMPAIGNS_CHANGED, AccountStateAction, AnonymousCDPError, AnonymousLoginError, AnonymousModeAlreadyOffError, AnonymousModeAlreadyOnError, BASE_STAGING_URL, BASE_URL, BASE_URL_PATH, BadRequestError, BasicNamiFlow, BorderMap, BorderSideMap, CAMPAIGN_NOT_AVAILABLE, CUSTOMER_ATTRIBUTES_KEY_PREFIX, CUSTOMER_JOURNEY_STATE_CHANGED, CUSTOM_HOST_PREFIX, CampaignNotAvailableError, CampaignRuleConversionEventType, CampaignRuleRepository, Capabilities, ClientError, ConfigRepository, ConflictError, CustomerJourneyRepository, DEVELOPMENT, DEVICE_API_TIMEOUT_LIMIT, DEVICE_ID_NOT_SET, DEVICE_ID_REQUIRED, DeviceIDRequiredError, DeviceRepository, EXTENDED_CLIENT_INFO_DELIMITER, EXTENDED_CLIENT_INFO_PREFIX, EXTENDED_PLATFORM, EXTENDED_PLATFORM_VERSION, EXTERNAL_ID_REQUIRED, EntitlementRepository, EntitlementUtils, ExternalIDRequiredError, FLOW_SCREENS_NOT_AVAILABLE, FlowScreensNotAvailableError, HTML_REGEX, INITIAL_APP_CONFIG, INITIAL_CAMPAIGN_RULES, INITIAL_PAYWALLS, INITIAL_PRODUCTS, INITIAL_SESSION_COUNTER_VALUE, INITIAL_SUCCESS, InternalServerError, KEY_SESSION_COUNTER, LIQUID_VARIABLE_REGEX, LOCAL_NAMI_ENTITLEMENTS, LOG_HTTP_REQUESTS, LOG_HTTP_TRAFFIC, LaunchCampaignError, LaunchContextResolver, LogLevel, NAMI_CONFIGURATION, NAMI_CUSTOMER_JOURNEY_STATE, NAMI_LANGUAGE_CODE, NAMI_LAST_IMPRESSION_ID, NAMI_LAUNCH_ID, NAMI_PROFILE, NAMI_PURCHASE_CHANNEL, NAMI_PURCHASE_IMPRESSION_ID, NAMI_SDK_PACKAGE_VERSION, NAMI_SDK_VERSION, NAMI_SESSION_ID, NAMI_STORAGE_KEYS, Nami, NamiAPI, NamiAnimationType, NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager, NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager, NamiFlowStepType, NamiPaywallAction, NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiProfileManager, NamiPurchaseManager, NamiRefs, NamiReservedActions, NotFoundError, PAYWALL_ACTION_EVENT, PLATFORM_ID_REQUIRED, PRODUCTION, PaywallManagerEvents, PaywallRepository, PaywallState, PlacementLabelResolver, PlatformIDRequiredError, ProductRepository, RECONFIG_SUCCESS, RetryLimitExceededError, SDKNotInitializedError, SDK_NOT_INITIALIZED, SERVER_NAMI_ENTITLEMENTS, SESSION_REQUIRED, SHOULD_SHOW_LOADING_INDICATOR, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, activateEntitlementByPurchase, activeEntitlements, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getPaywall, getPaywallDataFromLabel, getPercentagePriceDifference, getPeriodNumberInDays, getPeriodNumberInWeeks, getPlatformAdapters, getPriceDifference, getPricePerMonth, getProductDetail, getPurchaseAdapter, getReferenceSku, getSkuProductDetailKeys, getSkuSmartTextValue, getSlideSmartTextValue, getStandardBillingPeriod, getTranslate, getUrlParams, handleErrors, hasAllPaywalls, hasCapability, hasPurchaseManagement, initialState, invokeHandler, isAnonymousMode, isInitialConfigCompressed, isNamiFlowCampaign, isSubscription, isValidISODate, isValidUrl, logger, mapAnonymousCampaigns, namiBuySKU, normalizeLaunchContext, parseToSemver, postConversion, productDetail, registerPlatformAdapters, registerPurchaseAdapter, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateMinSDKVersion };
64152
+ export { ALREADY_CONFIGURED, ANONYMOUS_MODE, ANONYMOUS_MODE_ALREADY_OFF, ANONYMOUS_MODE_ALREADY_ON, ANONYMOUS_MODE_LOGIN_NOT_ALLOWED, ANONYMOUS_UUID, APIError, API_ACTIVE_ENTITLEMENTS, API_CAMPAIGN_RULES, API_CAMPAIGN_SESSION_TIMESTAMP, API_CONFIG, API_MAX_CALLS_LIMIT, API_PAYWALLS, API_PRODUCTS, API_RETRY_DELAY_SEC, API_TIMEOUT_LIMIT, API_VERSION, AUTH_DEVICE, AVAILABLE_ACTIVE_ENTITLEMENTS_CHANGED, AVAILABLE_CAMPAIGNS_CHANGED, AccountStateAction, AnonymousCDPError, AnonymousLoginError, AnonymousModeAlreadyOffError, AnonymousModeAlreadyOnError, BASE_STAGING_URL, BASE_URL, BASE_URL_PATH, BadRequestError, BasicNamiFlow, BorderMap, BorderSideMap, CAMPAIGN_NOT_AVAILABLE, CUSTOMER_ATTRIBUTES_KEY_PREFIX, CUSTOMER_JOURNEY_STATE_CHANGED, CUSTOM_HOST_PREFIX, CampaignNotAvailableError, CampaignRuleConversionEventType, CampaignRuleRepository, Capabilities, ClientError, ConfigRepository, ConflictError, CustomerJourneyRepository, DEVELOPMENT, DEVICE_API_TIMEOUT_LIMIT, DEVICE_ID_NOT_SET, DEVICE_ID_REQUIRED, DeviceIDRequiredError, DeviceRepository, EXTENDED_CLIENT_INFO_DELIMITER, EXTENDED_CLIENT_INFO_PREFIX, EXTENDED_PLATFORM, EXTENDED_PLATFORM_VERSION, EXTERNAL_ID_REQUIRED, EntitlementRepository, EntitlementUtils, ExternalIDRequiredError, FLOW_SCREENS_NOT_AVAILABLE, FlowScreensNotAvailableError, HTML_REGEX, INITIAL_APP_CONFIG, INITIAL_CAMPAIGN_RULES, INITIAL_PAYWALLS, INITIAL_PRODUCTS, INITIAL_SESSION_COUNTER_VALUE, INITIAL_SUCCESS, InternalServerError, KEY_SESSION_COUNTER, LIQUID_VARIABLE_REGEX, LOCAL_NAMI_ENTITLEMENTS, LOG_HTTP_REQUESTS, LOG_HTTP_TRAFFIC, LaunchCampaignError, LaunchContextResolver, LogLevel, NAMI_CONFIGURATION, NAMI_CUSTOMER_JOURNEY_STATE, NAMI_LANGUAGE_CODE, NAMI_LAST_IMPRESSION_ID, NAMI_LAUNCH_ID, NAMI_PROFILE, NAMI_PURCHASE_CHANNEL, NAMI_PURCHASE_IMPRESSION_ID, NAMI_SDK_PACKAGE_VERSION, NAMI_SDK_VERSION, NAMI_SESSION_ID, NAMI_STORAGE_KEYS, Nami, NamiAPI, NamiAnimationType, NamiCampaignManager, NamiCampaignRuleType, NamiConditionEvaluator, NamiCustomerManager, NamiEntitlementManager, NamiEventEmitter, NamiFlow, NamiFlowActionFunction, NamiFlowManager, NamiFlowStepType, NamiPaywallAction, NamiPaywallManager, PaywallManagerEvents as NamiPaywallManagerEvents, NamiProfileManager, NamiPurchaseManager, NamiRefs, NamiReservedActions, NotFoundError, PAYWALL_ACTION_EVENT, PLATFORM_ID_REQUIRED, PRODUCTION, PaywallManagerEvents, PaywallRepository, PaywallState, PlacementLabelResolver, PlatformIDRequiredError, ProductRepository, RECONFIG_SUCCESS, RetryLimitExceededError, SDKNotInitializedError, SDK_NOT_INITIALIZED, SERVER_NAMI_ENTITLEMENTS, SESSION_REQUIRED, SHOULD_SHOW_LOADING_INDICATOR, SKU_TEXT_REGEX, SMART_TEXT_PATTERN, STATUS_BAD_REQUEST, STATUS_CONFLICT, STATUS_INTERNAL_SERVER_ERROR, STATUS_NOT_FOUND, STATUS_SUCCESS, SessionService, SimpleEventTarget, StorageService, UNABLE_TO_UPDATE_CDP_ID, USE_STAGING_API, VALIDATE_PRODUCT_GROUPS, VAR_REGEX, activateEntitlementByPurchase, activeEntitlements, aggregateScreenreaderText, allCampaigns, allPaywalls, applyEntitlementActivation, audienceSplitPosition, bestUrlCampaignMatch, bigintToUuid, checkAnySkuHasPromoOffer, checkAnySkuHasTrialOffer, convertISO8601PeriodToText, convertLocale, convertOfferToPricingPhase, createNamiEntitlements, currentSku, empty, extractStandardPricingPhases, formatDate, formattedPrice, generateUUID, getApiCampaigns, getApiPaywalls, getBaseUrl, getBillingPeriodNumber, getCurrencyFormat, getDeviceData, getDeviceFormFactor, getDeviceScaleFactor, getEffectiveWebStyle, getEntitlementRefIdsForSku, getExtendedClientInfo, getFreeTrialPeriod, getInitialCampaigns, getInitialPaywalls, getPaywall, getPaywallDataFromLabel, getPercentagePriceDifference, getPeriodNumberInDays, getPeriodNumberInWeeks, getPlatformAdapters, getPriceDifference, getPricePerMonth, getProductDetail, getPurchaseAdapter, getReferenceSku, getSkuProductDetailKeys, getSkuSmartTextValue, getSlideSmartTextValue, getStandardBillingPeriod, getTranslate, getUrlParams, handleErrors, hasAllPaywalls, hasCapability, hasPurchaseManagement, initialState, invokeHandler, isAnonymousMode, isInitialConfigCompressed, isNamiFlowCampaign, isSubscription, isValidISODate, isValidUrl, logger, mapAnonymousCampaigns, namiBuySKU, normalizeLaunchContext, parseToSemver, postConversion, productDetail, registerPlatformAdapters, registerPurchaseAdapter, selectSegment, setActiveNamiEntitlements, shouldValidateProductGroups, skuItems, skuMapFromEntitlements, storageService, toDouble, toNamiEntitlements, toNamiSKU, tryParseB64Gzip, tryParseJson, updateRelatedSKUsForNamiEntitlement, uuidFromSplitPosition, validateMinSDKVersion };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@namiml/sdk-core",
3
- "version": "3.4.0-dev.202605141520",
3
+ "version": "3.4.0-dev.202605180118",
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",