@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.
Files changed (74) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/LICENSE +21 -0
  3. package/README.md +283 -0
  4. package/dist/customer-CoxPwe5o.d.cts +32 -0
  5. package/dist/customer-CoxPwe5o.d.ts +32 -0
  6. package/dist/http-BkZZZI8K.d.cts +72 -0
  7. package/dist/http-BkZZZI8K.d.ts +72 -0
  8. package/dist/index.cjs +486 -0
  9. package/dist/index.cjs.map +1 -0
  10. package/dist/index.d.cts +149 -0
  11. package/dist/index.d.ts +149 -0
  12. package/dist/index.js +478 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/react/index.cjs +82 -0
  15. package/dist/react/index.cjs.map +1 -0
  16. package/dist/react/index.d.cts +93 -0
  17. package/dist/react/index.d.ts +93 -0
  18. package/dist/react/index.js +75 -0
  19. package/dist/react/index.js.map +1 -0
  20. package/dist/resources/auth.cjs +88 -0
  21. package/dist/resources/auth.cjs.map +1 -0
  22. package/dist/resources/auth.d.cts +132 -0
  23. package/dist/resources/auth.d.ts +132 -0
  24. package/dist/resources/auth.js +86 -0
  25. package/dist/resources/auth.js.map +1 -0
  26. package/dist/resources/balance.cjs +21 -0
  27. package/dist/resources/balance.cjs.map +1 -0
  28. package/dist/resources/balance.d.cts +27 -0
  29. package/dist/resources/balance.d.ts +27 -0
  30. package/dist/resources/balance.js +19 -0
  31. package/dist/resources/balance.js.map +1 -0
  32. package/dist/resources/blog.cjs +39 -0
  33. package/dist/resources/blog.cjs.map +1 -0
  34. package/dist/resources/blog.d.cts +112 -0
  35. package/dist/resources/blog.d.ts +112 -0
  36. package/dist/resources/blog.js +37 -0
  37. package/dist/resources/blog.js.map +1 -0
  38. package/dist/resources/branding.cjs +16 -0
  39. package/dist/resources/branding.cjs.map +1 -0
  40. package/dist/resources/branding.d.cts +35 -0
  41. package/dist/resources/branding.d.ts +35 -0
  42. package/dist/resources/branding.js +14 -0
  43. package/dist/resources/branding.js.map +1 -0
  44. package/dist/resources/cards.cjs +91 -0
  45. package/dist/resources/cards.cjs.map +1 -0
  46. package/dist/resources/cards.d.cts +166 -0
  47. package/dist/resources/cards.d.ts +166 -0
  48. package/dist/resources/cards.js +89 -0
  49. package/dist/resources/cards.js.map +1 -0
  50. package/dist/resources/deposits.cjs +52 -0
  51. package/dist/resources/deposits.cjs.map +1 -0
  52. package/dist/resources/deposits.d.cts +168 -0
  53. package/dist/resources/deposits.d.ts +168 -0
  54. package/dist/resources/deposits.js +50 -0
  55. package/dist/resources/deposits.js.map +1 -0
  56. package/dist/resources/subscriptions.cjs +24 -0
  57. package/dist/resources/subscriptions.cjs.map +1 -0
  58. package/dist/resources/subscriptions.d.cts +36 -0
  59. package/dist/resources/subscriptions.d.ts +36 -0
  60. package/dist/resources/subscriptions.js +22 -0
  61. package/dist/resources/subscriptions.js.map +1 -0
  62. package/dist/telegram.cjs +557 -0
  63. package/dist/telegram.cjs.map +1 -0
  64. package/dist/telegram.d.cts +105 -0
  65. package/dist/telegram.d.ts +105 -0
  66. package/dist/telegram.js +550 -0
  67. package/dist/telegram.js.map +1 -0
  68. package/dist/webhooks.cjs +78 -0
  69. package/dist/webhooks.cjs.map +1 -0
  70. package/dist/webhooks.d.cts +70 -0
  71. package/dist/webhooks.d.ts +70 -0
  72. package/dist/webhooks.js +72 -0
  73. package/dist/webhooks.js.map +1 -0
  74. 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