@takeal/cusfront-sdk 0.1.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/CHANGELOG.md +66 -0
- package/LICENSE +21 -0
- package/README.md +283 -0
- package/dist/customer-CoxPwe5o.d.cts +32 -0
- package/dist/customer-CoxPwe5o.d.ts +32 -0
- package/dist/http-BkZZZI8K.d.cts +72 -0
- package/dist/http-BkZZZI8K.d.ts +72 -0
- package/dist/index.cjs +486 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +149 -0
- package/dist/index.d.ts +149 -0
- package/dist/index.js +478 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.cjs +82 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +93 -0
- package/dist/react/index.d.ts +93 -0
- package/dist/react/index.js +75 -0
- package/dist/react/index.js.map +1 -0
- package/dist/resources/auth.cjs +88 -0
- package/dist/resources/auth.cjs.map +1 -0
- package/dist/resources/auth.d.cts +132 -0
- package/dist/resources/auth.d.ts +132 -0
- package/dist/resources/auth.js +86 -0
- package/dist/resources/auth.js.map +1 -0
- package/dist/resources/balance.cjs +21 -0
- package/dist/resources/balance.cjs.map +1 -0
- package/dist/resources/balance.d.cts +27 -0
- package/dist/resources/balance.d.ts +27 -0
- package/dist/resources/balance.js +19 -0
- package/dist/resources/balance.js.map +1 -0
- package/dist/resources/blog.cjs +39 -0
- package/dist/resources/blog.cjs.map +1 -0
- package/dist/resources/blog.d.cts +112 -0
- package/dist/resources/blog.d.ts +112 -0
- package/dist/resources/blog.js +37 -0
- package/dist/resources/blog.js.map +1 -0
- package/dist/resources/branding.cjs +16 -0
- package/dist/resources/branding.cjs.map +1 -0
- package/dist/resources/branding.d.cts +35 -0
- package/dist/resources/branding.d.ts +35 -0
- package/dist/resources/branding.js +14 -0
- package/dist/resources/branding.js.map +1 -0
- package/dist/resources/cards.cjs +91 -0
- package/dist/resources/cards.cjs.map +1 -0
- package/dist/resources/cards.d.cts +166 -0
- package/dist/resources/cards.d.ts +166 -0
- package/dist/resources/cards.js +89 -0
- package/dist/resources/cards.js.map +1 -0
- package/dist/resources/deposits.cjs +52 -0
- package/dist/resources/deposits.cjs.map +1 -0
- package/dist/resources/deposits.d.cts +168 -0
- package/dist/resources/deposits.d.ts +168 -0
- package/dist/resources/deposits.js +50 -0
- package/dist/resources/deposits.js.map +1 -0
- package/dist/resources/subscriptions.cjs +24 -0
- package/dist/resources/subscriptions.cjs.map +1 -0
- package/dist/resources/subscriptions.d.cts +36 -0
- package/dist/resources/subscriptions.d.ts +36 -0
- package/dist/resources/subscriptions.js +22 -0
- package/dist/resources/subscriptions.js.map +1 -0
- package/dist/telegram.cjs +557 -0
- package/dist/telegram.cjs.map +1 -0
- package/dist/telegram.d.cts +105 -0
- package/dist/telegram.d.ts +105 -0
- package/dist/telegram.js +550 -0
- package/dist/telegram.js.map +1 -0
- package/dist/webhooks.cjs +78 -0
- package/dist/webhooks.cjs.map +1 -0
- package/dist/webhooks.d.cts +70 -0
- package/dist/webhooks.d.ts +70 -0
- package/dist/webhooks.js +72 -0
- package/dist/webhooks.js.map +1 -0
- package/package.json +123 -0
package/dist/telegram.js
ADDED
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
// src/error.ts
|
|
2
|
+
var ApiError = class extends Error {
|
|
3
|
+
constructor(status, code, message, body) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.body = body;
|
|
8
|
+
this.isApiError = true;
|
|
9
|
+
this.name = "ApiError";
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var NetworkError = class extends Error {
|
|
13
|
+
constructor(message, cause) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.cause = cause;
|
|
16
|
+
this.isNetworkError = true;
|
|
17
|
+
this.name = "NetworkError";
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// src/http.ts
|
|
22
|
+
var HttpClient = class {
|
|
23
|
+
constructor(baseUrl, tokens, fetchImpl) {
|
|
24
|
+
this.baseUrl = baseUrl;
|
|
25
|
+
this.tokens = tokens;
|
|
26
|
+
this.fetchImpl = fetchImpl;
|
|
27
|
+
if (!baseUrl) throw new Error("HttpClient: baseUrl is required");
|
|
28
|
+
}
|
|
29
|
+
async get(path, opts = {}) {
|
|
30
|
+
return this.request("GET", path, opts);
|
|
31
|
+
}
|
|
32
|
+
async post(path, opts = {}) {
|
|
33
|
+
return this.request("POST", path, opts);
|
|
34
|
+
}
|
|
35
|
+
async put(path, opts = {}) {
|
|
36
|
+
return this.request("PUT", path, opts);
|
|
37
|
+
}
|
|
38
|
+
async patch(path, opts = {}) {
|
|
39
|
+
return this.request("PATCH", path, opts);
|
|
40
|
+
}
|
|
41
|
+
async delete(path, opts = {}) {
|
|
42
|
+
return this.request("DELETE", path, opts);
|
|
43
|
+
}
|
|
44
|
+
async request(method, path, opts) {
|
|
45
|
+
const url = this.absolute(path);
|
|
46
|
+
const headers = {
|
|
47
|
+
Accept: "application/json"
|
|
48
|
+
};
|
|
49
|
+
if (opts.body !== void 0) headers["Content-Type"] = "application/json";
|
|
50
|
+
if (opts.idempotencyKey) headers["Idempotency-Key"] = opts.idempotencyKey;
|
|
51
|
+
if (!opts.skipAuth) {
|
|
52
|
+
const token = await Promise.resolve(this.tokens.get());
|
|
53
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
54
|
+
}
|
|
55
|
+
Object.assign(headers, opts.headers ?? {});
|
|
56
|
+
const init = {
|
|
57
|
+
method,
|
|
58
|
+
headers,
|
|
59
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
60
|
+
};
|
|
61
|
+
if (opts.body !== void 0) {
|
|
62
|
+
init.body = typeof opts.body === "string" ? opts.body : JSON.stringify(opts.body);
|
|
63
|
+
}
|
|
64
|
+
let resp;
|
|
65
|
+
try {
|
|
66
|
+
resp = await this.fetchImpl(url, init);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
throw new NetworkError(
|
|
69
|
+
e instanceof Error ? e.message : "network error",
|
|
70
|
+
e
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return parseResponse(resp);
|
|
74
|
+
}
|
|
75
|
+
absolute(path) {
|
|
76
|
+
if (path.startsWith("http://") || path.startsWith("https://")) return path;
|
|
77
|
+
const base = this.baseUrl.replace(/\/+$/, "");
|
|
78
|
+
const tail = path.startsWith("/") ? path : `/${path}`;
|
|
79
|
+
return `${base}${tail}`;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
async function parseResponse(resp) {
|
|
83
|
+
const text = await resp.text();
|
|
84
|
+
let body;
|
|
85
|
+
if (text.length === 0) {
|
|
86
|
+
body = void 0;
|
|
87
|
+
} else {
|
|
88
|
+
try {
|
|
89
|
+
body = JSON.parse(text);
|
|
90
|
+
} catch {
|
|
91
|
+
body = { raw: text };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (!resp.ok) {
|
|
95
|
+
const errBody = body ?? {};
|
|
96
|
+
throw new ApiError(
|
|
97
|
+
resp.status,
|
|
98
|
+
errBody.code ?? `http_${resp.status}`,
|
|
99
|
+
errBody.message ?? resp.statusText ?? "request failed",
|
|
100
|
+
body
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
return body;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/resources/auth.ts
|
|
107
|
+
var AuthResource = class {
|
|
108
|
+
constructor(http, tokens) {
|
|
109
|
+
this.http = http;
|
|
110
|
+
this.tokens = tokens;
|
|
111
|
+
}
|
|
112
|
+
/** Password-auth entry. May resolve to a JWT, or to a step-up
|
|
113
|
+
* challenge that needs `verifyTotp` / `verifyOtp` next. */
|
|
114
|
+
async login(input) {
|
|
115
|
+
const resp = await this.http.post("/auth/login", {
|
|
116
|
+
body: input,
|
|
117
|
+
skipAuth: true
|
|
118
|
+
});
|
|
119
|
+
if (resp.stage === "jwt") {
|
|
120
|
+
await Promise.resolve(this.tokens.set(resp.access_token));
|
|
121
|
+
}
|
|
122
|
+
return resp;
|
|
123
|
+
}
|
|
124
|
+
/** Verify a TOTP code (or XXXX-XXXX backup code) against a
|
|
125
|
+
* `totp_required` challenge. On success the JWT is stored. */
|
|
126
|
+
async verifyTotp(input) {
|
|
127
|
+
const resp = await this.http.post("/auth/login/totp", {
|
|
128
|
+
body: input,
|
|
129
|
+
skipAuth: true
|
|
130
|
+
});
|
|
131
|
+
await Promise.resolve(this.tokens.set(resp.access_token));
|
|
132
|
+
return resp;
|
|
133
|
+
}
|
|
134
|
+
/** Verify a 6-digit OTP code (Telegram / email) against an
|
|
135
|
+
* `otp_sent` challenge. */
|
|
136
|
+
async verifyOtp(input) {
|
|
137
|
+
const resp = await this.http.post("/auth/login/otp/verify", {
|
|
138
|
+
body: input,
|
|
139
|
+
skipAuth: true
|
|
140
|
+
});
|
|
141
|
+
await Promise.resolve(this.tokens.set(resp.access_token));
|
|
142
|
+
return resp;
|
|
143
|
+
}
|
|
144
|
+
/** Swap the 2FA method mid-flow. Resolves to `otp_sent` for
|
|
145
|
+
* telegram/email; for TOTP it short-circuits back to the challenge. */
|
|
146
|
+
async switchMethod(input) {
|
|
147
|
+
return this.http.post("/auth/login/2fa/switch", {
|
|
148
|
+
body: input,
|
|
149
|
+
skipAuth: true
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/** Exchange a signed Telegram Mini App `initData` payload for a session
|
|
153
|
+
* JWT. The server validates the initData HMAC against the deployment's bot
|
|
154
|
+
* token, then find-or-creates the end-user. First-time Telegram users are
|
|
155
|
+
* auto-provisioned with `user.email_pending === true` — prompt them for a
|
|
156
|
+
* real email and call {@link linkEmail}. Stores the JWT on success.
|
|
157
|
+
*
|
|
158
|
+
* Most consumers use the higher-level `fromInitData` / `fromTelegramWebApp`
|
|
159
|
+
* helpers in `@takeal/cusfront-sdk/telegram`; reach for this directly when
|
|
160
|
+
* you already hold a configured client. */
|
|
161
|
+
async exchangeTelegram(input) {
|
|
162
|
+
const resp = await this.http.post("/auth/telegram/exchange", {
|
|
163
|
+
body: { init_data: input.initData },
|
|
164
|
+
skipAuth: true
|
|
165
|
+
});
|
|
166
|
+
await Promise.resolve(this.tokens.set(resp.access_token));
|
|
167
|
+
return resp;
|
|
168
|
+
}
|
|
169
|
+
/** Attach a real email to the current user (and clear `email_pending`).
|
|
170
|
+
* Used after a Telegram exchange to satisfy the email requirement; also
|
|
171
|
+
* works for a password user changing their address. 409 if taken. */
|
|
172
|
+
async linkEmail(input) {
|
|
173
|
+
return this.http.post("/me/email", { body: { email: input.email } });
|
|
174
|
+
}
|
|
175
|
+
/** Current user — fails 401 when the stored JWT is expired or
|
|
176
|
+
* missing. Useful as a "do I have a session" probe on app boot. */
|
|
177
|
+
async me() {
|
|
178
|
+
return this.http.get("/auth/me");
|
|
179
|
+
}
|
|
180
|
+
/** Local sign-out. The SDK doesn't currently call a server-side
|
|
181
|
+
* endpoint (the Takeal API issues stateless JWTs); clearing the
|
|
182
|
+
* store is enough. Server-side session invalidation may land
|
|
183
|
+
* with a future session table. */
|
|
184
|
+
async signOut() {
|
|
185
|
+
await Promise.resolve(this.tokens.clear());
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// src/resources/deposits.ts
|
|
190
|
+
var DepositsResource = class {
|
|
191
|
+
constructor(http) {
|
|
192
|
+
this.http = http;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Initiate a top-up. Resolves to the freshly-created `Deposit`, whose
|
|
196
|
+
* `status` may already be `confirmed` (synchronous rails / mock) or
|
|
197
|
+
* `redirect_required` (3DS / APM — forward the user to `redirect_url`).
|
|
198
|
+
*/
|
|
199
|
+
/**
|
|
200
|
+
* Price a cross-currency deposit BEFORE paying. The rate is locked
|
|
201
|
+
* server-side until `expires_at`; deposits in the local currency need no
|
|
202
|
+
* quote (the API answers 400 `no_quote_needed`).
|
|
203
|
+
*/
|
|
204
|
+
async quote(input) {
|
|
205
|
+
return this.http.post("/me/deposits/quotes", { body: input });
|
|
206
|
+
}
|
|
207
|
+
async initiate(input) {
|
|
208
|
+
const { idempotencyKey, ...body } = input;
|
|
209
|
+
return this.http.post("/me/deposits", {
|
|
210
|
+
body,
|
|
211
|
+
idempotencyKey
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/** Fetch one deposit by id — the poll target while a redirect resolves. */
|
|
215
|
+
async get(id) {
|
|
216
|
+
return this.http.get(`/me/deposits/${encodeURIComponent(id)}`);
|
|
217
|
+
}
|
|
218
|
+
/** List the caller's deposits, most recent first. */
|
|
219
|
+
async list() {
|
|
220
|
+
return this.http.get("/me/deposits");
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Refund a previously-confirmed deposit. Synchronous when the
|
|
224
|
+
* connector settles inline; otherwise the row stays `pending` and flips
|
|
225
|
+
* later via the inbound `deposit.refunded` event.
|
|
226
|
+
*/
|
|
227
|
+
async refund(depositId, input) {
|
|
228
|
+
const { idempotencyKey, ...body } = input;
|
|
229
|
+
return this.http.post(
|
|
230
|
+
`/me/deposits/${encodeURIComponent(depositId)}/refunds`,
|
|
231
|
+
{ body, idempotencyKey }
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// src/resources/cards.ts
|
|
237
|
+
var CardsResource = class {
|
|
238
|
+
constructor(http) {
|
|
239
|
+
this.http = http;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Issue a card backed by the user's wallet balance. Resolves to the new
|
|
243
|
+
* `Card`. On a ledger failure after provider creation, the Takeal API rolls the row
|
|
244
|
+
* to `failed` (never an orphaned active card); inspect `status` /
|
|
245
|
+
* `failure_reason` on the result.
|
|
246
|
+
*/
|
|
247
|
+
async create(input) {
|
|
248
|
+
const { idempotencyKey, ...body } = input;
|
|
249
|
+
return this.http.post("/me/cards", {
|
|
250
|
+
body,
|
|
251
|
+
idempotencyKey
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
/** Fetch one card by id. */
|
|
255
|
+
async get(id) {
|
|
256
|
+
return this.http.get(`/me/cards/${encodeURIComponent(id)}`);
|
|
257
|
+
}
|
|
258
|
+
/** List the caller's cards, most recent first. */
|
|
259
|
+
async list() {
|
|
260
|
+
return this.http.get("/me/cards");
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Remaining balance of a prepaid card. `currency` is required by the Takeal API and
|
|
264
|
+
* forwarded as a query param.
|
|
265
|
+
*/
|
|
266
|
+
async balance(id, currency) {
|
|
267
|
+
const q = new URLSearchParams({ currency }).toString();
|
|
268
|
+
return this.http.get(
|
|
269
|
+
`/me/cards/${encodeURIComponent(id)}/balance?${q}`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Freeze the caller's own card. Reversible via {@link unfreeze}. The optional
|
|
274
|
+
* `reason` is recorded on the audit trail. Resolves to the updated card.
|
|
275
|
+
* 404 if the card isn't the caller's; 501 if the issuer can't freeze.
|
|
276
|
+
*/
|
|
277
|
+
async freeze(id, reason) {
|
|
278
|
+
return this.http.post(
|
|
279
|
+
`/me/cards/${encodeURIComponent(id)}/freeze`,
|
|
280
|
+
{ body: { reason } }
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Reveal the FULL PAN + CVV for the caller's own card. Re-auth gated
|
|
285
|
+
* (password, + TOTP when the user has it enrolled) and rate-limited
|
|
286
|
+
* server-side. The Takeal API never persists this data and marks the
|
|
287
|
+
* response no-store; the SDK returns it verbatim and holds nothing.
|
|
288
|
+
*
|
|
289
|
+
* **Consumer security duties** (the SDK can't enforce these for you):
|
|
290
|
+
* keep the result in volatile memory only, never write it to
|
|
291
|
+
* localStorage / logs, and clear it within `expires_in_seconds`.
|
|
292
|
+
*
|
|
293
|
+
* 401 bad re-auth · 403 not your card · 429 rate-limited · 501 connector
|
|
294
|
+
* has no sensitive-data endpoint · 503 provider error.
|
|
295
|
+
*/
|
|
296
|
+
async reveal(id, input) {
|
|
297
|
+
return this.http.post(
|
|
298
|
+
`/me/cards/${encodeURIComponent(id)}/reveal`,
|
|
299
|
+
{ body: { password: input.password, totp_code: input.totpCode } }
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
/** Unfreeze a previously-frozen card. Idempotent on an already-active card. */
|
|
303
|
+
async unfreeze(id) {
|
|
304
|
+
return this.http.post(
|
|
305
|
+
`/me/cards/${encodeURIComponent(id)}/unfreeze`,
|
|
306
|
+
{ body: {} }
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Terminate the caller's own card. **One-way** — a terminated card cannot be
|
|
311
|
+
* reactivated. The optional `reason` is audited. Idempotent on an
|
|
312
|
+
* already-terminated card.
|
|
313
|
+
*/
|
|
314
|
+
async terminate(id, reason) {
|
|
315
|
+
return this.http.post(
|
|
316
|
+
`/me/cards/${encodeURIComponent(id)}/terminate`,
|
|
317
|
+
{ body: { reason } }
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
// src/resources/balance.ts
|
|
323
|
+
var BalanceResource = class {
|
|
324
|
+
constructor(http) {
|
|
325
|
+
this.http = http;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Fetch the user's wallet balance for `currency` (ISO 4217, e.g. `"USD"`).
|
|
329
|
+
* Required — the ledger holds a separate balance per currency, so there is
|
|
330
|
+
* no "total" without one.
|
|
331
|
+
*/
|
|
332
|
+
async get(currency) {
|
|
333
|
+
const q = new URLSearchParams({ currency }).toString();
|
|
334
|
+
return this.http.get(`/me/balance?${q}`);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// src/resources/branding.ts
|
|
339
|
+
var BrandingResource = class {
|
|
340
|
+
constructor(http) {
|
|
341
|
+
this.http = http;
|
|
342
|
+
}
|
|
343
|
+
/** Fetch the deployment's public brand config. Safe to call before login. */
|
|
344
|
+
async get(opts = {}) {
|
|
345
|
+
return this.http.get("/branding", { skipAuth: true, signal: opts.signal });
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
// src/resources/blog.ts
|
|
350
|
+
var BlogResource = class {
|
|
351
|
+
constructor(http) {
|
|
352
|
+
this.http = http;
|
|
353
|
+
}
|
|
354
|
+
/** Published posts, newest first. No authentication required. */
|
|
355
|
+
async list(input = {}) {
|
|
356
|
+
const q = new URLSearchParams();
|
|
357
|
+
if (input.page) q.set("page", String(input.page));
|
|
358
|
+
if (input.perPage) q.set("per_page", String(input.perPage));
|
|
359
|
+
if (input.tag) q.set("tag", input.tag);
|
|
360
|
+
if (input.locale) q.set("locale", input.locale);
|
|
361
|
+
const qs = q.toString();
|
|
362
|
+
return this.http.get(`/blog/posts${qs ? `?${qs}` : ""}`, {
|
|
363
|
+
skipAuth: true
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
/** One post by slug. No authentication required. */
|
|
367
|
+
async get(slug) {
|
|
368
|
+
return this.http.get(`/blog/posts/${encodeURIComponent(slug)}`, {
|
|
369
|
+
skipAuth: true
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Absolute URL for an image path returned inside a post (`cover_url`, or an
|
|
374
|
+
* image block's `url`). Handy when the app renders on a different origin
|
|
375
|
+
* than the API.
|
|
376
|
+
*/
|
|
377
|
+
imageUrl(baseUrl, path) {
|
|
378
|
+
if (/^https?:\/\//i.test(path)) return path;
|
|
379
|
+
return `${baseUrl.replace(/\/$/, "")}${path.startsWith("/") ? "" : "/"}${path}`;
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
// src/resources/subscriptions.ts
|
|
384
|
+
var SubscriptionsResource = class {
|
|
385
|
+
constructor(http) {
|
|
386
|
+
this.http = http;
|
|
387
|
+
}
|
|
388
|
+
async get() {
|
|
389
|
+
return this.http.get("/me/subscriptions");
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Replace the whole set — this is a PUT, not a merge. Pass `[]` to opt out
|
|
393
|
+
* of everything.
|
|
394
|
+
*/
|
|
395
|
+
async set(channels) {
|
|
396
|
+
return this.http.put("/me/subscriptions", {
|
|
397
|
+
body: { channels }
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// src/token-store.ts
|
|
403
|
+
function defaultBrowserStore(key = "takeal_jwt") {
|
|
404
|
+
const fallback = inMemoryStore();
|
|
405
|
+
let usable = false;
|
|
406
|
+
try {
|
|
407
|
+
if (typeof globalThis !== "undefined" && "localStorage" in globalThis) {
|
|
408
|
+
const ls2 = globalThis.localStorage;
|
|
409
|
+
const probe = "__takeal_probe__";
|
|
410
|
+
ls2.setItem(probe, "1");
|
|
411
|
+
ls2.removeItem(probe);
|
|
412
|
+
usable = true;
|
|
413
|
+
}
|
|
414
|
+
} catch {
|
|
415
|
+
usable = false;
|
|
416
|
+
}
|
|
417
|
+
if (!usable) return fallback;
|
|
418
|
+
const ls = globalThis.localStorage;
|
|
419
|
+
return {
|
|
420
|
+
get: () => ls.getItem(key),
|
|
421
|
+
set: (token) => ls.setItem(key, token),
|
|
422
|
+
clear: () => ls.removeItem(key)
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
function inMemoryStore() {
|
|
426
|
+
let value = null;
|
|
427
|
+
return {
|
|
428
|
+
get: () => value,
|
|
429
|
+
set: (token) => {
|
|
430
|
+
value = token;
|
|
431
|
+
},
|
|
432
|
+
clear: () => {
|
|
433
|
+
value = null;
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// src/index.ts
|
|
439
|
+
function createClient(opts) {
|
|
440
|
+
const tokens = opts.tokenStore ?? pickDefaultStore();
|
|
441
|
+
const fetchImpl = opts.fetch ?? defaultFetch();
|
|
442
|
+
const http = new HttpClient(opts.baseUrl, tokens, fetchImpl);
|
|
443
|
+
return {
|
|
444
|
+
brand: opts.brand,
|
|
445
|
+
tokens,
|
|
446
|
+
auth: new AuthResource(http, tokens),
|
|
447
|
+
deposits: new DepositsResource(http),
|
|
448
|
+
cards: new CardsResource(http),
|
|
449
|
+
balance: new BalanceResource(http),
|
|
450
|
+
branding: new BrandingResource(http),
|
|
451
|
+
blog: new BlogResource(http),
|
|
452
|
+
subscriptions: new SubscriptionsResource(http)
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
function pickDefaultStore() {
|
|
456
|
+
if (typeof globalThis !== "undefined" && "localStorage" in globalThis) {
|
|
457
|
+
return defaultBrowserStore();
|
|
458
|
+
}
|
|
459
|
+
return inMemoryStore();
|
|
460
|
+
}
|
|
461
|
+
function defaultFetch() {
|
|
462
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.fetch === "function") {
|
|
463
|
+
return globalThis.fetch.bind(globalThis);
|
|
464
|
+
}
|
|
465
|
+
throw new Error(
|
|
466
|
+
"@takeal/cusfront-sdk: native fetch is not available in this runtime. Pass a polyfill via createClient({ fetch })."
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// src/telegram.ts
|
|
471
|
+
var InitDataError = class extends Error {
|
|
472
|
+
constructor(message, reason) {
|
|
473
|
+
super(message);
|
|
474
|
+
this.reason = reason;
|
|
475
|
+
this.name = "InitDataError";
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
function parseInitData(initData) {
|
|
479
|
+
const params = {};
|
|
480
|
+
const sp = new URLSearchParams(initData);
|
|
481
|
+
for (const [k, v] of sp.entries()) params[k] = v;
|
|
482
|
+
let user;
|
|
483
|
+
if (params.user) {
|
|
484
|
+
try {
|
|
485
|
+
user = JSON.parse(params.user);
|
|
486
|
+
} catch {
|
|
487
|
+
user = void 0;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
const authDateRaw = params.auth_date;
|
|
491
|
+
const auth_date = authDateRaw && /^\d+$/.test(authDateRaw) ? Number(authDateRaw) : void 0;
|
|
492
|
+
return {
|
|
493
|
+
raw: initData,
|
|
494
|
+
params,
|
|
495
|
+
...params.hash ? { hash: params.hash } : {},
|
|
496
|
+
...auth_date !== void 0 ? { auth_date } : {},
|
|
497
|
+
...params.query_id ? { query_id: params.query_id } : {},
|
|
498
|
+
...user ? { user } : {}
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
function assertWellFormed(initData, opts = {}) {
|
|
502
|
+
if (!initData) throw new InitDataError("initData is empty", "empty");
|
|
503
|
+
const parsed = parseInitData(initData);
|
|
504
|
+
if (!parsed.hash)
|
|
505
|
+
throw new InitDataError("initData is missing `hash`", "missing_hash");
|
|
506
|
+
if (parsed.auth_date === void 0)
|
|
507
|
+
throw new InitDataError(
|
|
508
|
+
"initData is missing a valid `auth_date`",
|
|
509
|
+
"missing_auth_date"
|
|
510
|
+
);
|
|
511
|
+
if (!parsed.user || typeof parsed.user.id !== "number")
|
|
512
|
+
throw new InitDataError(
|
|
513
|
+
"initData is missing a valid `user`",
|
|
514
|
+
"bad_user"
|
|
515
|
+
);
|
|
516
|
+
const maxAge = opts.maxAgeSeconds ?? 86400;
|
|
517
|
+
if (maxAge > 0) {
|
|
518
|
+
const now = opts.nowUnix ?? Math.floor(Date.now() / 1e3);
|
|
519
|
+
if (now - parsed.auth_date > maxAge)
|
|
520
|
+
throw new InitDataError(
|
|
521
|
+
"initData is stale (auth_date older than the freshness window)",
|
|
522
|
+
"stale"
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
return parsed;
|
|
526
|
+
}
|
|
527
|
+
async function fromInitData(initData, config, opts = {}) {
|
|
528
|
+
if (!opts.skipWellFormedCheck) assertWellFormed(initData, opts);
|
|
529
|
+
const client = createClient(config);
|
|
530
|
+
await client.auth.exchangeTelegram({ initData });
|
|
531
|
+
return client;
|
|
532
|
+
}
|
|
533
|
+
function readWebAppInitData() {
|
|
534
|
+
const tg = globalThis.Telegram;
|
|
535
|
+
const data = tg?.WebApp?.initData;
|
|
536
|
+
return data && data.length > 0 ? data : null;
|
|
537
|
+
}
|
|
538
|
+
async function fromTelegramWebApp(config, opts = {}) {
|
|
539
|
+
const initData = readWebAppInitData();
|
|
540
|
+
if (!initData)
|
|
541
|
+
throw new InitDataError(
|
|
542
|
+
"not running inside a Telegram Mini App (window.Telegram.WebApp.initData is empty)",
|
|
543
|
+
"empty"
|
|
544
|
+
);
|
|
545
|
+
return fromInitData(initData, config, opts);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
export { InitDataError, assertWellFormed, fromInitData, fromTelegramWebApp, parseInitData, readWebAppInitData };
|
|
549
|
+
//# sourceMappingURL=telegram.js.map
|
|
550
|
+
//# sourceMappingURL=telegram.js.map
|