@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,69 @@
1
+ /**
2
+ * Client-side dedupe for server grant callbacks, scoped to one adapter
3
+ * instance (one "processing scope"):
4
+ *
5
+ * - Concurrent grants for the same order share the in-flight Promise, so a
6
+ * duplicate callback never re-runs the server request.
7
+ * - A successfully granted order is reused within the same scope: later
8
+ * flows (pending-order recovery, duplicate success events) resolve
9
+ * immediately instead of re-granting.
10
+ * - Failed grants are never cached: the entry is removed when the attempt
11
+ * rejects, so the next call retries for real.
12
+ *
13
+ * This only reduces duplicate client-side work. Server-side grant
14
+ * idempotency (verify the order, persist exactly once) is a separate,
15
+ * mandatory server responsibility.
16
+ */
17
+ export class IapGrantCoordinator {
18
+ grant;
19
+ inFlight = new Map();
20
+ granted = new Map();
21
+ constructor(grant) {
22
+ this.grant = grant;
23
+ }
24
+ /**
25
+ * Runs (or joins) the grant for the target order. Resolves when the
26
+ * consumer's callback resolves; rejects with the callback's error when it
27
+ * fails, leaving the order retryable. A duplicate joins only when it
28
+ * describes the same target: orderId and sku must match, and a caller
29
+ * that does not know the subscriptionId (pending-order recovery) may join
30
+ * a cached subscription grant. Explicitly conflicting duplicates reject
31
+ * instead of silently inheriting another flow's grant.
32
+ */
33
+ run(target) {
34
+ const conflict = (existing) => new Error(`conflicting grant target for order ${target.orderId}: already ${existing.sku !== target.sku ? `sku ${existing.sku}` : `subscriptionId ${existing.subscriptionId}`}, requested ${target.sku}`);
35
+ // Join rules: sku must match. A caller without a subscriptionId
36
+ // (pending-order recovery) may join any cached grant for the order; a
37
+ // caller WITH one must match the cached subscriptionId exactly — a
38
+ // cached grant without a subscriptionId never satisfies an explicit one.
39
+ const sameTarget = (existing) => existing.sku === target.sku &&
40
+ (target.subscriptionId === undefined || existing.subscriptionId === target.subscriptionId);
41
+ const inFlight = this.inFlight.get(target.orderId);
42
+ if (inFlight) {
43
+ return sameTarget(inFlight.target) ? inFlight.promise : Promise.reject(conflict(inFlight.target));
44
+ }
45
+ const granted = this.granted.get(target.orderId);
46
+ if (granted) {
47
+ return sameTarget(granted) ? Promise.resolve() : Promise.reject(conflict(granted));
48
+ }
49
+ const attempt = (async () => {
50
+ await this.grant(target);
51
+ // Only successful grants persist: the consumer's contract is to
52
+ // resolve after the server verified the order and persisted the grant.
53
+ this.granted.set(target.orderId, target);
54
+ })();
55
+ this.inFlight.set(target.orderId, { target, promise: attempt });
56
+ attempt
57
+ .catch(() => {
58
+ // Failures are not cached; a later call retries the callback.
59
+ })
60
+ .finally(() => {
61
+ this.inFlight.delete(target.orderId);
62
+ });
63
+ return attempt;
64
+ }
65
+ /** True when this scope already granted the order successfully. */
66
+ isGranted(orderId) {
67
+ return this.granted.has(orderId);
68
+ }
69
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Internal platform contract shared by the /rn and /web IAP adapters.
3
+ * Each entry point declares its own structural copy of the official SDK
4
+ * surface (they are different npm packages with identical Domains API
5
+ * shapes); this module is the common, platform-neutral engine boundary and
6
+ * is never exported from a public subpath.
7
+ */
8
+ import type { IapOneTimePurchaseParams, IapPendingOrder, IapProduct, IapSubscriptionPurchaseParams } from "../index.js";
9
+ export interface IapSdkError {
10
+ code?: string;
11
+ message?: string;
12
+ }
13
+ export type IapGrantTarget = {
14
+ orderId: string;
15
+ sku: string;
16
+ subscriptionId?: string;
17
+ };
18
+ /** Official SDK functions optionally expose an isSupported() pre-check. */
19
+ type WithSupport<F> = F & {
20
+ isSupported?: () => boolean;
21
+ };
22
+ export interface IapPlatformSdk {
23
+ getProductItemList: WithSupport<() => Promise<{
24
+ products: IapProduct[];
25
+ }>>;
26
+ createOneTimePurchaseOrder: WithSupport<(params: IapOneTimePurchaseParams) => () => void>;
27
+ createSubscriptionPurchaseOrder: WithSupport<(params: IapSubscriptionPurchaseParams) => () => void>;
28
+ getPendingOrders: WithSupport<() => Promise<{
29
+ orders: IapPendingOrder[];
30
+ }>>;
31
+ completeProductGrant: WithSupport<(args: {
32
+ params: {
33
+ orderId: string;
34
+ };
35
+ }) => Promise<boolean>>;
36
+ }
37
+ /**
38
+ * Injection surface: installed framework versions may expose only some IAP
39
+ * functions. The loaders accept any partial surface; the adapter gates each
40
+ * operation individually at call time.
41
+ */
42
+ export type PartialIapPlatformSdk = {
43
+ [K in keyof IapPlatformSdk]?: IapPlatformSdk[K];
44
+ };
45
+ export type IapPlatformLoader = () => Promise<{
46
+ available: true;
47
+ module: PartialIapPlatformSdk;
48
+ } | {
49
+ available: false;
50
+ reason: string;
51
+ }>;
52
+ export declare function toIapErrorMessage(error: unknown): string;
53
+ export declare function toIapErrorCode(error: unknown): string | undefined;
54
+ export {};
55
+ //# sourceMappingURL=platform-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-contract.d.ts","sourceRoot":"","sources":["../../src/iap/platform-contract.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,wBAAwB,EACxB,eAAe,EACf,UAAU,EACV,6BAA6B,EAC9B,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,2EAA2E;AAC3E,KAAK,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,OAAO,CAAA;CAAE,CAAC;AAE1D,MAAM,WAAW,cAAc;IAC7B,kBAAkB,EAAE,WAAW,CAAC,MAAM,OAAO,CAAC;QAAE,QAAQ,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC,CAAC,CAAC;IAC3E,0BAA0B,EAAE,WAAW,CAAC,CAAC,MAAM,EAAE,wBAAwB,KAAK,MAAM,IAAI,CAAC,CAAC;IAC1F,+BAA+B,EAAE,WAAW,CAC1C,CAAC,MAAM,EAAE,6BAA6B,KAAK,MAAM,IAAI,CACtD,CAAC;IACF,gBAAgB,EAAE,WAAW,CAAC,MAAM,OAAO,CAAC;QAAE,MAAM,EAAE,eAAe,EAAE,CAAA;KAAE,CAAC,CAAC,CAAC;IAC5E,oBAAoB,EAAE,WAAW,CAC/B,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAC5D,CAAC;CACH;AAED;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG;KACjC,CAAC,IAAI,MAAM,cAAc,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,CAC3C;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,GAAG;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAC1F,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CASxD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAMjE"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Internal platform contract shared by the /rn and /web IAP adapters.
3
+ * Each entry point declares its own structural copy of the official SDK
4
+ * surface (they are different npm packages with identical Domains API
5
+ * shapes); this module is the common, platform-neutral engine boundary and
6
+ * is never exported from a public subpath.
7
+ */
8
+ export function toIapErrorMessage(error) {
9
+ if (error instanceof Error) {
10
+ return error.message;
11
+ }
12
+ if (typeof error === "object" && error !== null) {
13
+ const coded = error;
14
+ if (typeof coded.message === "string" && coded.message)
15
+ return coded.message;
16
+ }
17
+ return String(error);
18
+ }
19
+ export function toIapErrorCode(error) {
20
+ if (typeof error === "object" && error !== null) {
21
+ const code = error.code;
22
+ if (typeof code === "string" && code)
23
+ return code;
24
+ }
25
+ return undefined;
26
+ }
@@ -0,0 +1,34 @@
1
+ import type { IapPurchaseResult } from "../index.js";
2
+ import { type IapGrantCoordinator } from "./grant-coordinator.js";
3
+ export interface PurchaseFlowOptions {
4
+ /** The platform's order-creation function (already capability-checked). */
5
+ startOrder: ((params: import("../index.js").IapOneTimePurchaseParams) => () => void) | ((params: import("../index.js").IapSubscriptionPurchaseParams) => () => void);
6
+ coordinator: IapGrantCoordinator;
7
+ sku: string;
8
+ offerId?: string;
9
+ subscription: boolean;
10
+ /** Overall deadline for the purchase flow; 0 disables. */
11
+ timeoutMs: number;
12
+ }
13
+ /**
14
+ * Shared purchase flow for the /rn and /web IAP adapters, built on the
15
+ * common event-flow base:
16
+ *
17
+ * - The platform's `processProductGrant` slot wraps the consumer-injected
18
+ * server grant callback (through the coordinator's dedupe). The platform
19
+ * receives `true` only after the callback resolves.
20
+ * - Completion requires the success event's orderId to match the order the
21
+ * grant confirmed for THIS flow. A grant for a different order ends the
22
+ * flow as `failed` with `ORDER_MISMATCH`, and a success event arriving
23
+ * before the grant settles is parked until the grant for the same order
24
+ * confirms — a success event alone never completes a purchase.
25
+ * - A failed or throwing grant ends the flow as `grant_failed`; a purchase
26
+ * is never reported successful over a failed server grant.
27
+ * - `USER_CANCELED` maps to `canceled`; other SDK errors to `failed`.
28
+ * - Timeout yields `unknown` (with the known orderId when available): the
29
+ * server grant may already be in progress — resolve it through server
30
+ * verification and the pending-order recovery flow. Late events after
31
+ * any terminal state are ignored.
32
+ */
33
+ export declare function runPurchaseFlow(options: PurchaseFlowOptions): Promise<IapPurchaseResult>;
34
+ //# sourceMappingURL=purchase-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"purchase-flow.d.ts","sourceRoot":"","sources":["../../src/iap/purchase-flow.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,iBAAiB,EAGlB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAQlE,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,UAAU,EACN,CAAC,CAAC,MAAM,EAAE,OAAO,aAAa,EAAE,wBAAwB,KAAK,MAAM,IAAI,CAAC,GACxE,CAAC,CAAC,MAAM,EAAE,OAAO,aAAa,EAAE,6BAA6B,KAAK,MAAM,IAAI,CAAC,CAAC;IAClF,WAAW,EAAE,mBAAmB,CAAC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAsJxF"}
@@ -0,0 +1,167 @@
1
+ import { runEventFlow } from "../event-flow.js";
2
+ import {} from "./grant-coordinator.js";
3
+ import { toIapErrorCode, toIapErrorMessage } from "./platform-contract.js";
4
+ /**
5
+ * Shared purchase flow for the /rn and /web IAP adapters, built on the
6
+ * common event-flow base:
7
+ *
8
+ * - The platform's `processProductGrant` slot wraps the consumer-injected
9
+ * server grant callback (through the coordinator's dedupe). The platform
10
+ * receives `true` only after the callback resolves.
11
+ * - Completion requires the success event's orderId to match the order the
12
+ * grant confirmed for THIS flow. A grant for a different order ends the
13
+ * flow as `failed` with `ORDER_MISMATCH`, and a success event arriving
14
+ * before the grant settles is parked until the grant for the same order
15
+ * confirms — a success event alone never completes a purchase.
16
+ * - A failed or throwing grant ends the flow as `grant_failed`; a purchase
17
+ * is never reported successful over a failed server grant.
18
+ * - `USER_CANCELED` maps to `canceled`; other SDK errors to `failed`.
19
+ * - Timeout yields `unknown` (with the known orderId when available): the
20
+ * server grant may already be in progress — resolve it through server
21
+ * verification and the pending-order recovery flow. Late events after
22
+ * any terminal state are ignored.
23
+ */
24
+ export function runPurchaseFlow(options) {
25
+ const { startOrder, coordinator, sku, offerId, subscription, timeoutMs } = options;
26
+ // Per-flow state, shared by the register callbacks and the reducer.
27
+ let observedOrderId;
28
+ let observedSubscriptionId;
29
+ let confirmedOrderId;
30
+ let confirmedSubscriptionId;
31
+ let stashedSuccess;
32
+ let grantFailure;
33
+ const evaluate = () => {
34
+ if (grantFailure) {
35
+ return { done: true, result: { status: "grant_failed", ...grantFailure } };
36
+ }
37
+ if (stashedSuccess && confirmedOrderId !== undefined) {
38
+ if (confirmedOrderId !== stashedSuccess.orderId) {
39
+ return {
40
+ done: true,
41
+ result: {
42
+ status: "failed",
43
+ code: "ORDER_MISMATCH",
44
+ reason: `grant confirmed order ${confirmedOrderId} but the success event reported ${stashedSuccess.orderId}`
45
+ }
46
+ };
47
+ }
48
+ return {
49
+ done: true,
50
+ result: {
51
+ status: "completed",
52
+ orderId: confirmedOrderId,
53
+ ...(confirmedSubscriptionId !== undefined
54
+ ? { subscriptionId: confirmedSubscriptionId }
55
+ : {}),
56
+ success: stashedSuccess
57
+ }
58
+ };
59
+ }
60
+ return { done: false };
61
+ };
62
+ return runEventFlow({
63
+ timeoutMs,
64
+ onTimeout: () => ({
65
+ status: "unknown",
66
+ // Include the order the platform already handed to the grant slot,
67
+ // even when the grant itself has not settled — consumers need it to
68
+ // verify and recover the uncertain purchase.
69
+ orderId: confirmedOrderId ?? observedOrderId ?? stashedSuccess?.orderId,
70
+ ...(observedSubscriptionId !== undefined || confirmedSubscriptionId !== undefined
71
+ ? { subscriptionId: confirmedSubscriptionId ?? observedSubscriptionId }
72
+ : {}),
73
+ reason: "purchase flow timed out; the server grant may still be in progress — verify the order server-side and recover it via pending orders"
74
+ }),
75
+ register: (emit) => {
76
+ const processProductGrant = async (params) => {
77
+ observedOrderId = params.orderId;
78
+ observedSubscriptionId = params.subscriptionId;
79
+ try {
80
+ await coordinator.run({
81
+ orderId: params.orderId,
82
+ sku,
83
+ ...(params.subscriptionId !== undefined
84
+ ? { subscriptionId: params.subscriptionId }
85
+ : {})
86
+ });
87
+ confirmedOrderId = params.orderId;
88
+ confirmedSubscriptionId = params.subscriptionId;
89
+ emit({ kind: "grantSettled", orderId: params.orderId, ok: true });
90
+ return true;
91
+ }
92
+ catch (error) {
93
+ grantFailure = {
94
+ orderId: params.orderId,
95
+ reason: `server grant callback failed: ${toIapErrorMessage(error)}`
96
+ };
97
+ emit({ kind: "grantSettled", orderId: params.orderId, ok: false });
98
+ return false;
99
+ }
100
+ };
101
+ const params = {
102
+ options: {
103
+ sku,
104
+ ...(offerId !== undefined && offerId !== null ? { offerId } : {}),
105
+ processProductGrant
106
+ },
107
+ onEvent: (event) => {
108
+ if (event.type === "success") {
109
+ emit({ kind: "sdkSuccess", data: event.data });
110
+ }
111
+ },
112
+ onError: (error) => {
113
+ emit({ kind: "sdkError", error });
114
+ }
115
+ };
116
+ return startOrder(params);
117
+ },
118
+ reduce: (event) => {
119
+ switch (event.kind) {
120
+ case "sdkSuccess":
121
+ stashedSuccess = event.data;
122
+ return evaluate();
123
+ case "grantSettled":
124
+ return evaluate();
125
+ case "sdkError": {
126
+ if (grantFailure) {
127
+ // The server grant failed first; that outcome wins over the
128
+ // platform's error report.
129
+ return evaluate();
130
+ }
131
+ const code = toIapErrorCode(event.error);
132
+ if (confirmedOrderId !== undefined ||
133
+ observedOrderId !== undefined ||
134
+ stashedSuccess !== undefined) {
135
+ // The order is already identified (grant ran, is running, or the
136
+ // success event named it) — an SDK error here is not a
137
+ // definitive failure. Report unknown so the caller verifies
138
+ // server-side instead of retrying into a second paid order.
139
+ return {
140
+ done: true,
141
+ result: {
142
+ status: "unknown",
143
+ orderId: confirmedOrderId ?? observedOrderId ?? stashedSuccess?.orderId,
144
+ ...(confirmedSubscriptionId !== undefined ||
145
+ observedSubscriptionId !== undefined
146
+ ? { subscriptionId: confirmedSubscriptionId ?? observedSubscriptionId }
147
+ : {}),
148
+ reason: `SDK error after the order was identified (${code ?? toIapErrorMessage(event.error)}); the order may already be granted — verify server-side and recover it via pending orders`
149
+ }
150
+ };
151
+ }
152
+ if (code === "USER_CANCELED") {
153
+ return { done: true, result: { status: "canceled" } };
154
+ }
155
+ return {
156
+ done: true,
157
+ result: {
158
+ status: "failed",
159
+ ...(code !== undefined ? { code } : {}),
160
+ reason: toIapErrorMessage(event.error)
161
+ }
162
+ };
163
+ }
164
+ }
165
+ }
166
+ });
167
+ }
@@ -0,0 +1,44 @@
1
+ import { type SdkAnonymousKey, type SdkLoginResult } from "../index.js";
2
+ /**
3
+ * Internal platform contract for the login/anonymous-identity adapters,
4
+ * shared by /rn and /web. Each entry declares its own structural copy of
5
+ * the official SDK surface; this module is the common engine boundary and
6
+ * is never exported from a public subpath.
7
+ */
8
+ export interface TossAuthLike {
9
+ login: FunctionWithSupport<() => Promise<unknown>> | undefined;
10
+ }
11
+ export interface UserLike {
12
+ getAnonymousKey: FunctionWithSupport<() => Promise<unknown>> | undefined;
13
+ }
14
+ type FunctionWithSupport<F> = F & {
15
+ isSupported?: () => boolean;
16
+ };
17
+ export interface IdentityPlatformSdk {
18
+ TossAuth?: TossAuthLike;
19
+ User?: UserLike;
20
+ }
21
+ export type IdentityPlatformLoader = () => Promise<{
22
+ available: true;
23
+ module: IdentityPlatformSdk;
24
+ } | {
25
+ available: false;
26
+ reason: string;
27
+ }>;
28
+ /**
29
+ * Runs the platform login call and validates the result: the
30
+ * authorizationCode must be a non-empty string and the referrer one of the
31
+ * documented values, both preserved verbatim. SDK rejections propagate to
32
+ * the caller unchanged; unsupported environments reject with
33
+ * SdkError("UNSUPPORTED") when the platform exposes isSupported.
34
+ */
35
+ export declare function runSdkLogin(platform: IdentityPlatformSdk): Promise<SdkLoginResult>;
36
+ /**
37
+ * Runs the anonymous key lookup and validates the documented result shape
38
+ * ({ type: "HASH", hash }): sentinel values, missing or malformed results
39
+ * reject with SdkError("INVALID_ANONYMOUS_KEY") — the adapter never
40
+ * fabricates a key. SDK rejections propagate unchanged.
41
+ */
42
+ export declare function runSdkGetAnonymousKey(platform: IdentityPlatformSdk): Promise<SdkAnonymousKey>;
43
+ export {};
44
+ //# sourceMappingURL=platform-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-contract.d.ts","sourceRoot":"","sources":["../../src/identity/platform-contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAElF;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,mBAAmB,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;CAChE;AAED,MAAM,WAAW,QAAQ;IACvB,eAAe,EAAE,mBAAmB,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;CAC1E;AAED,KAAK,mBAAmB,CAAC,CAAC,IAAI,CAAC,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,OAAO,CAAA;CAAE,CAAC;AAGlE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,IAAI,CAAC,EAAE,QAAQ,CAAC;CACjB;AAED,MAAM,MAAM,sBAAsB,GAAG,MAAM,OAAO,CAChD;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,GAAG;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CACxF,CAAC;AAIF;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,cAAc,CAAC,CA+BzB;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,eAAe,CAAC,CA2B1B"}
@@ -0,0 +1,71 @@
1
+ import { SdkError } from "../index.js";
2
+ const REFERRERS = new Set(["DEFAULT", "SANDBOX"]);
3
+ /**
4
+ * Runs the platform login call and validates the result: the
5
+ * authorizationCode must be a non-empty string and the referrer one of the
6
+ * documented values, both preserved verbatim. SDK rejections propagate to
7
+ * the caller unchanged; unsupported environments reject with
8
+ * SdkError("UNSUPPORTED") when the platform exposes isSupported.
9
+ */
10
+ export async function runSdkLogin(platform) {
11
+ const login = platform.TossAuth?.login;
12
+ if (typeof login !== "function") {
13
+ throw new SdkError("UNSUPPORTED", "the installed SDK does not expose TossAuth.login");
14
+ }
15
+ if (typeof login.isSupported === "function" && !login.isSupported()) {
16
+ throw new SdkError("UNSUPPORTED", "login is not supported on this app version");
17
+ }
18
+ // Receiver-preserving member call for class-instance modules.
19
+ const result = await platform.TossAuth.login();
20
+ const authorizationCode = typeof result === "object" && result !== null
21
+ ? result.authorizationCode
22
+ : undefined;
23
+ const referrer = typeof result === "object" && result !== null
24
+ ? result.referrer
25
+ : undefined;
26
+ if (typeof authorizationCode !== "string" || !authorizationCode.trim()) {
27
+ throw new SdkError("INVALID_LOGIN_RESULT", "login result did not include a non-blank authorizationCode string");
28
+ }
29
+ if (typeof referrer !== "string" || !REFERRERS.has(referrer)) {
30
+ throw new SdkError("INVALID_LOGIN_RESULT", `login result carried an unrecognized referrer: ${String(referrer)}`);
31
+ }
32
+ return { authorizationCode, referrer: referrer };
33
+ }
34
+ /**
35
+ * Runs the anonymous key lookup and validates the documented result shape
36
+ * ({ type: "HASH", hash }): sentinel values, missing or malformed results
37
+ * reject with SdkError("INVALID_ANONYMOUS_KEY") — the adapter never
38
+ * fabricates a key. SDK rejections propagate unchanged.
39
+ */
40
+ export async function runSdkGetAnonymousKey(platform) {
41
+ const getAnonymousKey = platform.User?.getAnonymousKey;
42
+ if (typeof getAnonymousKey !== "function") {
43
+ throw new SdkError("UNSUPPORTED", "the installed SDK does not expose User.getAnonymousKey");
44
+ }
45
+ if (typeof getAnonymousKey.isSupported === "function" &&
46
+ !getAnonymousKey.isSupported()) {
47
+ throw new SdkError("UNSUPPORTED", "anonymous keys are not supported in this environment");
48
+ }
49
+ const result = await platform.User.getAnonymousKey();
50
+ const hash = typeof result === "object" && result !== null
51
+ ? result.hash
52
+ : undefined;
53
+ const type = typeof result === "object" && result !== null
54
+ ? result.type
55
+ : undefined;
56
+ if (type !== "HASH" || typeof hash !== "string" || !hash.trim()) {
57
+ throw new SdkError("INVALID_ANONYMOUS_KEY", `anonymous key result was not { type: "HASH", hash } with a non-blank hash: received ${describe(result)}`);
58
+ }
59
+ return { type: "HASH", hash };
60
+ }
61
+ function describe(value) {
62
+ if (value === undefined)
63
+ return "undefined";
64
+ if (value === null)
65
+ return "null";
66
+ if (typeof value === "object") {
67
+ const type = value.type;
68
+ return typeof type === "string" ? `{ type: ${type} }` : "an unrecognized object";
69
+ }
70
+ return typeof value;
71
+ }