@namiml/sdk-core 3.4.10-dev.202608230147 → 3.4.10-dev.202608230452

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.10",
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.10-dev.202608230147",
109
+ NAMI_SDK_PACKAGE_VERSION: exports.NAMI_SDK_PACKAGE_VERSION = "3.4.10-dev.202608230452",
110
110
  // environments
111
111
  PRODUCTION: exports.PRODUCTION = "production", DEVELOPMENT: exports.DEVELOPMENT = "development",
112
112
  // error messages
@@ -59057,6 +59057,7 @@ const getProductDetailHandlers = (product, referenceId, skus = []) => ({
59057
59057
  periodInMonthsAbbrev: () => getDurationInMonths(product, false, true, false, true),
59058
59058
  periodly: () => convertISO8601PeriodToPeriodly(getStandardBillingPeriod(product)),
59059
59059
  iso_subscription_duration: () => getISOSubscriptionDuration(product),
59060
+ bill_term: () => getBillTerm(product),
59060
59061
  invalid: () => false,
59061
59062
  unavailable: () => false,
59062
59063
  });
@@ -59216,6 +59217,21 @@ const getIntroductoryPhase = (product, referenceId) => {
59216
59217
  }
59217
59218
  return convertOfferToPricingPhase(offer, product.subscription_period);
59218
59219
  };
59220
+ // Analytics-stable bill-term names keyed by normalized ISO 8601 duration.
59221
+ // Deliberately NOT localized (unlike `periodly`) — these land in analytics
59222
+ // payloads via `{{ sku.bill_term }}`, where values must be stable per NAM-3429.
59223
+ const BILL_TERM_BY_ISO_DURATION = {
59224
+ P1D: "daily",
59225
+ P1W: "weekly",
59226
+ P1M: "monthly",
59227
+ P3M: "quarterly",
59228
+ P6M: "semiannual",
59229
+ P1Y: "annual",
59230
+ };
59231
+ const getBillTerm = (product) => {
59232
+ const iso = getISOSubscriptionDuration(product);
59233
+ return (iso && BILL_TERM_BY_ISO_DURATION[iso]) || null;
59234
+ };
59219
59235
  const getISOSubscriptionDuration = (product) => {
59220
59236
  // ["P1W", "P4W", "P1M", "P2M", "P3M", "P4M", "P6M", "P8M", "P1Y"]
59221
59237
  // P7D => P1W
@@ -62594,6 +62610,34 @@ class FlowLiquidResolver {
62594
62610
  const value = PaywallState.currentProvider?.state.formStates[fieldId];
62595
62611
  return formTagValue(value);
62596
62612
  }
62613
+ // Purchased-SKU context (NAM-3965): {{ sku.* }} resolves against the SKU
62614
+ // captured by the raise's `__buy_sku__` trigger — deliberately NOT the
62615
+ // selected SKU, so cart-add analytics report what was actually bought.
62616
+ // Unset until a buy action fires (token blanks, matching other platforms).
62617
+ if (key.startsWith("sku.")) {
62618
+ const purchased = flow?.purchasedSku;
62619
+ if (!purchased)
62620
+ return undefined;
62621
+ const leaf = key.substring("sku.".length);
62622
+ switch (leaf) {
62623
+ case "sku_ref_id":
62624
+ return purchased.skuRefId;
62625
+ case "name":
62626
+ // Control Center reference name; the store title stays reachable as
62627
+ // {{ sku.title }} via the product-detail table below.
62628
+ return purchased.name;
62629
+ // SKUs can grant several entitlements; the scalar contract takes the
62630
+ // first in payload order (NAM-3429).
62631
+ case "entitlement_ref_id":
62632
+ return purchased.entitlements[0]?.entitlement_ref_id ?? "";
62633
+ case "entitlement_name":
62634
+ return purchased.entitlements[0]?.name ?? "";
62635
+ default: {
62636
+ const value = getProductDetail(leaf, purchased.productDetails);
62637
+ return value != null ? String(value) : undefined;
62638
+ }
62639
+ }
62640
+ }
62597
62641
  switch (key) {
62598
62642
  case "campaignId":
62599
62643
  return screenState?.campaign?.rule;
@@ -64050,6 +64094,27 @@ function validateForm(validators, formStates) {
64050
64094
 
64051
64095
  /** Form field key convention for grouped handoffs: `<tag>::<payloadKey>`. */
64052
64096
  const HANDOFF_GROUP_KEY = /^([^:]+)::(.+)$/;
64097
+ /**
64098
+ * Web buttons hand the flow a public `NamiSKU` (`skuId`/`productDetails`) while
64099
+ * Expo hands the wire `PaywallSKU` (`sku_ref_id`/`product_details`); collapse
64100
+ * both onto one shape so `{{ sku.* }}` resolves identically on every platform.
64101
+ */
64102
+ function normalizePurchasedSku(sku) {
64103
+ if ('sku_ref_id' in sku) {
64104
+ return {
64105
+ skuRefId: sku.sku_ref_id,
64106
+ name: sku.name,
64107
+ entitlements: Array.isArray(sku.entitlements) ? sku.entitlements : [],
64108
+ productDetails: sku.product_details ?? null,
64109
+ };
64110
+ }
64111
+ return {
64112
+ skuRefId: sku.skuId,
64113
+ name: sku.name,
64114
+ entitlements: Array.isArray(sku.entitlements) ? sku.entitlements : [],
64115
+ productDetails: sku.productDetails ?? null,
64116
+ };
64117
+ }
64053
64118
  class BasicNamiFlow {
64054
64119
  constructor(flowObject = {}) {
64055
64120
  this.id = flowObject.id ?? '';
@@ -64418,7 +64483,19 @@ class NamiFlow extends BasicNamiFlow {
64418
64483
  ];
64419
64484
  return lifecycles.some(cycle => [cycle.before, cycle.action, cycle.after].some(actions => actions?.some(action => submitFns.includes(action.function))));
64420
64485
  }
64486
+ /**
64487
+ * Stash the SKU carried by a `__buy_sku__` trigger so `{{ sku.* }}` resolves
64488
+ * in this and later lifecycles of the raise. Runs before the lifecycle
64489
+ * lookup: a buy tap should update the context even when the current step
64490
+ * has no `__buy_sku__` lifecycles of its own.
64491
+ */
64492
+ captureBuySkuData(actionId, data) {
64493
+ if (actionId === NamiReservedActions.BUY_SKU && data?.sku) {
64494
+ this.purchasedSku = normalizePurchasedSku(data.sku);
64495
+ }
64496
+ }
64421
64497
  triggerActions(actionId, component, data) {
64498
+ this.captureBuySkuData(actionId, data);
64422
64499
  const lifecycles = this.lifecycles(actionId);
64423
64500
  if (!lifecycles)
64424
64501
  return;
@@ -64450,6 +64527,7 @@ class NamiFlow extends BasicNamiFlow {
64450
64527
  }
64451
64528
  }
64452
64529
  triggerBeforeActions(actionId, component, data) {
64530
+ this.captureBuySkuData(actionId, data);
64453
64531
  const lifecycles = this.lifecycles(actionId);
64454
64532
  if (!lifecycles)
64455
64533
  return;
@@ -64459,6 +64537,7 @@ class NamiFlow extends BasicNamiFlow {
64459
64537
  }
64460
64538
  }
64461
64539
  triggerAfterActions(actionId, component, data) {
64540
+ this.captureBuySkuData(actionId, data);
64462
64541
  const lifecycles = this.lifecycles(actionId);
64463
64542
  if (!lifecycles)
64464
64543
  return;
package/dist/index.d.ts CHANGED
@@ -2264,6 +2264,16 @@ declare class NamiFlowManager$2 {
2264
2264
  static getCurrentFlowStepAutoAdvanceDelay(): number | null;
2265
2265
  }
2266
2266
 
2267
+ /**
2268
+ * Normalized SKU captured when a `__buy_sku__` trigger hands the flow a SKU,
2269
+ * resolvable for the rest of the raise via `{{ sku.* }}` (NAM-3965).
2270
+ */
2271
+ interface PurchasedSkuContext {
2272
+ skuRefId?: string;
2273
+ name?: string;
2274
+ entitlements: IEntitlements$1[];
2275
+ productDetails?: NamiProductDetails | null;
2276
+ }
2267
2277
  declare class BasicNamiFlow implements NamiFlowObjectDTO {
2268
2278
  id: string;
2269
2279
  name: string;
@@ -2316,6 +2326,13 @@ declare class NamiFlow extends BasicNamiFlow {
2316
2326
  login?: Record<string, NamiHandoffAttributeValue>;
2317
2327
  permissions: Record<string, boolean>;
2318
2328
  };
2329
+ /**
2330
+ * SKU handed to the flow by the most recent `__buy_sku__` trigger, exposed
2331
+ * to flow-event Liquid as `{{ sku.* }}` (NAM-3965). Kept for the rest of the
2332
+ * raise so `__purchase_success__` lifecycles — which carry no data on any
2333
+ * platform — still resolve it; dies with the flow instance.
2334
+ */
2335
+ purchasedSku?: PurchasedSkuContext;
2319
2336
  /**
2320
2337
  * Per-raise flow-completion tracking (NAM-1545). Bound to the impression
2321
2338
  * of the current screen's raise; one completion result per impression
@@ -2387,6 +2404,13 @@ declare class NamiFlow extends BasicNamiFlow {
2387
2404
  * NamiFlowControl.isFormSubmitLifecycle / Android NamiFlow.isFormSubmitLifecycle.
2388
2405
  */
2389
2406
  isFormSubmitLifecycle(lifecycles: NamiFlowOn[]): boolean;
2407
+ /**
2408
+ * Stash the SKU carried by a `__buy_sku__` trigger so `{{ sku.* }}` resolves
2409
+ * in this and later lifecycles of the raise. Runs before the lifecycle
2410
+ * lookup: a buy tap should update the context even when the current step
2411
+ * has no `__buy_sku__` lifecycles of its own.
2412
+ */
2413
+ private captureBuySkuData;
2390
2414
  triggerActions(actionId: string, component?: any, data?: Record<string, any>): void;
2391
2415
  executeFullLifecycles(lifecycles: NamiFlowOn[], data?: Record<string, any>): void;
2392
2416
  triggerBeforeActions(actionId: string, component?: any, data?: Record<string, any>): void;
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.10",
106
106
  // full package version including dev suffix — stamped by scripts/version.sh
107
- NAMI_SDK_PACKAGE_VERSION = "3.4.10-dev.202608230147",
107
+ NAMI_SDK_PACKAGE_VERSION = "3.4.10-dev.202608230452",
108
108
  // environments
109
109
  PRODUCTION = "production", DEVELOPMENT = "development",
110
110
  // error messages
@@ -59055,6 +59055,7 @@ const getProductDetailHandlers = (product, referenceId, skus = []) => ({
59055
59055
  periodInMonthsAbbrev: () => getDurationInMonths(product, false, true, false, true),
59056
59056
  periodly: () => convertISO8601PeriodToPeriodly(getStandardBillingPeriod(product)),
59057
59057
  iso_subscription_duration: () => getISOSubscriptionDuration(product),
59058
+ bill_term: () => getBillTerm(product),
59058
59059
  invalid: () => false,
59059
59060
  unavailable: () => false,
59060
59061
  });
@@ -59214,6 +59215,21 @@ const getIntroductoryPhase = (product, referenceId) => {
59214
59215
  }
59215
59216
  return convertOfferToPricingPhase(offer, product.subscription_period);
59216
59217
  };
59218
+ // Analytics-stable bill-term names keyed by normalized ISO 8601 duration.
59219
+ // Deliberately NOT localized (unlike `periodly`) — these land in analytics
59220
+ // payloads via `{{ sku.bill_term }}`, where values must be stable per NAM-3429.
59221
+ const BILL_TERM_BY_ISO_DURATION = {
59222
+ P1D: "daily",
59223
+ P1W: "weekly",
59224
+ P1M: "monthly",
59225
+ P3M: "quarterly",
59226
+ P6M: "semiannual",
59227
+ P1Y: "annual",
59228
+ };
59229
+ const getBillTerm = (product) => {
59230
+ const iso = getISOSubscriptionDuration(product);
59231
+ return (iso && BILL_TERM_BY_ISO_DURATION[iso]) || null;
59232
+ };
59217
59233
  const getISOSubscriptionDuration = (product) => {
59218
59234
  // ["P1W", "P4W", "P1M", "P2M", "P3M", "P4M", "P6M", "P8M", "P1Y"]
59219
59235
  // P7D => P1W
@@ -62592,6 +62608,34 @@ class FlowLiquidResolver {
62592
62608
  const value = PaywallState.currentProvider?.state.formStates[fieldId];
62593
62609
  return formTagValue(value);
62594
62610
  }
62611
+ // Purchased-SKU context (NAM-3965): {{ sku.* }} resolves against the SKU
62612
+ // captured by the raise's `__buy_sku__` trigger — deliberately NOT the
62613
+ // selected SKU, so cart-add analytics report what was actually bought.
62614
+ // Unset until a buy action fires (token blanks, matching other platforms).
62615
+ if (key.startsWith("sku.")) {
62616
+ const purchased = flow?.purchasedSku;
62617
+ if (!purchased)
62618
+ return undefined;
62619
+ const leaf = key.substring("sku.".length);
62620
+ switch (leaf) {
62621
+ case "sku_ref_id":
62622
+ return purchased.skuRefId;
62623
+ case "name":
62624
+ // Control Center reference name; the store title stays reachable as
62625
+ // {{ sku.title }} via the product-detail table below.
62626
+ return purchased.name;
62627
+ // SKUs can grant several entitlements; the scalar contract takes the
62628
+ // first in payload order (NAM-3429).
62629
+ case "entitlement_ref_id":
62630
+ return purchased.entitlements[0]?.entitlement_ref_id ?? "";
62631
+ case "entitlement_name":
62632
+ return purchased.entitlements[0]?.name ?? "";
62633
+ default: {
62634
+ const value = getProductDetail(leaf, purchased.productDetails);
62635
+ return value != null ? String(value) : undefined;
62636
+ }
62637
+ }
62638
+ }
62595
62639
  switch (key) {
62596
62640
  case "campaignId":
62597
62641
  return screenState?.campaign?.rule;
@@ -64048,6 +64092,27 @@ function validateForm(validators, formStates) {
64048
64092
 
64049
64093
  /** Form field key convention for grouped handoffs: `<tag>::<payloadKey>`. */
64050
64094
  const HANDOFF_GROUP_KEY = /^([^:]+)::(.+)$/;
64095
+ /**
64096
+ * Web buttons hand the flow a public `NamiSKU` (`skuId`/`productDetails`) while
64097
+ * Expo hands the wire `PaywallSKU` (`sku_ref_id`/`product_details`); collapse
64098
+ * both onto one shape so `{{ sku.* }}` resolves identically on every platform.
64099
+ */
64100
+ function normalizePurchasedSku(sku) {
64101
+ if ('sku_ref_id' in sku) {
64102
+ return {
64103
+ skuRefId: sku.sku_ref_id,
64104
+ name: sku.name,
64105
+ entitlements: Array.isArray(sku.entitlements) ? sku.entitlements : [],
64106
+ productDetails: sku.product_details ?? null,
64107
+ };
64108
+ }
64109
+ return {
64110
+ skuRefId: sku.skuId,
64111
+ name: sku.name,
64112
+ entitlements: Array.isArray(sku.entitlements) ? sku.entitlements : [],
64113
+ productDetails: sku.productDetails ?? null,
64114
+ };
64115
+ }
64051
64116
  class BasicNamiFlow {
64052
64117
  constructor(flowObject = {}) {
64053
64118
  this.id = flowObject.id ?? '';
@@ -64416,7 +64481,19 @@ class NamiFlow extends BasicNamiFlow {
64416
64481
  ];
64417
64482
  return lifecycles.some(cycle => [cycle.before, cycle.action, cycle.after].some(actions => actions?.some(action => submitFns.includes(action.function))));
64418
64483
  }
64484
+ /**
64485
+ * Stash the SKU carried by a `__buy_sku__` trigger so `{{ sku.* }}` resolves
64486
+ * in this and later lifecycles of the raise. Runs before the lifecycle
64487
+ * lookup: a buy tap should update the context even when the current step
64488
+ * has no `__buy_sku__` lifecycles of its own.
64489
+ */
64490
+ captureBuySkuData(actionId, data) {
64491
+ if (actionId === NamiReservedActions.BUY_SKU && data?.sku) {
64492
+ this.purchasedSku = normalizePurchasedSku(data.sku);
64493
+ }
64494
+ }
64419
64495
  triggerActions(actionId, component, data) {
64496
+ this.captureBuySkuData(actionId, data);
64420
64497
  const lifecycles = this.lifecycles(actionId);
64421
64498
  if (!lifecycles)
64422
64499
  return;
@@ -64448,6 +64525,7 @@ class NamiFlow extends BasicNamiFlow {
64448
64525
  }
64449
64526
  }
64450
64527
  triggerBeforeActions(actionId, component, data) {
64528
+ this.captureBuySkuData(actionId, data);
64451
64529
  const lifecycles = this.lifecycles(actionId);
64452
64530
  if (!lifecycles)
64453
64531
  return;
@@ -64457,6 +64535,7 @@ class NamiFlow extends BasicNamiFlow {
64457
64535
  }
64458
64536
  }
64459
64537
  triggerAfterActions(actionId, component, data) {
64538
+ this.captureBuySkuData(actionId, data);
64460
64539
  const lifecycles = this.lifecycles(actionId);
64461
64540
  if (!lifecycles)
64462
64541
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@namiml/sdk-core",
3
- "version": "3.4.10-dev.202608230147",
3
+ "version": "3.4.10-dev.202608230452",
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",