@ait-kit/sdk 0.2.0 → 0.4.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 (68) hide show
  1. package/README.md +178 -9
  2. package/dist/index.d.ts +9 -5
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/platform-loader.d.ts +16 -0
  5. package/dist/platform-loader.d.ts.map +1 -0
  6. package/dist/platform-loader.js +35 -0
  7. package/dist/promotion/adapter.d.ts +3 -0
  8. package/dist/promotion/adapter.d.ts.map +1 -0
  9. package/dist/promotion/adapter.js +128 -0
  10. package/dist/promotion/platform-contract.d.ts +37 -0
  11. package/dist/promotion/platform-contract.d.ts.map +1 -0
  12. package/dist/promotion/platform-contract.js +1 -0
  13. package/dist/review/adapter.d.ts +3 -0
  14. package/dist/review/adapter.d.ts.map +1 -0
  15. package/dist/review/adapter.js +33 -0
  16. package/dist/review/platform-contract.d.ts +21 -0
  17. package/dist/review/platform-contract.d.ts.map +1 -0
  18. package/dist/review/platform-contract.js +1 -0
  19. package/dist/rn/ads.d.ts.map +1 -1
  20. package/dist/rn/ads.js +120 -119
  21. package/dist/rn/framework-loader.d.ts.map +1 -1
  22. package/dist/rn/framework-loader.js +6 -17
  23. package/dist/rn/iap.d.ts.map +1 -1
  24. package/dist/rn/iap.js +9 -28
  25. package/dist/rn/identity.d.ts.map +1 -1
  26. package/dist/rn/identity.js +11 -22
  27. package/dist/rn/index.d.ts +2 -0
  28. package/dist/rn/index.d.ts.map +1 -1
  29. package/dist/rn/index.js +2 -0
  30. package/dist/rn/notify-share.d.ts +3 -3
  31. package/dist/rn/notify-share.d.ts.map +1 -1
  32. package/dist/rn/notify-share.js +16 -33
  33. package/dist/rn/official-module.d.ts +83 -0
  34. package/dist/rn/official-module.d.ts.map +1 -0
  35. package/dist/rn/official-module.js +106 -0
  36. package/dist/rn/platform-loader.d.ts +4 -0
  37. package/dist/rn/platform-loader.d.ts.map +1 -0
  38. package/dist/rn/platform-loader.js +5 -0
  39. package/dist/rn/promotion.d.ts +9 -0
  40. package/dist/rn/promotion.d.ts.map +1 -0
  41. package/dist/rn/promotion.js +11 -0
  42. package/dist/rn/review.d.ts +7 -0
  43. package/dist/rn/review.d.ts.map +1 -0
  44. package/dist/rn/review.js +10 -0
  45. package/dist/rn/storage.d.ts.map +1 -1
  46. package/dist/rn/storage.js +4 -22
  47. package/dist/share/platform-contract.d.ts +4 -4
  48. package/dist/share/platform-contract.js +5 -5
  49. package/dist/web/framework-loader.d.ts.map +1 -1
  50. package/dist/web/framework-loader.js +7 -23
  51. package/dist/web/identity.d.ts.map +1 -1
  52. package/dist/web/identity.js +12 -33
  53. package/dist/web/index.d.ts +2 -0
  54. package/dist/web/index.d.ts.map +1 -1
  55. package/dist/web/index.js +4 -5
  56. package/dist/web/notify-share.d.ts +3 -2
  57. package/dist/web/notify-share.d.ts.map +1 -1
  58. package/dist/web/notify-share.js +11 -33
  59. package/dist/web/platform-loader.d.ts +4 -0
  60. package/dist/web/platform-loader.d.ts.map +1 -0
  61. package/dist/web/platform-loader.js +5 -0
  62. package/dist/web/promotion.d.ts +9 -0
  63. package/dist/web/promotion.d.ts.map +1 -0
  64. package/dist/web/promotion.js +10 -0
  65. package/dist/web/review.d.ts +7 -0
  66. package/dist/web/review.d.ts.map +1 -0
  67. package/dist/web/review.js +9 -0
  68. package/package.json +3 -3
package/README.md CHANGED
@@ -99,7 +99,7 @@ SDK initialization never grants or completes pending orders by itself.
99
99
  | Grant completion notify | ✅ | ✅ | sent only after server grant confirms |
100
100
  | Full-screen ads | ✅ | ➖ | ads are RN-only today |
101
101
  | Notification agreement | ✅ | ✅ | event-based, one template per request |
102
- | Share link / share sheet | ✅ | ✅ | `intoss://` paths; `closed` ≠ shared |
102
+ | Share link / share sheet | ✅ | ✅ | `intoss://` paths; `completed` ≠ shared |
103
103
  | Unsupported app version | `SdkError("UNSUPPORTED")` | same | per-function `isSupported` gates |
104
104
 
105
105
  Notification and sharing adapters ship in both entries (see below).
@@ -136,8 +136,13 @@ Contracts:
136
136
  reject with `SdkError("INVALID_LOGIN_RESULT")`; unsupported environments
137
137
  with `UNSUPPORTED`; SDK rejections propagate unchanged.
138
138
  - **Anonymous key** results must be the documented `{ type: "HASH", hash }`
139
- shape. Anything else (including sentinel values) rejects with
140
- `SdkError("INVALID_ANONYMOUS_KEY")` — the adapter never invents a key.
139
+ shape. Anything else (including the official SDK's `"ERROR"` sentinel)
140
+ rejects with `SdkError("INVALID_ANONYMOUS_KEY")` — the adapter never
141
+ invents a key. One exception: on React Native the official
142
+ `getAnonymousKey()` resolves `undefined` when the installed app is below
143
+ the feature's minimum version, which rejects with
144
+ `SdkError("UNSUPPORTED")` instead — an unsupported environment, not a
145
+ malformed key.
141
146
  - **Storage** keys and string values pass through byte-for-byte: no
142
147
  namespace prefixing, no key transformation. Compose namespaced keys
143
148
  yourself (e.g. `cart:items`). `get` resolves `null` for missing keys;
@@ -171,10 +176,10 @@ const result = await notification.requestAgreement("TEMPLATE_CODE");
171
176
  const share = createReactNativeShare();
172
177
  const link = await share.createLink("intoss://my-app/about", "https://cdn/og.png");
173
178
 
174
- // 3. Share sheet: "closed" means the sheet flow ended — nothing more.
179
+ // 3. Share sheet: "completed" means the SDK share call finished — nothing more.
175
180
  const uiResult = await share.sendMessage(`check this out ${link}`);
176
- // uiResult.status: "closed" | "failed" — closed does NOT prove the user
177
- // shared and never grants share-reward eligibility.
181
+ // uiResult.status: "completed" | "failed" — completed does NOT prove the
182
+ // user shared and never grants share-reward eligibility.
178
183
  ```
179
184
 
180
185
  Contracts:
@@ -186,8 +191,10 @@ Contracts:
186
191
  - **Share links** validate the documented `intoss://` path contract
187
192
  (`INVALID_SHARE_PATH` otherwise) and pass the resolved link through
188
193
  verbatim. OG image generation is the consumer's concern.
189
- - **Share sheet** resolution is `closed`, not "completed" — reward grants
190
- and completion tracking stay with the consumer.
194
+ - **Share sheet** resolution is `completed`, meaning ONLY that the SDK share
195
+ call finished — the underlying SDKs do not report whether the sheet opened,
196
+ closed, or the user actually shared. Reward grants and completion tracking
197
+ stay with the consumer. (Renamed from `closed` in 0.3.0; see Migrating.)
191
198
  - Unsupported surfaces/app versions reject with `SdkError("UNSUPPORTED")`.
192
199
 
193
200
  ## React Native full-screen ads
@@ -216,7 +223,16 @@ Behavior:
216
223
  are single-use: after a show flow ends (rewarded, dismissed, failed,
217
224
  timeout), a fresh load is required.
218
225
  - The whole show flow has one deadline (`showTimeoutMs`, default 60s);
219
- `loadTimeoutMs` (default 30s) bounds loads.
226
+ `loadTimeoutMs` (default 30s) bounds loads. The deadline is a single
227
+ budget covering SDK loading AND the provider event flow: a loader that
228
+ consumes most of it leaves only the remainder for the event wait, and a
229
+ fully consumed budget never registers with the provider. Timeout results
230
+ come from the event flow's own deadline, so the subscription is cleaned
231
+ up before the timeout settles — no orphaned listener outlives the call.
232
+ - Slot updates land inside the returned promise's settlement: a failed
233
+ load frees its slot before the caller's `catch` resumes (an immediate
234
+ retry starts a fresh registration), and a successful load is promoted
235
+ before the caller's `await` resumes (an immediate show works).
220
236
  - Missing or failed-to-import framework → `SdkError("SDK_UNAVAILABLE")`;
221
237
  unsupported app versions → `SdkError("UNSUPPORTED")`. Load failures are
222
238
  never permanently cached — a later call retries the import.
@@ -234,3 +250,156 @@ notification and sharing.)
234
250
 
235
251
  `@ait-kit/sdk` is versioned independently from the server API packages
236
252
  (`@ait-kit/api-*`), which release in lockstep with each other.
253
+
254
+ ## Verified official SDK versions
255
+
256
+ The adapters are developed and continuously checked against these exact
257
+ official releases (see `scripts/test-package-tarballs.mjs`):
258
+
259
+ | Official package | Verified version | Peer range |
260
+ |---|---|---|
261
+ | `@apps-in-toss/framework` (RN) | 2.10.10 | `>=2.10.10` |
262
+ | `@apps-in-toss/web-framework` (Web) | 3.4.0 | `>=3.4.0` |
263
+
264
+ Older versions may work where the runtime surfaces match (every capability
265
+ is checked per function and missing ones reject with `UNSUPPORTED`), but
266
+ only the versions above are verified. Newer versions are expected to work
267
+ via the same structural checks; if an official export shape changes, the
268
+ per-operation errors surface it instead of silent misbehavior.
269
+
270
+ ## Migrating
271
+
272
+ ### 0.3.0 — share sheet result `closed` → `completed`
273
+
274
+ `sendMessage` now resolves `{ status: "completed" }` instead of
275
+ `{ status: "closed" }`. The meaning also got precise: `completed` states
276
+ only that the SDK share **call** finished. It does not prove the share
277
+ sheet opened or closed, that the user shared, or that any reward
278
+ eligibility was earned. Update any `status === "closed"` checks; failure
279
+ results (`status: "failed"` with `code`/`reason`) are unchanged.
280
+
281
+ ### 0.3.0 — RN adapters call the official export shapes
282
+
283
+ The React Native default loaders now convert the official flat exports
284
+ (`appLogin`, `getAnonymousKey`, `requestNotificationAgreement`,
285
+ `getTossShareLink`, `share`) into the shared adapter contracts. Previously
286
+ a correctly installed official RN SDK still produced
287
+ `SdkError("UNSUPPORTED")` for these five capabilities because the loaders
288
+ looked for the web SDK's namespaced shapes. Custom `framework` injections
289
+ using the internal namespaced contract or a loader keep working unchanged.
290
+
291
+ One error-mapping change rides along: the official RN `getAnonymousKey()`
292
+ resolving `undefined` (installed app below the feature's minimum version)
293
+ now rejects with `SdkError("UNSUPPORTED")` instead of
294
+ `INVALID_ANONYMOUS_KEY`; the `"ERROR"` sentinel still rejects with
295
+ `INVALID_ANONYMOUS_KEY`.
296
+
297
+ ## Review requests
298
+
299
+ ```ts
300
+ import { createReactNativeReview } from "@ait-kit/sdk/rn";
301
+ // Web: import { createWebReview } from "@ait-kit/sdk/web";
302
+ const review = createReactNativeReview(); // share one instance across screens
303
+ if (await review.isSupported()) await review.request();
304
+ ```
305
+
306
+ `ReviewAdapter` is a runtime-neutral root type. Factories accept `framework`
307
+ (shared `Review.request` contract or async loader), like the existing adapters.
308
+ RN translates the official `requestReview()` export; Web calls `Review.request()`.
309
+ The official SDK loads only on use. Missing features/support checkers return
310
+ `false`; import failure throws `SDK_UNAVAILABLE`; support-check exceptions propagate.
311
+ `request()` rechecks support and throws `UNSUPPORTED` when unavailable.
312
+ Concurrent requests on one instance share one Promise, cleared after success or
313
+ failure. There is no retry or timeout.
314
+
315
+ `Promise<void>` completion does not establish whether UI appeared or a review
316
+ was written. Never grant rewards, unlock features, or block navigation on review
317
+ completion. Complete and persist the core action first, then start a separately
318
+ error-handled review request without awaiting it in the core flow. The consumer
319
+ owns request timing, session limits, cooldowns and storage. Do not log raw SDK
320
+ errors by default.
321
+
322
+ Official reference: [Review.request](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/review/review.request.md)
323
+ (Android/iOS Toss 5.253.0+, checked by the SDK). Verified package surfaces:
324
+ RN 2.10.10 and Web 3.4.0. Device UI behavior requires separate manual verification;
325
+ unit/tarball tests use injected SDKs or a stubbed native bridge.
326
+
327
+ ## Explicit direct promotion rewards
328
+
329
+ ```ts
330
+ import { createReactNativePromotion } from "@ait-kit/sdk/rn";
331
+ // Web: import { createWebPromotion } from "@ait-kit/sdk/web";
332
+ const promotion = createReactNativePromotion({ timeoutMs: 15_000 });
333
+ const support = await promotion.getSupport();
334
+ // RN 2.10.10 exposes no support checker: support is "unknown", not "supported".
335
+ // Opt into direct grants explicitly, with app-specific eligibility/budget checks.
336
+ ```
337
+
338
+ `PromotionAdapter`, `PromotionSupport`, `PromotionGrantInput` and
339
+ `PromotionGrantResult` are runtime-neutral root types. Both factories accept
340
+ `framework` (shared `Promotion.grantReward` contract or async loader).
341
+ RN calls `grantPromotionReward({ params: { promotionCode, amount } })`;
342
+ Web calls only `Promotion.grantReward({ promotionCode, amount })`.
343
+ There is no legacy Web fallback or fallback to/from server payment flows.
344
+
345
+ `getSupport()` reports `unsupported` for missing functions or an explicit false
346
+ check, `supported` for a true check, and `unknown` when a function has no checker.
347
+ Import failures throw `SDK_UNAVAILABLE`; support-check exceptions propagate.
348
+ Every grant rechecks support. RN's documented `undefined` and the provider's
349
+ `UNSUPPORTED_APP_VERSION` throw `UNSUPPORTED`; malformed Web responses stay unknown.
350
+
351
+ Input errors throw `INVALID_PROMOTION_INPUT` before loading/calling the SDK:
352
+ `promotionCode` must be non-empty without surrounding whitespace (never trimmed
353
+ into a different code), and `amount` must be a positive safe integer. Campaign
354
+ eligibility, maximum amounts and budgets remain the consumer's responsibility.
355
+
356
+ Results:
357
+
358
+ - `granted` has a non-empty `rewardKey` and means only that the SDK reported success.
359
+ - `rejected` carries an explicit documented refusal code: `4100`, `4104`, `4105`,
360
+ `4108`, `4109`, `4110`, `4112`, or `4114`.
361
+ - `unknown` means confirmation is required: SDK exceptions/sentinels, malformed or
362
+ contradictory responses, deadlines, `UNKNOWN_ERROR`, `4113`, or new provider
363
+ codes. `4113` never becomes an inferred already-granted ledger state.
364
+
365
+ Provider messages and raw errors are not included in grant results. Provider codes
366
+ are retained only as bounded uppercase alphanumeric/underscore tokens; do not log
367
+ reward keys, promotion codes or whole SDK responses.
368
+
369
+ Share one adapter instance. A concurrent grant throws `PROMOTION_IN_PROGRESS`;
370
+ completed results are never cached as idempotent payments. `timeoutMs` defaults to
371
+ 0 (disabled), accepts integers through 2147483647, and covers loading and the SDK
372
+ call. Elapsed time is checked before dispatch and when the SDK settles, so delayed
373
+ timer callbacks cannot start an overdue payment or turn a late result into success.
374
+ After timeout the result is unknown, but the instance stays locked until the
375
+ actual operation settles. A loader finishing after timeout never starts payment.
376
+ A late success/failure cannot change the returned timeout result. There is no
377
+ cancellation, automatic retry, or protection across instances/devices/restarts.
378
+
379
+ Consumer UI pattern (all callbacks are supplied by the app):
380
+
381
+ ```ts
382
+ async function grantOnce(input, ui) {
383
+ ui.disableGrantAction();
384
+ try {
385
+ const result = await promotion.grantReward(input);
386
+ if (result.status === "unknown") {
387
+ ui.showConfirmationRequired(); // keep disabled; never retry automatically
388
+ return;
389
+ }
390
+ ui.showSdkResult(result); // client report only; not a server ledger receipt
391
+ } catch {
392
+ ui.showConfirmationRequired(); // do not dump raw exceptions or auto-reenable
393
+ }
394
+ }
395
+ ```
396
+
397
+ Posting a client result to a server is not independent payment verification.
398
+ Consumers requiring a server ledger must keep their existing three-stage server
399
+ payment path. This API does not require client-side mTLS certificates/proxy tokens
400
+ and does not replace TrailBase claims or owned-currency accounting.
401
+
402
+ Official reference: [Promotion.grantReward](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/promotion/promotion.grantreward.md).
403
+ Contracts are checked against RN 2.10.10 and Web 3.4.0 published types/code with
404
+ synthetic bridge fixtures. Device checks and official test-promotion calls remain
405
+ manual; no real promotion is invoked by tests.
package/dist/index.d.ts CHANGED
@@ -28,7 +28,7 @@ export type AdShowResult = {
28
28
  reason?: string;
29
29
  };
30
30
  /** Stable error codes for SDK adapter failures. */
31
- export type SdkErrorCode = "SDK_UNAVAILABLE" | "UNSUPPORTED" | "AD_NOT_LOADED" | "AD_ALREADY_SHOWING" | "AD_LOAD_FAILED" | "AD_LOAD_TIMEOUT" | "INVALID_LOGIN_RESULT" | "INVALID_ANONYMOUS_KEY" | "INVALID_SHARE_PATH";
31
+ export type SdkErrorCode = "SDK_UNAVAILABLE" | "UNSUPPORTED" | "AD_NOT_LOADED" | "AD_ALREADY_SHOWING" | "AD_LOAD_FAILED" | "AD_LOAD_TIMEOUT" | "INVALID_LOGIN_RESULT" | "INVALID_ANONYMOUS_KEY" | "INVALID_PROMOTION_INPUT" | "PROMOTION_IN_PROGRESS" | "INVALID_SHARE_PATH";
32
32
  /** Terminal agreement outcomes delivered by the platform event. */
33
33
  export type SdkNotificationAgreementType = "newAgreement" | "alreadyAgreed" | "agreementRejected";
34
34
  /**
@@ -62,12 +62,14 @@ export type SdkNotificationAgreementResult = {
62
62
  reason?: string;
63
63
  };
64
64
  /**
65
- * Result of opening the share UI. `closed` means the native share sheet
66
- * flow ended normally — it does NOT prove the user actually shared, and it
67
- * never grants share-reward eligibility on its own.
65
+ * Result of opening the share UI. `completed` means ONLY that the SDK share
66
+ * call finished — it does NOT prove the share sheet opened or closed, that
67
+ * the user actually shared, and it never grants share-reward eligibility.
68
+ * (Renamed from `closed` in 0.3.0: the underlying SDKs guarantee the call
69
+ * completing, not the sheet's lifecycle.)
68
70
  */
69
71
  export type SdkShareUiResult = {
70
- status: "closed";
72
+ status: "completed";
71
73
  } | {
72
74
  status: "failed";
73
75
  code?: string;
@@ -212,4 +214,6 @@ export declare class SdkError extends Error {
212
214
  cause?: unknown;
213
215
  });
214
216
  }
217
+ export type { ReviewAdapter } from "./review/platform-contract.js";
218
+ export type { PromotionAdapter, PromotionSupport, PromotionGrantInput, PromotionGrantResult } from "./promotion/platform-contract.js";
215
219
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /** Internal runtime-neutral loader primitives; no official SDK is imported here. */
2
+ export type PlatformLoadResult<T> = {
3
+ available: true;
4
+ module: T;
5
+ } | {
6
+ available: false;
7
+ reason: string;
8
+ };
9
+ export type PlatformLoader<T> = () => Promise<PlatformLoadResult<T>>;
10
+ /** Preserve injected loaders verbatim and construct defaults only when needed. */
11
+ export declare function resolvePlatformLoader<T>(framework: T | PlatformLoader<T> | undefined, createDefault: () => PlatformLoader<T>): PlatformLoader<T>;
12
+ /** Cache only successful selections; unavailable results and thrown loads retry. */
13
+ export declare function createCachedPlatformLoader<T>(load: PlatformLoader<T>, errorReason: (error: unknown) => string): PlatformLoader<T>;
14
+ /** Preserve existing diagnostic detail policies chosen by each adapter. */
15
+ export declare function frameworkImportError(packageName: string, error: unknown, includeDetails: boolean): string;
16
+ //# sourceMappingURL=platform-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-loader.d.ts","sourceRoot":"","sources":["../src/platform-loader.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAC5B;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,GAC9B;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AACzC,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;AAErE,kFAAkF;AAClF,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,SAAS,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,SAAS,EAC5C,aAAa,EAAE,MAAM,cAAc,CAAC,CAAC,CAAC,GACrC,cAAc,CAAC,CAAC,CAAC,CAInB;AAED,oFAAoF;AACpF,wBAAgB,0BAA0B,CAAC,CAAC,EAC1C,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EACvB,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,GACtC,cAAc,CAAC,CAAC,CAAC,CAcnB;AAED,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,GAAG,MAAM,CAKzG"}
@@ -0,0 +1,35 @@
1
+ /** Preserve injected loaders verbatim and construct defaults only when needed. */
2
+ export function resolvePlatformLoader(framework, createDefault) {
3
+ if (!framework)
4
+ return createDefault();
5
+ if (typeof framework === "function")
6
+ return framework;
7
+ return async () => ({ available: true, module: framework });
8
+ }
9
+ /** Cache only successful selections; unavailable results and thrown loads retry. */
10
+ export function createCachedPlatformLoader(load, errorReason) {
11
+ let cached;
12
+ return async () => {
13
+ if (cached)
14
+ return { available: true, module: cached.module };
15
+ try {
16
+ const result = await load();
17
+ // Keep the cache independent from the mutable result envelope returned
18
+ // to callers, as the original per-adapter loaders did.
19
+ if (result.available)
20
+ cached = { available: true, module: result.module };
21
+ return result;
22
+ }
23
+ catch (error) {
24
+ return { available: false, reason: errorReason(error) };
25
+ }
26
+ };
27
+ }
28
+ /** Preserve existing diagnostic detail policies chosen by each adapter. */
29
+ export function frameworkImportError(packageName, error, includeDetails) {
30
+ const prefix = `failed to import ${packageName}`;
31
+ if (!includeDetails)
32
+ return prefix;
33
+ const message = error instanceof Error ? error.message : String(error);
34
+ return `${prefix}: ${message}`;
35
+ }
@@ -0,0 +1,3 @@
1
+ import type { PromotionAdapter, PromotionPlatformLoader } from "./platform-contract.js";
2
+ export declare function createPromotionAdapter(loader: PromotionPlatformLoader, timeoutMs?: number): PromotionAdapter;
3
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/promotion/adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAChB,uBAAuB,EACxB,MAAM,wBAAwB,CAAC;AAiDhC,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,uBAAuB,EAAE,SAAS,SAAI,GAAG,gBAAgB,CA+DvG"}
@@ -0,0 +1,128 @@
1
+ import { SdkError } from "../index.js";
2
+ // Official Promotion.grantReward documentation (RN 2.10.10 / Web 3.4.0).
3
+ // 4113 is intentionally absent: it cannot establish this attempt's outcome.
4
+ const REJECTION_CODES = new Set(["4100", "4104", "4105", "4108", "4109", "4110", "4112", "4114"]);
5
+ function unknown(reason, providerCode) {
6
+ return providerCode === undefined ? { status: "unknown", reason } : { status: "unknown", reason, providerCode };
7
+ }
8
+ function normalize(value, thrown) {
9
+ if (typeof value !== "object" || value === null) {
10
+ return unknown(thrown || value === "ERROR" ? "sdk_error" : "invalid_response");
11
+ }
12
+ const data = value;
13
+ const rawCode = data.errorCode ?? data.code;
14
+ const code = typeof rawCode === "string" && /^[A-Z0-9_]{1,64}$/.test(rawCode) ? rawCode : undefined;
15
+ const hasError = "errorCode" in data || "code" in data || "error" in data || "message" in data;
16
+ if (("key" in data && (thrown || hasError)) ||
17
+ (data.code !== undefined && data.errorCode !== undefined && data.code !== data.errorCode)) {
18
+ return unknown("invalid_response", code);
19
+ }
20
+ if (!thrown && typeof data.key === "string" && data.key.trim().length > 0) {
21
+ return { status: "granted", rewardKey: data.key };
22
+ }
23
+ if ("key" in data)
24
+ return unknown("invalid_response", code);
25
+ if (code === "UNSUPPORTED_APP_VERSION") {
26
+ throw new SdkError("UNSUPPORTED", "direct promotion rewards are not supported on this app version");
27
+ }
28
+ if (code && REJECTION_CODES.has(code))
29
+ return { status: "rejected", providerCode: code };
30
+ return unknown(code ? "ambiguous_provider_error" : thrown ? "sdk_error" : "invalid_response", code);
31
+ }
32
+ function support(platform) {
33
+ const grant = platform.Promotion?.grantReward;
34
+ if (typeof grant !== "function")
35
+ return "unsupported";
36
+ if (typeof grant.isSupported !== "function")
37
+ return "unknown";
38
+ return grant.isSupported() ? "supported" : "unsupported";
39
+ }
40
+ function validate(input) {
41
+ if (!input || typeof input.promotionCode !== "string" || !input.promotionCode.trim() ||
42
+ input.promotionCode !== input.promotionCode.trim() || !Number.isSafeInteger(input.amount) || input.amount <= 0) {
43
+ throw new SdkError("INVALID_PROMOTION_INPUT", "promotionCode must be non-empty without surrounding whitespace and amount must be a positive safe integer");
44
+ }
45
+ // Snapshot before loading; callers cannot change the pending payment input.
46
+ return { promotionCode: input.promotionCode, amount: input.amount };
47
+ }
48
+ export function createPromotionAdapter(loader, timeoutMs = 0) {
49
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0 || timeoutMs > 2_147_483_647) {
50
+ throw new SdkError("INVALID_PROMOTION_INPUT", "timeoutMs must be an integer from 0 to 2147483647");
51
+ }
52
+ let inFlight = false;
53
+ const load = async () => {
54
+ const result = await loader();
55
+ if (!result.available)
56
+ throw new SdkError("SDK_UNAVAILABLE", result.reason);
57
+ return result.module;
58
+ };
59
+ return {
60
+ async getSupport() { return support(await load()); },
61
+ grantReward(input) {
62
+ let validated;
63
+ try {
64
+ validated = validate(input);
65
+ }
66
+ catch (error) {
67
+ return Promise.reject(error);
68
+ }
69
+ if (inFlight)
70
+ return Promise.reject(new SdkError("PROMOTION_IN_PROGRESS", "a direct promotion request is still in progress"));
71
+ inFlight = true;
72
+ const now = typeof performance !== "undefined" && typeof performance.now === "function"
73
+ ? performance.now.bind(performance)
74
+ : Date.now;
75
+ const startedAt = now();
76
+ let expired = false;
77
+ // Timers cannot run while JS is blocked or draining microtasks. Check the
78
+ // elapsed budget too, using one clock source for the entire request.
79
+ const hasExpired = () => expired || (timeoutMs > 0 && now() - startedAt >= timeoutMs);
80
+ let timer;
81
+ const work = Promise.resolve().then(async () => {
82
+ if (hasExpired())
83
+ return unknown("timeout");
84
+ const platform = await load();
85
+ // A loading timeout must not launch a new payment later.
86
+ if (hasExpired())
87
+ return unknown("timeout");
88
+ const availability = support(platform);
89
+ if (hasExpired())
90
+ return unknown("timeout");
91
+ if (availability === "unsupported") {
92
+ throw new SdkError("UNSUPPORTED", "the installed SDK or app does not support direct promotion rewards");
93
+ }
94
+ let response;
95
+ try {
96
+ response = await platform.Promotion.grantReward(validated);
97
+ }
98
+ catch (error) {
99
+ if (hasExpired())
100
+ return unknown("timeout");
101
+ // The RN conversion uses this only for the documented undefined result.
102
+ if (error instanceof SdkError && error.code === "UNSUPPORTED")
103
+ throw error;
104
+ return normalize(error, true);
105
+ }
106
+ if (hasExpired())
107
+ return unknown("timeout");
108
+ return normalize(response, false);
109
+ }).catch((error) => {
110
+ // A delayed loader/support failure must not beat an elapsed deadline.
111
+ if (hasExpired())
112
+ return unknown("timeout");
113
+ throw error;
114
+ }).finally(() => {
115
+ inFlight = false;
116
+ if (timer !== undefined)
117
+ clearTimeout(timer);
118
+ });
119
+ if (timeoutMs === 0)
120
+ return work;
121
+ const deadline = new Promise(resolve => {
122
+ timer = setTimeout(() => { expired = true; resolve(unknown("timeout")); }, timeoutMs);
123
+ });
124
+ // Only actual SDK/load settlement releases the lock, not this race.
125
+ return Promise.race([work, deadline]);
126
+ }
127
+ };
128
+ }
@@ -0,0 +1,37 @@
1
+ export type PromotionSupport = "supported" | "unsupported" | "unknown";
2
+ export interface PromotionGrantInput {
3
+ promotionCode: string;
4
+ amount: number;
5
+ }
6
+ export type PromotionGrantResult = {
7
+ status: "granted";
8
+ rewardKey: string;
9
+ } | {
10
+ status: "rejected";
11
+ providerCode: string;
12
+ } | {
13
+ status: "unknown";
14
+ reason: "sdk_error" | "timeout" | "invalid_response" | "ambiguous_provider_error";
15
+ providerCode?: string;
16
+ };
17
+ /** Client SDK reports are not independently verified server ledger evidence. */
18
+ export interface PromotionAdapter {
19
+ getSupport(): Promise<PromotionSupport>;
20
+ grantReward(input: PromotionGrantInput): Promise<PromotionGrantResult>;
21
+ }
22
+ /** Injected results are untrusted, just like the native bridge response. */
23
+ export interface PromotionPlatformSdk {
24
+ Promotion?: {
25
+ grantReward?: ((input: PromotionGrantInput) => Promise<unknown>) & {
26
+ isSupported?: () => boolean;
27
+ };
28
+ };
29
+ }
30
+ export type PromotionPlatformLoader = () => Promise<{
31
+ available: true;
32
+ module: PromotionPlatformSdk;
33
+ } | {
34
+ available: false;
35
+ reason: string;
36
+ }>;
37
+ //# sourceMappingURL=platform-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-contract.d.ts","sourceRoot":"","sources":["../../src/promotion/platform-contract.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,aAAa,GAAG,SAAS,CAAC;AAEvE,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,oBAAoB,GAC5B;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC5C;IACE,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,kBAAkB,GAAG,0BAA0B,CAAC;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEN,gFAAgF;AAChF,MAAM,WAAW,gBAAgB;IAC/B,UAAU,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACxC,WAAW,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACxE;AAED,4EAA4E;AAC5E,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE;QACV,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG;YACjE,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;SAC7B,CAAC;KACH,CAAC;CACH;AAED,MAAM,MAAM,uBAAuB,GAAG,MAAM,OAAO,CAC/C;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAA;CAAE,GACjD;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CACvC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { ReviewAdapter, ReviewPlatformLoader } from "./platform-contract.js";
2
+ export declare function createReviewAdapter(loader: ReviewPlatformLoader): ReviewAdapter;
3
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/review/adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAqB,MAAM,wBAAwB,CAAC;AAErG,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,oBAAoB,GAAG,aAAa,CA6B/E"}
@@ -0,0 +1,33 @@
1
+ import { SdkError } from "../index.js";
2
+ export function createReviewAdapter(loader) {
3
+ let inFlight;
4
+ const load = async () => {
5
+ const result = await loader();
6
+ if (!result.available)
7
+ throw new SdkError("SDK_UNAVAILABLE", result.reason);
8
+ return result.module;
9
+ };
10
+ const supported = (platform) => {
11
+ const request = platform.Review?.request;
12
+ return typeof request === "function" &&
13
+ typeof request.isSupported === "function" && request.isSupported() === true;
14
+ };
15
+ return {
16
+ async isSupported() {
17
+ return supported(await load());
18
+ },
19
+ request() {
20
+ if (inFlight)
21
+ return inFlight;
22
+ // Schedule loading after publishing the lock, including reentrant loaders.
23
+ inFlight = Promise.resolve().then(async () => {
24
+ const platform = await load();
25
+ if (!supported(platform)) {
26
+ throw new SdkError("UNSUPPORTED", "review requests are not supported on this app version");
27
+ }
28
+ await platform.Review.request();
29
+ }).finally(() => { inFlight = undefined; });
30
+ return inFlight;
31
+ }
32
+ };
33
+ }
@@ -0,0 +1,21 @@
1
+ /** Minimal injectable shape shared by the platform adapters. */
2
+ export interface ReviewPlatformSdk {
3
+ Review?: {
4
+ request?: (() => Promise<void>) & {
5
+ isSupported?: () => boolean;
6
+ };
7
+ };
8
+ }
9
+ export type ReviewPlatformLoader = () => Promise<{
10
+ available: true;
11
+ module: ReviewPlatformSdk;
12
+ } | {
13
+ available: false;
14
+ reason: string;
15
+ }>;
16
+ /** Completion only means the SDK call settled, never that a review was written. */
17
+ export interface ReviewAdapter {
18
+ isSupported(): Promise<boolean>;
19
+ request(): Promise<void>;
20
+ }
21
+ //# sourceMappingURL=platform-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-contract.d.ts","sourceRoot":"","sources":["../../src/review/platform-contract.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG;YAAE,WAAW,CAAC,EAAE,MAAM,OAAO,CAAA;SAAE,CAAC;KACnE,CAAC;CACH;AAED,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,CAC5C;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,GAC9C;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CACvC,CAAC;AAEF,mFAAmF;AACnF,MAAM,WAAW,aAAa;IAC5B,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1 @@
1
- {"version":3,"file":"ads.d.ts","sourceRoot":"","sources":["../../src/rn/ads.ts"],"names":[],"mappings":"AAGA,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAEzE,OAAO,KAAK,EAAyB,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC1F,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AAE/B,wEAAwE;AACxE,MAAM,MAAM,SAAS,GAAG,YAAY,CAAC;AAErC,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,SAAS,CAAC,EAAE,eAAe,GAAG,mBAAmB,CAAC;IAClD,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,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;AAWD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CAuLxF"}
1
+ {"version":3,"file":"ads.d.ts","sourceRoot":"","sources":["../../src/rn/ads.ts"],"names":[],"mappings":"AAIA,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAEzE,OAAO,KAAK,EAAyB,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC1F,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AAE/B,wEAAwE;AACxE,MAAM,MAAM,SAAS,GAAG,YAAY,CAAC;AAErC,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,SAAS,CAAC,EAAE,eAAe,GAAG,mBAAmB,CAAC;IAClD,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,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,oBAAoB,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CAqKxF"}