@namiml/sdk-core 3.4.12-dev.202609300314 → 3.4.12-dev.202609302217

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.202609300314",
109
+ NAMI_SDK_PACKAGE_VERSION: exports.NAMI_SDK_PACKAGE_VERSION = "3.4.12-dev.202609302217",
110
110
  // environments
111
111
  PRODUCTION: exports.PRODUCTION = "production", DEVELOPMENT: exports.DEVELOPMENT = "development",
112
112
  // error messages
@@ -61301,6 +61301,168 @@ class SimpleEventTarget {
61301
61301
  }
61302
61302
  }
61303
61303
 
61304
+ /**
61305
+ * `${system.time.*}` Page conditions — the reference implementation of the cross-SDK contract in
61306
+ * `shared/time_conditions/README.md`, verified against `shared/time_conditions/vectors.json`.
61307
+ *
61308
+ * Every token resolves to the per-presentation snapshot instant; the zone lives on `expected`
61309
+ * (`Sunday@America/New_York`), so the instant is projected into that zone at comparison time and
61310
+ * the device zone and locale never matter. Invalid input fails closed: the assertion is `false`
61311
+ * for every operator, `notEquals` included.
61312
+ */
61313
+ const DAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
61314
+ const TIME_OF_DAY = /^([01]?\d|2[0-3]):([0-5]\d)$/;
61315
+ const INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/;
61316
+ const ORDERING = {
61317
+ equals: (a, b) => a === b,
61318
+ gt: (a, b) => a > b,
61319
+ gte: (a, b) => a >= b,
61320
+ lt: (a, b) => a < b,
61321
+ lte: (a, b) => a <= b,
61322
+ };
61323
+ const isOrderingOperator = (operator) => Object.prototype.hasOwnProperty.call(ORDERING, operator);
61324
+ /** True when `value` is a `${system.time.<leaf>}` token (any leaf; unknown leaves evaluate `false`). */
61325
+ const isSystemTimeAssertion = (assertion) => typeof assertion.value === "string" &&
61326
+ assertion.value.startsWith(exports.SYSTEM_TIME_TOKEN_PREFIX) &&
61327
+ assertion.value.endsWith("}");
61328
+ /** An ISO-8601 instant with a required offset, or `null`. */
61329
+ const parseInstant = (value) => {
61330
+ if (typeof value !== "string" || !INSTANT.test(value))
61331
+ return null;
61332
+ const date = new Date(value);
61333
+ return Number.isNaN(date.getTime()) ? null : date;
61334
+ };
61335
+ const isValidZone = (zone) => {
61336
+ if (!zone)
61337
+ return false;
61338
+ try {
61339
+ new Intl.DateTimeFormat("en-US", { timeZone: zone });
61340
+ return true;
61341
+ }
61342
+ catch {
61343
+ return false;
61344
+ }
61345
+ };
61346
+ /** Splits `<value>@<zone>` into its parts when there is exactly one `@` and the zone resolves. */
61347
+ const splitZoned = (expected) => {
61348
+ if (typeof expected !== "string")
61349
+ return null;
61350
+ const parts = expected.split("@");
61351
+ if (parts.length !== 2 || !parts[0] || !isValidZone(parts[1]))
61352
+ return null;
61353
+ return { value: parts[0], zone: parts[1] };
61354
+ };
61355
+ /** The snapshot seen in `zone`: English day name and minutes since local midnight. */
61356
+ const projectInZone = (now, zone) => {
61357
+ try {
61358
+ const [dayOfWeek, time] = formatInTimeZone(now, zone, "EEEE|HH:mm", { locale: enUS }).split("|");
61359
+ const [hours, minutes] = time.split(":").map(Number);
61360
+ return { dayOfWeek, minutes: hours * 60 + minutes };
61361
+ }
61362
+ catch {
61363
+ // Runtimes without Intl time-zone support (e.g. Hermes) fail closed.
61364
+ return null;
61365
+ }
61366
+ };
61367
+ const parseDay = (expected) => {
61368
+ const zoned = splitZoned(expected);
61369
+ return zoned && DAYS.includes(zoned.value) ? { day: zoned.value, zone: zoned.zone } : null;
61370
+ };
61371
+ const evaluateDayOfWeek = (operator, expected, now) => {
61372
+ const isDay = ({ day, zone }) => projectInZone(now, zone)?.dayOfWeek === day;
61373
+ if (operator === "in") {
61374
+ if (!Array.isArray(expected) || expected.length === 0)
61375
+ return false;
61376
+ const days = expected.map(parseDay);
61377
+ // Any invalid element invalidates the whole assertion, even when another element matches.
61378
+ if (days.some((day) => day === null))
61379
+ return false;
61380
+ return days.some((day) => isDay(day));
61381
+ }
61382
+ if (operator !== "equals" && operator !== "notEquals")
61383
+ return false;
61384
+ const day = parseDay(expected);
61385
+ if (!day)
61386
+ return false;
61387
+ const projected = projectInZone(now, day.zone);
61388
+ if (!projected)
61389
+ return false;
61390
+ return operator === "equals" ? projected.dayOfWeek === day.day : projected.dayOfWeek !== day.day;
61391
+ };
61392
+ const evaluateTimeOfDay = (operator, expected, now) => {
61393
+ if (!isOrderingOperator(operator))
61394
+ return false;
61395
+ const zoned = splitZoned(expected);
61396
+ const match = zoned && TIME_OF_DAY.exec(zoned.value);
61397
+ if (!zoned || !match)
61398
+ return false;
61399
+ const projected = projectInZone(now, zoned.zone);
61400
+ if (!projected)
61401
+ return false;
61402
+ return ORDERING[operator](projected.minutes, Number(match[1]) * 60 + Number(match[2]));
61403
+ };
61404
+ const evaluateNow = (operator, expected, now) => {
61405
+ if (!isOrderingOperator(operator))
61406
+ return false;
61407
+ const instant = parseInstant(expected);
61408
+ return instant !== null && ORDERING[operator](now.getTime(), instant.getTime());
61409
+ };
61410
+ /**
61411
+ * Evaluates a `${system.time.*}` assertion against the snapshot instant `now`.
61412
+ * Returns `null` when the assertion is not a time assertion, so callers fall through to their
61413
+ * existing evaluator; otherwise `true`/`false`, with every invalid input `false`.
61414
+ */
61415
+ const evaluateSystemTimeAssertion = (assertion, now) => {
61416
+ if (!isSystemTimeAssertion(assertion))
61417
+ return null;
61418
+ if (Number.isNaN(now.getTime()))
61419
+ return false;
61420
+ const leaf = assertion.value.slice(exports.SYSTEM_TIME_TOKEN_PREFIX.length, -1);
61421
+ switch (leaf) {
61422
+ case "dayOfWeek":
61423
+ return evaluateDayOfWeek(assertion.operator, assertion.expected, now);
61424
+ case "timeOfDay":
61425
+ return evaluateTimeOfDay(assertion.operator, assertion.expected, now);
61426
+ case "now":
61427
+ return evaluateNow(assertion.operator, assertion.expected, now);
61428
+ default:
61429
+ return false;
61430
+ }
61431
+ };
61432
+ /** Parses a `timeOverride=<ISO-8601 with offset>` namiCommand; `null` when it is not a valid one. */
61433
+ const parseTimeOverride = (command) => command.startsWith(exports.TIME_OVERRIDE_PREFIX) ? parseInstant(command.slice(exports.TIME_OVERRIDE_PREFIX.length)) : null;
61434
+ /**
61435
+ * The instant to snapshot for a Page or flow-screen presentation: the `timeOverride=` namiCommand
61436
+ * when present and valid, otherwise the device clock. Call it once per presentation.
61437
+ */
61438
+ const getSystemTimeNow = () => {
61439
+ const command = storageService
61440
+ .getNamiConfig()
61441
+ ?.namiCommands?.find((cmd) => cmd.startsWith(exports.TIME_OVERRIDE_PREFIX));
61442
+ if (command === undefined)
61443
+ return new Date();
61444
+ const override = parseTimeOverride(command);
61445
+ if (override === null) {
61446
+ logger.warn(`Ignoring invalid ${command}: expected ISO-8601 with an offset, e.g. ${exports.TIME_OVERRIDE_PREFIX}2026-09-13T13:00:00-04:00`);
61447
+ return new Date();
61448
+ }
61449
+ return override;
61450
+ };
61451
+ /**
61452
+ * Evaluates a Page assertion against a presentation's `PaywallState` snapshot
61453
+ * (`state.systemTimeSnapshot`, taken once in `PaywallState.setPaywall`). This is the one entry
61454
+ * point the core-based renderers (web, Expo/Vega) call ahead of their own operator switch.
61455
+ * Returns `null` for a non-time assertion, without touching the clock or the override; for a
61456
+ * state that was never presented it falls back to {@link getSystemTimeNow}.
61457
+ */
61458
+ const evaluateSystemTimeCondition = (assertion, state) => {
61459
+ if (!isSystemTimeAssertion(assertion))
61460
+ return null;
61461
+ const snapshot = state?.systemTimeSnapshot;
61462
+ const now = typeof snapshot === "number" && Number.isFinite(snapshot) ? new Date(snapshot) : getSystemTimeNow();
61463
+ return evaluateSystemTimeAssertion(assertion, now);
61464
+ };
61465
+
61304
61466
  let productDetails = [];
61305
61467
  function createEventWithDetail(name, payload) {
61306
61468
  if (typeof Event === 'function') {
@@ -61607,6 +61769,10 @@ class PaywallState extends SimpleEventTarget {
61607
61769
  // touching storage on the per-assertion render path. Assigned after the template's
61608
61770
  // initialState so an authored `platformId` cannot shadow the real one.
61609
61771
  state.platformId = storageService.getNamiConfig()?.appPlatformID ?? '';
61772
+ // NAM-4637: one instant per Page / flow-screen presentation for every `${system.time.*}`
61773
+ // condition, captured with `launch` and never re-read while the page stays up. Also after
61774
+ // the initialState spread, so authored data cannot pin it; `timeOverride=` can.
61775
+ state.systemTimeSnapshot = getSystemTimeNow().getTime();
61610
61776
  const launch = normalizeLaunchContext(state.launch, context, paywall.sku_menus);
61611
61777
  this.filteredSkuMenus = paywall.sku_menus ?? [];
61612
61778
  if (launch.productGroups.length) {
@@ -66416,154 +66582,6 @@ function resolveLoopSource(loopSource) {
66416
66582
  return [];
66417
66583
  }
66418
66584
 
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
-
66567
66585
  /**
66568
66586
  * Components that are skipped entirely (they and their subtree do not
66569
66587
  * contribute text to the page-level announcement).
@@ -66927,6 +66945,7 @@ exports.currentSku = currentSku;
66927
66945
  exports.didReceiveResponse = didReceiveResponse;
66928
66946
  exports.empty = empty;
66929
66947
  exports.evaluateSystemTimeAssertion = evaluateSystemTimeAssertion;
66948
+ exports.evaluateSystemTimeCondition = evaluateSystemTimeCondition;
66930
66949
  exports.extractStandardPricingPhases = extractStandardPricingPhases;
66931
66950
  exports.formatDate = formatDate;
66932
66951
  exports.formattedPrice = formattedPrice;
package/dist/index.d.ts CHANGED
@@ -1845,6 +1845,11 @@ type TPaywallContext = TInitialState & {
1845
1845
  orientation: TDeviceOrientation;
1846
1846
  focusedState: boolean;
1847
1847
  platformId: string;
1848
+ /**
1849
+ * Epoch ms every `${system.time.*}` condition on this presentation evaluates against
1850
+ * (NAM-4637). Taken once in `PaywallState.setPaywall`; `timeOverride=` pins it.
1851
+ */
1852
+ systemTimeSnapshot?: number;
1848
1853
  mediaList: {
1849
1854
  [key: string]: any;
1850
1855
  };
@@ -3334,6 +3339,16 @@ declare const parseTimeOverride: (command: string) => Date | null;
3334
3339
  * when present and valid, otherwise the device clock. Call it once per presentation.
3335
3340
  */
3336
3341
  declare const getSystemTimeNow: () => Date;
3342
+ /**
3343
+ * Evaluates a Page assertion against a presentation's `PaywallState` snapshot
3344
+ * (`state.systemTimeSnapshot`, taken once in `PaywallState.setPaywall`). This is the one entry
3345
+ * point the core-based renderers (web, Expo/Vega) call ahead of their own operator switch.
3346
+ * Returns `null` for a non-time assertion, without touching the clock or the override; for a
3347
+ * state that was never presented it falls back to {@link getSystemTimeNow}.
3348
+ */
3349
+ declare const evaluateSystemTimeCondition: (assertion: TTestObject, state: {
3350
+ systemTimeSnapshot?: number;
3351
+ } | undefined) => boolean | null;
3337
3352
 
3338
3353
  declare const getUrlParams: () => Record<string, string>;
3339
3354
 
@@ -4383,5 +4398,5 @@ declare namespace internal {
4383
4398
  };
4384
4399
  }
4385
4400
 
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 };
4401
+ 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, evaluateSystemTimeCondition, 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 };
4387
4402
  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.202609300314",
107
+ NAMI_SDK_PACKAGE_VERSION = "3.4.12-dev.202609302217",
108
108
  // environments
109
109
  PRODUCTION = "production", DEVELOPMENT = "development",
110
110
  // error messages
@@ -61299,6 +61299,168 @@ class SimpleEventTarget {
61299
61299
  }
61300
61300
  }
61301
61301
 
61302
+ /**
61303
+ * `${system.time.*}` Page conditions — the reference implementation of the cross-SDK contract in
61304
+ * `shared/time_conditions/README.md`, verified against `shared/time_conditions/vectors.json`.
61305
+ *
61306
+ * Every token resolves to the per-presentation snapshot instant; the zone lives on `expected`
61307
+ * (`Sunday@America/New_York`), so the instant is projected into that zone at comparison time and
61308
+ * the device zone and locale never matter. Invalid input fails closed: the assertion is `false`
61309
+ * for every operator, `notEquals` included.
61310
+ */
61311
+ const DAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
61312
+ const TIME_OF_DAY = /^([01]?\d|2[0-3]):([0-5]\d)$/;
61313
+ const INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/;
61314
+ const ORDERING = {
61315
+ equals: (a, b) => a === b,
61316
+ gt: (a, b) => a > b,
61317
+ gte: (a, b) => a >= b,
61318
+ lt: (a, b) => a < b,
61319
+ lte: (a, b) => a <= b,
61320
+ };
61321
+ const isOrderingOperator = (operator) => Object.prototype.hasOwnProperty.call(ORDERING, operator);
61322
+ /** True when `value` is a `${system.time.<leaf>}` token (any leaf; unknown leaves evaluate `false`). */
61323
+ const isSystemTimeAssertion = (assertion) => typeof assertion.value === "string" &&
61324
+ assertion.value.startsWith(SYSTEM_TIME_TOKEN_PREFIX) &&
61325
+ assertion.value.endsWith("}");
61326
+ /** An ISO-8601 instant with a required offset, or `null`. */
61327
+ const parseInstant = (value) => {
61328
+ if (typeof value !== "string" || !INSTANT.test(value))
61329
+ return null;
61330
+ const date = new Date(value);
61331
+ return Number.isNaN(date.getTime()) ? null : date;
61332
+ };
61333
+ const isValidZone = (zone) => {
61334
+ if (!zone)
61335
+ return false;
61336
+ try {
61337
+ new Intl.DateTimeFormat("en-US", { timeZone: zone });
61338
+ return true;
61339
+ }
61340
+ catch {
61341
+ return false;
61342
+ }
61343
+ };
61344
+ /** Splits `<value>@<zone>` into its parts when there is exactly one `@` and the zone resolves. */
61345
+ const splitZoned = (expected) => {
61346
+ if (typeof expected !== "string")
61347
+ return null;
61348
+ const parts = expected.split("@");
61349
+ if (parts.length !== 2 || !parts[0] || !isValidZone(parts[1]))
61350
+ return null;
61351
+ return { value: parts[0], zone: parts[1] };
61352
+ };
61353
+ /** The snapshot seen in `zone`: English day name and minutes since local midnight. */
61354
+ const projectInZone = (now, zone) => {
61355
+ try {
61356
+ const [dayOfWeek, time] = formatInTimeZone(now, zone, "EEEE|HH:mm", { locale: enUS }).split("|");
61357
+ const [hours, minutes] = time.split(":").map(Number);
61358
+ return { dayOfWeek, minutes: hours * 60 + minutes };
61359
+ }
61360
+ catch {
61361
+ // Runtimes without Intl time-zone support (e.g. Hermes) fail closed.
61362
+ return null;
61363
+ }
61364
+ };
61365
+ const parseDay = (expected) => {
61366
+ const zoned = splitZoned(expected);
61367
+ return zoned && DAYS.includes(zoned.value) ? { day: zoned.value, zone: zoned.zone } : null;
61368
+ };
61369
+ const evaluateDayOfWeek = (operator, expected, now) => {
61370
+ const isDay = ({ day, zone }) => projectInZone(now, zone)?.dayOfWeek === day;
61371
+ if (operator === "in") {
61372
+ if (!Array.isArray(expected) || expected.length === 0)
61373
+ return false;
61374
+ const days = expected.map(parseDay);
61375
+ // Any invalid element invalidates the whole assertion, even when another element matches.
61376
+ if (days.some((day) => day === null))
61377
+ return false;
61378
+ return days.some((day) => isDay(day));
61379
+ }
61380
+ if (operator !== "equals" && operator !== "notEquals")
61381
+ return false;
61382
+ const day = parseDay(expected);
61383
+ if (!day)
61384
+ return false;
61385
+ const projected = projectInZone(now, day.zone);
61386
+ if (!projected)
61387
+ return false;
61388
+ return operator === "equals" ? projected.dayOfWeek === day.day : projected.dayOfWeek !== day.day;
61389
+ };
61390
+ const evaluateTimeOfDay = (operator, expected, now) => {
61391
+ if (!isOrderingOperator(operator))
61392
+ return false;
61393
+ const zoned = splitZoned(expected);
61394
+ const match = zoned && TIME_OF_DAY.exec(zoned.value);
61395
+ if (!zoned || !match)
61396
+ return false;
61397
+ const projected = projectInZone(now, zoned.zone);
61398
+ if (!projected)
61399
+ return false;
61400
+ return ORDERING[operator](projected.minutes, Number(match[1]) * 60 + Number(match[2]));
61401
+ };
61402
+ const evaluateNow = (operator, expected, now) => {
61403
+ if (!isOrderingOperator(operator))
61404
+ return false;
61405
+ const instant = parseInstant(expected);
61406
+ return instant !== null && ORDERING[operator](now.getTime(), instant.getTime());
61407
+ };
61408
+ /**
61409
+ * Evaluates a `${system.time.*}` assertion against the snapshot instant `now`.
61410
+ * Returns `null` when the assertion is not a time assertion, so callers fall through to their
61411
+ * existing evaluator; otherwise `true`/`false`, with every invalid input `false`.
61412
+ */
61413
+ const evaluateSystemTimeAssertion = (assertion, now) => {
61414
+ if (!isSystemTimeAssertion(assertion))
61415
+ return null;
61416
+ if (Number.isNaN(now.getTime()))
61417
+ return false;
61418
+ const leaf = assertion.value.slice(SYSTEM_TIME_TOKEN_PREFIX.length, -1);
61419
+ switch (leaf) {
61420
+ case "dayOfWeek":
61421
+ return evaluateDayOfWeek(assertion.operator, assertion.expected, now);
61422
+ case "timeOfDay":
61423
+ return evaluateTimeOfDay(assertion.operator, assertion.expected, now);
61424
+ case "now":
61425
+ return evaluateNow(assertion.operator, assertion.expected, now);
61426
+ default:
61427
+ return false;
61428
+ }
61429
+ };
61430
+ /** Parses a `timeOverride=<ISO-8601 with offset>` namiCommand; `null` when it is not a valid one. */
61431
+ const parseTimeOverride = (command) => command.startsWith(TIME_OVERRIDE_PREFIX) ? parseInstant(command.slice(TIME_OVERRIDE_PREFIX.length)) : null;
61432
+ /**
61433
+ * The instant to snapshot for a Page or flow-screen presentation: the `timeOverride=` namiCommand
61434
+ * when present and valid, otherwise the device clock. Call it once per presentation.
61435
+ */
61436
+ const getSystemTimeNow = () => {
61437
+ const command = storageService
61438
+ .getNamiConfig()
61439
+ ?.namiCommands?.find((cmd) => cmd.startsWith(TIME_OVERRIDE_PREFIX));
61440
+ if (command === undefined)
61441
+ return new Date();
61442
+ const override = parseTimeOverride(command);
61443
+ if (override === null) {
61444
+ logger.warn(`Ignoring invalid ${command}: expected ISO-8601 with an offset, e.g. ${TIME_OVERRIDE_PREFIX}2026-09-13T13:00:00-04:00`);
61445
+ return new Date();
61446
+ }
61447
+ return override;
61448
+ };
61449
+ /**
61450
+ * Evaluates a Page assertion against a presentation's `PaywallState` snapshot
61451
+ * (`state.systemTimeSnapshot`, taken once in `PaywallState.setPaywall`). This is the one entry
61452
+ * point the core-based renderers (web, Expo/Vega) call ahead of their own operator switch.
61453
+ * Returns `null` for a non-time assertion, without touching the clock or the override; for a
61454
+ * state that was never presented it falls back to {@link getSystemTimeNow}.
61455
+ */
61456
+ const evaluateSystemTimeCondition = (assertion, state) => {
61457
+ if (!isSystemTimeAssertion(assertion))
61458
+ return null;
61459
+ const snapshot = state?.systemTimeSnapshot;
61460
+ const now = typeof snapshot === "number" && Number.isFinite(snapshot) ? new Date(snapshot) : getSystemTimeNow();
61461
+ return evaluateSystemTimeAssertion(assertion, now);
61462
+ };
61463
+
61302
61464
  let productDetails = [];
61303
61465
  function createEventWithDetail(name, payload) {
61304
61466
  if (typeof Event === 'function') {
@@ -61605,6 +61767,10 @@ class PaywallState extends SimpleEventTarget {
61605
61767
  // touching storage on the per-assertion render path. Assigned after the template's
61606
61768
  // initialState so an authored `platformId` cannot shadow the real one.
61607
61769
  state.platformId = storageService.getNamiConfig()?.appPlatformID ?? '';
61770
+ // NAM-4637: one instant per Page / flow-screen presentation for every `${system.time.*}`
61771
+ // condition, captured with `launch` and never re-read while the page stays up. Also after
61772
+ // the initialState spread, so authored data cannot pin it; `timeOverride=` can.
61773
+ state.systemTimeSnapshot = getSystemTimeNow().getTime();
61608
61774
  const launch = normalizeLaunchContext(state.launch, context, paywall.sku_menus);
61609
61775
  this.filteredSkuMenus = paywall.sku_menus ?? [];
61610
61776
  if (launch.productGroups.length) {
@@ -66414,154 +66580,6 @@ function resolveLoopSource(loopSource) {
66414
66580
  return [];
66415
66581
  }
66416
66582
 
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
-
66565
66583
  /**
66566
66584
  * Components that are skipped entirely (they and their subtree do not
66567
66585
  * contribute text to the page-level announcement).
@@ -66847,4 +66865,4 @@ var internal = /*#__PURE__*/Object.freeze({
66847
66865
  NamiPurchaseManager: NamiPurchaseManager
66848
66866
  });
66849
66867
 
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 };
66868
+ 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, evaluateSystemTimeCondition, 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.202609300314",
3
+ "version": "3.4.12-dev.202609302217",
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",