@monetize.software/sdk 3.0.0-alpha.3 → 3.0.0-alpha.5

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.
Files changed (128) hide show
  1. package/dist/chunks/PaywallUI-BD5hRY2P.js +26 -0
  2. package/dist/chunks/PaywallUI-BD5hRY2P.js.map +1 -0
  3. package/dist/chunks/PaywallUI-D7lp-bC5.js +3206 -0
  4. package/dist/chunks/PaywallUI-D7lp-bC5.js.map +1 -0
  5. package/dist/chunks/ar-BCHXVoE2.js +114 -0
  6. package/dist/chunks/ar-BCHXVoE2.js.map +1 -0
  7. package/dist/chunks/ar-CsJNZJSr.js +2 -0
  8. package/dist/chunks/ar-CsJNZJSr.js.map +1 -0
  9. package/dist/chunks/cs-B5NqpTW_.js +110 -0
  10. package/dist/chunks/cs-B5NqpTW_.js.map +1 -0
  11. package/dist/chunks/cs-BydWUC0e.js +2 -0
  12. package/dist/chunks/cs-BydWUC0e.js.map +1 -0
  13. package/dist/chunks/da-BJrGZ3LD.js +110 -0
  14. package/dist/chunks/da-BJrGZ3LD.js.map +1 -0
  15. package/dist/chunks/da-DNhiAQnh.js +2 -0
  16. package/dist/chunks/da-DNhiAQnh.js.map +1 -0
  17. package/dist/chunks/de-H8ztFOie.js +2 -0
  18. package/dist/chunks/de-H8ztFOie.js.map +1 -0
  19. package/dist/chunks/de-aepBKwsb.js +130 -0
  20. package/dist/chunks/de-aepBKwsb.js.map +1 -0
  21. package/dist/chunks/el-DRfoadtI.js +2 -0
  22. package/dist/chunks/el-DRfoadtI.js.map +1 -0
  23. package/dist/chunks/el-DTLQoX2D.js +114 -0
  24. package/dist/chunks/el-DTLQoX2D.js.map +1 -0
  25. package/dist/chunks/es-CLutF-D_.js +130 -0
  26. package/dist/chunks/es-CLutF-D_.js.map +1 -0
  27. package/dist/chunks/es-GlaYesNR.js +2 -0
  28. package/dist/chunks/es-GlaYesNR.js.map +1 -0
  29. package/dist/chunks/fi-BIHFyScH.js +2 -0
  30. package/dist/chunks/fi-BIHFyScH.js.map +1 -0
  31. package/dist/chunks/fi-DZ4csxqk.js +110 -0
  32. package/dist/chunks/fi-DZ4csxqk.js.map +1 -0
  33. package/dist/chunks/fr-BtZILUNZ.js +2 -0
  34. package/dist/chunks/fr-BtZILUNZ.js.map +1 -0
  35. package/dist/chunks/fr-jJU1SSpj.js +130 -0
  36. package/dist/chunks/fr-jJU1SSpj.js.map +1 -0
  37. package/dist/chunks/he-D9obGPNj.js +114 -0
  38. package/dist/chunks/he-D9obGPNj.js.map +1 -0
  39. package/dist/chunks/he-vSDRE4Nn.js +2 -0
  40. package/dist/chunks/he-vSDRE4Nn.js.map +1 -0
  41. package/dist/chunks/hi-B90FsnP6.js +2 -0
  42. package/dist/chunks/hi-B90FsnP6.js.map +1 -0
  43. package/dist/chunks/hi-pM8SQwZ3.js +114 -0
  44. package/dist/chunks/hi-pM8SQwZ3.js.map +1 -0
  45. package/dist/chunks/hu-DWVFODsS.js +2 -0
  46. package/dist/chunks/hu-DWVFODsS.js.map +1 -0
  47. package/dist/chunks/hu-E0m9WgbD.js +110 -0
  48. package/dist/chunks/hu-E0m9WgbD.js.map +1 -0
  49. package/dist/chunks/id-C6poPvby.js +110 -0
  50. package/dist/chunks/id-C6poPvby.js.map +1 -0
  51. package/dist/chunks/id-Ce2gzMVT.js +2 -0
  52. package/dist/chunks/id-Ce2gzMVT.js.map +1 -0
  53. package/dist/chunks/it-B2RSFyVd.js +130 -0
  54. package/dist/chunks/it-B2RSFyVd.js.map +1 -0
  55. package/dist/chunks/it-u-Gu44bl.js +2 -0
  56. package/dist/chunks/it-u-Gu44bl.js.map +1 -0
  57. package/dist/chunks/ja-CM-VgVG6.js +134 -0
  58. package/dist/chunks/ja-CM-VgVG6.js.map +1 -0
  59. package/dist/chunks/ja-CQy8RaRa.js +2 -0
  60. package/dist/chunks/ja-CQy8RaRa.js.map +1 -0
  61. package/dist/chunks/ko-BRnb7vJ7.js +2 -0
  62. package/dist/chunks/ko-BRnb7vJ7.js.map +1 -0
  63. package/dist/chunks/ko-C451fA21.js +134 -0
  64. package/dist/chunks/ko-C451fA21.js.map +1 -0
  65. package/dist/chunks/nl-CJelco6J.js +2 -0
  66. package/dist/chunks/nl-CJelco6J.js.map +1 -0
  67. package/dist/chunks/nl-DzQfJPo2.js +130 -0
  68. package/dist/chunks/nl-DzQfJPo2.js.map +1 -0
  69. package/dist/chunks/no-B51be8KT.js +110 -0
  70. package/dist/chunks/no-B51be8KT.js.map +1 -0
  71. package/dist/chunks/no-BwTjSZ4K.js +2 -0
  72. package/dist/chunks/no-BwTjSZ4K.js.map +1 -0
  73. package/dist/chunks/pl-5rTEkvfY.js +110 -0
  74. package/dist/chunks/pl-5rTEkvfY.js.map +1 -0
  75. package/dist/chunks/pl-kO82vcjb.js +2 -0
  76. package/dist/chunks/pl-kO82vcjb.js.map +1 -0
  77. package/dist/chunks/pt-CsJzaSjg.js +2 -0
  78. package/dist/chunks/pt-CsJzaSjg.js.map +1 -0
  79. package/dist/chunks/pt-JwqffZ9u.js +130 -0
  80. package/dist/chunks/pt-JwqffZ9u.js.map +1 -0
  81. package/dist/chunks/ro-BE_wJ1td.js +110 -0
  82. package/dist/chunks/ro-BE_wJ1td.js.map +1 -0
  83. package/dist/chunks/ro-ue15Ina4.js +2 -0
  84. package/dist/chunks/ro-ue15Ina4.js.map +1 -0
  85. package/dist/chunks/ru-B1iMOhX0.js +2 -0
  86. package/dist/chunks/ru-B1iMOhX0.js.map +1 -0
  87. package/dist/chunks/ru-BviATvLb.js +124 -0
  88. package/dist/chunks/ru-BviATvLb.js.map +1 -0
  89. package/dist/chunks/sv-CkNYpUVy.js +2 -0
  90. package/dist/chunks/sv-CkNYpUVy.js.map +1 -0
  91. package/dist/chunks/sv-DabGF9WL.js +110 -0
  92. package/dist/chunks/sv-DabGF9WL.js.map +1 -0
  93. package/dist/chunks/th-BiF-bNo0.js +114 -0
  94. package/dist/chunks/th-BiF-bNo0.js.map +1 -0
  95. package/dist/chunks/th-Cu80HK4y.js +2 -0
  96. package/dist/chunks/th-Cu80HK4y.js.map +1 -0
  97. package/dist/chunks/tr-B7c0afXV.js +2 -0
  98. package/dist/chunks/tr-B7c0afXV.js.map +1 -0
  99. package/dist/chunks/tr-xZuly8X8.js +110 -0
  100. package/dist/chunks/tr-xZuly8X8.js.map +1 -0
  101. package/dist/chunks/uk-BO106B0H.js +2 -0
  102. package/dist/chunks/uk-BO106B0H.js.map +1 -0
  103. package/dist/chunks/uk-KlkAaHuy.js +124 -0
  104. package/dist/chunks/uk-KlkAaHuy.js.map +1 -0
  105. package/dist/chunks/vi-BVCeumNE.js +110 -0
  106. package/dist/chunks/vi-BVCeumNE.js.map +1 -0
  107. package/dist/chunks/vi-CZ6ow40D.js +2 -0
  108. package/dist/chunks/vi-CZ6ow40D.js.map +1 -0
  109. package/dist/chunks/zh-BhP80WI1.js +2 -0
  110. package/dist/chunks/zh-BhP80WI1.js.map +1 -0
  111. package/dist/chunks/zh-C_ghwmqi.js +134 -0
  112. package/dist/chunks/zh-C_ghwmqi.js.map +1 -0
  113. package/dist/core.cjs +1 -1
  114. package/dist/core.cjs.map +1 -1
  115. package/dist/core.d.ts +152 -11
  116. package/dist/core.js +433 -348
  117. package/dist/core.js.map +1 -1
  118. package/dist/index.cjs +1 -1
  119. package/dist/index.d.ts +166 -22
  120. package/dist/index.js +1 -1
  121. package/dist/ui.cjs +1 -1
  122. package/dist/ui.d.ts +166 -22
  123. package/dist/ui.js +1 -1
  124. package/package.json +32 -31
  125. package/dist/chunks/PaywallUI-BWU_1hsh.js +0 -26
  126. package/dist/chunks/PaywallUI-BWU_1hsh.js.map +0 -1
  127. package/dist/chunks/PaywallUI-C3W0eKDo.js +0 -2233
  128. package/dist/chunks/PaywallUI-C3W0eKDo.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./core.cjs"),r=require("./chunks/PaywallUI-BWU_1hsh.js");exports.ApiClient=e.ApiClient;exports.ApiGatewayClient=e.ApiGatewayClient;exports.AuthClient=e.AuthClient;exports.BillingClient=e.BillingClient;exports.EventTracker=e.EventTracker;exports.PaywallError=e.PaywallError;exports.QuotaExceededError=e.QuotaExceededError;exports.SDK_VERSION=e.SDK_VERSION;exports.STORAGE_KEYS=e.STORAGE_KEYS;exports.createStorage=e.createStorage;exports.ensureVisitorId=e.ensureVisitorId;exports.generateVisitorId=e.generateVisitorId;exports.PaywallUI=r.PaywallUI;exports.blockRegistry=r.blockRegistry;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./core.cjs"),r=require("./chunks/PaywallUI-BD5hRY2P.js");exports.ApiClient=e.ApiClient;exports.ApiGatewayClient=e.ApiGatewayClient;exports.AuthClient=e.AuthClient;exports.BillingClient=e.BillingClient;exports.EventTracker=e.EventTracker;exports.PaywallError=e.PaywallError;exports.QuotaExceededError=e.QuotaExceededError;exports.SDK_VERSION=e.SDK_VERSION;exports.STORAGE_KEYS=e.STORAGE_KEYS;exports.createStorage=e.createStorage;exports.ensureVisitorId=e.ensureVisitorId;exports.generateVisitorId=e.generateVisitorId;exports.PaywallUI=r.PaywallUI;exports.blockRegistry=r.blockRegistry;
2
2
  //# sourceMappingURL=index.cjs.map
package/dist/index.d.ts CHANGED
@@ -60,7 +60,9 @@ export declare class ApiGatewayClient {
60
60
 
61
61
  export declare interface ApiGatewayClientOptions {
62
62
  paywallId: string;
63
- apiOrigin?: string;
63
+ /** Origin серверного API SDK — обязательное поле, тот же `custom_domain`, что
64
+ * у BillingClient/AuthClient. См. {@link BillingClientOptions.apiOrigin}. */
65
+ apiOrigin: string;
64
66
  /** AuthClient — Bearer добавляется автоматически. На 401 от gateway клиент
65
67
  * не делает refresh: AuthClient уже сделал lazy-refresh в getAccessToken. */
66
68
  auth?: AuthClient;
@@ -80,7 +82,28 @@ export declare interface ApiGatewayClientOptions {
80
82
  onQuotaExceeded?: (err: QuotaExceededError) => void;
81
83
  }
82
84
 
83
- export declare type AuthChangeListener = (session: AuthSession | null) => void;
85
+ /** Дискриминатор для `onAuthChange`. Позволяет listener'у отличать первый
86
+ * callback (восстановление сессии из storage / синтетический snapshot для
87
+ * свежей подписки) от реальных переходов. Конвенция Supabase, минус события,
88
+ * которых у нас нет (MFA, EMAIL_VERIFIED).
89
+ *
90
+ * - INITIAL_SESSION — единственный гарантированный первый callback на каждую
91
+ * подписку. Дёргается через microtask после resolve hydrated-promise, даже
92
+ * если session=null. Listener'ы по этому event'у НЕ должны делать побочные
93
+ * эффекты типа force-refetch — это просто доставка стартового state'а.
94
+ * - SIGNED_IN — свежий вход: email/OAuth/anon, или появление session в этом
95
+ * инстансе из другого контекста (storage.watch), когда раньше был null.
96
+ * - SIGNED_OUT — signOut, revokeAllSessions, 401 на refresh, удаление session
97
+ * из другого контекста.
98
+ * - TOKEN_REFRESHED — тот же user, обновлённые токены: refresh(), либо
99
+ * storage.watch когда содержимое сменилось но user.id остался.
100
+ * - USER_UPDATED — изменился user.email / user.user_metadata (updatePassword,
101
+ * upgradeAnonymousToEmail) при том же user.id.
102
+ * - PASSWORD_RECOVERY — verifyOtp(type='recovery'). Listener знает, что надо
103
+ * показать «set new password» UI вместо обычного post-login flow'а. */
104
+ declare type AuthChangeEvent = 'INITIAL_SESSION' | 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED' | 'PASSWORD_RECOVERY';
105
+
106
+ export declare type AuthChangeListener = (event: AuthChangeEvent, session: AuthSession | null) => void;
84
107
 
85
108
  export declare class AuthClient {
86
109
  readonly paywallId: string;
@@ -399,8 +422,19 @@ export declare class AuthClient {
399
422
  }): Promise<void>;
400
423
  /**
401
424
  * Подписка на изменения session: signin/signup/refresh/signOut/expired-401.
402
- * Колбек вызывается с текущим snapshot через microtask (если session есть)
403
- * + на каждое реальное изменение. Возвращает unsubscribe.
425
+ *
426
+ * Гарантированный контракт: ПЕРВЫЙ callback каждому subscriber'у — всегда
427
+ * `event = 'INITIAL_SESSION'`, дёргается асинхронно после resolve hydrate'а
428
+ * (даже если session=null — listener получает explicit «нет сессии», а не
429
+ * молчание). Все последующие callback'и — реальные переходы с конкретным
430
+ * event'ом (SIGNED_IN / SIGNED_OUT / TOKEN_REFRESHED / USER_UPDATED /
431
+ * PASSWORD_RECOVERY).
432
+ *
433
+ * Это позволяет listener'у безопасно делать «only on real signin» побочные
434
+ * эффекты (force refetch balances и т.п.) через `event === 'SIGNED_IN'`,
435
+ * не путая их с восстановлением из storage.
436
+ *
437
+ * Возвращает unsubscribe.
404
438
  */
405
439
  onAuthChange(cb: AuthChangeListener): () => void;
406
440
  private isFresh;
@@ -426,6 +460,18 @@ export declare class AuthClient {
426
460
  private readAnonRefreshToken;
427
461
  private writeAnonRefreshToken;
428
462
  private clearAnonRefreshToken;
463
+ /**
464
+ * Last-used auth method + email — для UI бейджа "Last used" и pre-fill'а
465
+ * email-инпута. Storage paywall-scoped, поэтому переключение между
466
+ * пейволами на одном host'е не пересекает данные. Чтение всегда возвращает
467
+ * объект — отсутствующие поля = null. */
468
+ getLastLogin(): Promise<LastLogin | null>;
469
+ /** Запись method и email атомарно (для email/password flows — оба известны
470
+ * на момент signin/signup'а). OAuth-flows используют раздельные
471
+ * recordLastLoginMethod (до popup) и recordLastLoginEmail (после exchange). */
472
+ private recordLastLogin;
473
+ private recordLastLoginMethod;
474
+ private recordLastLoginEmail;
429
475
  /**
430
476
  * Читает stable visitor_id из storage если он там уже есть. НЕ генерит:
431
477
  * AuthClient может быть инстанцирован раньше BillingClient, а синтетический
@@ -438,7 +484,9 @@ export declare class AuthClient {
438
484
 
439
485
  export declare interface AuthClientOptions {
440
486
  paywallId: string;
441
- apiOrigin?: string;
487
+ /** Origin серверного API SDK — обязательное поле, тот же `custom_domain`, что
488
+ * у BillingClient. См. {@link BillingClientOptions.apiOrigin}. */
489
+ apiOrigin: string;
442
490
  storage?: StorageAdapter;
443
491
  fetch?: typeof fetch;
444
492
  openPopup?: (url: string, name: string) => Window | null;
@@ -816,7 +864,15 @@ export declare class BillingClient {
816
864
 
817
865
  export declare interface BillingClientOptions {
818
866
  paywallId: string;
819
- apiOrigin?: string;
867
+ /**
868
+ * Origin серверного API SDK — обязательное поле. Должно совпадать с
869
+ * `custom_domain`, заданным для пейвола в платформе (модерация привязывает
870
+ * домен к paywall_id). SDK сверяет это значение с `bootstrap.settings.custom_domain`
871
+ * на первом ответе и кидает `invalid_config` при расхождении — защита от
872
+ * опечаток интегратора. Промежуточный `appbox.space` в новом SDK НЕ
873
+ * используется (это только для legacy v2).
874
+ */
875
+ apiOrigin: string;
820
876
  identity?: Identity;
821
877
  storage?: StorageAdapter;
822
878
  capabilities?: string[];
@@ -938,6 +994,18 @@ export declare interface Identity {
938
994
  anonymousId?: string;
939
995
  }
940
996
 
997
+ export declare interface LastLogin {
998
+ method: LastLoginMethod;
999
+ email: string | null;
1000
+ }
1001
+
1002
+ /** Метод, которым юзер залогинился в последний раз на этом пейволе.
1003
+ * Хранится per-paywall в storage и используется UI чтобы:
1004
+ * - предзаполнить email-инпут last-known email'ом;
1005
+ * - подсветить ту же OAuth-кнопку / email-форму бейджем "Last used".
1006
+ * `email` — email/password forms (signin или signup → confirm). */
1007
+ export declare type LastLoginMethod = OAuthProvider | 'email';
1008
+
941
1009
  export declare interface Layout {
942
1010
  type: 'modal';
943
1011
  blocks: LayoutBlock[];
@@ -953,9 +1021,13 @@ export declare type LayoutBlock = {
953
1021
  } | {
954
1022
  type: 'price_grid';
955
1023
  priceIds?: string[];
956
- /** Раскладка карточек цен. `vertical` (default) — стек сверху вниз;
957
- * `horizontal` — ряд side-by-side. v2-аналог `view: 'default' | 'telegram'`. */
958
- view?: 'vertical' | 'horizontal';
1024
+ /** Раскладка карточек цен:
1025
+ * - `vertical` (default) стек карточек сверху вниз;
1026
+ * - `compact` компактный список (одна строка на цену, без карточки,
1027
+ * с разделителем); v2-аналог `view: 'telegram'`;
1028
+ * - `horizontal` — несколько карточек рядом в ряд; v2-only с момента
1029
+ * SDK 3.0 (legacy не показывает выбор этой опции в админке). */
1030
+ view?: 'vertical' | 'compact' | 'horizontal';
959
1031
  /** ID цены, которая помечается лейблом «популярный план». v2-аналог
960
1032
  * пары `price_label_id` + `price_label`. */
961
1033
  popular_price_id?: string;
@@ -964,7 +1036,10 @@ export declare type LayoutBlock = {
964
1036
  popular_label?: string;
965
1037
  } | {
966
1038
  type: 'cta_button';
967
- label: string;
1039
+ /** Текст на кнопке. Если не задан — рендерер сам подберёт по
1040
+ * selected price'у и `trial_days`: "Start N-Day Free Trial",
1041
+ * "Get Lifetime Access", "Get Monthly Plan" и т.п. */
1042
+ label?: string;
968
1043
  action: 'checkout' | 'close';
969
1044
  priceId?: string;
970
1045
  } | {
@@ -983,8 +1058,23 @@ export declare type LayoutBlock = {
983
1058
  /** Скрывать панель, если юзер уже залогинен. По умолчанию true.
984
1059
  * false — показываем "Signed in as ... [Sign out]" даже после логина. */
985
1060
  hide_when_authenticated?: boolean;
986
- /** Заголовок над формой (h2). Если опущензаголовок не рендерится. */
1061
+ /** Кастомный заголовок над формой. Если заданотображается вместо
1062
+ * дефолтного (определяется по mode'у — "Welcome back!" / "Welcome!" /
1063
+ * "Forgot password?" / ...). Без `submit_label` также используется как
1064
+ * submit-лейбл для signin (например `heading="Restore Purchases"` →
1065
+ * submit тоже "Restore Purchases"). Длинные heading (типа "Войдите,
1066
+ * чтобы продолжить покупку") в кнопку влезают плохо — задай
1067
+ * `submit_label` отдельно. */
987
1068
  heading?: string;
1069
+ /** Подпись под заголовком. Если опущен — подставляется default-текст
1070
+ * для текущего mode'а. Передай пустую строку чтобы скрыть подпись. */
1071
+ subheading?: string;
1072
+ /** Явный текст submit-кнопки. Имеет приоритет над heading-эхо. Нужен
1073
+ * для интентов с длинным descriptive heading'ом (preauth: "Войдите,
1074
+ * чтобы продолжить покупку" — кнопка только "Войти"). Для коротких
1075
+ * action-headings (restore: "Restore Purchases") опускай — эхо
1076
+ * даёт правильный UX. */
1077
+ submit_label?: string;
988
1078
  } | {
989
1079
  /** Список фич/преимуществ продукта. v2-аналог `features_list` + `features_view`.
990
1080
  * До 5 элементов — рендерим как чек-лист с заголовком и описанием. */
@@ -1008,6 +1098,35 @@ export declare type LayoutBlock = {
1008
1098
  desc: string;
1009
1099
  count: number;
1010
1100
  }>;
1101
+ } | {
1102
+ /** Money-back guarantee badge под cta_button: иконка + жирный заголовок +
1103
+ * пояснение мелким шрифтом + bottom divider, который визуально стыкуется
1104
+ * с current_session ниже. v2-аналог inline-блока в `PaywallPricing`. */
1105
+ type: 'guarantee_badge';
1106
+ /** Заголовок жирным. По умолчанию "100% Money-Back Guarantee". */
1107
+ title?: string;
1108
+ /** Подзаголовок мелким серым. По умолчанию
1109
+ * "Not satisfied? We'll refund you — no questions asked.". */
1110
+ subtitle?: string;
1111
+ /** Иконка слева от заголовка. По умолчанию `dollar_shield` —
1112
+ * зелёный shield с долларом (legacy-вид). `none` — без иконки. */
1113
+ icon?: 'dollar_shield' | 'none';
1114
+ } | {
1115
+ /** Urgency-баннер с countdown'ом до конца offer'а. Берёт первый offer
1116
+ * из `bootstrap.offers` с валидным `expires_at` или `duration_minutes`.
1117
+ * Авто-скрывается по истечении, чтобы не показывать "0d 0h 0m 0s".
1118
+ * Размещение — обычно первый блок в layout (над heading). */
1119
+ type: 'offer_banner';
1120
+ /** ID конкретного offer'а из bootstrap.offers. Если не задано — берётся
1121
+ * первый offer с активным таймером. */
1122
+ offer_id?: string;
1123
+ /** Текст слева от countdown'а. Если опущен — берётся `offer.label`,
1124
+ * иначе fallback "Limited-time offer". К нему дописывается процент:
1125
+ * "{title} {discount_percent}%" если discount задан. */
1126
+ title?: string;
1127
+ /** В превью админки — игнорировать expired-state, всё равно показывать
1128
+ * banner с нулями. Прод-режим — false (banner исчезает). */
1129
+ force?: boolean;
1011
1130
  };
1012
1131
 
1013
1132
  /** Локализационные оверрайды для одного языка. Накатываются поверх дефолтного
@@ -1167,9 +1286,19 @@ declare interface PaywallEventPayloads {
1167
1286
  /** User-state изменился (bootstrap snapshot, getUser refresh, watcher tick).
1168
1287
  * Дёргается также сразу с last-known user после первой подписки. */
1169
1288
  userChange: PaywallUser;
1170
- /** Auth-session изменилась (signin / signup / refresh / signout / 401-revoke).
1171
- * null = разлогинен. Дёргается сразу с last-known session после первой подписки. */
1172
- authChange: AuthSession | null;
1289
+ /** Auth-session изменилась. Payload содержит `event` (см. AuthChangeEvent
1290
+ * INITIAL_SESSION / SIGNED_IN / SIGNED_OUT / TOKEN_REFRESHED / USER_UPDATED /
1291
+ * PASSWORD_RECOVERY) и `session` (null = разлогинен).
1292
+ *
1293
+ * Гарантированный контракт: первый callback каждому subscriber'у — всегда
1294
+ * INITIAL_SESSION с восстановленной из storage сессией (или null если нет).
1295
+ * Дальше — реальные переходы. Listener'у с побочными эффектами вроде
1296
+ * force-refetch balances ловить SIGNED_IN, а не любой truthy session,
1297
+ * иначе reload страницы будет триггерить лишний запрос. */
1298
+ authChange: {
1299
+ event: AuthChangeEvent;
1300
+ session: AuthSession | null;
1301
+ };
1173
1302
  /** Триал заблокировал показ модалки. payload содержит свежий статус (после
1174
1303
  * recordBlock). Для `mode: 'time'` — startedAt/expiresAt/remainingMs;
1175
1304
  * для `mode: 'opens'` — remainingActions/totalActions. Хост может
@@ -1189,7 +1318,15 @@ declare interface PaywallEventPayloads {
1189
1318
  export declare interface PaywallOffer {
1190
1319
  id: string;
1191
1320
  discount_percent: number | null;
1321
+ /** Абсолютная expiration date (ISO 8601). Если задана — countdown отсчитывает
1322
+ * до этого момента, по истечении offer считается истёкшим. */
1192
1323
  expires_at: string | null;
1324
+ /** Относительный таймер: сколько минут offer живёт **от первого просмотра
1325
+ * пейвола** данным юзером. Старт хранится в clientStorage под ключом
1326
+ * `pw-offer-{id}-start`, по истечении — auto-cleanup. Используется, когда
1327
+ * бэк хочет показывать "remaining time" без жёсткой server-time, а считать
1328
+ * относительно сессии юзера. expires_at имеет приоритет если задано. */
1329
+ duration_minutes?: number | null;
1193
1330
  price_id: string | null;
1194
1331
  label?: string | null;
1195
1332
  }
@@ -1239,6 +1376,14 @@ export declare interface PaywallSettings {
1239
1376
  brand_color?: string | null;
1240
1377
  custom_css?: string | null;
1241
1378
  locale_default?: string | null;
1379
+ /** Origin, на котором живёт бэк пейвола (тот же, что мерчант передаёт в
1380
+ * `BillingClientOptions.apiOrigin` при инициализации SDK). Бэк присылает
1381
+ * его на каждом bootstrap'е, SDK сверяет с init.apiOrigin — расхождение
1382
+ * даёт `invalid_config` (защита от опечатки интегратора). Без схемы:
1383
+ * "pay.your-domain.com" или "https://pay.your-domain.com" — оба валидны.
1384
+ * Для новых пейволов поле всегда заполнено (модерация требует custom_domain);
1385
+ * для legacy v2 может быть null/undefined. */
1386
+ custom_domain?: string | null;
1242
1387
  runtime_mode?: 'client' | 'hybrid' | 'server' | 'client-native' | 'hybrid-native';
1243
1388
  /** true, если эквайринг пейвола в test-mode — SDK рисует TEST MODE бейдж. */
1244
1389
  is_test_mode?: boolean;
@@ -1561,14 +1706,7 @@ export declare interface PaywallUIOptions extends Omit<BillingClientOptions, 'au
1561
1706
  * флеш на blocked-странах/устройствах хуже воспринимаемой латентности.
1562
1707
  */
1563
1708
  mountThenLoad?: boolean;
1564
- /**
1565
- * Inline-режим для live-preview редактора админки. Host позиционируется
1566
- * `absolute inset:0` внутри родителя (вместо fixed-viewport'а), overlay
1567
- * Modal'а тоже становится absolute, body-scroll не лочится. ОБЯЗАТЕЛЬНО
1568
- * передавать `host` (HTMLElement) с positioned parent'ом — иначе absolute
1569
- * уйдёт к ближайшему positioned ancestor'у или к html. По умолчанию false.
1570
- */
1571
- inline?: boolean;
1709
+ /* Excluded from this release type: inline */
1572
1710
  }
1573
1711
 
1574
1712
  export declare interface PaywallUser {
@@ -1580,6 +1718,12 @@ export declare interface PaywallUser {
1580
1718
  started_at: string | null;
1581
1719
  expires_at: string | null;
1582
1720
  } | null;
1721
+ /** Был ли у юзера хотя бы один trial по этому пейволу когда-либо (включая
1722
+ * истёкшие и отменённые). Anti-abuse флаг для UI: CtaButton скрывает
1723
+ * "Start N-Day Free Trial" если true. Серверный enforcement в
1724
+ * `/start-checkout` дублирует — даже если UI обмануть, бэк не отдаст
1725
+ * trial_days в Stripe/Paddle. */
1726
+ had_previous_trial: boolean;
1583
1727
  }
1584
1728
 
1585
1729
  export declare interface PaywallUserPurchase {
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ApiClient as t, ApiGatewayClient as a, AuthClient as i, BillingClient as l, EventTracker as o, PaywallError as n, QuotaExceededError as E, SDK_VERSION as s, STORAGE_KEYS as S, createStorage as c, ensureVisitorId as d, generateVisitorId as g } from "./core.js";
2
- import { P as y, b as A } from "./chunks/PaywallUI-C3W0eKDo.js";
2
+ import { P as y, b as A } from "./chunks/PaywallUI-D7lp-bC5.js";
3
3
  export {
4
4
  t as ApiClient,
5
5
  a as ApiGatewayClient,
package/dist/ui.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/PaywallUI-BWU_1hsh.js");exports.PaywallUI=e.PaywallUI;exports.blockRegistry=e.blockRegistry;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/PaywallUI-BD5hRY2P.js");exports.PaywallUI=e.PaywallUI;exports.blockRegistry=e.blockRegistry;
2
2
  //# sourceMappingURL=ui.cjs.map
package/dist/ui.d.ts CHANGED
@@ -46,7 +46,9 @@ declare class ApiGatewayClient {
46
46
 
47
47
  declare interface ApiGatewayClientOptions {
48
48
  paywallId: string;
49
- apiOrigin?: string;
49
+ /** Origin серверного API SDK — обязательное поле, тот же `custom_domain`, что
50
+ * у BillingClient/AuthClient. См. {@link BillingClientOptions.apiOrigin}. */
51
+ apiOrigin: string;
50
52
  /** AuthClient — Bearer добавляется автоматически. На 401 от gateway клиент
51
53
  * не делает refresh: AuthClient уже сделал lazy-refresh в getAccessToken. */
52
54
  auth?: AuthClient;
@@ -66,7 +68,28 @@ declare interface ApiGatewayClientOptions {
66
68
  onQuotaExceeded?: (err: QuotaExceededError) => void;
67
69
  }
68
70
 
69
- declare type AuthChangeListener = (session: AuthSession | null) => void;
71
+ /** Дискриминатор для `onAuthChange`. Позволяет listener'у отличать первый
72
+ * callback (восстановление сессии из storage / синтетический snapshot для
73
+ * свежей подписки) от реальных переходов. Конвенция Supabase, минус события,
74
+ * которых у нас нет (MFA, EMAIL_VERIFIED).
75
+ *
76
+ * - INITIAL_SESSION — единственный гарантированный первый callback на каждую
77
+ * подписку. Дёргается через microtask после resolve hydrated-promise, даже
78
+ * если session=null. Listener'ы по этому event'у НЕ должны делать побочные
79
+ * эффекты типа force-refetch — это просто доставка стартового state'а.
80
+ * - SIGNED_IN — свежий вход: email/OAuth/anon, или появление session в этом
81
+ * инстансе из другого контекста (storage.watch), когда раньше был null.
82
+ * - SIGNED_OUT — signOut, revokeAllSessions, 401 на refresh, удаление session
83
+ * из другого контекста.
84
+ * - TOKEN_REFRESHED — тот же user, обновлённые токены: refresh(), либо
85
+ * storage.watch когда содержимое сменилось но user.id остался.
86
+ * - USER_UPDATED — изменился user.email / user.user_metadata (updatePassword,
87
+ * upgradeAnonymousToEmail) при том же user.id.
88
+ * - PASSWORD_RECOVERY — verifyOtp(type='recovery'). Listener знает, что надо
89
+ * показать «set new password» UI вместо обычного post-login flow'а. */
90
+ declare type AuthChangeEvent = 'INITIAL_SESSION' | 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED' | 'PASSWORD_RECOVERY';
91
+
92
+ declare type AuthChangeListener = (event: AuthChangeEvent, session: AuthSession | null) => void;
70
93
 
71
94
  declare class AuthClient {
72
95
  readonly paywallId: string;
@@ -385,8 +408,19 @@ declare class AuthClient {
385
408
  }): Promise<void>;
386
409
  /**
387
410
  * Подписка на изменения session: signin/signup/refresh/signOut/expired-401.
388
- * Колбек вызывается с текущим snapshot через microtask (если session есть)
389
- * + на каждое реальное изменение. Возвращает unsubscribe.
411
+ *
412
+ * Гарантированный контракт: ПЕРВЫЙ callback каждому subscriber'у — всегда
413
+ * `event = 'INITIAL_SESSION'`, дёргается асинхронно после resolve hydrate'а
414
+ * (даже если session=null — listener получает explicit «нет сессии», а не
415
+ * молчание). Все последующие callback'и — реальные переходы с конкретным
416
+ * event'ом (SIGNED_IN / SIGNED_OUT / TOKEN_REFRESHED / USER_UPDATED /
417
+ * PASSWORD_RECOVERY).
418
+ *
419
+ * Это позволяет listener'у безопасно делать «only on real signin» побочные
420
+ * эффекты (force refetch balances и т.п.) через `event === 'SIGNED_IN'`,
421
+ * не путая их с восстановлением из storage.
422
+ *
423
+ * Возвращает unsubscribe.
390
424
  */
391
425
  onAuthChange(cb: AuthChangeListener): () => void;
392
426
  private isFresh;
@@ -412,6 +446,18 @@ declare class AuthClient {
412
446
  private readAnonRefreshToken;
413
447
  private writeAnonRefreshToken;
414
448
  private clearAnonRefreshToken;
449
+ /**
450
+ * Last-used auth method + email — для UI бейджа "Last used" и pre-fill'а
451
+ * email-инпута. Storage paywall-scoped, поэтому переключение между
452
+ * пейволами на одном host'е не пересекает данные. Чтение всегда возвращает
453
+ * объект — отсутствующие поля = null. */
454
+ getLastLogin(): Promise<LastLogin | null>;
455
+ /** Запись method и email атомарно (для email/password flows — оба известны
456
+ * на момент signin/signup'а). OAuth-flows используют раздельные
457
+ * recordLastLoginMethod (до popup) и recordLastLoginEmail (после exchange). */
458
+ private recordLastLogin;
459
+ private recordLastLoginMethod;
460
+ private recordLastLoginEmail;
415
461
  /**
416
462
  * Читает stable visitor_id из storage если он там уже есть. НЕ генерит:
417
463
  * AuthClient может быть инстанцирован раньше BillingClient, а синтетический
@@ -424,7 +470,9 @@ declare class AuthClient {
424
470
 
425
471
  declare interface AuthClientOptions {
426
472
  paywallId: string;
427
- apiOrigin?: string;
473
+ /** Origin серверного API SDK — обязательное поле, тот же `custom_domain`, что
474
+ * у BillingClient. См. {@link BillingClientOptions.apiOrigin}. */
475
+ apiOrigin: string;
428
476
  storage?: StorageAdapter;
429
477
  fetch?: typeof fetch;
430
478
  openPopup?: (url: string, name: string) => Window | null;
@@ -802,7 +850,15 @@ declare class BillingClient {
802
850
 
803
851
  declare interface BillingClientOptions {
804
852
  paywallId: string;
805
- apiOrigin?: string;
853
+ /**
854
+ * Origin серверного API SDK — обязательное поле. Должно совпадать с
855
+ * `custom_domain`, заданным для пейвола в платформе (модерация привязывает
856
+ * домен к paywall_id). SDK сверяет это значение с `bootstrap.settings.custom_domain`
857
+ * на первом ответе и кидает `invalid_config` при расхождении — защита от
858
+ * опечаток интегратора. Промежуточный `appbox.space` в новом SDK НЕ
859
+ * используется (это только для legacy v2).
860
+ */
861
+ apiOrigin: string;
806
862
  identity?: Identity;
807
863
  storage?: StorageAdapter;
808
864
  capabilities?: string[];
@@ -877,6 +933,18 @@ declare interface Identity {
877
933
  anonymousId?: string;
878
934
  }
879
935
 
936
+ declare interface LastLogin {
937
+ method: LastLoginMethod;
938
+ email: string | null;
939
+ }
940
+
941
+ /** Метод, которым юзер залогинился в последний раз на этом пейволе.
942
+ * Хранится per-paywall в storage и используется UI чтобы:
943
+ * - предзаполнить email-инпут last-known email'ом;
944
+ * - подсветить ту же OAuth-кнопку / email-форму бейджем "Last used".
945
+ * `email` — email/password forms (signin или signup → confirm). */
946
+ declare type LastLoginMethod = OAuthProvider | 'email';
947
+
880
948
  declare interface Layout {
881
949
  type: 'modal';
882
950
  blocks: LayoutBlock[];
@@ -892,9 +960,13 @@ declare type LayoutBlock = {
892
960
  } | {
893
961
  type: 'price_grid';
894
962
  priceIds?: string[];
895
- /** Раскладка карточек цен. `vertical` (default) — стек сверху вниз;
896
- * `horizontal` — ряд side-by-side. v2-аналог `view: 'default' | 'telegram'`. */
897
- view?: 'vertical' | 'horizontal';
963
+ /** Раскладка карточек цен:
964
+ * - `vertical` (default) стек карточек сверху вниз;
965
+ * - `compact` компактный список (одна строка на цену, без карточки,
966
+ * с разделителем); v2-аналог `view: 'telegram'`;
967
+ * - `horizontal` — несколько карточек рядом в ряд; v2-only с момента
968
+ * SDK 3.0 (legacy не показывает выбор этой опции в админке). */
969
+ view?: 'vertical' | 'compact' | 'horizontal';
898
970
  /** ID цены, которая помечается лейблом «популярный план». v2-аналог
899
971
  * пары `price_label_id` + `price_label`. */
900
972
  popular_price_id?: string;
@@ -903,7 +975,10 @@ declare type LayoutBlock = {
903
975
  popular_label?: string;
904
976
  } | {
905
977
  type: 'cta_button';
906
- label: string;
978
+ /** Текст на кнопке. Если не задан — рендерер сам подберёт по
979
+ * selected price'у и `trial_days`: "Start N-Day Free Trial",
980
+ * "Get Lifetime Access", "Get Monthly Plan" и т.п. */
981
+ label?: string;
907
982
  action: 'checkout' | 'close';
908
983
  priceId?: string;
909
984
  } | {
@@ -922,8 +997,23 @@ declare type LayoutBlock = {
922
997
  /** Скрывать панель, если юзер уже залогинен. По умолчанию true.
923
998
  * false — показываем "Signed in as ... [Sign out]" даже после логина. */
924
999
  hide_when_authenticated?: boolean;
925
- /** Заголовок над формой (h2). Если опущензаголовок не рендерится. */
1000
+ /** Кастомный заголовок над формой. Если заданотображается вместо
1001
+ * дефолтного (определяется по mode'у — "Welcome back!" / "Welcome!" /
1002
+ * "Forgot password?" / ...). Без `submit_label` также используется как
1003
+ * submit-лейбл для signin (например `heading="Restore Purchases"` →
1004
+ * submit тоже "Restore Purchases"). Длинные heading (типа "Войдите,
1005
+ * чтобы продолжить покупку") в кнопку влезают плохо — задай
1006
+ * `submit_label` отдельно. */
926
1007
  heading?: string;
1008
+ /** Подпись под заголовком. Если опущен — подставляется default-текст
1009
+ * для текущего mode'а. Передай пустую строку чтобы скрыть подпись. */
1010
+ subheading?: string;
1011
+ /** Явный текст submit-кнопки. Имеет приоритет над heading-эхо. Нужен
1012
+ * для интентов с длинным descriptive heading'ом (preauth: "Войдите,
1013
+ * чтобы продолжить покупку" — кнопка только "Войти"). Для коротких
1014
+ * action-headings (restore: "Restore Purchases") опускай — эхо
1015
+ * даёт правильный UX. */
1016
+ submit_label?: string;
927
1017
  } | {
928
1018
  /** Список фич/преимуществ продукта. v2-аналог `features_list` + `features_view`.
929
1019
  * До 5 элементов — рендерим как чек-лист с заголовком и описанием. */
@@ -947,6 +1037,35 @@ declare type LayoutBlock = {
947
1037
  desc: string;
948
1038
  count: number;
949
1039
  }>;
1040
+ } | {
1041
+ /** Money-back guarantee badge под cta_button: иконка + жирный заголовок +
1042
+ * пояснение мелким шрифтом + bottom divider, который визуально стыкуется
1043
+ * с current_session ниже. v2-аналог inline-блока в `PaywallPricing`. */
1044
+ type: 'guarantee_badge';
1045
+ /** Заголовок жирным. По умолчанию "100% Money-Back Guarantee". */
1046
+ title?: string;
1047
+ /** Подзаголовок мелким серым. По умолчанию
1048
+ * "Not satisfied? We'll refund you — no questions asked.". */
1049
+ subtitle?: string;
1050
+ /** Иконка слева от заголовка. По умолчанию `dollar_shield` —
1051
+ * зелёный shield с долларом (legacy-вид). `none` — без иконки. */
1052
+ icon?: 'dollar_shield' | 'none';
1053
+ } | {
1054
+ /** Urgency-баннер с countdown'ом до конца offer'а. Берёт первый offer
1055
+ * из `bootstrap.offers` с валидным `expires_at` или `duration_minutes`.
1056
+ * Авто-скрывается по истечении, чтобы не показывать "0d 0h 0m 0s".
1057
+ * Размещение — обычно первый блок в layout (над heading). */
1058
+ type: 'offer_banner';
1059
+ /** ID конкретного offer'а из bootstrap.offers. Если не задано — берётся
1060
+ * первый offer с активным таймером. */
1061
+ offer_id?: string;
1062
+ /** Текст слева от countdown'а. Если опущен — берётся `offer.label`,
1063
+ * иначе fallback "Limited-time offer". К нему дописывается процент:
1064
+ * "{title} {discount_percent}%" если discount задан. */
1065
+ title?: string;
1066
+ /** В превью админки — игнорировать expired-state, всё равно показывать
1067
+ * banner с нулями. Прод-режим — false (banner исчезает). */
1068
+ force?: boolean;
950
1069
  };
951
1070
 
952
1071
  /** Локализационные оверрайды для одного языка. Накатываются поверх дефолтного
@@ -1106,9 +1225,19 @@ declare interface PaywallEventPayloads {
1106
1225
  /** User-state изменился (bootstrap snapshot, getUser refresh, watcher tick).
1107
1226
  * Дёргается также сразу с last-known user после первой подписки. */
1108
1227
  userChange: PaywallUser;
1109
- /** Auth-session изменилась (signin / signup / refresh / signout / 401-revoke).
1110
- * null = разлогинен. Дёргается сразу с last-known session после первой подписки. */
1111
- authChange: AuthSession | null;
1228
+ /** Auth-session изменилась. Payload содержит `event` (см. AuthChangeEvent
1229
+ * INITIAL_SESSION / SIGNED_IN / SIGNED_OUT / TOKEN_REFRESHED / USER_UPDATED /
1230
+ * PASSWORD_RECOVERY) и `session` (null = разлогинен).
1231
+ *
1232
+ * Гарантированный контракт: первый callback каждому subscriber'у — всегда
1233
+ * INITIAL_SESSION с восстановленной из storage сессией (или null если нет).
1234
+ * Дальше — реальные переходы. Listener'у с побочными эффектами вроде
1235
+ * force-refetch balances ловить SIGNED_IN, а не любой truthy session,
1236
+ * иначе reload страницы будет триггерить лишний запрос. */
1237
+ authChange: {
1238
+ event: AuthChangeEvent;
1239
+ session: AuthSession | null;
1240
+ };
1112
1241
  /** Триал заблокировал показ модалки. payload содержит свежий статус (после
1113
1242
  * recordBlock). Для `mode: 'time'` — startedAt/expiresAt/remainingMs;
1114
1243
  * для `mode: 'opens'` — remainingActions/totalActions. Хост может
@@ -1128,7 +1257,15 @@ declare interface PaywallEventPayloads {
1128
1257
  declare interface PaywallOffer {
1129
1258
  id: string;
1130
1259
  discount_percent: number | null;
1260
+ /** Абсолютная expiration date (ISO 8601). Если задана — countdown отсчитывает
1261
+ * до этого момента, по истечении offer считается истёкшим. */
1131
1262
  expires_at: string | null;
1263
+ /** Относительный таймер: сколько минут offer живёт **от первого просмотра
1264
+ * пейвола** данным юзером. Старт хранится в clientStorage под ключом
1265
+ * `pw-offer-{id}-start`, по истечении — auto-cleanup. Используется, когда
1266
+ * бэк хочет показывать "remaining time" без жёсткой server-time, а считать
1267
+ * относительно сессии юзера. expires_at имеет приоритет если задано. */
1268
+ duration_minutes?: number | null;
1132
1269
  price_id: string | null;
1133
1270
  label?: string | null;
1134
1271
  }
@@ -1178,6 +1315,14 @@ declare interface PaywallSettings {
1178
1315
  brand_color?: string | null;
1179
1316
  custom_css?: string | null;
1180
1317
  locale_default?: string | null;
1318
+ /** Origin, на котором живёт бэк пейвола (тот же, что мерчант передаёт в
1319
+ * `BillingClientOptions.apiOrigin` при инициализации SDK). Бэк присылает
1320
+ * его на каждом bootstrap'е, SDK сверяет с init.apiOrigin — расхождение
1321
+ * даёт `invalid_config` (защита от опечатки интегратора). Без схемы:
1322
+ * "pay.your-domain.com" или "https://pay.your-domain.com" — оба валидны.
1323
+ * Для новых пейволов поле всегда заполнено (модерация требует custom_domain);
1324
+ * для legacy v2 может быть null/undefined. */
1325
+ custom_domain?: string | null;
1181
1326
  runtime_mode?: 'client' | 'hybrid' | 'server' | 'client-native' | 'hybrid-native';
1182
1327
  /** true, если эквайринг пейвола в test-mode — SDK рисует TEST MODE бейдж. */
1183
1328
  is_test_mode?: boolean;
@@ -1500,14 +1645,7 @@ export declare interface PaywallUIOptions extends Omit<BillingClientOptions, 'au
1500
1645
  * флеш на blocked-странах/устройствах хуже воспринимаемой латентности.
1501
1646
  */
1502
1647
  mountThenLoad?: boolean;
1503
- /**
1504
- * Inline-режим для live-preview редактора админки. Host позиционируется
1505
- * `absolute inset:0` внутри родителя (вместо fixed-viewport'а), overlay
1506
- * Modal'а тоже становится absolute, body-scroll не лочится. ОБЯЗАТЕЛЬНО
1507
- * передавать `host` (HTMLElement) с positioned parent'ом — иначе absolute
1508
- * уйдёт к ближайшему positioned ancestor'у или к html. По умолчанию false.
1509
- */
1510
- inline?: boolean;
1648
+ /* Excluded from this release type: inline */
1511
1649
  }
1512
1650
 
1513
1651
  declare interface PaywallUser {
@@ -1519,6 +1657,12 @@ declare interface PaywallUser {
1519
1657
  started_at: string | null;
1520
1658
  expires_at: string | null;
1521
1659
  } | null;
1660
+ /** Был ли у юзера хотя бы один trial по этому пейволу когда-либо (включая
1661
+ * истёкшие и отменённые). Anti-abuse флаг для UI: CtaButton скрывает
1662
+ * "Start N-Day Free Trial" если true. Серверный enforcement в
1663
+ * `/start-checkout` дублирует — даже если UI обмануть, бэк не отдаст
1664
+ * trial_days в Stripe/Paddle. */
1665
+ had_previous_trial: boolean;
1522
1666
  }
1523
1667
 
1524
1668
  declare interface PaywallUserPurchase {
package/dist/ui.js CHANGED
@@ -1,4 +1,4 @@
1
- import { P as o, b as r } from "./chunks/PaywallUI-C3W0eKDo.js";
1
+ import { P as o, b as r } from "./chunks/PaywallUI-D7lp-bC5.js";
2
2
  export {
3
3
  o as PaywallUI,
4
4
  r as blockRegistry