@tma.sh/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/dist/bot/index.js +185 -0
- package/dist/chunk-CDN4MZRW.js +120 -0
- package/dist/client/index.js +8 -0
- package/dist/react/index.js +66 -0
- package/dist/server/index.js +144 -0
- package/dist/svelte/index.js +61 -0
- package/package.json +65 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// src/bot/context.ts
|
|
2
|
+
var TELEGRAM_API_BASE = "https://api.telegram.org/bot";
|
|
3
|
+
var createTelegramCaller = (botToken) => {
|
|
4
|
+
const callTelegram = async (method, body) => {
|
|
5
|
+
let response;
|
|
6
|
+
try {
|
|
7
|
+
response = await fetch(`${TELEGRAM_API_BASE}${botToken}/${method}`, {
|
|
8
|
+
method: "POST",
|
|
9
|
+
headers: { "Content-Type": "application/json" },
|
|
10
|
+
body: JSON.stringify(body)
|
|
11
|
+
});
|
|
12
|
+
} catch {
|
|
13
|
+
throw new Error(`Telegram API request failed for method "${method}"`);
|
|
14
|
+
}
|
|
15
|
+
const data = await response.json();
|
|
16
|
+
if (!data.ok) {
|
|
17
|
+
throw new Error(`Telegram API error: ${data.description ?? "unknown"}`);
|
|
18
|
+
}
|
|
19
|
+
return data.result;
|
|
20
|
+
};
|
|
21
|
+
return callTelegram;
|
|
22
|
+
};
|
|
23
|
+
var createKV = (binding) => ({
|
|
24
|
+
get: async (key) => {
|
|
25
|
+
const value = await binding.get(key);
|
|
26
|
+
if (value === null) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(value);
|
|
31
|
+
} catch {
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
set: async (key, value, ttl) => {
|
|
36
|
+
const serialized = typeof value === "string" ? value : JSON.stringify(value);
|
|
37
|
+
await binding.put(
|
|
38
|
+
key,
|
|
39
|
+
serialized,
|
|
40
|
+
ttl ? { expirationTtl: ttl } : void 0
|
|
41
|
+
);
|
|
42
|
+
},
|
|
43
|
+
delete: async (key) => {
|
|
44
|
+
await binding.delete(key);
|
|
45
|
+
},
|
|
46
|
+
list: async (prefix) => {
|
|
47
|
+
const result = await binding.list(prefix ? { prefix } : void 0);
|
|
48
|
+
return result.keys.map((k) => k.name);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
var resolveChatId = (update) => update.message?.chat.id ?? update.callback_query?.message?.chat.id ?? update.inline_query?.from.id;
|
|
52
|
+
var resolveFrom = (update) => update.message?.from ?? update.callback_query?.from ?? update.inline_query?.from ?? update.pre_checkout_query?.from;
|
|
53
|
+
var createBotContext = (update, env) => {
|
|
54
|
+
const callTelegram = createTelegramCaller(env.BOT_TOKEN);
|
|
55
|
+
const chatId = resolveChatId(update);
|
|
56
|
+
const from = resolveFrom(update);
|
|
57
|
+
const kv = createKV(env.KV);
|
|
58
|
+
const api = {
|
|
59
|
+
call: (method, body) => callTelegram(method, body ?? {})
|
|
60
|
+
};
|
|
61
|
+
const requireChatId = () => {
|
|
62
|
+
if (chatId === void 0) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
"reply() requires a chat context (message or callback_query update)"
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return chatId;
|
|
68
|
+
};
|
|
69
|
+
const reply = (text, options) => callTelegram("sendMessage", {
|
|
70
|
+
chat_id: requireChatId(),
|
|
71
|
+
text,
|
|
72
|
+
...options
|
|
73
|
+
});
|
|
74
|
+
const replyPhoto = (photo, options) => callTelegram("sendPhoto", {
|
|
75
|
+
chat_id: requireChatId(),
|
|
76
|
+
photo,
|
|
77
|
+
...options
|
|
78
|
+
});
|
|
79
|
+
const answerCallbackQuery = (text, options) => callTelegram("answerCallbackQuery", {
|
|
80
|
+
callback_query_id: update.callback_query?.id,
|
|
81
|
+
...text !== void 0 ? { text } : {},
|
|
82
|
+
...options
|
|
83
|
+
});
|
|
84
|
+
const answerInlineQuery = (results, options) => callTelegram("answerInlineQuery", {
|
|
85
|
+
inline_query_id: update.inline_query?.id,
|
|
86
|
+
results,
|
|
87
|
+
...options
|
|
88
|
+
});
|
|
89
|
+
const answerPreCheckoutQuery = (ok, errorMessage) => callTelegram("answerPreCheckoutQuery", {
|
|
90
|
+
pre_checkout_query_id: update.pre_checkout_query?.id,
|
|
91
|
+
ok,
|
|
92
|
+
...errorMessage !== void 0 ? { error_message: errorMessage } : {}
|
|
93
|
+
});
|
|
94
|
+
return {
|
|
95
|
+
update,
|
|
96
|
+
message: update.message,
|
|
97
|
+
callbackQuery: update.callback_query,
|
|
98
|
+
inlineQuery: update.inline_query,
|
|
99
|
+
preCheckoutQuery: update.pre_checkout_query,
|
|
100
|
+
chatId,
|
|
101
|
+
from,
|
|
102
|
+
env,
|
|
103
|
+
kv,
|
|
104
|
+
api,
|
|
105
|
+
reply,
|
|
106
|
+
replyPhoto,
|
|
107
|
+
answerCallbackQuery,
|
|
108
|
+
answerInlineQuery,
|
|
109
|
+
answerPreCheckoutQuery
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// src/bot/runtime.ts
|
|
114
|
+
var composeMiddleware = (middlewares, handler) => {
|
|
115
|
+
return async (ctx) => {
|
|
116
|
+
let index = -1;
|
|
117
|
+
const dispatch = async (i) => {
|
|
118
|
+
if (i <= index) {
|
|
119
|
+
throw new Error("next() called multiple times");
|
|
120
|
+
}
|
|
121
|
+
index = i;
|
|
122
|
+
const middleware = middlewares[i];
|
|
123
|
+
if (middleware) {
|
|
124
|
+
await middleware(ctx, () => dispatch(i + 1));
|
|
125
|
+
} else {
|
|
126
|
+
await handler(ctx);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
await dispatch(0);
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
var routeUpdate = async (config, ctx) => {
|
|
133
|
+
if (ctx.message && config.onMessage) {
|
|
134
|
+
await config.onMessage(ctx);
|
|
135
|
+
} else if (ctx.callbackQuery && config.onCallbackQuery) {
|
|
136
|
+
await config.onCallbackQuery(ctx);
|
|
137
|
+
} else if (ctx.inlineQuery && config.onInlineQuery) {
|
|
138
|
+
await config.onInlineQuery(ctx);
|
|
139
|
+
} else if (ctx.preCheckoutQuery && config.onPreCheckoutQuery) {
|
|
140
|
+
await config.onPreCheckoutQuery(ctx);
|
|
141
|
+
} else if (config.onUpdate) {
|
|
142
|
+
await config.onUpdate(ctx);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
var createBotWorker = (config) => {
|
|
146
|
+
const fetch2 = async (request, env, _ctx) => {
|
|
147
|
+
if (request.method !== "POST") {
|
|
148
|
+
return new Response("Method Not Allowed", { status: 405 });
|
|
149
|
+
}
|
|
150
|
+
let update;
|
|
151
|
+
try {
|
|
152
|
+
update = await request.json();
|
|
153
|
+
} catch {
|
|
154
|
+
return new Response("Invalid request body", { status: 400 });
|
|
155
|
+
}
|
|
156
|
+
if (!update || typeof update.update_id !== "number") {
|
|
157
|
+
return new Response("Invalid Telegram update", { status: 400 });
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
const botCtx = createBotContext(update, env);
|
|
161
|
+
const middlewares = config.middleware ?? [];
|
|
162
|
+
const handler = composeMiddleware(
|
|
163
|
+
middlewares,
|
|
164
|
+
(ctx) => routeUpdate(config, ctx)
|
|
165
|
+
);
|
|
166
|
+
await handler(botCtx);
|
|
167
|
+
return new Response("OK", { status: 200 });
|
|
168
|
+
} catch (error) {
|
|
169
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
170
|
+
console.error(`Bot worker error: ${message}`);
|
|
171
|
+
return new Response("Internal Server Error", { status: 500 });
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
return { fetch: fetch2 };
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// src/bot/define-bot.ts
|
|
178
|
+
var defineBot = (config) => {
|
|
179
|
+
const worker = createBotWorker(config);
|
|
180
|
+
return { ...config, ...worker };
|
|
181
|
+
};
|
|
182
|
+
export {
|
|
183
|
+
createKV,
|
|
184
|
+
defineBot
|
|
185
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// src/client/http.ts
|
|
2
|
+
var TMAError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
constructor(message, code = "SDK_ERROR") {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "TMAError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var DEFAULT_API_URL = "https://api.tma.sh";
|
|
11
|
+
var TRAILING_SLASHES = /\/+$/;
|
|
12
|
+
var resolveApiUrl = (apiUrl) => (apiUrl ?? DEFAULT_API_URL).replace(TRAILING_SLASHES, "");
|
|
13
|
+
var makeRequest = async (baseUrl, path, options = {}) => {
|
|
14
|
+
const { method = "GET", body, jwt } = options;
|
|
15
|
+
const url = `${baseUrl}${path}`;
|
|
16
|
+
const headers = {
|
|
17
|
+
"Content-Type": "application/json"
|
|
18
|
+
};
|
|
19
|
+
if (jwt) {
|
|
20
|
+
headers.Authorization = `Bearer ${jwt}`;
|
|
21
|
+
}
|
|
22
|
+
const response = await fetch(url, {
|
|
23
|
+
method,
|
|
24
|
+
headers,
|
|
25
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
26
|
+
});
|
|
27
|
+
const json = await response.json();
|
|
28
|
+
if (!json.success) {
|
|
29
|
+
throw new TMAError(json.error, `HTTP_${response.status}`);
|
|
30
|
+
}
|
|
31
|
+
return json.data;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/client/auth.ts
|
|
35
|
+
var storedJwt = null;
|
|
36
|
+
var setStoredJwt = (jwt) => {
|
|
37
|
+
storedJwt = jwt;
|
|
38
|
+
};
|
|
39
|
+
var getStoredJwt = () => storedJwt;
|
|
40
|
+
var createAuthClient = (config) => {
|
|
41
|
+
const baseUrl = resolveApiUrl(config?.apiUrl);
|
|
42
|
+
return {
|
|
43
|
+
validate: async (initData, projectId) => {
|
|
44
|
+
const result = await makeRequest(
|
|
45
|
+
baseUrl,
|
|
46
|
+
"/sdk/v1/auth/validate",
|
|
47
|
+
{
|
|
48
|
+
method: "POST",
|
|
49
|
+
body: { initData, projectId }
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
setStoredJwt(result.jwt);
|
|
53
|
+
return result;
|
|
54
|
+
},
|
|
55
|
+
getJwt: () => storedJwt
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// src/client/kv.ts
|
|
60
|
+
var requireJwt = () => {
|
|
61
|
+
const jwt = getStoredJwt();
|
|
62
|
+
if (!jwt) {
|
|
63
|
+
throw new TMAError(
|
|
64
|
+
"Authentication required. Call auth.validate() before using KV storage.",
|
|
65
|
+
"AUTH_REQUIRED"
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return jwt;
|
|
69
|
+
};
|
|
70
|
+
var encodeKey = (key) => encodeURIComponent(key);
|
|
71
|
+
var createKVClient = (config) => {
|
|
72
|
+
const baseUrl = resolveApiUrl(config?.apiUrl);
|
|
73
|
+
return {
|
|
74
|
+
set: async (key, value) => {
|
|
75
|
+
const jwt = requireJwt();
|
|
76
|
+
await makeRequest(baseUrl, `/sdk/v1/kv/${encodeKey(key)}`, {
|
|
77
|
+
method: "PUT",
|
|
78
|
+
body: value,
|
|
79
|
+
jwt
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
get: async (key) => {
|
|
83
|
+
const jwt = requireJwt();
|
|
84
|
+
try {
|
|
85
|
+
return await makeRequest(baseUrl, `/sdk/v1/kv/${encodeKey(key)}`, {
|
|
86
|
+
jwt
|
|
87
|
+
});
|
|
88
|
+
} catch (error) {
|
|
89
|
+
if (error instanceof TMAError && error.code === "HTTP_404") {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
remove: async (key) => {
|
|
96
|
+
const jwt = requireJwt();
|
|
97
|
+
await makeRequest(baseUrl, `/sdk/v1/kv/${encodeKey(key)}`, {
|
|
98
|
+
method: "DELETE",
|
|
99
|
+
jwt
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
list: (prefix) => {
|
|
103
|
+
const jwt = requireJwt();
|
|
104
|
+
const query = prefix ? `?prefix=${encodeURIComponent(prefix)}` : "";
|
|
105
|
+
return makeRequest(baseUrl, `/sdk/v1/kv${query}`, { jwt });
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// src/client/index.ts
|
|
111
|
+
var createTMA = (config) => ({
|
|
112
|
+
auth: createAuthClient(config),
|
|
113
|
+
kv: createKVClient(config)
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
export {
|
|
117
|
+
TMAError,
|
|
118
|
+
createAuthClient,
|
|
119
|
+
createTMA
|
|
120
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createAuthClient,
|
|
3
|
+
createTMA
|
|
4
|
+
} from "../chunk-CDN4MZRW.js";
|
|
5
|
+
|
|
6
|
+
// src/react/index.ts
|
|
7
|
+
import { useEffect, useState } from "react";
|
|
8
|
+
var getInitData = () => window.Telegram?.WebApp?.initData;
|
|
9
|
+
var LOADING_STATE = {
|
|
10
|
+
user: null,
|
|
11
|
+
jwt: null,
|
|
12
|
+
isLoading: true,
|
|
13
|
+
error: null
|
|
14
|
+
};
|
|
15
|
+
var NO_TELEGRAM_STATE = {
|
|
16
|
+
user: null,
|
|
17
|
+
jwt: null,
|
|
18
|
+
isLoading: false,
|
|
19
|
+
error: null
|
|
20
|
+
};
|
|
21
|
+
var makeErrorState = (err) => ({
|
|
22
|
+
user: null,
|
|
23
|
+
jwt: null,
|
|
24
|
+
isLoading: false,
|
|
25
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
26
|
+
});
|
|
27
|
+
var resolveAuthState = async (config) => {
|
|
28
|
+
const initData = getInitData();
|
|
29
|
+
if (!initData) {
|
|
30
|
+
return NO_TELEGRAM_STATE;
|
|
31
|
+
}
|
|
32
|
+
if (!config?.projectId) {
|
|
33
|
+
return makeErrorState("projectId is required in config");
|
|
34
|
+
}
|
|
35
|
+
const authClient = createAuthClient(config);
|
|
36
|
+
const result = await authClient.validate(initData, config.projectId);
|
|
37
|
+
return {
|
|
38
|
+
user: result.user,
|
|
39
|
+
jwt: result.jwt,
|
|
40
|
+
isLoading: false,
|
|
41
|
+
error: null
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
var useTelegramAuth = (config) => {
|
|
45
|
+
const [state, setState] = useState(LOADING_STATE);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
let cancelled = false;
|
|
48
|
+
resolveAuthState(config).then((resolved) => {
|
|
49
|
+
if (!cancelled) {
|
|
50
|
+
setState(resolved);
|
|
51
|
+
}
|
|
52
|
+
}).catch((err) => {
|
|
53
|
+
if (!cancelled) {
|
|
54
|
+
setState(makeErrorState(err));
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return () => {
|
|
58
|
+
cancelled = true;
|
|
59
|
+
};
|
|
60
|
+
}, [config]);
|
|
61
|
+
return state;
|
|
62
|
+
};
|
|
63
|
+
export {
|
|
64
|
+
createTMA,
|
|
65
|
+
useTelegramAuth
|
|
66
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// src/server/auth.ts
|
|
2
|
+
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
3
|
+
import { createMiddleware } from "hono/factory";
|
|
4
|
+
var DEFAULT_JWKS_URL = "https://api.tma.sh/.well-known/jwks.json";
|
|
5
|
+
var requireUser = (options) => {
|
|
6
|
+
const jwks = options?.jwks ?? createRemoteJWKSet(new URL(options?.jwksUrl ?? DEFAULT_JWKS_URL));
|
|
7
|
+
return createMiddleware(async (c, next) => {
|
|
8
|
+
const authHeader = c.req.header("Authorization");
|
|
9
|
+
if (!authHeader?.startsWith("Bearer ")) {
|
|
10
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
11
|
+
}
|
|
12
|
+
const token = authHeader.slice(7);
|
|
13
|
+
if (token.length === 0) {
|
|
14
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const { payload } = await jwtVerify(token, jwks);
|
|
18
|
+
const user = {
|
|
19
|
+
telegramId: payload.telegramId,
|
|
20
|
+
firstName: payload.firstName,
|
|
21
|
+
lastName: payload.lastName,
|
|
22
|
+
username: payload.username,
|
|
23
|
+
projectId: payload.projectId
|
|
24
|
+
};
|
|
25
|
+
c.set("user", user);
|
|
26
|
+
await next();
|
|
27
|
+
} catch {
|
|
28
|
+
return c.json({ error: "Invalid or expired token" }, 401);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// src/server/kv.ts
|
|
34
|
+
var createKV = (binding) => ({
|
|
35
|
+
get: async (key) => {
|
|
36
|
+
const value = await binding.get(key, "json");
|
|
37
|
+
return value;
|
|
38
|
+
},
|
|
39
|
+
set: async (key, value, ttl) => {
|
|
40
|
+
await binding.put(
|
|
41
|
+
key,
|
|
42
|
+
JSON.stringify(value),
|
|
43
|
+
ttl ? { expirationTtl: ttl } : void 0
|
|
44
|
+
);
|
|
45
|
+
},
|
|
46
|
+
delete: async (key) => {
|
|
47
|
+
await binding.delete(key);
|
|
48
|
+
},
|
|
49
|
+
list: async (prefix) => {
|
|
50
|
+
const result = await binding.list({ prefix });
|
|
51
|
+
return result.keys.map((k) => k.name);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// src/server/index.ts
|
|
56
|
+
var validateInitData = async (initData, botToken) => {
|
|
57
|
+
const params = new URLSearchParams(initData);
|
|
58
|
+
const hash = params.get("hash");
|
|
59
|
+
if (!hash) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
params.delete("hash");
|
|
63
|
+
const sortedKeys = [];
|
|
64
|
+
params.forEach((_value, key) => {
|
|
65
|
+
sortedKeys.push(key);
|
|
66
|
+
});
|
|
67
|
+
sortedKeys.sort();
|
|
68
|
+
const dataCheckString = sortedKeys.map((key) => `${key}=${params.get(key)}`).join("\n");
|
|
69
|
+
const authDateStr = params.get("auth_date");
|
|
70
|
+
if (!authDateStr) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const authDate = Number.parseInt(authDateStr, 10);
|
|
74
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
75
|
+
if (Number.isNaN(authDate) || now - authDate > MAX_AUTH_AGE_SECONDS) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const secretKey = await hmacSha256(
|
|
79
|
+
new TextEncoder().encode("WebAppData"),
|
|
80
|
+
botToken
|
|
81
|
+
);
|
|
82
|
+
const isValid = await verifyHmac(
|
|
83
|
+
new Uint8Array(secretKey),
|
|
84
|
+
dataCheckString,
|
|
85
|
+
hash
|
|
86
|
+
);
|
|
87
|
+
if (!isValid) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const userJson = params.get("user");
|
|
91
|
+
if (!userJson) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const raw = JSON.parse(userJson);
|
|
96
|
+
if (!(raw.id && raw.first_name)) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
telegramId: raw.id,
|
|
101
|
+
firstName: raw.first_name,
|
|
102
|
+
lastName: raw.last_name,
|
|
103
|
+
username: raw.username,
|
|
104
|
+
authDate
|
|
105
|
+
};
|
|
106
|
+
} catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
var MAX_AUTH_AGE_SECONDS = 300;
|
|
111
|
+
var hmacSha256 = async (key, data) => {
|
|
112
|
+
const cryptoKey = await crypto.subtle.importKey(
|
|
113
|
+
"raw",
|
|
114
|
+
key,
|
|
115
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
116
|
+
false,
|
|
117
|
+
["sign"]
|
|
118
|
+
);
|
|
119
|
+
return crypto.subtle.sign("HMAC", cryptoKey, new TextEncoder().encode(data));
|
|
120
|
+
};
|
|
121
|
+
var verifyHmac = async (key, data, expectedHex) => {
|
|
122
|
+
const cryptoKey = await crypto.subtle.importKey(
|
|
123
|
+
"raw",
|
|
124
|
+
key,
|
|
125
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
126
|
+
false,
|
|
127
|
+
["verify"]
|
|
128
|
+
);
|
|
129
|
+
const expectedBytes = Uint8Array.from(
|
|
130
|
+
expectedHex.match(/.{2}/g) ?? [],
|
|
131
|
+
(h) => Number.parseInt(h, 16)
|
|
132
|
+
);
|
|
133
|
+
return crypto.subtle.verify(
|
|
134
|
+
"HMAC",
|
|
135
|
+
cryptoKey,
|
|
136
|
+
expectedBytes,
|
|
137
|
+
new TextEncoder().encode(data)
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
export {
|
|
141
|
+
createKV,
|
|
142
|
+
requireUser,
|
|
143
|
+
validateInitData
|
|
144
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createAuthClient,
|
|
3
|
+
createTMA
|
|
4
|
+
} from "../chunk-CDN4MZRW.js";
|
|
5
|
+
|
|
6
|
+
// src/svelte/index.ts
|
|
7
|
+
import { writable } from "svelte/store";
|
|
8
|
+
var createTelegramAuth = (config) => {
|
|
9
|
+
const store = writable({
|
|
10
|
+
user: null,
|
|
11
|
+
jwt: null,
|
|
12
|
+
isLoading: true,
|
|
13
|
+
error: null
|
|
14
|
+
});
|
|
15
|
+
const authenticate = async () => {
|
|
16
|
+
const initData = typeof window !== "undefined" ? window.Telegram?.WebApp?.initData : void 0;
|
|
17
|
+
if (!initData) {
|
|
18
|
+
store.set({
|
|
19
|
+
user: null,
|
|
20
|
+
jwt: null,
|
|
21
|
+
isLoading: false,
|
|
22
|
+
error: null
|
|
23
|
+
});
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const projectId = config?.projectId;
|
|
27
|
+
if (!projectId) {
|
|
28
|
+
store.set({
|
|
29
|
+
user: null,
|
|
30
|
+
jwt: null,
|
|
31
|
+
isLoading: false,
|
|
32
|
+
error: new Error("projectId is required in config")
|
|
33
|
+
});
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const authClient = createAuthClient(config);
|
|
38
|
+
const result = await authClient.validate(initData, projectId);
|
|
39
|
+
store.set({
|
|
40
|
+
user: result.user,
|
|
41
|
+
jwt: result.jwt,
|
|
42
|
+
isLoading: false,
|
|
43
|
+
error: null
|
|
44
|
+
});
|
|
45
|
+
} catch (err) {
|
|
46
|
+
store.set({
|
|
47
|
+
user: null,
|
|
48
|
+
jwt: null,
|
|
49
|
+
isLoading: false,
|
|
50
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
authenticate().catch(() => {
|
|
55
|
+
});
|
|
56
|
+
return store;
|
|
57
|
+
};
|
|
58
|
+
export {
|
|
59
|
+
createTMA,
|
|
60
|
+
createTelegramAuth
|
|
61
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tma.sh/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "SDK for Telegram Mini Apps on TMA -- auth, KV storage, and framework bindings",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/client/index.js",
|
|
10
|
+
"types": "./dist/client/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./react": {
|
|
13
|
+
"import": "./dist/react/index.js",
|
|
14
|
+
"types": "./dist/react/index.d.ts"
|
|
15
|
+
},
|
|
16
|
+
"./svelte": {
|
|
17
|
+
"import": "./dist/svelte/index.js",
|
|
18
|
+
"types": "./dist/svelte/index.d.ts"
|
|
19
|
+
},
|
|
20
|
+
"./server": {
|
|
21
|
+
"import": "./dist/server/index.js",
|
|
22
|
+
"types": "./dist/server/index.d.ts"
|
|
23
|
+
},
|
|
24
|
+
"./bot": {
|
|
25
|
+
"import": "./dist/bot/index.js",
|
|
26
|
+
"types": "./dist/bot/index.d.ts"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsup && tsc -p tsconfig.build.json",
|
|
34
|
+
"check-types": "tsc --noEmit",
|
|
35
|
+
"prepublishOnly": "bun run build"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"jose": "^6.0.11"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"hono": ">=4.0.0",
|
|
42
|
+
"react": ">=18.0.0",
|
|
43
|
+
"svelte": ">=4.0.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependenciesMeta": {
|
|
46
|
+
"hono": {
|
|
47
|
+
"optional": true
|
|
48
|
+
},
|
|
49
|
+
"react": {
|
|
50
|
+
"optional": true
|
|
51
|
+
},
|
|
52
|
+
"svelte": {
|
|
53
|
+
"optional": true
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@cloudflare/workers-types": "^4.20250214.0",
|
|
58
|
+
"@types/react": "^19.2.14",
|
|
59
|
+
"hono": "^4.7.10",
|
|
60
|
+
"react": "^19.0.0",
|
|
61
|
+
"svelte": "^5.0.0",
|
|
62
|
+
"tsup": "^8.0.0",
|
|
63
|
+
"typescript": "catalog:"
|
|
64
|
+
}
|
|
65
|
+
}
|