@ait-kit/sdk 0.2.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 (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +236 -0
  3. package/dist/event-flow.d.ts +36 -0
  4. package/dist/event-flow.d.ts.map +1 -0
  5. package/dist/event-flow.js +101 -0
  6. package/dist/iap/adapter.d.ts +47 -0
  7. package/dist/iap/adapter.d.ts.map +1 -0
  8. package/dist/iap/adapter.js +175 -0
  9. package/dist/iap/grant-coordinator.d.ts +36 -0
  10. package/dist/iap/grant-coordinator.d.ts.map +1 -0
  11. package/dist/iap/grant-coordinator.js +69 -0
  12. package/dist/iap/platform-contract.d.ts +55 -0
  13. package/dist/iap/platform-contract.d.ts.map +1 -0
  14. package/dist/iap/platform-contract.js +26 -0
  15. package/dist/iap/purchase-flow.d.ts +34 -0
  16. package/dist/iap/purchase-flow.d.ts.map +1 -0
  17. package/dist/iap/purchase-flow.js +167 -0
  18. package/dist/identity/platform-contract.d.ts +44 -0
  19. package/dist/identity/platform-contract.d.ts.map +1 -0
  20. package/dist/identity/platform-contract.js +71 -0
  21. package/dist/index.d.ts +215 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +16 -0
  24. package/dist/notification/platform-contract.d.ts +43 -0
  25. package/dist/notification/platform-contract.d.ts.map +1 -0
  26. package/dist/notification/platform-contract.js +89 -0
  27. package/dist/rn/ads.d.ts +39 -0
  28. package/dist/rn/ads.d.ts.map +1 -0
  29. package/dist/rn/ads.js +226 -0
  30. package/dist/rn/framework-contract.d.ts +45 -0
  31. package/dist/rn/framework-contract.d.ts.map +1 -0
  32. package/dist/rn/framework-contract.js +7 -0
  33. package/dist/rn/framework-loader.d.ts +22 -0
  34. package/dist/rn/framework-loader.d.ts.map +1 -0
  35. package/dist/rn/framework-loader.js +26 -0
  36. package/dist/rn/iap.d.ts +19 -0
  37. package/dist/rn/iap.d.ts.map +1 -0
  38. package/dist/rn/iap.js +35 -0
  39. package/dist/rn/identity.d.ts +20 -0
  40. package/dist/rn/identity.d.ts.map +1 -0
  41. package/dist/rn/identity.js +42 -0
  42. package/dist/rn/index.d.ts +27 -0
  43. package/dist/rn/index.d.ts.map +1 -0
  44. package/dist/rn/index.js +24 -0
  45. package/dist/rn/notify-share.d.ts +42 -0
  46. package/dist/rn/notify-share.d.ts.map +1 -0
  47. package/dist/rn/notify-share.js +159 -0
  48. package/dist/rn/storage.d.ts +18 -0
  49. package/dist/rn/storage.d.ts.map +1 -0
  50. package/dist/rn/storage.js +64 -0
  51. package/dist/share/platform-contract.d.ts +41 -0
  52. package/dist/share/platform-contract.d.ts.map +1 -0
  53. package/dist/share/platform-contract.js +34 -0
  54. package/dist/storage/platform-contract.d.ts +26 -0
  55. package/dist/storage/platform-contract.d.ts.map +1 -0
  56. package/dist/storage/platform-contract.js +16 -0
  57. package/dist/web/framework-loader.d.ts +9 -0
  58. package/dist/web/framework-loader.d.ts.map +1 -0
  59. package/dist/web/framework-loader.js +32 -0
  60. package/dist/web/iap-contract.d.ts +13 -0
  61. package/dist/web/iap-contract.d.ts.map +1 -0
  62. package/dist/web/iap-contract.js +1 -0
  63. package/dist/web/identity.d.ts +30 -0
  64. package/dist/web/identity.d.ts.map +1 -0
  65. package/dist/web/identity.js +87 -0
  66. package/dist/web/index.d.ts +47 -0
  67. package/dist/web/index.d.ts.map +1 -0
  68. package/dist/web/index.js +43 -0
  69. package/dist/web/notify-share.d.ts +37 -0
  70. package/dist/web/notify-share.d.ts.map +1 -0
  71. package/dist/web/notify-share.js +154 -0
  72. package/package.json +66 -0
@@ -0,0 +1,215 @@
1
+ /**
2
+ * `@ait-kit/sdk` — shared frontend contracts for Apps in Toss mini apps.
3
+ *
4
+ * The root entry is runtime-neutral: common types and errors only, with no
5
+ * imports of any official runtime SDK. Runtime adapters live in subpaths —
6
+ * `@ait-kit/sdk/rn` for React Native (ads today; more domains later).
7
+ */
8
+ /** Reward payload reported by rewarded ad flows. */
9
+ export interface AdReward {
10
+ unitType: string;
11
+ unitAmount: number;
12
+ }
13
+ /**
14
+ * Terminal outcome of showing a full-screen ad.
15
+ *
16
+ * `rewarded` is only ever produced from the provider's actual reward event —
17
+ * loading, showing, or dismissing an ad never implies a reward. Server-side
18
+ * reward verification, session checks, and ledger updates stay with the
19
+ * consumer.
20
+ */
21
+ export type AdShowResult = {
22
+ status: "rewarded";
23
+ reward: AdReward;
24
+ } | {
25
+ status: "dismissed";
26
+ } | {
27
+ status: "failed";
28
+ reason?: string;
29
+ };
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";
32
+ /** Terminal agreement outcomes delivered by the platform event. */
33
+ export type SdkNotificationAgreementType = "newAgreement" | "alreadyAgreed" | "agreementRejected";
34
+ /**
35
+ * Result of one notification agreement request. `templateCode` and the
36
+ * platform's raw event are preserved verbatim, and the outcome describes
37
+ * ONLY this single request — it is not the user's global notification
38
+ * setting, nor any server-persisted consent state (syncing those is the
39
+ * consumer's job).
40
+ */
41
+ export type SdkNotificationAgreementResult = {
42
+ status: "agreed";
43
+ agreement: Exclude<SdkNotificationAgreementType, "agreementRejected">;
44
+ templateCode: string;
45
+ sourceEvent: {
46
+ type: string;
47
+ };
48
+ } | {
49
+ status: "rejected";
50
+ templateCode: string;
51
+ sourceEvent: {
52
+ type: string;
53
+ };
54
+ } | {
55
+ status: "failed";
56
+ templateCode: string;
57
+ code?: string;
58
+ reason?: string;
59
+ } | {
60
+ status: "timeout";
61
+ templateCode: string;
62
+ reason?: string;
63
+ };
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.
68
+ */
69
+ export type SdkShareUiResult = {
70
+ status: "closed";
71
+ } | {
72
+ status: "failed";
73
+ code?: string;
74
+ reason?: string;
75
+ };
76
+ export type SdkLoginReferrer = "DEFAULT" | "SANDBOX";
77
+ /**
78
+ * Validated login result. Both values come from the platform SDK and pass
79
+ * through unchanged: exchange `authorizationCode` for tokens on YOUR server
80
+ * (see @ait-kit/api-core's login token endpoint) and create the application
81
+ * session there — the SDK adapter never performs the exchange.
82
+ */
83
+ export interface SdkLoginResult {
84
+ authorizationCode: string;
85
+ referrer: SdkLoginReferrer;
86
+ }
87
+ /**
88
+ * Validated anonymous identity: the SDK-issued per-mini-app hash. The
89
+ * adapter never fabricates a key when the SDK cannot provide one — a
90
+ * missing or malformed result is an error, not a placeholder.
91
+ */
92
+ export interface SdkAnonymousKey {
93
+ type: "HASH";
94
+ hash: string;
95
+ }
96
+ /** Minimal storage contract shared by the /rn and /web adapters. */
97
+ export interface SdkStorage {
98
+ /** Resolves the stored string, or null when the key has no value. */
99
+ get(key: string): Promise<string | null>;
100
+ /** Persists a string; rejections propagate to the caller. */
101
+ set(key: string, value: string): Promise<void>;
102
+ /** Removes the value for a key; rejections propagate to the caller. */
103
+ remove(key: string): Promise<void>;
104
+ }
105
+ export type IapProductType = "CONSUMABLE" | "NON_CONSUMABLE" | "SUBSCRIPTION";
106
+ export type IapSubscriptionRenewalCycle = "WEEKLY" | "MONTHLY" | "YEARLY";
107
+ export interface IapSubscriptionOffer {
108
+ type: "FREE_TRIAL" | "NEW_SUBSCRIPTION" | "RETURNING";
109
+ offerId: string;
110
+ period: string;
111
+ displayAmount?: string;
112
+ }
113
+ export interface IapProduct {
114
+ sku: string;
115
+ type: IapProductType;
116
+ displayName: string;
117
+ displayAmount: string;
118
+ iconUrl: string;
119
+ description: string;
120
+ /** Subscription products only. */
121
+ renewalCycle?: IapSubscriptionRenewalCycle;
122
+ offers?: IapSubscriptionOffer[];
123
+ }
124
+ export interface IapPendingOrder {
125
+ orderId: string;
126
+ sku: string;
127
+ paymentCompletedDate: string;
128
+ }
129
+ /** Success payload delivered by the platform's purchase success event. */
130
+ export interface IapPurchaseSuccessInfo {
131
+ orderId: string;
132
+ displayName: string;
133
+ displayAmount: string;
134
+ amount: number;
135
+ currency: string;
136
+ fraction: number;
137
+ miniAppIconUrl: string | null;
138
+ }
139
+ /**
140
+ * Consumer-injected server grant callback.
141
+ *
142
+ * Contract: resolve only AFTER your server has verified the order with the
143
+ * provider and persisted the grant. Resolving on the SDK event alone (or
144
+ * before persistence) breaks the purchase contract; rejecting or throwing
145
+ * marks the grant as failed and the purchase never reports completion.
146
+ * Server-side verification is the consumer's responsibility — SDK events
147
+ * are not payment verification.
148
+ */
149
+ export type IapGrantCallback = (target: {
150
+ orderId: string;
151
+ sku: string;
152
+ subscriptionId?: string;
153
+ }) => Promise<void>;
154
+ /**
155
+ * Terminal outcome of a purchase flow. `completed` requires BOTH the
156
+ * platform's success event for the order AND your grant callback having
157
+ * resolved for that exact order — a grant for a different order never
158
+ * completes a purchase, and a success event alone never completes one.
159
+ */
160
+ export type IapPurchaseResult = {
161
+ status: "completed";
162
+ orderId: string;
163
+ subscriptionId?: string;
164
+ success: IapPurchaseSuccessInfo;
165
+ } | {
166
+ status: "canceled";
167
+ } | {
168
+ status: "failed";
169
+ code?: string;
170
+ reason?: string;
171
+ } | {
172
+ status: "grant_failed";
173
+ orderId: string;
174
+ reason?: string;
175
+ } | {
176
+ status: "unknown";
177
+ orderId?: string;
178
+ subscriptionId?: string;
179
+ reason?: string;
180
+ };
181
+ export interface IapGrantOrderParams {
182
+ orderId: string;
183
+ subscriptionId?: string;
184
+ }
185
+ export interface IapOneTimePurchaseParams {
186
+ options: {
187
+ sku: string;
188
+ processProductGrant: (params: IapGrantOrderParams) => boolean | Promise<boolean>;
189
+ };
190
+ onEvent: (event: {
191
+ type: "success";
192
+ data: IapPurchaseSuccessInfo;
193
+ }) => void | Promise<void>;
194
+ onError: (error: unknown) => void | Promise<void>;
195
+ }
196
+ export interface IapSubscriptionPurchaseParams {
197
+ options: {
198
+ sku: string;
199
+ offerId?: string | null;
200
+ processProductGrant: (params: IapGrantOrderParams) => boolean | Promise<boolean>;
201
+ };
202
+ onEvent: (event: {
203
+ type: "success";
204
+ data: IapPurchaseSuccessInfo;
205
+ }) => void | Promise<void>;
206
+ onError: (error: unknown) => void | Promise<void>;
207
+ }
208
+ /** Typed error surfaced by the SDK adapters. */
209
+ export declare class SdkError extends Error {
210
+ code: SdkErrorCode;
211
+ constructor(code: SdkErrorCode, message: string, options?: {
212
+ cause?: unknown;
213
+ });
214
+ }
215
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `@ait-kit/sdk` — shared frontend contracts for Apps in Toss mini apps.
3
+ *
4
+ * The root entry is runtime-neutral: common types and errors only, with no
5
+ * imports of any official runtime SDK. Runtime adapters live in subpaths —
6
+ * `@ait-kit/sdk/rn` for React Native (ads today; more domains later).
7
+ */
8
+ /** Typed error surfaced by the SDK adapters. */
9
+ export class SdkError extends Error {
10
+ code;
11
+ constructor(code, message, options) {
12
+ super(message, options);
13
+ this.name = "SdkError";
14
+ this.code = code;
15
+ }
16
+ }
@@ -0,0 +1,43 @@
1
+ import type { SdkNotificationAgreementResult } from "../index.js";
2
+ /**
3
+ * Internal platform contract for the notification-agreement adapter,
4
+ * shared by /rn and /web (never exported from a public subpath).
5
+ */
6
+ export interface NotificationAgreementParams {
7
+ options: {
8
+ templateCode: string;
9
+ };
10
+ onEvent: (result: {
11
+ type: string;
12
+ }) => void | Promise<void>;
13
+ onError: (error: unknown) => void | Promise<void>;
14
+ }
15
+ type FunctionWithSupport<F> = F & {
16
+ isSupported?: () => boolean;
17
+ };
18
+ export interface NotificationLike {
19
+ requestAgreement: FunctionWithSupport<(params: NotificationAgreementParams) => () => void> | undefined;
20
+ }
21
+ export interface NotificationPlatformSdk {
22
+ Notification?: NotificationLike;
23
+ }
24
+ export type NotificationPlatformLoader = () => Promise<{
25
+ available: true;
26
+ module: NotificationPlatformSdk;
27
+ } | {
28
+ available: false;
29
+ reason: string;
30
+ }>;
31
+ /**
32
+ * Runs one agreement request on the shared event-flow base: settle-once,
33
+ * duplicate/late-event immunity, cleanup exactly once with exceptions
34
+ * swallowed away from the result, registration-throw recovery, and one
35
+ * overall deadline. The templateCode and the platform's raw event are
36
+ * preserved verbatim in every outcome; the result describes only this
37
+ * request, never a global notification setting or server consent state.
38
+ */
39
+ export declare function runRequestAgreement(platform: NotificationPlatformSdk, templateCode: string, timeoutMs: number): Promise<SdkNotificationAgreementResult>;
40
+ export declare function readErrorCode(error: unknown): string | undefined;
41
+ export declare function readErrorMessage(error: unknown): string;
42
+ export {};
43
+ //# sourceMappingURL=platform-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-contract.d.ts","sourceRoot":"","sources":["../../src/notification/platform-contract.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAElE;;;GAGG;AAEH,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAClC,OAAO,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAED,KAAK,mBAAmB,CAAC,CAAC,IAAI,CAAC,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,OAAO,CAAA;CAAE,CAAC;AAElE,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,EACZ,mBAAmB,CAAC,CAAC,MAAM,EAAE,2BAA2B,KAAK,MAAM,IAAI,CAAC,GACxE,SAAS,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAED,MAAM,MAAM,0BAA0B,GAAG,MAAM,OAAO,CACpD;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,uBAAuB,CAAA;CAAE,GAAG;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAC5F,CAAC;AAQF;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,uBAAuB,EACjC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,8BAA8B,CAAC,CA6DzC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAMhE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAOvD"}
@@ -0,0 +1,89 @@
1
+ import { runEventFlow } from "../event-flow.js";
2
+ const TERMINAL_AGREEMENTS = new Set(["newAgreement", "alreadyAgreed", "agreementRejected"]);
3
+ /**
4
+ * Runs one agreement request on the shared event-flow base: settle-once,
5
+ * duplicate/late-event immunity, cleanup exactly once with exceptions
6
+ * swallowed away from the result, registration-throw recovery, and one
7
+ * overall deadline. The templateCode and the platform's raw event are
8
+ * preserved verbatim in every outcome; the result describes only this
9
+ * request, never a global notification setting or server consent state.
10
+ */
11
+ export function runRequestAgreement(platform, templateCode, timeoutMs) {
12
+ return runEventFlow({
13
+ timeoutMs,
14
+ onTimeout: () => ({
15
+ status: "timeout",
16
+ templateCode,
17
+ reason: `agreement request timed out after ${timeoutMs}ms; the user may still act — resolve the state server-side before retrying`
18
+ }),
19
+ register: (emit) => platform.Notification.requestAgreement({
20
+ options: { templateCode },
21
+ onEvent: (result) => {
22
+ // Preserve the platform's raw event object verbatim (including
23
+ // any metadata beyond `type`) per the public contract.
24
+ emit({
25
+ kind: "agreement",
26
+ type: result.type,
27
+ sourceEvent: result
28
+ });
29
+ },
30
+ onError: (error) => {
31
+ emit({ kind: "sdkError", error });
32
+ }
33
+ }),
34
+ reduce: (event) => {
35
+ switch (event.kind) {
36
+ case "agreement": {
37
+ if (!TERMINAL_AGREEMENTS.has(event.type)) {
38
+ return { done: false };
39
+ }
40
+ if (event.type === "agreementRejected") {
41
+ return {
42
+ done: true,
43
+ result: { status: "rejected", templateCode, sourceEvent: event.sourceEvent }
44
+ };
45
+ }
46
+ return {
47
+ done: true,
48
+ result: {
49
+ status: "agreed",
50
+ agreement: event.type,
51
+ templateCode,
52
+ sourceEvent: event.sourceEvent
53
+ }
54
+ };
55
+ }
56
+ case "sdkError": {
57
+ const code = readErrorCode(event.error);
58
+ return {
59
+ done: true,
60
+ result: {
61
+ status: "failed",
62
+ templateCode,
63
+ ...(code !== undefined ? { code } : {}),
64
+ reason: readErrorMessage(event.error)
65
+ }
66
+ };
67
+ }
68
+ }
69
+ }
70
+ });
71
+ }
72
+ export function readErrorCode(error) {
73
+ if (typeof error === "object" && error !== null) {
74
+ const code = error.code;
75
+ if (typeof code === "string" && code)
76
+ return code;
77
+ }
78
+ return undefined;
79
+ }
80
+ export function readErrorMessage(error) {
81
+ if (error instanceof Error)
82
+ return error.message;
83
+ if (typeof error === "object" && error !== null) {
84
+ const message = error.message;
85
+ if (typeof message === "string" && message)
86
+ return message;
87
+ }
88
+ return String(error);
89
+ }
@@ -0,0 +1,39 @@
1
+ import { type AdShowResult } from "../index.js";
2
+ import type { FullScreenAdSupport } from "./framework-contract.js";
3
+ import { type FrameworkLoader } from "./framework-loader.js";
4
+ /** Ad kinds the SDK distinguishes; only full-screen ads exist today. */
5
+ export type SdkAdType = "fullscreen";
6
+ export interface ReactNativeAdsOptions {
7
+ /**
8
+ * Framework loader. Defaults to a lazy `import("@apps-in-toss/framework")`
9
+ * that retries after failures; tests and consumers can inject a loader or
10
+ * a module instance directly.
11
+ */
12
+ framework?: FrameworkLoader | FullScreenAdSupport;
13
+ /** Overall deadline for the show flow (default 60000ms; 0 disables). */
14
+ showTimeoutMs?: number;
15
+ /**
16
+ * Overall deadline for each load, covering framework acquisition and the
17
+ * provider load flow (default 30000ms; 0 disables).
18
+ */
19
+ loadTimeoutMs?: number;
20
+ }
21
+ export interface ReactNativeAds {
22
+ /** Loads (or joins an in-flight load of) the full-screen ad for adGroupId. */
23
+ loadFullScreenAd(adGroupId: string): Promise<void>;
24
+ /**
25
+ * Shows the full-screen ad whose load already completed and resolves with
26
+ * its terminal outcome. The ad is single-use and claimed synchronously:
27
+ * once a show starts, the unit needs a fresh load for the next attempt.
28
+ */
29
+ showFullScreenAd(adGroupId: string): Promise<AdShowResult>;
30
+ }
31
+ /**
32
+ * React Native ad adapter over `@apps-in-toss/framework`. Load state is
33
+ * tracked per ad type + ad unit, in-flight duplicate loads share one
34
+ * request, and rewards come only from the provider's `userEarnedReward`
35
+ * event. The adapter produces ad outcomes; server-side reward requests,
36
+ * session checks, and ledger handling belong to the consumer.
37
+ */
38
+ export declare function createReactNativeAds(options?: ReactNativeAdsOptions): ReactNativeAds;
39
+ //# sourceMappingURL=ads.d.ts.map
@@ -0,0 +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"}
package/dist/rn/ads.js ADDED
@@ -0,0 +1,226 @@
1
+ // Relative import: the package ships per-file output (no bundling), so
2
+ // the /rn entry and the root entry share one SdkError constructor and
3
+ // `instanceof` holds for consumers importing either entry.
4
+ import { SdkError } from "../index.js";
5
+ import { runEventFlow } from "../event-flow.js";
6
+ import { createDefaultFrameworkLoader } from "./framework-loader.js";
7
+ const DEFAULT_SHOW_TIMEOUT_MS = 60_000;
8
+ const DEFAULT_LOAD_TIMEOUT_MS = 30_000;
9
+ /**
10
+ * React Native ad adapter over `@apps-in-toss/framework`. Load state is
11
+ * tracked per ad type + ad unit, in-flight duplicate loads share one
12
+ * request, and rewards come only from the provider's `userEarnedReward`
13
+ * event. The adapter produces ad outcomes; server-side reward requests,
14
+ * session checks, and ledger handling belong to the consumer.
15
+ */
16
+ export function createReactNativeAds(options = {}) {
17
+ const loader = normalizeLoader(options.framework);
18
+ const showTimeoutMs = options.showTimeoutMs ?? DEFAULT_SHOW_TIMEOUT_MS;
19
+ const loadTimeoutMs = options.loadTimeoutMs ?? DEFAULT_LOAD_TIMEOUT_MS;
20
+ const slots = new Map();
21
+ const showing = new Set();
22
+ const adKey = (adType, adGroupId) => `${adType}:${adGroupId}`;
23
+ const load = (adGroupId) => {
24
+ const id = adKey("fullscreen", adGroupId);
25
+ const existing = slots.get(id);
26
+ // Duplicate loads join the in-flight request, and an already-completed
27
+ // load stays loaded until a show claims it - neither re-registers.
28
+ if (existing) {
29
+ return existing.kind === "loaded" ? Promise.resolve() : existing.promise;
30
+ }
31
+ const request = (async () => {
32
+ // The deadline covers framework acquisition as well as the provider
33
+ // load flow, and a timed-out task must not register with the provider
34
+ // afterwards: a retried load would otherwise race a late registration.
35
+ let cancelled = false;
36
+ const task = (async () => {
37
+ const framework = await loadFramework();
38
+ if (cancelled) {
39
+ throw new SdkError("AD_LOAD_TIMEOUT", "full-screen ad load timed out");
40
+ }
41
+ if (typeof framework.loadFullScreenAd.isSupported === "function" &&
42
+ !framework.loadFullScreenAd.isSupported()) {
43
+ throw new SdkError("UNSUPPORTED", "full-screen ads are not supported on this app version");
44
+ }
45
+ const outcome = await runEventFlow({
46
+ register: (emit) => framework.loadFullScreenAd({
47
+ options: { adGroupId },
48
+ onEvent: emit,
49
+ onError: (error) => emit({ type: "sdkError", error })
50
+ }),
51
+ reduce: (event) => event.type === "loaded"
52
+ ? { done: true, result: undefined }
53
+ : { done: true, result: loadFailure(event.error) },
54
+ onTimeout: () => new SdkError("AD_LOAD_TIMEOUT", "full-screen ad load timed out"),
55
+ timeoutMs: loadTimeoutMs
56
+ });
57
+ if (outcome instanceof SdkError) {
58
+ throw outcome;
59
+ }
60
+ })();
61
+ // If the deadline wins, the orphaned task rejects into this sink.
62
+ task.catch(() => { });
63
+ let timer;
64
+ const deadline = new Promise((_, reject) => {
65
+ if (loadTimeoutMs > 0) {
66
+ timer = setTimeout(() => {
67
+ cancelled = true;
68
+ reject(new SdkError("AD_LOAD_TIMEOUT", "full-screen ad load timed out"));
69
+ }, loadTimeoutMs);
70
+ }
71
+ });
72
+ try {
73
+ return await Promise.race([task, deadline]);
74
+ }
75
+ finally {
76
+ if (timer !== undefined)
77
+ clearTimeout(timer);
78
+ }
79
+ })();
80
+ slots.set(id, { kind: "loading", promise: request });
81
+ request
82
+ .then(() => {
83
+ // Promote only when this exact request still owns the slot: a
84
+ // timed-out task resolving late must not mark a newer retry loaded.
85
+ const current = slots.get(id);
86
+ if (current?.kind === "loading" && current.promise === request) {
87
+ slots.set(id, { kind: "loaded" });
88
+ }
89
+ })
90
+ .catch(() => {
91
+ // A failed load frees the slot so the next attempt retries.
92
+ const current = slots.get(id);
93
+ if (current?.kind === "loading" && current.promise === request) {
94
+ slots.delete(id);
95
+ }
96
+ });
97
+ return request;
98
+ };
99
+ const show = (adGroupId) => {
100
+ const id = adKey("fullscreen", adGroupId);
101
+ if (showing.has(id)) {
102
+ return Promise.reject(new SdkError("AD_ALREADY_SHOWING", `ad group ${adGroupId} is already being shown`));
103
+ }
104
+ const slot = slots.get(id);
105
+ if (!slot || slot.kind === "loading") {
106
+ return Promise.reject(new SdkError("AD_NOT_LOADED", `loadFullScreenAd must complete before showing ad group ${adGroupId}`));
107
+ }
108
+ // Claim synchronously: the loaded marker is consumed the moment the show
109
+ // starts, so a concurrent reload registers fresh instead of aliasing a
110
+ // marker this show will never own again.
111
+ slots.delete(id);
112
+ showing.add(id);
113
+ return (async () => {
114
+ // The deadline covers framework acquisition as well as the show flow:
115
+ // a stalled loader must release the showing slot instead of wedging
116
+ // the ad group forever, and a late-resolving loader must not register
117
+ // a show the deadline already ended.
118
+ let reward;
119
+ let cancelled = false;
120
+ const timedOut = () => ({ status: "failed", reason: "ad show flow timed out" });
121
+ try {
122
+ let timer;
123
+ const deadline = new Promise((resolve) => {
124
+ if (showTimeoutMs > 0) {
125
+ timer = setTimeout(() => {
126
+ cancelled = true;
127
+ resolve(timedOut());
128
+ }, showTimeoutMs);
129
+ }
130
+ });
131
+ const task = (async () => {
132
+ const framework = await loadFramework();
133
+ if (cancelled) {
134
+ return timedOut();
135
+ }
136
+ if (typeof framework.showFullScreenAd.isSupported === "function" &&
137
+ !framework.showFullScreenAd.isSupported()) {
138
+ throw new SdkError("UNSUPPORTED", "full-screen ads are not supported on this app version");
139
+ }
140
+ return await runEventFlow({
141
+ register: (emit) => framework.showFullScreenAd({
142
+ options: { adGroupId },
143
+ onEvent: emit,
144
+ onError: (error) => emit({ type: "sdkError", error })
145
+ }),
146
+ reduce: (event) => {
147
+ if (event.type === "sdkError") {
148
+ const message = errorMessage(event.error);
149
+ return {
150
+ done: true,
151
+ result: { status: "failed", reason: `full-screen ad show failed: ${message}` }
152
+ };
153
+ }
154
+ return reduceShowEvent(event, () => reward, (value) => (reward = value));
155
+ },
156
+ onTimeout: timedOut,
157
+ timeoutMs: showTimeoutMs
158
+ });
159
+ })();
160
+ try {
161
+ return await Promise.race([task, deadline]);
162
+ }
163
+ finally {
164
+ if (timer !== undefined)
165
+ clearTimeout(timer);
166
+ }
167
+ }
168
+ finally {
169
+ showing.delete(id);
170
+ }
171
+ })();
172
+ };
173
+ async function loadFramework() {
174
+ const result = await loader();
175
+ if (!result.available) {
176
+ throw new SdkError("SDK_UNAVAILABLE", result.reason);
177
+ }
178
+ return result.module;
179
+ }
180
+ return { loadFullScreenAd: load, showFullScreenAd: show };
181
+ }
182
+ function reduceShowEvent(event, getReward, setReward) {
183
+ switch (event.type) {
184
+ case "userEarnedReward": {
185
+ // Runtime guard against payload-shape drift; the typed contract marks
186
+ // data as required.
187
+ if (event.data) {
188
+ setReward({ unitType: event.data.unitType, unitAmount: event.data.unitAmount });
189
+ }
190
+ return { done: false };
191
+ }
192
+ case "dismissed": {
193
+ const earned = getReward();
194
+ // Reward only when the provider actually emitted userEarnedReward;
195
+ // dismissal alone never grants anything.
196
+ return earned
197
+ ? { done: true, result: { status: "rewarded", reward: earned } }
198
+ : { done: true, result: { status: "dismissed" } };
199
+ }
200
+ case "failedToShow":
201
+ return { done: true, result: { status: "failed", reason: "failedToShow" } };
202
+ default:
203
+ // requested / show / impression / clicked keep the flow running.
204
+ return { done: false };
205
+ }
206
+ }
207
+ /** Transient provider failures are retryable and must not read as UNSUPPORTED. */
208
+ function loadFailure(error) {
209
+ if (error instanceof SdkError)
210
+ return error;
211
+ return new SdkError("AD_LOAD_FAILED", `full-screen ad load failed: ${errorMessage(error)}`, {
212
+ cause: error
213
+ });
214
+ }
215
+ function errorMessage(error) {
216
+ return error instanceof Error ? error.message : String(error);
217
+ }
218
+ function normalizeLoader(framework) {
219
+ if (!framework) {
220
+ return createDefaultFrameworkLoader();
221
+ }
222
+ if (typeof framework === "function") {
223
+ return framework;
224
+ }
225
+ return async () => ({ available: true, module: framework });
226
+ }