UNPKG

@telegram-apps/bridge

Version:

TypeScript package to provide communication layer between Mini App and Telegram application.

551 lines (550 loc) 15.2 kB
import { is as y, looseObject as u, function as $, boolean as x, nullish as d, number as A, string as w, optional as C, unknown as Z, parse as v, pipe as j, any as O } from "valibot"; import { AbortablePromise as N } from "better-promises"; import { AbortablePromise as Xe, CancelledError as Ze, ManualPromise as Oe, TimeoutError as Ve, isCancelledError as et, isTimeoutError as tt } from "better-promises"; import { createLogger as V, createCbCollector as ee, getStorageValue as te, setStorageValue as z, deepSnakeToCamelObjKeys as re } from "@telegram-apps/toolkit"; import { createLogger as ot } from "@telegram-apps/toolkit"; import { themeParams as oe, jsonParse as D, MiniAppsMessageSchema as G, isLaunchParamsQuery as J, parseLaunchParamsQuery as E, serializeLaunchParamsQuery as ne } from "@telegram-apps/transformers"; import ae from "mitt"; import { signal as P } from "@telegram-apps/signals"; import { errorClass as b, errorClassWithData as se } from "error-kid"; function B(e) { return btoa( encodeURIComponent(e).replace(/%([0-9A-F]{2})/g, (t, r) => String.fromCharCode(parseInt(`0x${r}`))) ).replace(/\+/g, "-").replace(/\//g, "_"); } function ce(e) { return decodeURIComponent( atob(e).replace(/-/g, "+").replace(/_/g, "/").split("").map((t) => "%" + ("00" + t.charCodeAt(0).toString(16)).slice(-2)).join("") ); } function Q(e) { return y( u({ TelegramWebviewProxy: u({ postEvent: $() }) }), e ); } function K() { try { return window.self !== window.top; } catch { return !0; } } function ie(e, t) { const r = ae(), o = /* @__PURE__ */ new Map(), s = (n, a, c) => { c || (c = !1); const i = o.get(n) || /* @__PURE__ */ new Map(); o.set(n, i); const _ = i.get(a) || []; i.set(a, _); const l = _.findIndex((m) => m[1] === c); l >= 0 && (r.off(n, _[l][0]), _.splice(l, 1), !_.length && i.delete(a), i.size || (o.delete(n), !o.size && t())); }; return [ function(a, c, i) { !o.size && e(); function _() { s(a, c, i); } function l(...M) { i && _(), a === "*" ? c(M) : c(...M); } r.on(a, l); const m = o.get(a) || /* @__PURE__ */ new Map(); o.set(a, m); const S = m.get(c) || []; return m.set(c, S), S.push([l, i || !1]), _; }, s, // eslint-disable-next-line @typescript-eslint/unbound-method r.emit, function() { const a = r.all.size; r.all.clear(), o.clear(), a && t(); } ]; } function T(e, t) { window.dispatchEvent(new MessageEvent("message", { data: JSON.stringify({ eventType: e, eventData: t }), // We specify window.parent to imitate the case, the parent iframe sent us this event. source: window.parent })); } let h = !1; const q = (e) => { f().log("Event received:", e); }; function pe(e) { e !== h && (h = e, h ? F("*", q) : ue("*", q)); } const f = P(V("Bridge", { bgColor: "#9147ff", textColor: "white", shouldLog() { return h; } })), _e = { clipboard_text_received: u({ req_id: w(), data: d(w()) }), custom_method_invoked: u({ req_id: w(), result: C(Z()), error: C(w()) }), popup_closed: d( u({ button_id: d(w(), () => { }) }), {} ), viewport_changed: u({ height: A(), width: d(A(), () => window.innerWidth), is_state_stable: x(), is_expanded: x() }), theme_changed: u({ theme_params: oe() }) }; function L(e) { if (e.source !== window.parent) return; let t; try { t = v(j(w(), D(), G), e.data); } catch { return; } const { eventType: r, eventData: o } = t, s = _e[r]; let n; try { n = s ? v(s, o) : o; } catch (a) { return f().forceError( [ `An error occurred processing the "${r}" event from the Telegram application.`, "Please, file an issue here:", "https://github.com/Telegram-Mini-Apps/telegram-apps/issues/new/choose" ].join(` `), t, a ); } le(r, n); } const [ F, ue, le, fe ] = ie( () => { const e = window, t = { receiveEvent: T }; e.TelegramGameProxy_receiveEvent = T, e.TelegramGameProxy = t, e.Telegram = { WebView: t }, window.addEventListener("message", L); }, () => { ["TelegramGameProxy_receiveEvent", "TelegramGameProxy", "Telegram"].forEach((e) => { delete window[e]; }), window.removeEventListener("message", L); } ), [ we, qe ] = b( "MethodUnsupportedError", (e, t) => [ `Method "${e}" is unsupported in Mini Apps version ${t}` ] ), [ me, Le ] = b( "MethodParameterUnsupportedError", (e, t, r) => [ `Parameter "${t}" of "${e}" method is unsupported in Mini Apps version ${r}` ] ), [ ge, Ue ] = se( "LaunchParamsRetrieveError", (e) => ({ errors: e }), (e) => [ [ "Unable to retrieve launch parameters from any known source. Perhaps, you have opened your app outside Telegram?", "📖 Refer to docs for more information:", "https://docs.telegram-mini-apps.com/packages/telegram-apps-bridge/environment", "", "Collected errors:", ...e.map(([t, r]) => `Source: ${t} / ${r instanceof Error ? r.message : String(r)}`) ].join(` `) ] ), [ be, Ie ] = b( "InvalidLaunchParamsError", (e, t) => [ `Invalid value for launch params: ${e}`, { cause: t } ] ), [de, Re] = b("UnknownEnvError"), [ he, We ] = b( "InvokeCustomMethodError", (e) => [`Server returned error: ${e}`] ), g = P((...e) => { try { window.parent.postMessage(...e); } catch (t) { t instanceof SyntaxError ? f().forceError( "Unable to call window.parent.postMessage due to incorrectly configured target origin. Use the setTargetOrigin method to allow this origin to receive events", t ) : f().forceError(t); } }), ye = (...e) => g()(...e), k = P("https://web.telegram.org"); function $e(e) { k.set(e), f().log("New target origin set", e); } function Y(e, t) { f().log("Posting event:", t ? { eventType: e, eventData: t } : { eventType: e }); const r = window, o = JSON.stringify({ eventType: e, eventData: t }); if (K()) return ye(o, k()); if (Q(r)) { r.TelegramWebviewProxy.postEvent(e, JSON.stringify(t)); return; } if (y(u({ external: u({ notify: $() }) }), r)) { r.external.notify(o); return; } throw new de(); } function H(e, t, r) { r || (r = {}); const { capture: o } = r, [s, n] = ee(); return new N((a) => { (Array.isArray(t) ? t : [t]).forEach((c) => { s( F(c, (i) => { (!o || (Array.isArray(t) ? o({ event: c, payload: i }) : o(i))) && a(i); }) ); }), (r.postEvent || Y)(e, r.params); }, r).finally(n); } const U = "launchParams"; function I(e) { return e.replace(/^[^?#]*[?#]/, "").replace(/[?#]/g, "&"); } function X() { const e = []; for (const [t, r] of [ // Try to retrieve launch parameters from the current location. This method can return // nothing in case, location was changed, and then the page was reloaded. [() => I(window.location.href), "window.location.href"], // Then, try using the lower level API - window.performance. [() => { const o = performance.getEntriesByType("navigation")[0]; return o && I(o.name); }, "performance navigation entries"], [() => te(U), "local storage"] ]) { const o = t(); if (!o) { e.push([r, new Error("Source is empty")]); continue; } if (J(o)) return z(U, o), o; try { E(o); } catch (s) { e.push([r, s]); } } throw new ge(e); } function ve(e) { const t = E(X()); return e ? re(t) : t; } function je(e, t) { if (!e) try { return ve(), !0; } catch { return !1; } return N.fn(async (r) => { if (Q(window)) return !0; try { return await H("web_app_request_theme", "theme_changed", r), !0; } catch { return !1; } }, t || { timeout: 100 }); } function Ne({ launchParams: e, onEvent: t, resetPostMessage: r } = {}) { if (e) { const n = typeof e == "string" || e instanceof URLSearchParams ? e.toString() : ( // Here we have to trick serializeLaunchParamsQuery into thinking, it serializes a valid // value. We are doing it because we are working with tgWebAppData presented as a // string, not an object as serializeLaunchParamsQuery requires. ne({ ...e, tgWebAppData: void 0 }) + (e.tgWebAppData ? `&tgWebAppData=${encodeURIComponent(e.tgWebAppData.toString())}` : "") ); if (!J(n)) try { E(n); } catch (a) { throw new be(n, a); } z("launchParams", n); } if (K()) { if (!t) return; const n = j( w(), D(), G ); r && g.reset(); const a = g(); g.set((...c) => { const [i] = c, _ = () => { a(...c); }; if (y(n, i)) { const l = v(n, i); t([l.eventType, l.eventData], _); } else _(); }); return; } const o = window.TelegramWebviewProxy || {}, s = o.postEvent || (() => { }); window.TelegramWebviewProxy = { ...o, postEvent(n, a) { const c = () => { s(n, a); }; t ? t([n, a ? JSON.parse(a) : void 0], c) : c(); } }, f().log("Environment was mocked by the mockTelegramEnv function"); } function ze() { return new URLSearchParams(X()).get("tgWebAppData") || void 0; } function Ee(e) { return ({ req_id: t }) => t === e; } function R(e) { return e.split(".").map(Number); } function Pe(e, t) { const r = R(e), o = R(t), s = Math.max(r.length, o.length); for (let n = 0; n < s; n += 1) { const a = r[n] || 0, c = o[n] || 0; if (a !== c) return a > c ? 1 : -1; } return 0; } function p(e, t) { return Pe(e, t) <= 0; } function W(e, t, r) { if (typeof r == "string") { if (e === "web_app_open_link") { if (t === "try_instant_view") return p("6.4", r); if (t === "try_browser") return p("7.6", r); } if (e === "web_app_set_header_color" && t === "color") return p("6.9", r); if (e === "web_app_close" && t === "return_back") return p("7.6", r); if (e === "web_app_setup_main_button" && t === "has_shine_effect") return p("7.10", r); } switch (e) { case "web_app_open_tg_link": case "web_app_open_invoice": case "web_app_setup_back_button": case "web_app_set_background_color": case "web_app_set_header_color": case "web_app_trigger_haptic_feedback": return p("6.1", t); case "web_app_open_popup": return p("6.2", t); case "web_app_close_scan_qr_popup": case "web_app_open_scan_qr_popup": case "web_app_read_text_from_clipboard": return p("6.4", t); case "web_app_switch_inline_query": return p("6.7", t); case "web_app_invoke_custom_method": case "web_app_request_write_access": case "web_app_request_phone": return p("6.9", t); case "web_app_setup_settings_button": return p("6.10", t); case "web_app_biometry_get_info": case "web_app_biometry_open_settings": case "web_app_biometry_request_access": case "web_app_biometry_request_auth": case "web_app_biometry_update_token": return p("7.2", t); case "web_app_setup_swipe_behavior": return p("7.7", t); case "web_app_share_to_story": return p("7.8", t); case "web_app_setup_secondary_button": case "web_app_set_bottom_bar_color": return p("7.10", t); case "web_app_request_safe_area": case "web_app_request_content_safe_area": case "web_app_request_fullscreen": case "web_app_exit_fullscreen": case "web_app_set_emoji_status": case "web_app_add_to_home_screen": case "web_app_check_home_screen": case "web_app_request_emoji_status_access": case "web_app_check_location": case "web_app_open_location_settings": case "web_app_request_file_download": case "web_app_request_location": case "web_app_send_prepared_message": case "web_app_start_accelerometer": case "web_app_start_device_orientation": case "web_app_start_gyroscope": case "web_app_stop_accelerometer": case "web_app_stop_device_orientation": case "web_app_stop_gyroscope": case "web_app_toggle_orientation_lock": return p("8.0", t); default: return [ "iframe_ready", "iframe_will_reload", "web_app_close", "web_app_data_send", "web_app_expand", "web_app_open_link", "web_app_ready", "web_app_request_theme", "web_app_request_viewport", "web_app_setup_main_button", "web_app_setup_closing_behavior" ].includes(e); } } function De(e, t) { t || (t = "strict"); const r = typeof t == "function" ? t : (o) => { const { method: s, version: n } = o, a = "param" in o ? new me(s, o.param, n) : new we(s, n); if (t === "strict") throw a; return f().forceWarn(a.message); }; return (o, s) => W(o, e) ? o === "web_app_set_header_color" && y(u({ color: O() }), s) && !W(o, "color", e) ? r({ version: e, method: o, param: "color" }) : Y(o, s) : r({ version: e, method: o }); } function Ge(e) { const t = B(e); if (t.length > 512) throw new Error("Value is too long for start parameter"); return t; } const Je = ce; function Be(e) { return B(e).length <= 512; } function Qe(e, t, r, o) { return H("web_app_invoke_custom_method", "custom_method_invoked", { ...o || {}, params: { method: e, params: t, req_id: r }, capture: Ee(r) }).then(({ result: s, error: n }) => { if (n) throw new he(n); return s; }); } function Ke() { Object.hasOwn || (Object.hasOwn = function(e, t) { return Object.prototype.hasOwnProperty.call(e, t); }); } function Fe() { fe(), pe(!1), [g, k].forEach((e) => { e.unsubAll(), e.reset(); }); } export { Xe as AbortablePromise, Ze as CancelledError, be as InvalidLaunchParamsError, he as InvokeCustomMethodError, ge as LaunchParamsRetrieveError, Oe as ManualPromise, me as MethodParameterUnsupportedError, we as MethodUnsupportedError, Ve as TimeoutError, de as UnknownEnvError, Ke as applyPolyfills, Ee as captureSameReq, Pe as compareVersions, ot as createLogger, De as createPostEvent, Ge as createStartParam, ce as decodeBase64Url, Je as decodeStartParam, T as emitEvent, B as encodeBase64Url, Q as hasWebviewProxy, Qe as invokeCustomMethod, et as isCancelledError, K as isIframe, Ie as isInvalidLaunchParamsError, We as isInvokeCustomMethodError, Ue as isLaunchParamsRetrieveError, Le as isMethodMethodParameterUnsupportedError, qe as isMethodUnsupportedError, Be as isSafeToCreateStartParam, je as isTMA, tt as isTimeoutError, Re as isUnknownEnvError, f as logger, Ne as mockTelegramEnv, ue as off, fe as offAll, F as on, Y as postEvent, ye as postMessage, g as postMessageImplementation, H as request, Fe as resetPackageState, ve as retrieveLaunchParams, ze as retrieveRawInitData, X as retrieveRawLaunchParams, pe as setDebug, $e as setTargetOrigin, W as supports, k as targetOrigin }; //# sourceMappingURL=index.js.map