@namiml/expo-sdk 3.4.5-dev.202607100416 → 3.4.5-dev.202607102110

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
@@ -292,6 +292,7 @@ class ExpoPurchaseAdapter {
292
292
  }
293
293
 
294
294
  const expoUIAdapter = new ExpoUIAdapter();
295
+ const expoPurchaseAdapter = new ExpoPurchaseAdapter();
295
296
  let adaptersRegistered = false;
296
297
  function registerExpoAdapters() {
297
298
  if (adaptersRegistered) {
@@ -303,7 +304,7 @@ function registerExpoAdapters() {
303
304
  device: new ExpoDeviceAdapter(),
304
305
  ui: expoUIAdapter,
305
306
  });
306
- sdkCore.registerPurchaseAdapter(new ExpoPurchaseAdapter());
307
+ sdkCore.registerPurchaseAdapter(expoPurchaseAdapter);
307
308
  adaptersRegistered = true;
308
309
  }
309
310
  catch (error) {
@@ -312,6 +313,79 @@ function registerExpoAdapters() {
312
313
  }
313
314
  registerExpoAdapters();
314
315
 
316
+ // Accumulated store product details, keyed by product_ref_id (== sku_ref_id).
317
+ const productsById = new Map();
318
+ // Serializes backfill runs so concurrent launches/prefetches don't double-fetch
319
+ // or race the map (parity with the Apple SDK's backfillTaskLock).
320
+ let pending = Promise.resolve(false);
321
+ /** Collect unique store product ids (sku_ref_id) from the given paywalls' sku menus. */
322
+ function collectSkuRefIds(paywalls) {
323
+ const ids = new Set();
324
+ for (const paywall of paywalls ?? []) {
325
+ for (const menu of paywall?.sku_menus ?? []) {
326
+ for (const sku of menu?.products ?? []) {
327
+ if (sku?.sku_ref_id)
328
+ ids.add(sku.sku_ref_id);
329
+ }
330
+ }
331
+ }
332
+ return [...ids];
333
+ }
334
+ /**
335
+ * Fetch store product details for any of `skuRefIds` not already loaded, merge them into
336
+ * the accumulated set, and push the union to core via NamiPaywallManager.setProductDetails.
337
+ * Serialized; returns whether new products were added. Never throws.
338
+ */
339
+ function backfill(skuRefIds) {
340
+ pending = pending.then(() => backfillOnce(skuRefIds));
341
+ return pending;
342
+ }
343
+ async function backfillOnce(skuRefIds) {
344
+ const missing = [...new Set(skuRefIds)].filter((id) => id && !productsById.has(id));
345
+ if (!missing.length)
346
+ return false;
347
+ try {
348
+ const products = await expoPurchaseAdapter.getProducts(missing);
349
+ let added = false;
350
+ for (const product of products ?? []) {
351
+ if (product?.product_ref_id) {
352
+ productsById.set(product.product_ref_id, product);
353
+ added = true;
354
+ }
355
+ }
356
+ if (added) {
357
+ sdkCore.NamiPaywallManager.setProductDetails([...productsById.values()]);
358
+ }
359
+ return added;
360
+ }
361
+ catch (error) {
362
+ sdkCore.logger.warn('[NamiExpo] Failed to load store products.', error);
363
+ return false;
364
+ }
365
+ }
366
+ /** Clear accumulated products. Called on Nami.reset so reconfigure doesn't serve stale data. */
367
+ function reset() {
368
+ pending = pending.then(() => {
369
+ productsById.clear();
370
+ return false;
371
+ });
372
+ }
373
+
374
+ /**
375
+ * Prefetch StoreKit/Play product details for every configured paywall's SKUs so that
376
+ * prices are available before any paywall mounts (mirrors the Apple SDK startup load).
377
+ * Fire-and-forget and error-safe: a failure here never blocks configure.
378
+ */
379
+ async function prefetchProducts() {
380
+ try {
381
+ const ids = collectSkuRefIds(sdkCore.NamiPaywallManager.allPaywalls());
382
+ if (ids.length)
383
+ await backfill(ids);
384
+ }
385
+ catch (error) {
386
+ sdkCore.logger.warn('[NamiExpo] Product prefetch skipped.', error);
387
+ }
388
+ }
315
389
  class Nami {
316
390
  static get sdkVersion() {
317
391
  return sdkCore.Nami.sdkVersion();
@@ -320,9 +394,12 @@ class Nami {
320
394
  return sdkCore.Nami.sdkPackageVersion();
321
395
  }
322
396
  static async configure(config) {
323
- return sdkCore.Nami.configure(config);
397
+ const result = await sdkCore.Nami.configure(config);
398
+ void prefetchProducts();
399
+ return result;
324
400
  }
325
401
  static async reset() {
402
+ reset();
326
403
  await sdkCore.Nami.reset?.();
327
404
  }
328
405
  }
@@ -3706,6 +3783,9 @@ const NamiView = ({ placement, url, onClose, onSignIn, onDeepLink, onPurchase, o
3706
3783
  launchCacheRef.current.set(cacheKey, nextLaunch);
3707
3784
  }
3708
3785
  prewarmPaywallFonts([paywall, ...flowPaywalls]);
3786
+ const launchSkuRefIds = collectSkuRefIds([paywall, ...flowPaywalls].filter((item) => item != null));
3787
+ if (launchSkuRefIds.length)
3788
+ void backfill(launchSkuRefIds);
3709
3789
  if (initialPaywall?.fonts) {
3710
3790
  void prepareAndLoadFontsWithTimeout(initialPaywall.fonts);
3711
3791
  }