@ait-kit/sdk 0.3.0 → 0.5.0

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 (120) hide show
  1. package/README.md +197 -13
  2. package/dist/ads/adapter.d.ts +36 -0
  3. package/dist/ads/adapter.d.ts.map +1 -0
  4. package/dist/ads/adapter.js +219 -0
  5. package/dist/ads/platform-contract.d.ts +45 -0
  6. package/dist/ads/platform-contract.d.ts.map +1 -0
  7. package/dist/ads/platform-contract.js +7 -0
  8. package/dist/iap/adapter.d.ts +11 -1
  9. package/dist/iap/adapter.d.ts.map +1 -1
  10. package/dist/iap/adapter.js +13 -0
  11. package/dist/iap/order-history.d.ts +6 -0
  12. package/dist/iap/order-history.d.ts.map +1 -0
  13. package/dist/iap/order-history.js +54 -0
  14. package/dist/iap/platform-contract.d.ts +9 -1
  15. package/dist/iap/platform-contract.d.ts.map +1 -1
  16. package/dist/iap/platform-contract.js +1 -1
  17. package/dist/iap/purchase-flow.d.ts +1 -1
  18. package/dist/iap/purchase-flow.js +1 -1
  19. package/dist/iap/subscription-info.d.ts +6 -0
  20. package/dist/iap/subscription-info.d.ts.map +1 -0
  21. package/dist/iap/subscription-info.js +38 -0
  22. package/dist/identity/platform-contract.d.ts +1 -1
  23. package/dist/index.d.ts +30 -3
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/notification/platform-contract.d.ts +1 -1
  27. package/dist/platform-loader.d.ts +16 -0
  28. package/dist/platform-loader.d.ts.map +1 -0
  29. package/dist/platform-loader.js +35 -0
  30. package/dist/promotion/adapter.d.ts +3 -0
  31. package/dist/promotion/adapter.d.ts.map +1 -0
  32. package/dist/promotion/adapter.js +128 -0
  33. package/dist/promotion/platform-contract.d.ts +37 -0
  34. package/dist/promotion/platform-contract.d.ts.map +1 -0
  35. package/dist/review/adapter.d.ts +3 -0
  36. package/dist/review/adapter.d.ts.map +1 -0
  37. package/dist/review/adapter.js +33 -0
  38. package/dist/review/platform-contract.d.ts +21 -0
  39. package/dist/review/platform-contract.d.ts.map +1 -0
  40. package/dist/review/platform-contract.js +1 -0
  41. package/dist/rn/ads.d.ts +7 -36
  42. package/dist/rn/ads.d.ts.map +1 -1
  43. package/dist/rn/ads.js +3 -229
  44. package/dist/rn/framework-contract.d.ts +1 -44
  45. package/dist/rn/framework-contract.d.ts.map +1 -1
  46. package/dist/rn/framework-contract.js +0 -6
  47. package/dist/rn/framework-loader.d.ts.map +1 -1
  48. package/dist/rn/framework-loader.js +6 -17
  49. package/dist/rn/iap.d.ts +1 -1
  50. package/dist/rn/iap.d.ts.map +1 -1
  51. package/dist/rn/iap.js +10 -29
  52. package/dist/rn/identity.d.ts.map +1 -1
  53. package/dist/rn/identity.js +4 -21
  54. package/dist/rn/index.d.ts +2 -0
  55. package/dist/rn/index.d.ts.map +1 -1
  56. package/dist/rn/index.js +2 -0
  57. package/dist/rn/notify-share.d.ts.map +1 -1
  58. package/dist/rn/notify-share.js +6 -48
  59. package/dist/rn/official-module.d.ts +16 -0
  60. package/dist/rn/official-module.d.ts.map +1 -1
  61. package/dist/rn/official-module.js +20 -0
  62. package/dist/rn/platform-loader.d.ts +4 -0
  63. package/dist/rn/platform-loader.d.ts.map +1 -0
  64. package/dist/rn/platform-loader.js +5 -0
  65. package/dist/rn/promotion.d.ts +9 -0
  66. package/dist/rn/promotion.d.ts.map +1 -0
  67. package/dist/rn/promotion.js +11 -0
  68. package/dist/rn/review.d.ts +7 -0
  69. package/dist/rn/review.d.ts.map +1 -0
  70. package/dist/rn/review.js +10 -0
  71. package/dist/rn/storage.d.ts.map +1 -1
  72. package/dist/rn/storage.js +4 -24
  73. package/dist/share/platform-contract.d.ts +1 -1
  74. package/dist/storage/platform-contract.d.ts +1 -1
  75. package/dist/webview/ads.d.ts +10 -0
  76. package/dist/webview/ads.d.ts.map +1 -0
  77. package/dist/webview/ads.js +14 -0
  78. package/dist/webview/banner-contract.d.ts +59 -0
  79. package/dist/webview/banner-contract.d.ts.map +1 -0
  80. package/dist/webview/banner-contract.js +1 -0
  81. package/dist/webview/banner.d.ts +15 -0
  82. package/dist/webview/banner.d.ts.map +1 -0
  83. package/dist/webview/banner.js +188 -0
  84. package/dist/{web → webview}/framework-loader.d.ts +1 -1
  85. package/dist/webview/framework-loader.d.ts.map +1 -0
  86. package/dist/webview/framework-loader.js +16 -0
  87. package/dist/{web → webview}/iap-contract.d.ts +4 -4
  88. package/dist/webview/iap-contract.d.ts.map +1 -0
  89. package/dist/webview/iap-contract.js +1 -0
  90. package/dist/webview/identity.d.ts +40 -0
  91. package/dist/webview/identity.d.ts.map +1 -0
  92. package/dist/webview/identity.js +70 -0
  93. package/dist/webview/index.d.ts +62 -0
  94. package/dist/webview/index.d.ts.map +1 -0
  95. package/dist/webview/index.js +51 -0
  96. package/dist/webview/notify-share.d.ts +50 -0
  97. package/dist/webview/notify-share.d.ts.map +1 -0
  98. package/dist/{web → webview}/notify-share.js +19 -59
  99. package/dist/webview/platform-loader.d.ts +4 -0
  100. package/dist/webview/platform-loader.d.ts.map +1 -0
  101. package/dist/webview/platform-loader.js +5 -0
  102. package/dist/webview/promotion.d.ts +13 -0
  103. package/dist/webview/promotion.d.ts.map +1 -0
  104. package/dist/webview/promotion.js +12 -0
  105. package/dist/webview/review.d.ts +11 -0
  106. package/dist/webview/review.d.ts.map +1 -0
  107. package/dist/webview/review.js +11 -0
  108. package/package.json +7 -3
  109. package/dist/web/framework-loader.d.ts.map +0 -1
  110. package/dist/web/framework-loader.js +0 -32
  111. package/dist/web/iap-contract.d.ts.map +0 -1
  112. package/dist/web/identity.d.ts +0 -30
  113. package/dist/web/identity.d.ts.map +0 -1
  114. package/dist/web/identity.js +0 -110
  115. package/dist/web/index.d.ts +0 -47
  116. package/dist/web/index.d.ts.map +0 -1
  117. package/dist/web/index.js +0 -43
  118. package/dist/web/notify-share.d.ts +0 -38
  119. package/dist/web/notify-share.d.ts.map +0 -1
  120. /package/dist/{web/iap-contract.js → promotion/platform-contract.js} +0 -0
package/README.md CHANGED
@@ -13,20 +13,20 @@ npm install @ait-kit/sdk
13
13
  |---|---|
14
14
  | `@ait-kit/sdk` (root) | Runtime-neutral shared contracts: ad + IAP types, `SdkError`. Importable anywhere — plain Node, web, React Native — with no official SDK installed. |
15
15
  | `@ait-kit/sdk/rn` | React Native adapters (full-screen ads, IAP). Requires the official `@apps-in-toss/framework`, declared as an **optional peer** and imported lazily. Never requires the web SDK. |
16
- | `@ait-kit/sdk/web` | Web adapters (IAP). Requires the official `@apps-in-toss/web-framework`, declared as an **optional peer** and imported lazily. Never requires the React Native SDK. |
16
+ | `@ait-kit/sdk/webview` | WebView adapters for ads, banners, purchases, identity, storage, notifications, sharing, reviews, and promotions. Requires the official `@apps-in-toss/web-framework`, declared as an **optional peer** and imported lazily. Never requires the React Native SDK. |
17
17
 
18
18
  The two platform entries share one internal engine but keep completely
19
19
  separate SDK connections — in JavaScript and in the shipped type
20
20
  declarations — so an `/rn` consumer never installs the web package and vice
21
21
  versa.
22
22
 
23
- ## In-app purchases (RN + Web)
23
+ ## In-app purchases (RN + WebView)
24
24
 
25
25
  Both entries expose the same IAP surface; only the loader differs:
26
26
 
27
27
  ```ts
28
28
  import { createReactNativeIap } from "@ait-kit/sdk/rn"; // or:
29
- import { createWebIap } from "@ait-kit/sdk/web";
29
+ import { createWebViewIap } from "@ait-kit/sdk/webview";
30
30
 
31
31
  const iap = createReactNativeIap({
32
32
  // The ONLY place product delivery happens. Resolve only after YOUR
@@ -90,25 +90,28 @@ SDK initialization never grants or completes pending orders by itself.
90
90
 
91
91
  ### Feature matrix
92
92
 
93
- | Capability | `/rn` | `/web` | Notes |
93
+ | Capability | `/rn` | `/webview` | Notes |
94
94
  |---|---|---|---|
95
95
  | Product list | ✅ | ✅ | one-time + subscription together |
96
96
  | One-time purchase | ✅ | ✅ | grant callback contract applies |
97
97
  | Subscription purchase | ✅ | ✅ | `offerId` optional; `subscriptionId` surfaced on completion |
98
+ | Subscription info | ✅ | ✅ | read-only provider status and access snapshot |
99
+ | Completed/refunded orders | ✅ cursor | ✅ first page only | read-only; no automatic restore or refund actions |
98
100
  | Pending orders | ✅ | ✅ | recovery is consumer-driven |
99
101
  | Grant completion notify | ✅ | ✅ | sent only after server grant confirms |
100
- | Full-screen ads | ✅ | ➖ | ads are RN-only today |
102
+ | Full-screen / rewarded ads | ✅ | ✅ | reward events only; reload after each show |
103
+ | Banner lifecycle | ➖ | ✅ | initialize, attach, cancel, and destroy an owned handle |
101
104
  | Notification agreement | ✅ | ✅ | event-based, one template per request |
102
105
  | Share link / share sheet | ✅ | ✅ | `intoss://` paths; `completed` ≠ shared |
103
106
  | Unsupported app version | `SdkError("UNSUPPORTED")` | same | per-function `isSupported` gates |
104
107
 
105
108
  Notification and sharing adapters ship in both entries (see below).
106
109
 
107
- ## Login, anonymous identity, and storage (RN + Web)
110
+ ## Login, anonymous identity, and storage (RN + WebView)
108
111
 
109
112
  ```ts
110
113
  import { createReactNativeIdentity, createReactNativeStorage } from "@ait-kit/sdk/rn";
111
- // or: import { createWebIdentity, createWebStorage } from "@ait-kit/sdk/web";
114
+ // or: import { createWebViewIdentity, createWebViewStorage } from "@ait-kit/sdk/webview";
112
115
 
113
116
  const identity = createReactNativeIdentity();
114
117
 
@@ -153,14 +156,14 @@ Contracts:
153
156
  - Session invalidation when the anonymous identifier changes, migration of
154
157
  previously stored keys, and bootstrap sequencing stay with the consumer.
155
158
 
156
- ## Notification agreement and sharing (RN + Web)
159
+ ## Notification agreement and sharing (RN + WebView)
157
160
 
158
161
  ```ts
159
162
  import {
160
163
  createReactNativeNotification,
161
164
  createReactNativeShare
162
165
  } from "@ait-kit/sdk/rn";
163
- // or: import { createWebNotification, createWebShare } from "@ait-kit/sdk/web";
166
+ // or: import { createWebViewNotification, createWebViewShare } from "@ait-kit/sdk/webview";
164
167
 
165
168
  // 1. Notification agreement: one template, one request.
166
169
  const notification = createReactNativeNotification();
@@ -197,11 +200,12 @@ Contracts:
197
200
  stay with the consumer. (Renamed from `closed` in 0.3.0; see Migrating.)
198
201
  - Unsupported surfaces/app versions reject with `SdkError("UNSUPPORTED")`.
199
202
 
200
- ## React Native full-screen ads
203
+ ## React Native and WebView full-screen ads
201
204
 
202
205
  ```ts
203
206
  import { createReactNativeAds } from "@ait-kit/sdk/rn";
204
207
 
208
+ // WebView: import { createWebViewAds } from "@ait-kit/sdk/webview";
205
209
  const ads = createReactNativeAds();
206
210
  await ads.loadFullScreenAd("AD_GROUP_ID"); // joins an in-flight duplicate load
207
211
  const result = await ads.showFullScreenAd("AD_GROUP_ID");
@@ -243,8 +247,7 @@ Behavior:
243
247
 
244
248
  The SDK provides ad behavior and outcomes. Your application owns server-side
245
249
  ad reward requests, user session checks, payout limits, and ledger updates.
246
- (Login/anonymous-key helpers and storage arrive in later entries, as do
247
- notification and sharing.)
250
+ Identity, storage, notification, and sharing adapters are also available in both entries.
248
251
 
249
252
  ## Versioning
250
253
 
@@ -259,7 +262,7 @@ official releases (see `scripts/test-package-tarballs.mjs`):
259
262
  | Official package | Verified version | Peer range |
260
263
  |---|---|---|
261
264
  | `@apps-in-toss/framework` (RN) | 2.10.10 | `>=2.10.10` |
262
- | `@apps-in-toss/web-framework` (Web) | 3.4.0 | `>=3.4.0` |
265
+ | `@apps-in-toss/web-framework` (WebView) | 3.4.0 | `>=3.4.0` |
263
266
 
264
267
  Older versions may work where the runtime surfaces match (every capability
265
268
  is checked per function and missing ones reject with `UNSUPPORTED`), but
@@ -293,3 +296,184 @@ resolving `undefined` (installed app below the feature's minimum version)
293
296
  now rejects with `SdkError("UNSUPPORTED")` instead of
294
297
  `INVALID_ANONYMOUS_KEY`; the `"ERROR"` sentinel still rejects with
295
298
  `INVALID_ANONYMOUS_KEY`.
299
+
300
+ ## Review requests
301
+
302
+ ```ts
303
+ import { createReactNativeReview } from "@ait-kit/sdk/rn";
304
+ // WebView: import { createWebViewReview } from "@ait-kit/sdk/webview";
305
+ const review = createReactNativeReview(); // share one instance across screens
306
+ if (await review.isSupported()) await review.request();
307
+ ```
308
+
309
+ `ReviewAdapter` is a runtime-neutral root type. Factories accept `framework`
310
+ (shared `Review.request` contract or async loader), like the existing adapters.
311
+ RN translates the official `requestReview()` export; WebView calls `Review.request()`.
312
+ The official SDK loads only on use. Missing features/support checkers return
313
+ `false`; import failure throws `SDK_UNAVAILABLE`; support-check exceptions propagate.
314
+ `request()` rechecks support and throws `UNSUPPORTED` when unavailable.
315
+ Concurrent requests on one instance share one Promise, cleared after success or
316
+ failure. There is no retry or timeout.
317
+
318
+ `Promise<void>` completion does not establish whether UI appeared or a review
319
+ was written. Never grant rewards, unlock features, or block navigation on review
320
+ completion. Complete and persist the core action first, then start a separately
321
+ error-handled review request without awaiting it in the core flow. The consumer
322
+ owns request timing, session limits, cooldowns and storage. Do not log raw SDK
323
+ errors by default.
324
+
325
+ Official reference: [Review.request](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/review/review.request.md)
326
+ (Android/iOS Toss 5.253.0+, checked by the SDK). Verified package surfaces:
327
+ RN 2.10.10 and WebView 3.4.0. Device UI behavior requires separate manual verification;
328
+ unit/tarball tests use injected SDKs or a stubbed native bridge.
329
+
330
+ ## Explicit direct promotion rewards
331
+
332
+ ```ts
333
+ import { createReactNativePromotion } from "@ait-kit/sdk/rn";
334
+ // WebView: import { createWebViewPromotion } from "@ait-kit/sdk/webview";
335
+ const promotion = createReactNativePromotion({ timeoutMs: 15_000 });
336
+ const support = await promotion.getSupport();
337
+ // RN 2.10.10 exposes no support checker: support is "unknown", not "supported".
338
+ // Opt into direct grants explicitly, with app-specific eligibility/budget checks.
339
+ ```
340
+
341
+ `PromotionAdapter`, `PromotionSupport`, `PromotionGrantInput` and
342
+ `PromotionGrantResult` are runtime-neutral root types. Both factories accept
343
+ `framework` (shared `Promotion.grantReward` contract or async loader).
344
+ RN calls `grantPromotionReward({ params: { promotionCode, amount } })`;
345
+ WebView calls only `Promotion.grantReward({ promotionCode, amount })`.
346
+ There is no legacy WebView fallback or fallback to/from server payment flows.
347
+
348
+ `getSupport()` reports `unsupported` for missing functions or an explicit false
349
+ check, `supported` for a true check, and `unknown` when a function has no checker.
350
+ Import failures throw `SDK_UNAVAILABLE`; support-check exceptions propagate.
351
+ Every grant rechecks support. RN's documented `undefined` and the provider's
352
+ `UNSUPPORTED_APP_VERSION` throw `UNSUPPORTED`; malformed WebView responses stay unknown.
353
+
354
+ Input errors throw `INVALID_PROMOTION_INPUT` before loading/calling the SDK:
355
+ `promotionCode` must be non-empty without surrounding whitespace (never trimmed
356
+ into a different code), and `amount` must be a positive safe integer. Campaign
357
+ eligibility, maximum amounts and budgets remain the consumer's responsibility.
358
+
359
+ Results:
360
+
361
+ - `granted` has a non-empty `rewardKey` and means only that the SDK reported success.
362
+ - `rejected` carries an explicit documented refusal code: `4100`, `4104`, `4105`,
363
+ `4108`, `4109`, `4110`, `4112`, or `4114`.
364
+ - `unknown` means confirmation is required: SDK exceptions/sentinels, malformed or
365
+ contradictory responses, deadlines, `UNKNOWN_ERROR`, `4113`, or new provider
366
+ codes. `4113` never becomes an inferred already-granted ledger state.
367
+
368
+ Provider messages and raw errors are not included in grant results. Provider codes
369
+ are retained only as bounded uppercase alphanumeric/underscore tokens; do not log
370
+ reward keys, promotion codes or whole SDK responses.
371
+
372
+ Share one adapter instance. A concurrent grant throws `PROMOTION_IN_PROGRESS`;
373
+ completed results are never cached as idempotent payments. `timeoutMs` defaults to
374
+ 0 (disabled), accepts integers through 2147483647, and covers loading and the SDK
375
+ call. Elapsed time is checked before dispatch and when the SDK settles, so delayed
376
+ timer callbacks cannot start an overdue payment or turn a late result into success.
377
+ After timeout the result is unknown, but the instance stays locked until the
378
+ actual operation settles. A loader finishing after timeout never starts payment.
379
+ A late success/failure cannot change the returned timeout result. There is no
380
+ cancellation, automatic retry, or protection across instances/devices/restarts.
381
+
382
+ Consumer UI pattern (all callbacks are supplied by the app):
383
+
384
+ ```ts
385
+ async function grantOnce(input, ui) {
386
+ ui.disableGrantAction();
387
+ try {
388
+ const result = await promotion.grantReward(input);
389
+ if (result.status === "unknown") {
390
+ ui.showConfirmationRequired(); // keep disabled; never retry automatically
391
+ return;
392
+ }
393
+ ui.showSdkResult(result); // client report only; not a server ledger receipt
394
+ } catch {
395
+ ui.showConfirmationRequired(); // do not dump raw exceptions or auto-reenable
396
+ }
397
+ }
398
+ ```
399
+
400
+ Posting a client result to a server is not independent payment verification.
401
+ Consumers requiring a server ledger must keep their existing three-stage server
402
+ payment path. This API does not require client-side mTLS certificates/proxy tokens
403
+ and does not replace TrailBase claims or owned-currency accounting.
404
+
405
+ Official reference: [Promotion.grantReward](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/promotion/promotion.grantreward.md).
406
+ Contracts are checked against RN 2.10.10 and WebView 3.4.0 published types/code with
407
+ synthetic bridge fixtures. Device checks and official test-promotion calls remain
408
+ manual; no real promotion is invoked by tests.
409
+
410
+ ### WebView naming migration
411
+
412
+ Use `@ait-kit/sdk/webview` and the `createWebView*` factories / `WebView*` types.
413
+ The legacy `@ait-kit/sdk/web` entry, `createWeb*` factories, and `Web*` types remain
414
+ available as deprecated aliases with identical behavior. The official peer package
415
+ name remains `@apps-in-toss/web-framework`.
416
+
417
+ WebView ads use the official flat `loadFullScreenAd` and `showFullScreenAd` exports.
418
+ Create them with `createWebViewAds()` from `@ait-kit/sdk/webview`; the same
419
+ load/show deadlines, single-use rules, and reward-event semantics described above apply.
420
+
421
+ ### Subscription status queries
422
+
423
+ Both IAP adapters expose `getSubscriptionInfo(orderId)` and return `{ subscription }`
424
+ with the provider's `catalogId`, `status`, `expiresAt`, `isAutoRenew`,
425
+ `gracePeriodExpiresAt`, and `isAccessible` fields. Future status strings are preserved;
426
+ access is never inferred from a status string. Treat this as a provider snapshot,
427
+ not server-side entitlement verification. The query never calls `grant` or changes entitlements.
428
+
429
+ Missing capabilities, unsupported host versions, and the RN unsupported `undefined`
430
+ response throw `SdkError("UNSUPPORTED")`. Blank IDs throw `INVALID_IAP_INPUT`;
431
+ malformed payloads throw `INVALID_IAP_RESULT`. Other provider errors remain observable.
432
+
433
+ ### Completed and refunded order history
434
+
435
+ `iap.getCompletedOrRefundedOrders()` returns `{ orders, hasNext, nextKey?, pagination }`.
436
+ Each order retains its provider `orderId`, `sku`, `status` (`COMPLETED` or `REFUNDED`),
437
+ and `date`. This read-only query never grants goods or revokes entitlements.
438
+
439
+ RN pages have `pagination: "cursor"`; pass `{ key: page.nextKey }` to explicitly
440
+ request the next page when `hasNext` is true and a cursor is present. WebView pages
441
+ have `pagination: "first_page_only"`: the current official WebView SDK accepts no
442
+ cursor. `hasNext` may still be true, but passing a non-null key throws `UNSUPPORTED`
443
+ before provider dispatch. Do not loop WebView page-one calls or treat them as a full restore.
444
+
445
+ Missing/unsupported capabilities throw `UNSUPPORTED`, malformed inputs throw
446
+ `INVALID_IAP_INPUT`, and malformed pages throw `INVALID_IAP_RESULT`. Other provider
447
+ errors propagate without automatic retries.
448
+
449
+ ### WebView banner ads
450
+
451
+ ```ts
452
+ import { createWebViewBannerAds } from "@ait-kit/sdk/webview";
453
+ const ads = createWebViewBannerAds();
454
+ const controller = new AbortController();
455
+ const banner = await ads.attachBanner("AD_GROUP_ID", "#banner", {
456
+ theme: "auto", tone: "grey", variant: "card", signal: controller.signal,
457
+ callbacks: { onNoFill: () => console.log("No banner available") }
458
+ });
459
+ // On unmount: controller.abort() also cancels a pending attachment.
460
+ banner.destroy(); // idempotent; only this handle is destroyed
461
+ ```
462
+
463
+ Initialization is lazy, shared within an adapter, and bounded by `initializeTimeoutMs`
464
+ (default 15 seconds; integer milliseconds from 1 to 2,147,483,647). A failed initialization can be retried.
465
+ An attachment resolves when the provider returns its handle; it does not claim that an
466
+ ad rendered or earned a reward. Render/view/click/impression/failure/no-fill callbacks
467
+ are forwarded on a microtask and suppressed after destruction. Synchronous attachment
468
+ failure rejects with `BANNER_ATTACH_FAILED`.
469
+
470
+ Each live attachment must own a distinct DOM element. SDK instances reject duplicate
471
+ targets with `BANNER_TARGET_IN_USE` until the previous handle is destroyed. Do not mix
472
+ raw provider attachments and adapter attachments on the same element. No global
473
+ `TossAds.destroyAll()` call is made. Use an `AbortSignal` when the target may unmount
474
+ while initialization is pending; the pending call rejects after initialization settles
475
+ and never attaches to an aborted target.
476
+
477
+ If provider cleanup throws, the handle still becomes locally destroyed and releases its
478
+ target claim; the provider exception remains observable. An attachment failure combined
479
+ with a cleanup failure rejects with an `AggregateError` retaining both errors.
@@ -0,0 +1,36 @@
1
+ import { type AdShowResult } from "../index.js";
2
+ import type { FullScreenAdSupport } from "./platform-contract.js";
3
+ import type { PlatformLoader } from "../platform-loader.js";
4
+ export type AdsPlatformLoader = PlatformLoader<FullScreenAdSupport>;
5
+ /** Ad kinds the SDK distinguishes; only full-screen ads exist today. */
6
+ export type SdkAdType = "fullscreen";
7
+ export interface AdsOptions {
8
+ /** Resolved platform loader, supplied by the runtime-specific factory. */
9
+ loader: AdsPlatformLoader;
10
+ /** Overall deadline for the show flow (default 60000ms; 0 disables). */
11
+ showTimeoutMs?: number;
12
+ /**
13
+ * Overall deadline for each load, covering framework acquisition and the
14
+ * provider load flow (default 30000ms; 0 disables).
15
+ */
16
+ loadTimeoutMs?: number;
17
+ }
18
+ export interface AdsAdapter {
19
+ /** Loads (or joins an in-flight load of) the full-screen ad for adGroupId. */
20
+ loadFullScreenAd(adGroupId: string): Promise<void>;
21
+ /**
22
+ * Shows the full-screen ad whose load already completed and resolves with
23
+ * its terminal outcome. The ad is single-use and claimed synchronously:
24
+ * once a show starts, the unit needs a fresh load for the next attempt.
25
+ */
26
+ showFullScreenAd(adGroupId: string): Promise<AdShowResult>;
27
+ }
28
+ /**
29
+ * Shared full-screen ad adapter. Load state is
30
+ * tracked per ad type + ad unit, in-flight duplicate loads share one
31
+ * request, and rewards come only from the provider's `userEarnedReward`
32
+ * event. The adapter produces ad outcomes; server-side reward requests,
33
+ * session checks, and ledger handling belong to the consumer.
34
+ */
35
+ export declare function createAdsAdapter(options: AdsOptions): AdsAdapter;
36
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/ads/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAEzE,OAAO,KAAK,EAAyB,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACzF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,mBAAmB,CAAC,CAAC;AAEpE,wEAAwE;AACxE,MAAM,MAAM,SAAS,GAAG,YAAY,CAAC;AAErC,MAAM,WAAW,UAAU;IACzB,0EAA0E;IAC1E,MAAM,EAAE,iBAAiB,CAAC;IAC1B,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,8EAA8E;IAC9E,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD;;;;OAIG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC5D;AAmED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU,CAqKhE"}
@@ -0,0 +1,219 @@
1
+ import { SdkError } from "../index.js";
2
+ import { runEventFlow } from "../event-flow.js";
3
+ const DEFAULT_SHOW_TIMEOUT_MS = 60_000;
4
+ const DEFAULT_LOAD_TIMEOUT_MS = 30_000;
5
+ function createAdDeadline(timeoutMs, now = monotonicNow) {
6
+ if (!(timeoutMs > 0)) {
7
+ return { remainingMs: () => undefined, race: (promise) => promise };
8
+ }
9
+ const startedAt = now();
10
+ return {
11
+ remainingMs: () => timeoutMs - (now() - startedAt),
12
+ race: (promise) => new Promise((resolve, reject) => {
13
+ const timer = setTimeout(() => resolve(undefined), timeoutMs);
14
+ // Attaching handlers also sinks a settlement that happens after the
15
+ // deadline already won; the no-op reject/resolve after settling is
16
+ // ignored by promise semantics.
17
+ promise.then((value) => {
18
+ clearTimeout(timer);
19
+ resolve(value);
20
+ }, (error) => {
21
+ clearTimeout(timer);
22
+ reject(error);
23
+ });
24
+ })
25
+ };
26
+ }
27
+ /**
28
+ * Monotonic elapsed time: a system wall-clock correction while the loader
29
+ * is pending must not shrink or stretch the remaining budget.
30
+ */
31
+ function monotonicNow() {
32
+ return typeof performance !== "undefined" && typeof performance.now === "function"
33
+ ? performance.now()
34
+ : Date.now();
35
+ }
36
+ /**
37
+ * Shared full-screen ad adapter. Load state is
38
+ * tracked per ad type + ad unit, in-flight duplicate loads share one
39
+ * request, and rewards come only from the provider's `userEarnedReward`
40
+ * event. The adapter produces ad outcomes; server-side reward requests,
41
+ * session checks, and ledger handling belong to the consumer.
42
+ */
43
+ export function createAdsAdapter(options) {
44
+ const loader = options.loader;
45
+ const showTimeoutMs = options.showTimeoutMs ?? DEFAULT_SHOW_TIMEOUT_MS;
46
+ const loadTimeoutMs = options.loadTimeoutMs ?? DEFAULT_LOAD_TIMEOUT_MS;
47
+ const slots = new Map();
48
+ const showing = new Set();
49
+ const adKey = (adType, adGroupId) => `${adType}:${adGroupId}`;
50
+ const load = (adGroupId) => {
51
+ const id = adKey("fullscreen", adGroupId);
52
+ const existing = slots.get(id);
53
+ // Duplicate loads join the in-flight request, and an already-completed
54
+ // load stays loaded until a show claims it - neither re-registers.
55
+ if (existing) {
56
+ return existing.kind === "loaded" ? Promise.resolve() : existing.promise;
57
+ }
58
+ // Slot updates run inside this promise's settlement — before the
59
+ // caller's await/catch continuation — so a caller that immediately
60
+ // retries after a failure starts a fresh registration, and a caller
61
+ // that immediately shows after a success finds the slot loaded. The
62
+ // identity check keeps a stale task from touching a newer task's slot.
63
+ const promise = new Promise((resolve, reject) => {
64
+ runLoad(adGroupId).then(() => {
65
+ const current = slots.get(id);
66
+ if (current?.kind === "loading" && current.promise === promise) {
67
+ slots.set(id, { kind: "loaded" });
68
+ }
69
+ resolve();
70
+ }, (error) => {
71
+ const current = slots.get(id);
72
+ if (current?.kind === "loading" && current.promise === promise) {
73
+ slots.delete(id);
74
+ }
75
+ reject(error);
76
+ });
77
+ });
78
+ slots.set(id, { kind: "loading", promise });
79
+ return promise;
80
+ };
81
+ const show = (adGroupId) => {
82
+ const id = adKey("fullscreen", adGroupId);
83
+ if (showing.has(id)) {
84
+ return Promise.reject(new SdkError("AD_ALREADY_SHOWING", `ad group ${adGroupId} is already being shown`));
85
+ }
86
+ const slot = slots.get(id);
87
+ if (!slot || slot.kind === "loading") {
88
+ return Promise.reject(new SdkError("AD_NOT_LOADED", `loadFullScreenAd must complete before showing ad group ${adGroupId}`));
89
+ }
90
+ // Claim synchronously: the loaded marker is consumed the moment the show
91
+ // starts, so a concurrent reload registers fresh instead of aliasing a
92
+ // marker this show will never own again.
93
+ slots.delete(id);
94
+ showing.add(id);
95
+ return (async () => {
96
+ const timedOut = () => ({ status: "failed", reason: "ad show flow timed out" });
97
+ try {
98
+ // The whole show shares one budget: the loader races it, the event
99
+ // flow gets what remains, and the flow's own deadline produces the
100
+ // timeout result — its subscription cleanup completes before this
101
+ // promise settles, so `showing` is only released after the
102
+ // subscription is gone.
103
+ const deadline = createAdDeadline(showTimeoutMs);
104
+ const framework = await deadline.race(loadFramework());
105
+ if (!framework) {
106
+ return timedOut();
107
+ }
108
+ if (typeof framework.showFullScreenAd.isSupported === "function" &&
109
+ !framework.showFullScreenAd.isSupported()) {
110
+ throw new SdkError("UNSUPPORTED", "full-screen ads are not supported on this app version");
111
+ }
112
+ const remaining = deadline.remainingMs();
113
+ if (remaining !== undefined && remaining <= 0) {
114
+ return timedOut();
115
+ }
116
+ let reward;
117
+ return await runEventFlow({
118
+ register: (emit) => framework.showFullScreenAd({
119
+ options: { adGroupId },
120
+ onEvent: emit,
121
+ onError: (error) => emit({ type: "sdkError", error })
122
+ }),
123
+ reduce: (event) => {
124
+ if (event.type === "sdkError") {
125
+ const message = errorMessage(event.error);
126
+ return {
127
+ done: true,
128
+ result: { status: "failed", reason: `full-screen ad show failed: ${message}` }
129
+ };
130
+ }
131
+ return reduceShowEvent(event, () => reward, (value) => (reward = value));
132
+ },
133
+ onTimeout: timedOut,
134
+ timeoutMs: remaining ?? 0
135
+ });
136
+ }
137
+ finally {
138
+ showing.delete(id);
139
+ }
140
+ })();
141
+ };
142
+ async function runLoad(adGroupId) {
143
+ // One deadline covers framework acquisition AND the provider flow. A
144
+ // loader that wins the race only just — or too late — leaves no budget
145
+ // for the provider registration, which is then skipped entirely.
146
+ const deadline = createAdDeadline(loadTimeoutMs);
147
+ const framework = await deadline.race(loadFramework());
148
+ if (!framework) {
149
+ throw new SdkError("AD_LOAD_TIMEOUT", "full-screen ad load timed out");
150
+ }
151
+ if (typeof framework.loadFullScreenAd.isSupported === "function" &&
152
+ !framework.loadFullScreenAd.isSupported()) {
153
+ throw new SdkError("UNSUPPORTED", "full-screen ads are not supported on this app version");
154
+ }
155
+ const remaining = deadline.remainingMs();
156
+ if (remaining !== undefined && remaining <= 0) {
157
+ throw new SdkError("AD_LOAD_TIMEOUT", "full-screen ad load timed out");
158
+ }
159
+ const outcome = await runEventFlow({
160
+ register: (emit) => framework.loadFullScreenAd({
161
+ options: { adGroupId },
162
+ onEvent: emit,
163
+ onError: (error) => emit({ type: "sdkError", error })
164
+ }),
165
+ reduce: (event) => event.type === "loaded"
166
+ ? { done: true, result: undefined }
167
+ : { done: true, result: loadFailure(event.error) },
168
+ onTimeout: () => new SdkError("AD_LOAD_TIMEOUT", "full-screen ad load timed out"),
169
+ timeoutMs: remaining ?? 0
170
+ });
171
+ if (outcome instanceof SdkError) {
172
+ throw outcome;
173
+ }
174
+ }
175
+ async function loadFramework() {
176
+ const result = await loader();
177
+ if (!result.available) {
178
+ throw new SdkError("SDK_UNAVAILABLE", result.reason);
179
+ }
180
+ return result.module;
181
+ }
182
+ return { loadFullScreenAd: load, showFullScreenAd: show };
183
+ }
184
+ function reduceShowEvent(event, getReward, setReward) {
185
+ switch (event.type) {
186
+ case "userEarnedReward": {
187
+ // Runtime guard against payload-shape drift; the typed contract marks
188
+ // data as required.
189
+ if (event.data) {
190
+ setReward({ unitType: event.data.unitType, unitAmount: event.data.unitAmount });
191
+ }
192
+ return { done: false };
193
+ }
194
+ case "dismissed": {
195
+ const earned = getReward();
196
+ // Reward only when the provider actually emitted userEarnedReward;
197
+ // dismissal alone never grants anything.
198
+ return earned
199
+ ? { done: true, result: { status: "rewarded", reward: earned } }
200
+ : { done: true, result: { status: "dismissed" } };
201
+ }
202
+ case "failedToShow":
203
+ return { done: true, result: { status: "failed", reason: "failedToShow" } };
204
+ default:
205
+ // requested / show / impression / clicked keep the flow running.
206
+ return { done: false };
207
+ }
208
+ }
209
+ /** Transient provider failures are retryable and must not read as UNSUPPORTED. */
210
+ function loadFailure(error) {
211
+ if (error instanceof SdkError)
212
+ return error;
213
+ return new SdkError("AD_LOAD_FAILED", `full-screen ad load failed: ${errorMessage(error)}`, {
214
+ cause: error
215
+ });
216
+ }
217
+ function errorMessage(error) {
218
+ return error instanceof Error ? error.message : String(error);
219
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Structural contract for the official React Native and WebView full-screen Ads APIs,
3
+ * declared from the published documentation so this package type-checks and
4
+ * ships without requiring the official SDK to be installed. The runtime
5
+ * module stays an optional peer dependency.
6
+ */
7
+ export interface LoadFullScreenAdParams {
8
+ options: {
9
+ adGroupId: string;
10
+ };
11
+ onEvent: (event: {
12
+ type: "loaded";
13
+ }) => void;
14
+ onError: (error: unknown) => void;
15
+ }
16
+ /** Discriminated union per the provider contract: only userEarnedReward carries data. */
17
+ export type FullScreenAdShowEvent = {
18
+ type: "requested" | "show" | "impression" | "clicked" | "dismissed" | "failedToShow";
19
+ } | {
20
+ type: "userEarnedReward";
21
+ data: {
22
+ unitType: string;
23
+ unitAmount: number;
24
+ };
25
+ };
26
+ export interface ShowFullScreenAdParams {
27
+ options: {
28
+ adGroupId: string;
29
+ };
30
+ onEvent: (event: FullScreenAdShowEvent) => void;
31
+ onError: (error: unknown) => void;
32
+ }
33
+ export interface FullScreenAdFunctions {
34
+ loadFullScreenAd(params: LoadFullScreenAdParams): () => void;
35
+ showFullScreenAd(params: ShowFullScreenAdParams): () => void;
36
+ }
37
+ export interface FullScreenAdSupport {
38
+ loadFullScreenAd: FullScreenAdFunctions["loadFullScreenAd"] & {
39
+ isSupported?: () => boolean;
40
+ };
41
+ showFullScreenAd: FullScreenAdFunctions["showFullScreenAd"] & {
42
+ isSupported?: () => boolean;
43
+ };
44
+ }
45
+ //# sourceMappingURL=platform-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-contract.d.ts","sourceRoot":"","sources":["../../src/ads/platform-contract.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/B,OAAO,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7C,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACnC;AAED,yFAAyF;AACzF,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,cAAc,CAAA;CAAE,GACxF;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAEjF,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAChD,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,IAAI,CAAC;IAC7D,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,IAAI,CAAC;CAC9D;AAED,MAAM,WAAW,mBAAmB;IAClC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,GAAG;QAC5D,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;KAC7B,CAAC;IACF,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,GAAG;QAC5D,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;KAC7B,CAAC;CACH"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Structural contract for the official React Native and WebView full-screen Ads APIs,
3
+ * declared from the published documentation so this package type-checks and
4
+ * ships without requiring the official SDK to be installed. The runtime
5
+ * module stays an optional peer dependency.
6
+ */
7
+ export {};
@@ -1,7 +1,9 @@
1
- import { type IapGrantCallback, type IapProduct } from "../index.js";
1
+ import { type IapGrantCallback, type IapProduct, type IapSubscriptionInfo, type IapOrderHistoryPage } from "../index.js";
2
2
  import type { IapPendingOrder, IapPurchaseResult } from "../index.js";
3
3
  import { type IapPlatformLoader } from "./platform-contract.js";
4
4
  export interface IapAdapterOptions {
5
+ /** Provider paging capability, fixed by the runtime factory. */
6
+ orderHistoryPagination: "cursor" | "first_page_only";
5
7
  grant: IapGrantCallback;
6
8
  /** Loader for the platform SDK module (already normalized per entry point). */
7
9
  loader: IapPlatformLoader;
@@ -14,6 +16,14 @@ export interface IapRecoveryResult {
14
16
  reason?: string;
15
17
  }
16
18
  export interface IapAdapter {
19
+ /** Read-only history; WebView rejects non-null cursors rather than repeating page one. */
20
+ getCompletedOrRefundedOrders(params?: {
21
+ key?: string | null;
22
+ }): Promise<IapOrderHistoryPage>;
23
+ /** Read-only provider snapshot; never grants or revokes entitlements. */
24
+ getSubscriptionInfo(orderId: string): Promise<{
25
+ subscription: IapSubscriptionInfo;
26
+ }>;
17
27
  /** Lists purchasable products (one-time and subscription together). */
18
28
  getProductItemList(): Promise<{
19
29
  products: IapProduct[];
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/iap/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gBAAgB,EAErB,KAAK,UAAU,EAEhB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,KAAK,iBAAiB,EAA8B,MAAM,wBAAwB,CAAC;AAG5F,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,gBAAgB,CAAC;IACxB,+EAA+E;IAC/E,MAAM,EAAE,iBAAiB,CAAC;IAC1B,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,WAAW,GAAG,cAAc,GAAG,eAAe,CAAC;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,kBAAkB,IAAI,OAAO,CAAC;QAAE,QAAQ,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC,CAAC;IAC1D;;;;OAIG;IACH,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACzD,uEAAuE;IACvE,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAChF,6EAA6E;IAC7E,gBAAgB,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,eAAe,EAAE,CAAA;KAAE,CAAC,CAAC;IAC3D;;;;;OAKG;IACH,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACzE;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CA0JvE"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/iap/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gBAAgB,EAErB,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EAEzB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,KAAK,iBAAiB,EAA8B,MAAM,wBAAwB,CAAC;AAK5F,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,sBAAsB,EAAE,QAAQ,GAAG,iBAAiB,CAAC;IACrD,KAAK,EAAE,gBAAgB,CAAC;IACxB,+EAA+E;IAC/E,MAAM,EAAE,iBAAiB,CAAC;IAC1B,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,WAAW,GAAG,cAAc,GAAG,eAAe,CAAC;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,0FAA0F;IAC1F,4BAA4B,CAAC,MAAM,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC7F,yEAAyE;IACzE,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,YAAY,EAAE,mBAAmB,CAAA;KAAE,CAAC,CAAC;IACrF,uEAAuE;IACvE,kBAAkB,IAAI,OAAO,CAAC;QAAE,QAAQ,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC,CAAC;IAC1D;;;;OAIG;IACH,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACzD,uEAAuE;IACvE,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAChF,6EAA6E;IAC7E,gBAAgB,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,eAAe,EAAE,CAAA;KAAE,CAAC,CAAC;IAC3D;;;;;OAKG;IACH,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACzE;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAuKvE"}