Skip to content

Commit

Permalink
Merge pull request #640 from Telegram-Mini-Apps/feature/retrieve-raw-lp
Browse files Browse the repository at this point in the history
Feature/retrieve raw lp
  • Loading branch information
heyqbnk authored Jan 30, 2025
2 parents 1895d11 + e356df3 commit d2ea5eb
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 65 deletions.
5 changes: 5 additions & 0 deletions .changeset/gorgeous-forks-clap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@telegram-apps/sdk": patch
---

Remove some errors exported from bridge.
5 changes: 5 additions & 0 deletions .changeset/polite-spies-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@telegram-apps/bridge": minor
---

Implement `retrieveRawLaunchParams`.
13 changes: 2 additions & 11 deletions packages/bridge/src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { errorClass, errorClassWithData } from 'error-kid';
import { errorClass } from 'error-kid';
import type { Version } from '@telegram-apps/types';

export const [
Expand Down Expand Up @@ -30,11 +30,7 @@ const retrieveLaunchParamsError = [
export const [
LaunchParamsRetrieveError,
isLaunchParamsRetrieveError,
] = errorClassWithData<unknown[], [errors: unknown[]]>(
'LaunchParamsRetrieveError',
errors => errors,
retrieveLaunchParamsError,
);
] = errorClass<unknown[]>('LaunchParamsRetrieveError', retrieveLaunchParamsError);

export const [
InvalidLaunchParamsError,
Expand All @@ -43,11 +39,6 @@ export const [
`Invalid value for launch params: ${value}`,
]);

export const [
InitDataRetrieveError,
isInitDataRetrieveError,
] = errorClass('InitDataRetrieveError', retrieveLaunchParamsError);

export const [UnknownEnvError, isUnknownEnvError] = errorClass('UnknownEnvError');

export const [
Expand Down
3 changes: 1 addition & 2 deletions packages/bridge/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export {
type RetrieveLPResultCamelCased,
type RetrieveLPResult,
} from '@/launch-params/retrieveLaunchParams.js';
export { retrieveRawLaunchParams } from '@/launch-params/retrieveRawLaunchParams.js';
export { retrieveRawInitData } from '@/launch-params/retrieveRawInitData.js';

export type * from '@/methods/types/index.js';
Expand Down Expand Up @@ -55,8 +56,6 @@ export {
isMethodMethodParameterUnsupportedError,
UnknownEnvError,
isUnknownEnvError,
InitDataRetrieveError,
isInitDataRetrieveError,
InvalidLaunchParamsError,
isInvalidLaunchParamsError,
} from '@/errors.js';
Expand Down
21 changes: 2 additions & 19 deletions packages/bridge/src/launch-params/retrieveLaunchParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import { LaunchParamsSchema, parseLaunchParamsQuery } from '@telegram-apps/trans
import {
type DeepConvertSnakeKeysToCamelCase,
deepSnakeToCamelObjKeys,
setStorageValue,
} from '@telegram-apps/toolkit';
import type { InferOutput } from 'valibot';

import { LaunchParamsRetrieveError } from '@/errors.js';
import { forEachLpSource } from '@/launch-params/forEachLpSource.js';
import { retrieveRawLaunchParams } from '@/launch-params/retrieveRawLaunchParams.js';

export type RetrieveLPResult = InferOutput<typeof LaunchParamsSchema>;
export type RetrieveLPResultCamelCased =
Expand Down Expand Up @@ -37,21 +35,6 @@ export function retrieveLaunchParams(camelCase: true): RetrieveLPResultCamelCase
export function retrieveLaunchParams(camelCase?: boolean):
| RetrieveLPResult
| RetrieveLPResultCamelCased {
const errors: unknown[] = [];
let launchParams: RetrieveLPResult | undefined;

forEachLpSource(v => {
try {
launchParams = parseLaunchParamsQuery(v);
setStorageValue('launchParams', v);
return false;
} catch (e) {
errors.push(e);
return true;
}
});
if (!launchParams) {
throw new LaunchParamsRetrieveError(errors);
}
const launchParams = parseLaunchParamsQuery(retrieveRawLaunchParams());
return camelCase ? deepSnakeToCamelObjKeys(launchParams) : launchParams;
}
25 changes: 3 additions & 22 deletions packages/bridge/src/launch-params/retrieveRawInitData.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,9 @@
import { isLaunchParamsQuery } from '@telegram-apps/transformers';

import { forEachLpSource } from '@/launch-params/forEachLpSource.js';
import { InitDataRetrieveError } from '@/errors.js';
import { retrieveLaunchParams } from '@/launch-params/retrieveLaunchParams.js';
import { retrieveRawLaunchParams } from '@/launch-params/retrieveRawLaunchParams.js';

/**
* @returns Raw init data from any known source.
* @throws {InitDataRetrieveError} Unable to retrieve init data from any known source.
* @throws {LaunchParamsRetrieveError} Unable to retrieve launch params from any known source.
*/
export function retrieveRawInitData(): string | undefined {
// Init data depends on the launch parameters. We also want them to be saved.
try {
retrieveLaunchParams();
} catch {
throw new InitDataRetrieveError();
}

let initData: string | undefined;
forEachLpSource(v => {
if (isLaunchParamsQuery(v)) {
initData = new URLSearchParams(v).get('tgWebAppData') || undefined;
return false;
}
return true;
});
return initData;
return new URLSearchParams(retrieveRawLaunchParams()).get('tgWebAppData') || undefined;
}
46 changes: 46 additions & 0 deletions packages/bridge/src/launch-params/retrieveRawLaunchParams.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { afterEach, describe, expect, it, vi } from 'vitest';

import { retrieveRawLaunchParams } from '@/launch-params/retrieveRawLaunchParams.js';

afterEach(() => {
vi.restoreAllMocks();
});

describe('window.location.href contains launch params', () => {
it('should retrieve launch params from the window.location.href. Throw an error if data is invalid or missing', () => {
vi
.spyOn(window.location, 'href', 'get')
.mockImplementationOnce(() => {
return '/abc?tgWebAppStartParam=location_hash#tgWebAppPlatform=tdesktop&tgWebAppVersion=7.0&tgWebAppThemeParams=%7B%7D';
});

expect(retrieveRawLaunchParams()).toBe('tgWebAppStartParam=location_hash&tgWebAppPlatform=tdesktop&tgWebAppVersion=7.0&tgWebAppThemeParams=%7B%7D');
});
});

describe('first navigation entry contains launch params', () => {
it('should retrieve launch params from the window.performance. Throw an error if data is invalid or missing', () => {
vi
.spyOn(performance, 'getEntriesByType')
.mockImplementationOnce(() => [{
name: '/abc?tgWebAppStartParam=performance#tgWebAppPlatform=macos&tgWebAppVersion=7.3&tgWebAppThemeParams=%7B%7D',
}] as any);

expect(retrieveRawLaunchParams()).toBe('tgWebAppStartParam=performance&tgWebAppPlatform=macos&tgWebAppVersion=7.3&tgWebAppThemeParams=%7B%7D');
});
});

describe('session storage contains launch params', () => {
it('should return launch parameters from the session storage tapps/launchParams key. If data is missing or invalid, throw an error', () => {
const spy = vi
.spyOn(sessionStorage, 'getItem')
.mockImplementationOnce(() => '');
expect(() => retrieveRawLaunchParams()).toThrow();

spy.mockClear();
spy.mockImplementationOnce(() => {
return '"tgWebAppPlatform=android&tgWebAppThemeParams=%7B%22bg_color%22%3A%22%23ffffff%22%7D&tgWebAppVersion=7.5"';
});
expect(retrieveRawLaunchParams()).toBe('tgWebAppPlatform=android&tgWebAppThemeParams=%7B%22bg_color%22%3A%22%23ffffff%22%7D&tgWebAppVersion=7.5');
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { getStorageValue } from '@telegram-apps/toolkit';
import { isLaunchParamsQuery } from '@telegram-apps/transformers';
import { getStorageValue, setStorageValue } from '@telegram-apps/toolkit';

import { LaunchParamsRetrieveError } from '@/errors.js';

const SESSION_STORAGE_KEY = 'launchParams';

/**
* @param urlString - URL to extract launch parameters from.
Expand All @@ -14,25 +19,27 @@ function fromURL(urlString: string): string {
}

/**
* Runs the specified function for each value, where the value is one stored in any known
* launch parameters source.
* @param fn - function to run. Should return false when the execution must be stopped.
* @returns Launch parameters in a raw format from any known source.
* @throws {LaunchParamsRetrieveError} Unable to retrieve launch parameters. They are probably
* invalid.
*/
export function forEachLpSource(fn: (value: string) => boolean): void {
export function retrieveRawLaunchParams(): string {
for (const retrieve 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.
() => fromURL(window.location.href),
// Then, try using the lower level API - window.performance.
() => {
const navigationEntry = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined;
return navigationEntry ? fromURL(navigationEntry.name) : undefined;
return navigationEntry && fromURL(navigationEntry.name);
},
() => getStorageValue<string>('launchParams') || '',
() => getStorageValue<string>(SESSION_STORAGE_KEY),
]) {
const v = retrieve();
if (v && !fn(v)) {
return;
if (v && isLaunchParamsQuery(v)) {
setStorageValue(SESSION_STORAGE_KEY, v);
return v;
}
}
throw new LaunchParamsRetrieveError();
}
3 changes: 1 addition & 2 deletions packages/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export {
on,
off,
retrieveLaunchParams,
retrieveRawLaunchParams,
retrieveRawInitData,
type RetrieveLPResult,
type RetrieveLPResultCamelCased,
Expand Down Expand Up @@ -114,8 +115,6 @@ export {
isLaunchParamsRetrieveError,
LaunchParamsRetrieveError,
InvalidLaunchParamsError,
InitDataRetrieveError,
isInitDataRetrieveError,
isInvalidLaunchParamsError,
type BackgroundColor,
type PopupParams,
Expand Down

0 comments on commit d2ea5eb

Please sign in to comment.