@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.
- package/LICENSE +21 -0
- package/README.md +236 -0
- package/dist/event-flow.d.ts +36 -0
- package/dist/event-flow.d.ts.map +1 -0
- package/dist/event-flow.js +101 -0
- package/dist/iap/adapter.d.ts +47 -0
- package/dist/iap/adapter.d.ts.map +1 -0
- package/dist/iap/adapter.js +175 -0
- package/dist/iap/grant-coordinator.d.ts +36 -0
- package/dist/iap/grant-coordinator.d.ts.map +1 -0
- package/dist/iap/grant-coordinator.js +69 -0
- package/dist/iap/platform-contract.d.ts +55 -0
- package/dist/iap/platform-contract.d.ts.map +1 -0
- package/dist/iap/platform-contract.js +26 -0
- package/dist/iap/purchase-flow.d.ts +34 -0
- package/dist/iap/purchase-flow.d.ts.map +1 -0
- package/dist/iap/purchase-flow.js +167 -0
- package/dist/identity/platform-contract.d.ts +44 -0
- package/dist/identity/platform-contract.d.ts.map +1 -0
- package/dist/identity/platform-contract.js +71 -0
- package/dist/index.d.ts +215 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/notification/platform-contract.d.ts +43 -0
- package/dist/notification/platform-contract.d.ts.map +1 -0
- package/dist/notification/platform-contract.js +89 -0
- package/dist/rn/ads.d.ts +39 -0
- package/dist/rn/ads.d.ts.map +1 -0
- package/dist/rn/ads.js +226 -0
- package/dist/rn/framework-contract.d.ts +45 -0
- package/dist/rn/framework-contract.d.ts.map +1 -0
- package/dist/rn/framework-contract.js +7 -0
- package/dist/rn/framework-loader.d.ts +22 -0
- package/dist/rn/framework-loader.d.ts.map +1 -0
- package/dist/rn/framework-loader.js +26 -0
- package/dist/rn/iap.d.ts +19 -0
- package/dist/rn/iap.d.ts.map +1 -0
- package/dist/rn/iap.js +35 -0
- package/dist/rn/identity.d.ts +20 -0
- package/dist/rn/identity.d.ts.map +1 -0
- package/dist/rn/identity.js +42 -0
- package/dist/rn/index.d.ts +27 -0
- package/dist/rn/index.d.ts.map +1 -0
- package/dist/rn/index.js +24 -0
- package/dist/rn/notify-share.d.ts +42 -0
- package/dist/rn/notify-share.d.ts.map +1 -0
- package/dist/rn/notify-share.js +159 -0
- package/dist/rn/storage.d.ts +18 -0
- package/dist/rn/storage.d.ts.map +1 -0
- package/dist/rn/storage.js +64 -0
- package/dist/share/platform-contract.d.ts +41 -0
- package/dist/share/platform-contract.d.ts.map +1 -0
- package/dist/share/platform-contract.js +34 -0
- package/dist/storage/platform-contract.d.ts +26 -0
- package/dist/storage/platform-contract.d.ts.map +1 -0
- package/dist/storage/platform-contract.js +16 -0
- package/dist/web/framework-loader.d.ts +9 -0
- package/dist/web/framework-loader.d.ts.map +1 -0
- package/dist/web/framework-loader.js +32 -0
- package/dist/web/iap-contract.d.ts +13 -0
- package/dist/web/iap-contract.d.ts.map +1 -0
- package/dist/web/iap-contract.js +1 -0
- package/dist/web/identity.d.ts +30 -0
- package/dist/web/identity.d.ts.map +1 -0
- package/dist/web/identity.js +87 -0
- package/dist/web/index.d.ts +47 -0
- package/dist/web/index.d.ts.map +1 -0
- package/dist/web/index.js +43 -0
- package/dist/web/notify-share.d.ts +37 -0
- package/dist/web/notify-share.d.ts.map +1 -0
- package/dist/web/notify-share.js +154 -0
- package/package.json +66 -0
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|
package/dist/rn/ads.d.ts
ADDED
|
@@ -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
|
+
}
|