@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
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { H as HttpClient, T as TokenStore } from '../http-BkZZZI8K.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Auth resource — `client.auth.*`.
|
|
5
|
+
*
|
|
6
|
+
* Covers the end-user-facing staged auth flow: password login
|
|
7
|
+
* with optional TOTP/OTP step-up. The shape mirrors the responses
|
|
8
|
+
* the Takeal API returns from `/auth/login`, `/auth/login/totp`,
|
|
9
|
+
* `/auth/login/otp/verify`, `/auth/login/2fa/switch`, and `/auth/me`.
|
|
10
|
+
*
|
|
11
|
+
* The login methods stash the JWT in the configured `TokenStore` on
|
|
12
|
+
* success so subsequent `client.*` calls authenticate automatically.
|
|
13
|
+
* Step-up responses (TotpRequired / OtpSent) carry their own short-
|
|
14
|
+
* lived challenge token which the caller passes to the next method
|
|
15
|
+
* — those tokens DO NOT go through the TokenStore (they're not the
|
|
16
|
+
* end-state JWT).
|
|
17
|
+
*/
|
|
18
|
+
interface User {
|
|
19
|
+
id: string;
|
|
20
|
+
email: string;
|
|
21
|
+
role: string;
|
|
22
|
+
is_active: boolean;
|
|
23
|
+
/** True for users auto-provisioned via the Telegram Mini App who still hold
|
|
24
|
+
* a synthetic placeholder email. The client should prompt for a real email
|
|
25
|
+
* and attach it via `auth.linkEmail` — this flips the flag to false. */
|
|
26
|
+
email_pending?: boolean;
|
|
27
|
+
created_at: string;
|
|
28
|
+
updated_at: string;
|
|
29
|
+
}
|
|
30
|
+
interface LoginInput {
|
|
31
|
+
email: string;
|
|
32
|
+
password: string;
|
|
33
|
+
}
|
|
34
|
+
/** Discriminated union — `stage` is the discriminator. */
|
|
35
|
+
type LoginResponse = JwtIssued | TotpRequired | TotpSetupRequired;
|
|
36
|
+
interface JwtIssued {
|
|
37
|
+
stage: "jwt";
|
|
38
|
+
access_token: string;
|
|
39
|
+
expires_at: string;
|
|
40
|
+
user: User;
|
|
41
|
+
/** True iff the user's role grants `system.view_hub`.
|
|
42
|
+
* Cusfront ignores this; the operator console uses it to gate `/dashboard`. */
|
|
43
|
+
hub_access?: boolean;
|
|
44
|
+
}
|
|
45
|
+
interface TotpRequired {
|
|
46
|
+
stage: "totp_required";
|
|
47
|
+
challenge_token: string;
|
|
48
|
+
expires_at: string;
|
|
49
|
+
/** Alternate second factors. Pick one with `/2fa/switch`. */
|
|
50
|
+
available_methods: AvailableMethod[];
|
|
51
|
+
}
|
|
52
|
+
interface TotpSetupRequired {
|
|
53
|
+
stage: "totp_setup_required";
|
|
54
|
+
challenge_token: string;
|
|
55
|
+
expires_at: string;
|
|
56
|
+
/** OTPAuth URI rendered as a QR by the consumer. */
|
|
57
|
+
otpauth_url: string;
|
|
58
|
+
/** Same secret base32-encoded — for manual entry alongside QR. */
|
|
59
|
+
secret_base32: string;
|
|
60
|
+
}
|
|
61
|
+
interface AvailableMethod {
|
|
62
|
+
kind: "totp" | "telegram_otp" | "email_otp";
|
|
63
|
+
/** Short user-facing hint, e.g. `"Telegram (@user)"` or `"e****@example.com"`. */
|
|
64
|
+
hint?: string;
|
|
65
|
+
}
|
|
66
|
+
interface TotpVerifyInput {
|
|
67
|
+
challenge_token: string;
|
|
68
|
+
/** 6-digit TOTP code OR `XXXX-XXXX` backup code. */
|
|
69
|
+
code: string;
|
|
70
|
+
}
|
|
71
|
+
interface OtpVerifyInput {
|
|
72
|
+
challenge_token: string;
|
|
73
|
+
/** 6-digit one-time code delivered via the selected channel. */
|
|
74
|
+
code: string;
|
|
75
|
+
}
|
|
76
|
+
interface SwitchMethodInput {
|
|
77
|
+
challenge_token: string;
|
|
78
|
+
method: AvailableMethod["kind"];
|
|
79
|
+
}
|
|
80
|
+
interface OtpSentResponse {
|
|
81
|
+
stage: "otp_sent";
|
|
82
|
+
challenge_token: string;
|
|
83
|
+
expires_at: string;
|
|
84
|
+
method: AvailableMethod["kind"];
|
|
85
|
+
hint?: string;
|
|
86
|
+
available_methods: AvailableMethod[];
|
|
87
|
+
}
|
|
88
|
+
declare class AuthResource {
|
|
89
|
+
private readonly http;
|
|
90
|
+
private readonly tokens;
|
|
91
|
+
constructor(http: HttpClient, tokens: TokenStore);
|
|
92
|
+
/** Password-auth entry. May resolve to a JWT, or to a step-up
|
|
93
|
+
* challenge that needs `verifyTotp` / `verifyOtp` next. */
|
|
94
|
+
login(input: LoginInput): Promise<LoginResponse>;
|
|
95
|
+
/** Verify a TOTP code (or XXXX-XXXX backup code) against a
|
|
96
|
+
* `totp_required` challenge. On success the JWT is stored. */
|
|
97
|
+
verifyTotp(input: TotpVerifyInput): Promise<JwtIssued>;
|
|
98
|
+
/** Verify a 6-digit OTP code (Telegram / email) against an
|
|
99
|
+
* `otp_sent` challenge. */
|
|
100
|
+
verifyOtp(input: OtpVerifyInput): Promise<JwtIssued>;
|
|
101
|
+
/** Swap the 2FA method mid-flow. Resolves to `otp_sent` for
|
|
102
|
+
* telegram/email; for TOTP it short-circuits back to the challenge. */
|
|
103
|
+
switchMethod(input: SwitchMethodInput): Promise<OtpSentResponse>;
|
|
104
|
+
/** Exchange a signed Telegram Mini App `initData` payload for a session
|
|
105
|
+
* JWT. The server validates the initData HMAC against the deployment's bot
|
|
106
|
+
* token, then find-or-creates the end-user. First-time Telegram users are
|
|
107
|
+
* auto-provisioned with `user.email_pending === true` — prompt them for a
|
|
108
|
+
* real email and call {@link linkEmail}. Stores the JWT on success.
|
|
109
|
+
*
|
|
110
|
+
* Most consumers use the higher-level `fromInitData` / `fromTelegramWebApp`
|
|
111
|
+
* helpers in `@takeal/cusfront-sdk/telegram`; reach for this directly when
|
|
112
|
+
* you already hold a configured client. */
|
|
113
|
+
exchangeTelegram(input: {
|
|
114
|
+
initData: string;
|
|
115
|
+
}): Promise<JwtIssued>;
|
|
116
|
+
/** Attach a real email to the current user (and clear `email_pending`).
|
|
117
|
+
* Used after a Telegram exchange to satisfy the email requirement; also
|
|
118
|
+
* works for a password user changing their address. 409 if taken. */
|
|
119
|
+
linkEmail(input: {
|
|
120
|
+
email: string;
|
|
121
|
+
}): Promise<User>;
|
|
122
|
+
/** Current user — fails 401 when the stored JWT is expired or
|
|
123
|
+
* missing. Useful as a "do I have a session" probe on app boot. */
|
|
124
|
+
me(): Promise<User>;
|
|
125
|
+
/** Local sign-out. The SDK doesn't currently call a server-side
|
|
126
|
+
* endpoint (the Takeal API issues stateless JWTs); clearing the
|
|
127
|
+
* store is enough. Server-side session invalidation may land
|
|
128
|
+
* with a future session table. */
|
|
129
|
+
signOut(): Promise<void>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export { AuthResource, type AvailableMethod, type JwtIssued, type LoginInput, type LoginResponse, type OtpSentResponse, type OtpVerifyInput, type SwitchMethodInput, type TotpRequired, type TotpSetupRequired, type TotpVerifyInput, type User };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// src/resources/auth.ts
|
|
2
|
+
var AuthResource = class {
|
|
3
|
+
constructor(http, tokens) {
|
|
4
|
+
this.http = http;
|
|
5
|
+
this.tokens = tokens;
|
|
6
|
+
}
|
|
7
|
+
/** Password-auth entry. May resolve to a JWT, or to a step-up
|
|
8
|
+
* challenge that needs `verifyTotp` / `verifyOtp` next. */
|
|
9
|
+
async login(input) {
|
|
10
|
+
const resp = await this.http.post("/auth/login", {
|
|
11
|
+
body: input,
|
|
12
|
+
skipAuth: true
|
|
13
|
+
});
|
|
14
|
+
if (resp.stage === "jwt") {
|
|
15
|
+
await Promise.resolve(this.tokens.set(resp.access_token));
|
|
16
|
+
}
|
|
17
|
+
return resp;
|
|
18
|
+
}
|
|
19
|
+
/** Verify a TOTP code (or XXXX-XXXX backup code) against a
|
|
20
|
+
* `totp_required` challenge. On success the JWT is stored. */
|
|
21
|
+
async verifyTotp(input) {
|
|
22
|
+
const resp = await this.http.post("/auth/login/totp", {
|
|
23
|
+
body: input,
|
|
24
|
+
skipAuth: true
|
|
25
|
+
});
|
|
26
|
+
await Promise.resolve(this.tokens.set(resp.access_token));
|
|
27
|
+
return resp;
|
|
28
|
+
}
|
|
29
|
+
/** Verify a 6-digit OTP code (Telegram / email) against an
|
|
30
|
+
* `otp_sent` challenge. */
|
|
31
|
+
async verifyOtp(input) {
|
|
32
|
+
const resp = await this.http.post("/auth/login/otp/verify", {
|
|
33
|
+
body: input,
|
|
34
|
+
skipAuth: true
|
|
35
|
+
});
|
|
36
|
+
await Promise.resolve(this.tokens.set(resp.access_token));
|
|
37
|
+
return resp;
|
|
38
|
+
}
|
|
39
|
+
/** Swap the 2FA method mid-flow. Resolves to `otp_sent` for
|
|
40
|
+
* telegram/email; for TOTP it short-circuits back to the challenge. */
|
|
41
|
+
async switchMethod(input) {
|
|
42
|
+
return this.http.post("/auth/login/2fa/switch", {
|
|
43
|
+
body: input,
|
|
44
|
+
skipAuth: true
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** Exchange a signed Telegram Mini App `initData` payload for a session
|
|
48
|
+
* JWT. The server validates the initData HMAC against the deployment's bot
|
|
49
|
+
* token, then find-or-creates the end-user. First-time Telegram users are
|
|
50
|
+
* auto-provisioned with `user.email_pending === true` — prompt them for a
|
|
51
|
+
* real email and call {@link linkEmail}. Stores the JWT on success.
|
|
52
|
+
*
|
|
53
|
+
* Most consumers use the higher-level `fromInitData` / `fromTelegramWebApp`
|
|
54
|
+
* helpers in `@takeal/cusfront-sdk/telegram`; reach for this directly when
|
|
55
|
+
* you already hold a configured client. */
|
|
56
|
+
async exchangeTelegram(input) {
|
|
57
|
+
const resp = await this.http.post("/auth/telegram/exchange", {
|
|
58
|
+
body: { init_data: input.initData },
|
|
59
|
+
skipAuth: true
|
|
60
|
+
});
|
|
61
|
+
await Promise.resolve(this.tokens.set(resp.access_token));
|
|
62
|
+
return resp;
|
|
63
|
+
}
|
|
64
|
+
/** Attach a real email to the current user (and clear `email_pending`).
|
|
65
|
+
* Used after a Telegram exchange to satisfy the email requirement; also
|
|
66
|
+
* works for a password user changing their address. 409 if taken. */
|
|
67
|
+
async linkEmail(input) {
|
|
68
|
+
return this.http.post("/me/email", { body: { email: input.email } });
|
|
69
|
+
}
|
|
70
|
+
/** Current user — fails 401 when the stored JWT is expired or
|
|
71
|
+
* missing. Useful as a "do I have a session" probe on app boot. */
|
|
72
|
+
async me() {
|
|
73
|
+
return this.http.get("/auth/me");
|
|
74
|
+
}
|
|
75
|
+
/** Local sign-out. The SDK doesn't currently call a server-side
|
|
76
|
+
* endpoint (the Takeal API issues stateless JWTs); clearing the
|
|
77
|
+
* store is enough. Server-side session invalidation may land
|
|
78
|
+
* with a future session table. */
|
|
79
|
+
async signOut() {
|
|
80
|
+
await Promise.resolve(this.tokens.clear());
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export { AuthResource };
|
|
85
|
+
//# sourceMappingURL=auth.js.map
|
|
86
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/resources/auth.ts"],"names":[],"mappings":";AAoGO,IAAM,eAAN,MAAmB;AAAA,EACxB,WAAA,CACmB,MACA,MAAA,EACjB;AAFiB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAChB;AAAA;AAAA;AAAA,EAIH,MAAM,MAAM,KAAA,EAA2C;AACrD,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,IAAA,CAAK,KAAoB,aAAA,EAAe;AAAA,MAC9D,IAAA,EAAM,KAAA;AAAA,MACN,QAAA,EAAU;AAAA,KACX,CAAA;AACD,IAAA,IAAI,IAAA,CAAK,UAAU,KAAA,EAAO;AACxB,MAAA,MAAM,QAAQ,OAAA,CAAQ,IAAA,CAAK,OAAO,GAAA,CAAI,IAAA,CAAK,YAAY,CAAC,CAAA;AAAA,IAC1D;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA,EAIA,MAAM,WAAW,KAAA,EAA4C;AAC3D,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,IAAA,CAAK,KAAgB,kBAAA,EAAoB;AAAA,MAC/D,IAAA,EAAM,KAAA;AAAA,MACN,QAAA,EAAU;AAAA,KACX,CAAA;AACD,IAAA,MAAM,QAAQ,OAAA,CAAQ,IAAA,CAAK,OAAO,GAAA,CAAI,IAAA,CAAK,YAAY,CAAC,CAAA;AACxD,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA,EAIA,MAAM,UAAU,KAAA,EAA2C;AACzD,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,IAAA,CAAK,KAAgB,wBAAA,EAA0B;AAAA,MACrE,IAAA,EAAM,KAAA;AAAA,MACN,QAAA,EAAU;AAAA,KACX,CAAA;AACD,IAAA,MAAM,QAAQ,OAAA,CAAQ,IAAA,CAAK,OAAO,GAAA,CAAI,IAAA,CAAK,YAAY,CAAC,CAAA;AACxD,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA,EAIA,MAAM,aAAa,KAAA,EAAoD;AACrE,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAAsB,wBAAA,EAA0B;AAAA,MAC/D,IAAA,EAAM,KAAA;AAAA,MACN,QAAA,EAAU;AAAA,KACX,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,iBAAiB,KAAA,EAAiD;AACtE,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,IAAA,CAAK,KAAgB,yBAAA,EAA2B;AAAA,MACtE,IAAA,EAAM,EAAE,SAAA,EAAW,KAAA,CAAM,QAAA,EAAS;AAAA,MAClC,QAAA,EAAU;AAAA,KACX,CAAA;AACD,IAAA,MAAM,QAAQ,OAAA,CAAQ,IAAA,CAAK,OAAO,GAAA,CAAI,IAAA,CAAK,YAAY,CAAC,CAAA;AACxD,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,KAAA,EAAyC;AACvD,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAAW,WAAA,EAAa,EAAE,IAAA,EAAM,EAAE,KAAA,EAAO,KAAA,CAAM,KAAA,EAAM,EAAG,CAAA;AAAA,EAC3E;AAAA;AAAA;AAAA,EAIA,MAAM,EAAA,GAAoB;AACxB,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAAU,UAAU,CAAA;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAA,GAAyB;AAC7B,IAAA,MAAM,OAAA,CAAQ,OAAA,CAAQ,IAAA,CAAK,MAAA,CAAO,OAAO,CAAA;AAAA,EAC3C;AACF","file":"auth.js","sourcesContent":["import type { HttpClient } from \"../http.js\";\nimport type { TokenStore } from \"../token-store.js\";\n\n/**\n * Auth resource — `client.auth.*`.\n *\n * Covers the end-user-facing staged auth flow: password login\n * with optional TOTP/OTP step-up. The shape mirrors the responses\n * the Takeal API returns from `/auth/login`, `/auth/login/totp`,\n * `/auth/login/otp/verify`, `/auth/login/2fa/switch`, and `/auth/me`.\n *\n * The login methods stash the JWT in the configured `TokenStore` on\n * success so subsequent `client.*` calls authenticate automatically.\n * Step-up responses (TotpRequired / OtpSent) carry their own short-\n * lived challenge token which the caller passes to the next method\n * — those tokens DO NOT go through the TokenStore (they're not the\n * end-state JWT).\n */\n\nexport interface User {\n id: string;\n email: string;\n role: string;\n is_active: boolean;\n /** True for users auto-provisioned via the Telegram Mini App who still hold\n * a synthetic placeholder email. The client should prompt for a real email\n * and attach it via `auth.linkEmail` — this flips the flag to false. */\n email_pending?: boolean;\n created_at: string;\n updated_at: string;\n}\n\nexport interface LoginInput {\n email: string;\n password: string;\n}\n\n/** Discriminated union — `stage` is the discriminator. */\nexport type LoginResponse = JwtIssued | TotpRequired | TotpSetupRequired;\n\nexport interface JwtIssued {\n stage: \"jwt\";\n access_token: string;\n expires_at: string;\n user: User;\n /** True iff the user's role grants `system.view_hub`.\n * Cusfront ignores this; the operator console uses it to gate `/dashboard`. */\n hub_access?: boolean;\n}\n\nexport interface TotpRequired {\n stage: \"totp_required\";\n challenge_token: string;\n expires_at: string;\n /** Alternate second factors. Pick one with `/2fa/switch`. */\n available_methods: AvailableMethod[];\n}\n\nexport interface TotpSetupRequired {\n stage: \"totp_setup_required\";\n challenge_token: string;\n expires_at: string;\n /** OTPAuth URI rendered as a QR by the consumer. */\n otpauth_url: string;\n /** Same secret base32-encoded — for manual entry alongside QR. */\n secret_base32: string;\n}\n\nexport interface AvailableMethod {\n kind: \"totp\" | \"telegram_otp\" | \"email_otp\";\n /** Short user-facing hint, e.g. `\"Telegram (@user)\"` or `\"e****@example.com\"`. */\n hint?: string;\n}\n\nexport interface TotpVerifyInput {\n challenge_token: string;\n /** 6-digit TOTP code OR `XXXX-XXXX` backup code. */\n code: string;\n}\n\nexport interface OtpVerifyInput {\n challenge_token: string;\n /** 6-digit one-time code delivered via the selected channel. */\n code: string;\n}\n\nexport interface SwitchMethodInput {\n challenge_token: string;\n method: AvailableMethod[\"kind\"];\n}\n\nexport interface OtpSentResponse {\n stage: \"otp_sent\";\n challenge_token: string;\n expires_at: string;\n method: AvailableMethod[\"kind\"];\n hint?: string;\n available_methods: AvailableMethod[];\n}\n\nexport class AuthResource {\n constructor(\n private readonly http: HttpClient,\n private readonly tokens: TokenStore,\n ) {}\n\n /** Password-auth entry. May resolve to a JWT, or to a step-up\n * challenge that needs `verifyTotp` / `verifyOtp` next. */\n async login(input: LoginInput): Promise<LoginResponse> {\n const resp = await this.http.post<LoginResponse>(\"/auth/login\", {\n body: input,\n skipAuth: true,\n });\n if (resp.stage === \"jwt\") {\n await Promise.resolve(this.tokens.set(resp.access_token));\n }\n return resp;\n }\n\n /** Verify a TOTP code (or XXXX-XXXX backup code) against a\n * `totp_required` challenge. On success the JWT is stored. */\n async verifyTotp(input: TotpVerifyInput): Promise<JwtIssued> {\n const resp = await this.http.post<JwtIssued>(\"/auth/login/totp\", {\n body: input,\n skipAuth: true,\n });\n await Promise.resolve(this.tokens.set(resp.access_token));\n return resp;\n }\n\n /** Verify a 6-digit OTP code (Telegram / email) against an\n * `otp_sent` challenge. */\n async verifyOtp(input: OtpVerifyInput): Promise<JwtIssued> {\n const resp = await this.http.post<JwtIssued>(\"/auth/login/otp/verify\", {\n body: input,\n skipAuth: true,\n });\n await Promise.resolve(this.tokens.set(resp.access_token));\n return resp;\n }\n\n /** Swap the 2FA method mid-flow. Resolves to `otp_sent` for\n * telegram/email; for TOTP it short-circuits back to the challenge. */\n async switchMethod(input: SwitchMethodInput): Promise<OtpSentResponse> {\n return this.http.post<OtpSentResponse>(\"/auth/login/2fa/switch\", {\n body: input,\n skipAuth: true,\n });\n }\n\n /** Exchange a signed Telegram Mini App `initData` payload for a session\n * JWT. The server validates the initData HMAC against the deployment's bot\n * token, then find-or-creates the end-user. First-time Telegram users are\n * auto-provisioned with `user.email_pending === true` — prompt them for a\n * real email and call {@link linkEmail}. Stores the JWT on success.\n *\n * Most consumers use the higher-level `fromInitData` / `fromTelegramWebApp`\n * helpers in `@takeal/cusfront-sdk/telegram`; reach for this directly when\n * you already hold a configured client. */\n async exchangeTelegram(input: { initData: string }): Promise<JwtIssued> {\n const resp = await this.http.post<JwtIssued>(\"/auth/telegram/exchange\", {\n body: { init_data: input.initData },\n skipAuth: true,\n });\n await Promise.resolve(this.tokens.set(resp.access_token));\n return resp;\n }\n\n /** Attach a real email to the current user (and clear `email_pending`).\n * Used after a Telegram exchange to satisfy the email requirement; also\n * works for a password user changing their address. 409 if taken. */\n async linkEmail(input: { email: string }): Promise<User> {\n return this.http.post<User>(\"/me/email\", { body: { email: input.email } });\n }\n\n /** Current user — fails 401 when the stored JWT is expired or\n * missing. Useful as a \"do I have a session\" probe on app boot. */\n async me(): Promise<User> {\n return this.http.get<User>(\"/auth/me\");\n }\n\n /** Local sign-out. The SDK doesn't currently call a server-side\n * endpoint (the Takeal API issues stateless JWTs); clearing the\n * store is enough. Server-side session invalidation may land\n * with a future session table. */\n async signOut(): Promise<void> {\n await Promise.resolve(this.tokens.clear());\n }\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/resources/balance.ts
|
|
4
|
+
var BalanceResource = class {
|
|
5
|
+
constructor(http) {
|
|
6
|
+
this.http = http;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Fetch the user's wallet balance for `currency` (ISO 4217, e.g. `"USD"`).
|
|
10
|
+
* Required — the ledger holds a separate balance per currency, so there is
|
|
11
|
+
* no "total" without one.
|
|
12
|
+
*/
|
|
13
|
+
async get(currency) {
|
|
14
|
+
const q = new URLSearchParams({ currency }).toString();
|
|
15
|
+
return this.http.get(`/me/balance?${q}`);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
exports.BalanceResource = BalanceResource;
|
|
20
|
+
//# sourceMappingURL=balance.cjs.map
|
|
21
|
+
//# sourceMappingURL=balance.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/resources/balance.ts"],"names":[],"mappings":";;;AAiBO,IAAM,kBAAN,MAAsB;AAAA,EAC3B,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOhD,MAAM,IAAI,QAAA,EAAoC;AAC5C,IAAA,MAAM,IAAI,IAAI,eAAA,CAAgB,EAAE,QAAA,EAAU,EAAE,QAAA,EAAS;AACrD,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAAa,CAAA,YAAA,EAAe,CAAC,CAAA,CAAE,CAAA;AAAA,EAClD;AACF","file":"balance.cjs","sourcesContent":["import type { HttpClient } from \"../http.js\";\n\n/**\n * Balance resource — `client.balance.*`.\n *\n * The user's wallet balance, tracked in the ledger. Mirrors\n * the Takeal API's `GET /me/balance?currency=<code>` route. Balance is per-currency:\n * the endpoint requires a `currency` query param and returns the amount held\n * in that currency.\n */\n\n/** Wallet balance for one currency. `amount` is a decimal string. */\nexport interface Balance {\n amount: string;\n currency: string;\n}\n\nexport class BalanceResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Fetch the user's wallet balance for `currency` (ISO 4217, e.g. `\"USD\"`).\n * Required — the ledger holds a separate balance per currency, so there is\n * no \"total\" without one.\n */\n async get(currency: string): Promise<Balance> {\n const q = new URLSearchParams({ currency }).toString();\n return this.http.get<Balance>(`/me/balance?${q}`);\n }\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { H as HttpClient } from '../http-BkZZZI8K.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Balance resource — `client.balance.*`.
|
|
5
|
+
*
|
|
6
|
+
* The user's wallet balance, tracked in the ledger. Mirrors
|
|
7
|
+
* the Takeal API's `GET /me/balance?currency=<code>` route. Balance is per-currency:
|
|
8
|
+
* the endpoint requires a `currency` query param and returns the amount held
|
|
9
|
+
* in that currency.
|
|
10
|
+
*/
|
|
11
|
+
/** Wallet balance for one currency. `amount` is a decimal string. */
|
|
12
|
+
interface Balance {
|
|
13
|
+
amount: string;
|
|
14
|
+
currency: string;
|
|
15
|
+
}
|
|
16
|
+
declare class BalanceResource {
|
|
17
|
+
private readonly http;
|
|
18
|
+
constructor(http: HttpClient);
|
|
19
|
+
/**
|
|
20
|
+
* Fetch the user's wallet balance for `currency` (ISO 4217, e.g. `"USD"`).
|
|
21
|
+
* Required — the ledger holds a separate balance per currency, so there is
|
|
22
|
+
* no "total" without one.
|
|
23
|
+
*/
|
|
24
|
+
get(currency: string): Promise<Balance>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { type Balance, BalanceResource };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { H as HttpClient } from '../http-BkZZZI8K.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Balance resource — `client.balance.*`.
|
|
5
|
+
*
|
|
6
|
+
* The user's wallet balance, tracked in the ledger. Mirrors
|
|
7
|
+
* the Takeal API's `GET /me/balance?currency=<code>` route. Balance is per-currency:
|
|
8
|
+
* the endpoint requires a `currency` query param and returns the amount held
|
|
9
|
+
* in that currency.
|
|
10
|
+
*/
|
|
11
|
+
/** Wallet balance for one currency. `amount` is a decimal string. */
|
|
12
|
+
interface Balance {
|
|
13
|
+
amount: string;
|
|
14
|
+
currency: string;
|
|
15
|
+
}
|
|
16
|
+
declare class BalanceResource {
|
|
17
|
+
private readonly http;
|
|
18
|
+
constructor(http: HttpClient);
|
|
19
|
+
/**
|
|
20
|
+
* Fetch the user's wallet balance for `currency` (ISO 4217, e.g. `"USD"`).
|
|
21
|
+
* Required — the ledger holds a separate balance per currency, so there is
|
|
22
|
+
* no "total" without one.
|
|
23
|
+
*/
|
|
24
|
+
get(currency: string): Promise<Balance>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { type Balance, BalanceResource };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/resources/balance.ts
|
|
2
|
+
var BalanceResource = class {
|
|
3
|
+
constructor(http) {
|
|
4
|
+
this.http = http;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Fetch the user's wallet balance for `currency` (ISO 4217, e.g. `"USD"`).
|
|
8
|
+
* Required — the ledger holds a separate balance per currency, so there is
|
|
9
|
+
* no "total" without one.
|
|
10
|
+
*/
|
|
11
|
+
async get(currency) {
|
|
12
|
+
const q = new URLSearchParams({ currency }).toString();
|
|
13
|
+
return this.http.get(`/me/balance?${q}`);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export { BalanceResource };
|
|
18
|
+
//# sourceMappingURL=balance.js.map
|
|
19
|
+
//# sourceMappingURL=balance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/resources/balance.ts"],"names":[],"mappings":";AAiBO,IAAM,kBAAN,MAAsB;AAAA,EAC3B,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOhD,MAAM,IAAI,QAAA,EAAoC;AAC5C,IAAA,MAAM,IAAI,IAAI,eAAA,CAAgB,EAAE,QAAA,EAAU,EAAE,QAAA,EAAS;AACrD,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAAa,CAAA,YAAA,EAAe,CAAC,CAAA,CAAE,CAAA;AAAA,EAClD;AACF","file":"balance.js","sourcesContent":["import type { HttpClient } from \"../http.js\";\n\n/**\n * Balance resource — `client.balance.*`.\n *\n * The user's wallet balance, tracked in the ledger. Mirrors\n * the Takeal API's `GET /me/balance?currency=<code>` route. Balance is per-currency:\n * the endpoint requires a `currency` query param and returns the amount held\n * in that currency.\n */\n\n/** Wallet balance for one currency. `amount` is a decimal string. */\nexport interface Balance {\n amount: string;\n currency: string;\n}\n\nexport class BalanceResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Fetch the user's wallet balance for `currency` (ISO 4217, e.g. `\"USD\"`).\n * Required — the ledger holds a separate balance per currency, so there is\n * no \"total\" without one.\n */\n async get(currency: string): Promise<Balance> {\n const q = new URLSearchParams({ currency }).toString();\n return this.http.get<Balance>(`/me/balance?${q}`);\n }\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/resources/blog.ts
|
|
4
|
+
var BlogResource = class {
|
|
5
|
+
constructor(http) {
|
|
6
|
+
this.http = http;
|
|
7
|
+
}
|
|
8
|
+
/** Published posts, newest first. No authentication required. */
|
|
9
|
+
async list(input = {}) {
|
|
10
|
+
const q = new URLSearchParams();
|
|
11
|
+
if (input.page) q.set("page", String(input.page));
|
|
12
|
+
if (input.perPage) q.set("per_page", String(input.perPage));
|
|
13
|
+
if (input.tag) q.set("tag", input.tag);
|
|
14
|
+
if (input.locale) q.set("locale", input.locale);
|
|
15
|
+
const qs = q.toString();
|
|
16
|
+
return this.http.get(`/blog/posts${qs ? `?${qs}` : ""}`, {
|
|
17
|
+
skipAuth: true
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/** One post by slug. No authentication required. */
|
|
21
|
+
async get(slug) {
|
|
22
|
+
return this.http.get(`/blog/posts/${encodeURIComponent(slug)}`, {
|
|
23
|
+
skipAuth: true
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Absolute URL for an image path returned inside a post (`cover_url`, or an
|
|
28
|
+
* image block's `url`). Handy when the app renders on a different origin
|
|
29
|
+
* than the API.
|
|
30
|
+
*/
|
|
31
|
+
imageUrl(baseUrl, path) {
|
|
32
|
+
if (/^https?:\/\//i.test(path)) return path;
|
|
33
|
+
return `${baseUrl.replace(/\/$/, "")}${path.startsWith("/") ? "" : "/"}${path}`;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
exports.BlogResource = BlogResource;
|
|
38
|
+
//# sourceMappingURL=blog.cjs.map
|
|
39
|
+
//# sourceMappingURL=blog.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/resources/blog.ts"],"names":[],"mappings":";;;AAmFO,IAAM,eAAN,MAAmB;AAAA,EACxB,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA,EAGhD,MAAM,IAAA,CAAK,KAAA,GAAuB,EAAC,EAAsB;AACvD,IAAA,MAAM,CAAA,GAAI,IAAI,eAAA,EAAgB;AAC9B,IAAA,IAAI,KAAA,CAAM,MAAM,CAAA,CAAE,GAAA,CAAI,QAAQ,MAAA,CAAO,KAAA,CAAM,IAAI,CAAC,CAAA;AAChD,IAAA,IAAI,KAAA,CAAM,SAAS,CAAA,CAAE,GAAA,CAAI,YAAY,MAAA,CAAO,KAAA,CAAM,OAAO,CAAC,CAAA;AAC1D,IAAA,IAAI,MAAM,GAAA,EAAK,CAAA,CAAE,GAAA,CAAI,KAAA,EAAO,MAAM,GAAG,CAAA;AACrC,IAAA,IAAI,MAAM,MAAA,EAAQ,CAAA,CAAE,GAAA,CAAI,QAAA,EAAU,MAAM,MAAM,CAAA;AAC9C,IAAA,MAAM,EAAA,GAAK,EAAE,QAAA,EAAS;AACtB,IAAA,OAAO,IAAA,CAAK,KAAK,GAAA,CAAc,CAAA,WAAA,EAAc,KAAK,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,GAAK,EAAE,CAAA,CAAA,EAAI;AAAA,MACjE,QAAA,EAAU;AAAA,KACX,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,IAAI,IAAA,EAAiC;AACzC,IAAA,OAAO,KAAK,IAAA,CAAK,GAAA,CAAc,eAAe,kBAAA,CAAmB,IAAI,CAAC,CAAA,CAAA,EAAI;AAAA,MACxE,QAAA,EAAU;AAAA,KACX,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAA,CAAS,SAAiB,IAAA,EAAsB;AAC9C,IAAA,IAAI,eAAA,CAAgB,IAAA,CAAK,IAAI,CAAA,EAAG,OAAO,IAAA;AACvC,IAAA,OAAO,CAAA,EAAG,OAAA,CAAQ,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,EAAG,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,GAAI,EAAA,GAAK,GAAG,GAAG,IAAI,CAAA,CAAA;AAAA,EAC/E;AACF","file":"blog.cjs","sourcesContent":["import type { HttpClient } from \"../http.js\";\n\n/**\n * Blog resource — `client.blog.*`.\n *\n * Posts are public: these calls work before the user logs in, which is what\n * a marketing page or a Mini App landing screen needs.\n *\n * A post arrives as a **structured document**, not as HTML. `body_blocks` is\n * a flat list of typed nodes; map each `type` onto your own component and the\n * post inherits your site's styling. `body_markdown` is there too if you'd\n * rather run your own renderer.\n *\n * ```ts\n * const { posts } = await client.blog.list({ perPage: 5 });\n * const post = await client.blog.get(posts[0].slug);\n *\n * post.body_blocks.map((b) => {\n * switch (b.type) {\n * case \"heading\": return <Heading level={b.level}>{b.text}</Heading>;\n * case \"paragraph\": return <P>{b.text}</P>;\n * case \"image\": return <Figure src={b.url} alt={b.alt} caption={b.caption} />;\n * case \"list\": return <List ordered={b.ordered} items={b.items} />;\n * case \"quote\": return <Quote>{b.text}</Quote>;\n * case \"code\": return <Code lang={b.lang}>{b.text}</Code>;\n * case \"divider\": return <Hr />;\n * }\n * });\n * ```\n *\n * Inline emphasis (`**bold**`, `[link](url)`) is left as Markdown inside\n * block text — every renderer already knows what to do with it.\n */\n\n/** One node of a post body. Discriminated on `type`. */\nexport type BlogBlock =\n | { type: \"heading\"; level: number; text: string }\n | { type: \"paragraph\"; text: string }\n | { type: \"image\"; url: string; alt?: string; caption?: string }\n | { type: \"list\"; ordered: boolean; items: string[] }\n | { type: \"quote\"; text: string }\n | { type: \"code\"; lang?: string; text: string }\n | { type: \"divider\" };\n\nexport interface BlogPost {\n /** Stable public key — link by this. */\n slug: string;\n title: string;\n /** Teaser for cards; falls back to the first paragraph. */\n excerpt: string;\n /** Image URL, relative to the API origin. Absent when no cover is set. */\n cover_url?: string;\n /** Raw Markdown, for consumers that bring their own renderer. */\n body_markdown: string;\n /** The recommended input for rendering — see the module docs. */\n body_blocks: BlogBlock[];\n tags: string[];\n /** Present only when the post declares one. */\n locale?: string;\n /** Rough read time in minutes (minimum 1). */\n reading_minutes: number;\n published_at: string | null;\n updated_at: string;\n}\n\nexport interface BlogListInput {\n /** 1-based. Default 1. */\n page?: number;\n /** 1..=50. Default 10. */\n perPage?: number;\n /** Only posts carrying this tag. */\n tag?: string;\n /** Only posts in this locale. */\n locale?: string;\n}\n\nexport interface BlogList {\n posts: BlogPost[];\n total: number;\n page: number;\n per_page: number;\n}\n\nexport class BlogResource {\n constructor(private readonly http: HttpClient) {}\n\n /** Published posts, newest first. No authentication required. */\n async list(input: BlogListInput = {}): Promise<BlogList> {\n const q = new URLSearchParams();\n if (input.page) q.set(\"page\", String(input.page));\n if (input.perPage) q.set(\"per_page\", String(input.perPage));\n if (input.tag) q.set(\"tag\", input.tag);\n if (input.locale) q.set(\"locale\", input.locale);\n const qs = q.toString();\n return this.http.get<BlogList>(`/blog/posts${qs ? `?${qs}` : \"\"}`, {\n skipAuth: true,\n });\n }\n\n /** One post by slug. No authentication required. */\n async get(slug: string): Promise<BlogPost> {\n return this.http.get<BlogPost>(`/blog/posts/${encodeURIComponent(slug)}`, {\n skipAuth: true,\n });\n }\n\n /**\n * Absolute URL for an image path returned inside a post (`cover_url`, or an\n * image block's `url`). Handy when the app renders on a different origin\n * than the API.\n */\n imageUrl(baseUrl: string, path: string): string {\n if (/^https?:\\/\\//i.test(path)) return path;\n return `${baseUrl.replace(/\\/$/, \"\")}${path.startsWith(\"/\") ? \"\" : \"/\"}${path}`;\n }\n}\n"]}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { H as HttpClient } from '../http-BkZZZI8K.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Blog resource — `client.blog.*`.
|
|
5
|
+
*
|
|
6
|
+
* Posts are public: these calls work before the user logs in, which is what
|
|
7
|
+
* a marketing page or a Mini App landing screen needs.
|
|
8
|
+
*
|
|
9
|
+
* A post arrives as a **structured document**, not as HTML. `body_blocks` is
|
|
10
|
+
* a flat list of typed nodes; map each `type` onto your own component and the
|
|
11
|
+
* post inherits your site's styling. `body_markdown` is there too if you'd
|
|
12
|
+
* rather run your own renderer.
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* const { posts } = await client.blog.list({ perPage: 5 });
|
|
16
|
+
* const post = await client.blog.get(posts[0].slug);
|
|
17
|
+
*
|
|
18
|
+
* post.body_blocks.map((b) => {
|
|
19
|
+
* switch (b.type) {
|
|
20
|
+
* case "heading": return <Heading level={b.level}>{b.text}</Heading>;
|
|
21
|
+
* case "paragraph": return <P>{b.text}</P>;
|
|
22
|
+
* case "image": return <Figure src={b.url} alt={b.alt} caption={b.caption} />;
|
|
23
|
+
* case "list": return <List ordered={b.ordered} items={b.items} />;
|
|
24
|
+
* case "quote": return <Quote>{b.text}</Quote>;
|
|
25
|
+
* case "code": return <Code lang={b.lang}>{b.text}</Code>;
|
|
26
|
+
* case "divider": return <Hr />;
|
|
27
|
+
* }
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Inline emphasis (`**bold**`, `[link](url)`) is left as Markdown inside
|
|
32
|
+
* block text — every renderer already knows what to do with it.
|
|
33
|
+
*/
|
|
34
|
+
/** One node of a post body. Discriminated on `type`. */
|
|
35
|
+
type BlogBlock = {
|
|
36
|
+
type: "heading";
|
|
37
|
+
level: number;
|
|
38
|
+
text: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: "paragraph";
|
|
41
|
+
text: string;
|
|
42
|
+
} | {
|
|
43
|
+
type: "image";
|
|
44
|
+
url: string;
|
|
45
|
+
alt?: string;
|
|
46
|
+
caption?: string;
|
|
47
|
+
} | {
|
|
48
|
+
type: "list";
|
|
49
|
+
ordered: boolean;
|
|
50
|
+
items: string[];
|
|
51
|
+
} | {
|
|
52
|
+
type: "quote";
|
|
53
|
+
text: string;
|
|
54
|
+
} | {
|
|
55
|
+
type: "code";
|
|
56
|
+
lang?: string;
|
|
57
|
+
text: string;
|
|
58
|
+
} | {
|
|
59
|
+
type: "divider";
|
|
60
|
+
};
|
|
61
|
+
interface BlogPost {
|
|
62
|
+
/** Stable public key — link by this. */
|
|
63
|
+
slug: string;
|
|
64
|
+
title: string;
|
|
65
|
+
/** Teaser for cards; falls back to the first paragraph. */
|
|
66
|
+
excerpt: string;
|
|
67
|
+
/** Image URL, relative to the API origin. Absent when no cover is set. */
|
|
68
|
+
cover_url?: string;
|
|
69
|
+
/** Raw Markdown, for consumers that bring their own renderer. */
|
|
70
|
+
body_markdown: string;
|
|
71
|
+
/** The recommended input for rendering — see the module docs. */
|
|
72
|
+
body_blocks: BlogBlock[];
|
|
73
|
+
tags: string[];
|
|
74
|
+
/** Present only when the post declares one. */
|
|
75
|
+
locale?: string;
|
|
76
|
+
/** Rough read time in minutes (minimum 1). */
|
|
77
|
+
reading_minutes: number;
|
|
78
|
+
published_at: string | null;
|
|
79
|
+
updated_at: string;
|
|
80
|
+
}
|
|
81
|
+
interface BlogListInput {
|
|
82
|
+
/** 1-based. Default 1. */
|
|
83
|
+
page?: number;
|
|
84
|
+
/** 1..=50. Default 10. */
|
|
85
|
+
perPage?: number;
|
|
86
|
+
/** Only posts carrying this tag. */
|
|
87
|
+
tag?: string;
|
|
88
|
+
/** Only posts in this locale. */
|
|
89
|
+
locale?: string;
|
|
90
|
+
}
|
|
91
|
+
interface BlogList {
|
|
92
|
+
posts: BlogPost[];
|
|
93
|
+
total: number;
|
|
94
|
+
page: number;
|
|
95
|
+
per_page: number;
|
|
96
|
+
}
|
|
97
|
+
declare class BlogResource {
|
|
98
|
+
private readonly http;
|
|
99
|
+
constructor(http: HttpClient);
|
|
100
|
+
/** Published posts, newest first. No authentication required. */
|
|
101
|
+
list(input?: BlogListInput): Promise<BlogList>;
|
|
102
|
+
/** One post by slug. No authentication required. */
|
|
103
|
+
get(slug: string): Promise<BlogPost>;
|
|
104
|
+
/**
|
|
105
|
+
* Absolute URL for an image path returned inside a post (`cover_url`, or an
|
|
106
|
+
* image block's `url`). Handy when the app renders on a different origin
|
|
107
|
+
* than the API.
|
|
108
|
+
*/
|
|
109
|
+
imageUrl(baseUrl: string, path: string): string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export { type BlogBlock, type BlogList, type BlogListInput, type BlogPost, BlogResource };
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { H as HttpClient } from '../http-BkZZZI8K.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Blog resource — `client.blog.*`.
|
|
5
|
+
*
|
|
6
|
+
* Posts are public: these calls work before the user logs in, which is what
|
|
7
|
+
* a marketing page or a Mini App landing screen needs.
|
|
8
|
+
*
|
|
9
|
+
* A post arrives as a **structured document**, not as HTML. `body_blocks` is
|
|
10
|
+
* a flat list of typed nodes; map each `type` onto your own component and the
|
|
11
|
+
* post inherits your site's styling. `body_markdown` is there too if you'd
|
|
12
|
+
* rather run your own renderer.
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* const { posts } = await client.blog.list({ perPage: 5 });
|
|
16
|
+
* const post = await client.blog.get(posts[0].slug);
|
|
17
|
+
*
|
|
18
|
+
* post.body_blocks.map((b) => {
|
|
19
|
+
* switch (b.type) {
|
|
20
|
+
* case "heading": return <Heading level={b.level}>{b.text}</Heading>;
|
|
21
|
+
* case "paragraph": return <P>{b.text}</P>;
|
|
22
|
+
* case "image": return <Figure src={b.url} alt={b.alt} caption={b.caption} />;
|
|
23
|
+
* case "list": return <List ordered={b.ordered} items={b.items} />;
|
|
24
|
+
* case "quote": return <Quote>{b.text}</Quote>;
|
|
25
|
+
* case "code": return <Code lang={b.lang}>{b.text}</Code>;
|
|
26
|
+
* case "divider": return <Hr />;
|
|
27
|
+
* }
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Inline emphasis (`**bold**`, `[link](url)`) is left as Markdown inside
|
|
32
|
+
* block text — every renderer already knows what to do with it.
|
|
33
|
+
*/
|
|
34
|
+
/** One node of a post body. Discriminated on `type`. */
|
|
35
|
+
type BlogBlock = {
|
|
36
|
+
type: "heading";
|
|
37
|
+
level: number;
|
|
38
|
+
text: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: "paragraph";
|
|
41
|
+
text: string;
|
|
42
|
+
} | {
|
|
43
|
+
type: "image";
|
|
44
|
+
url: string;
|
|
45
|
+
alt?: string;
|
|
46
|
+
caption?: string;
|
|
47
|
+
} | {
|
|
48
|
+
type: "list";
|
|
49
|
+
ordered: boolean;
|
|
50
|
+
items: string[];
|
|
51
|
+
} | {
|
|
52
|
+
type: "quote";
|
|
53
|
+
text: string;
|
|
54
|
+
} | {
|
|
55
|
+
type: "code";
|
|
56
|
+
lang?: string;
|
|
57
|
+
text: string;
|
|
58
|
+
} | {
|
|
59
|
+
type: "divider";
|
|
60
|
+
};
|
|
61
|
+
interface BlogPost {
|
|
62
|
+
/** Stable public key — link by this. */
|
|
63
|
+
slug: string;
|
|
64
|
+
title: string;
|
|
65
|
+
/** Teaser for cards; falls back to the first paragraph. */
|
|
66
|
+
excerpt: string;
|
|
67
|
+
/** Image URL, relative to the API origin. Absent when no cover is set. */
|
|
68
|
+
cover_url?: string;
|
|
69
|
+
/** Raw Markdown, for consumers that bring their own renderer. */
|
|
70
|
+
body_markdown: string;
|
|
71
|
+
/** The recommended input for rendering — see the module docs. */
|
|
72
|
+
body_blocks: BlogBlock[];
|
|
73
|
+
tags: string[];
|
|
74
|
+
/** Present only when the post declares one. */
|
|
75
|
+
locale?: string;
|
|
76
|
+
/** Rough read time in minutes (minimum 1). */
|
|
77
|
+
reading_minutes: number;
|
|
78
|
+
published_at: string | null;
|
|
79
|
+
updated_at: string;
|
|
80
|
+
}
|
|
81
|
+
interface BlogListInput {
|
|
82
|
+
/** 1-based. Default 1. */
|
|
83
|
+
page?: number;
|
|
84
|
+
/** 1..=50. Default 10. */
|
|
85
|
+
perPage?: number;
|
|
86
|
+
/** Only posts carrying this tag. */
|
|
87
|
+
tag?: string;
|
|
88
|
+
/** Only posts in this locale. */
|
|
89
|
+
locale?: string;
|
|
90
|
+
}
|
|
91
|
+
interface BlogList {
|
|
92
|
+
posts: BlogPost[];
|
|
93
|
+
total: number;
|
|
94
|
+
page: number;
|
|
95
|
+
per_page: number;
|
|
96
|
+
}
|
|
97
|
+
declare class BlogResource {
|
|
98
|
+
private readonly http;
|
|
99
|
+
constructor(http: HttpClient);
|
|
100
|
+
/** Published posts, newest first. No authentication required. */
|
|
101
|
+
list(input?: BlogListInput): Promise<BlogList>;
|
|
102
|
+
/** One post by slug. No authentication required. */
|
|
103
|
+
get(slug: string): Promise<BlogPost>;
|
|
104
|
+
/**
|
|
105
|
+
* Absolute URL for an image path returned inside a post (`cover_url`, or an
|
|
106
|
+
* image block's `url`). Handy when the app renders on a different origin
|
|
107
|
+
* than the API.
|
|
108
|
+
*/
|
|
109
|
+
imageUrl(baseUrl: string, path: string): string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export { type BlogBlock, type BlogList, type BlogListInput, type BlogPost, BlogResource };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/resources/blog.ts
|
|
2
|
+
var BlogResource = class {
|
|
3
|
+
constructor(http) {
|
|
4
|
+
this.http = http;
|
|
5
|
+
}
|
|
6
|
+
/** Published posts, newest first. No authentication required. */
|
|
7
|
+
async list(input = {}) {
|
|
8
|
+
const q = new URLSearchParams();
|
|
9
|
+
if (input.page) q.set("page", String(input.page));
|
|
10
|
+
if (input.perPage) q.set("per_page", String(input.perPage));
|
|
11
|
+
if (input.tag) q.set("tag", input.tag);
|
|
12
|
+
if (input.locale) q.set("locale", input.locale);
|
|
13
|
+
const qs = q.toString();
|
|
14
|
+
return this.http.get(`/blog/posts${qs ? `?${qs}` : ""}`, {
|
|
15
|
+
skipAuth: true
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/** One post by slug. No authentication required. */
|
|
19
|
+
async get(slug) {
|
|
20
|
+
return this.http.get(`/blog/posts/${encodeURIComponent(slug)}`, {
|
|
21
|
+
skipAuth: true
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Absolute URL for an image path returned inside a post (`cover_url`, or an
|
|
26
|
+
* image block's `url`). Handy when the app renders on a different origin
|
|
27
|
+
* than the API.
|
|
28
|
+
*/
|
|
29
|
+
imageUrl(baseUrl, path) {
|
|
30
|
+
if (/^https?:\/\//i.test(path)) return path;
|
|
31
|
+
return `${baseUrl.replace(/\/$/, "")}${path.startsWith("/") ? "" : "/"}${path}`;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export { BlogResource };
|
|
36
|
+
//# sourceMappingURL=blog.js.map
|
|
37
|
+
//# sourceMappingURL=blog.js.map
|