@alfalab/bridge-to-native 1.0.0 → 1.0.1

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.
@@ -79,6 +79,8 @@ class NativeNavigationAndTitleService {
79
79
  reload() {
80
80
  this.nativeHistoryStack.push(1 /* NativeHistoryStackSpecialValues.TemporaryReloadStub */); // небольшой костыль, чтобы переиспользовать server-side сценарий
81
81
  this.saveNativeHistoryStack();
82
+ // информация для серверной стороны B2N, что происходит `reload` и парсить запрос на предмет NA параметров не нужно (в нем их скорее всего не будет)
83
+ document.cookie = `${query_and_headers_keys_1.COOKIE_KEY_BRIDGE_TO_NATIVE_RELOAD}=true; Path=/`;
82
84
  }
83
85
  setInitialView(nativeTitle = '') {
84
86
  this.nativeHistoryStack = [nativeTitle];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfalab/bridge-to-native",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "license": "MIT",
5
5
  "description": "Утилита для удобной работы веб приложения внутри нативного приложения и коммуникации с ним.",
6
6
  "engines": {
@@ -1,6 +1,7 @@
1
1
  export declare const HEADER_KEY_COOKIE = "cookie";
2
2
  export declare const HEADER_KEY_USER_AGENT = "user-agent";
3
3
  export declare const COOKIE_KEY_BRIDGE_TO_NATIVE_DATA = "bridgeToNativeData";
4
+ export declare const COOKIE_KEY_BRIDGE_TO_NATIVE_RELOAD = "bridgeToNativeReload";
4
5
  export declare const HEADER_KEY_NATIVE_APPVERSION = "app-version";
5
6
  export declare const QUERY_B2N_NEXT_PAGEID = "b2n-next-page-id";
6
7
  export declare const QUERY_B2N_TITLE = "b2n-title";
@@ -3,7 +3,7 @@
3
3
  * Ключи стандартных заголовков.
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SS_KEY_BRIDGE_TO_NATIVE_HISTORY_STACK = exports.QUERY_NATIVE_THEME = exports.QUERY_NATIVE_IOS_APPVERSION = exports.QUERY_NATIVE_IOS_APPID = exports.QUERY_B2N_TITLE_DEPRECATED = exports.QUERY_B2N_TITLE = exports.QUERY_B2N_NEXT_PAGEID = exports.HEADER_KEY_NATIVE_APPVERSION = exports.COOKIE_KEY_BRIDGE_TO_NATIVE_DATA = exports.HEADER_KEY_USER_AGENT = exports.HEADER_KEY_COOKIE = void 0;
6
+ exports.SS_KEY_BRIDGE_TO_NATIVE_HISTORY_STACK = exports.QUERY_NATIVE_THEME = exports.QUERY_NATIVE_IOS_APPVERSION = exports.QUERY_NATIVE_IOS_APPID = exports.QUERY_B2N_TITLE_DEPRECATED = exports.QUERY_B2N_TITLE = exports.QUERY_B2N_NEXT_PAGEID = exports.HEADER_KEY_NATIVE_APPVERSION = exports.COOKIE_KEY_BRIDGE_TO_NATIVE_RELOAD = exports.COOKIE_KEY_BRIDGE_TO_NATIVE_DATA = exports.HEADER_KEY_USER_AGENT = exports.HEADER_KEY_COOKIE = void 0;
7
7
  exports.HEADER_KEY_COOKIE = 'cookie';
8
8
  exports.HEADER_KEY_USER_AGENT = 'user-agent';
9
9
  /*
@@ -11,6 +11,10 @@ exports.HEADER_KEY_USER_AGENT = 'user-agent';
11
11
  */
12
12
  // Ключ cookie, с помощью которого серверная часть B2N передаст на клиент информацию об NA.
13
13
  exports.COOKIE_KEY_BRIDGE_TO_NATIVE_DATA = 'bridgeToNativeData';
14
+ // Ключ cookie, указывающий, что запрос выполнен после вызова `bridgeToNative.reload()`.
15
+ // Используется для предотвращения перезаписи cookie bridgeToNativeData, чтобы сохранить
16
+ // актуальные параметры нативного приложения
17
+ exports.COOKIE_KEY_BRIDGE_TO_NATIVE_RELOAD = 'bridgeToNativeReload';
14
18
  // Нативное приложение на обеих платформах подмешивает этот заголовок к запросу за HTML.
15
19
  // TODO:
16
20
  // * Исследовать, делает ли оно это при запросах к прочим ресурсам;
@@ -11,7 +11,8 @@ const utils_1 = require("./utils");
11
11
  */
12
12
  function isWebviewEnv(request) {
13
13
  // Выставленная ранее кука — однозначный индикатор вебвью окружения.
14
- if ((0, utils_1.hasBridgeToNativeDataCookie)(request)) {
14
+ const cookieHeader = (0, utils_1.getHeaderValue)(request, query_and_headers_keys_1.HEADER_KEY_COOKIE);
15
+ if ((0, utils_1.hasBridgeToNativeDataCookie)(cookieHeader)) {
15
16
  return true;
16
17
  }
17
18
  const appVersion = (0, utils_1.getHeaderValue)(request, query_and_headers_keys_1.HEADER_KEY_NATIVE_APPVERSION);
@@ -1,3 +1,4 @@
1
+ import { type NativeParams } from '../types';
1
2
  import { type UniversalRequest } from './types';
2
3
  /**
3
4
  * Парсит запрос, доставая из него данные о нативном приложении,
@@ -10,4 +11,4 @@ import { type UniversalRequest } from './types';
10
11
  * Нужно передать функцию, которая средствами используемого веб-сервера добавит заголовок в ответ.
11
12
  * b2native с её помощью добавит `Set-Cookie` заголовок с некоторыми данными для своего клиентского кода.
12
13
  */
13
- export declare function prepareNativeAppDetailsForClient(request: UniversalRequest, setResponseHeader: (headerKey: string, headerValue: string) => void): void;
14
+ export declare function prepareNativeAppDetailsForClient(request: UniversalRequest, setResponseHeader: (headerKey: string, headerValue: string) => void): Partial<NativeParams>;
@@ -17,17 +17,27 @@ const utils_1 = require("./utils");
17
17
  * b2native с её помощью добавит `Set-Cookie` заголовок с некоторыми данными для своего клиентского кода.
18
18
  */
19
19
  function prepareNativeAppDetailsForClient(request, setResponseHeader) {
20
- // Если кука с данными о нативном приложении уже есть, информация уже собрана,
21
- // делать больше ничего не нужно.
22
- // Возможна смена темы нативного приложения (светлая/тёмная) во время вебвью-сессии.
23
- // Но веб об этом не узнает, т.к. нативное приложение сообщает об этом
24
- // только при старте нового вебвью.
25
- if ((0, utils_1.hasBridgeToNativeDataCookie)(request)) {
26
- return;
27
- }
20
+ // Проверяем наличие куки bridgeToNativeData в запросе. Если куки нет устанавливаем её.
21
+ // Также сверяем тему из query-параметров с темой, сохранённой в куке, потому что
22
+ // при повторном открытии WebView сессионная кука может сохраниться, и если тема приложения
23
+ // изменилась между сессиями, WebView и нативное приложение будут не синхронизированы.
24
+ // В таком случае обновляем куку актуальными данными из query-параметров
25
+ // Для случая когда передан флаг `reload` куку перезаписывать не нужно, потому что:
26
+ // 1) Данных NA в запросе с большой вероятностью не будет;
27
+ // 2) клиентская сторона сохранит всё, что нужно в SessionStorage
28
+ const cookieHeader = (0, utils_1.getHeaderValue)(request, query_and_headers_keys_1.HEADER_KEY_COOKIE);
28
29
  const nativeParams = parseRequest(request);
29
- const serializedNativeParams = encodeURIComponent(JSON.stringify(nativeParams));
30
- setResponseHeader('Set-Cookie', `${query_and_headers_keys_1.COOKIE_KEY_BRIDGE_TO_NATIVE_DATA}=${serializedNativeParams}`);
30
+ const hasReloadFlag = cookieHeader?.includes(`${query_and_headers_keys_1.COOKIE_KEY_BRIDGE_TO_NATIVE_RELOAD}=true`);
31
+ const shouldUpdateCookie = !(0, utils_1.hasBridgeToNativeDataCookie)(cookieHeader) || !isSameTheme(request, cookieHeader);
32
+ if (!hasReloadFlag && shouldUpdateCookie) {
33
+ const serializedNativeParams = encodeURIComponent(JSON.stringify(nativeParams));
34
+ setResponseHeader('Set-Cookie', `${query_and_headers_keys_1.COOKIE_KEY_BRIDGE_TO_NATIVE_DATA}=${serializedNativeParams}; Path=/`);
35
+ return nativeParams;
36
+ }
37
+ if (hasReloadFlag) {
38
+ setResponseHeader('Set-Cookie', `${query_and_headers_keys_1.COOKIE_KEY_BRIDGE_TO_NATIVE_RELOAD}=false; Max-Age=0; Path=/`);
39
+ }
40
+ return nativeParams;
31
41
  }
32
42
  function parseRequest(request) {
33
43
  // Прихраним «сервисные» query-параметры от нативного приложения,
@@ -76,3 +86,18 @@ function parseRequest(request) {
76
86
  }
77
87
  return nativeParams;
78
88
  }
89
+ // Возвращает результат сравнения темы полученной, из query-параметров
90
+ // с темой, полученной из cookie
91
+ function isSameTheme(request, cookies) {
92
+ if (!cookies)
93
+ return false;
94
+ const [themeFromQuery] = (0, utils_1.getQueryValues)(request, [query_and_headers_keys_1.QUERY_NATIVE_THEME]);
95
+ const parsedCookies = (0, utils_1.parseCookies)(cookies);
96
+ const bridgeCookie = parsedCookies[query_and_headers_keys_1.COOKIE_KEY_BRIDGE_TO_NATIVE_DATA];
97
+ try {
98
+ return JSON.parse(bridgeCookie || '{}').theme === themeFromQuery;
99
+ }
100
+ catch (_) {
101
+ return false;
102
+ }
103
+ }
package/server/utils.d.ts CHANGED
@@ -21,4 +21,5 @@ export declare function getQueryValues(request: UniversalRequest, queryKeys: str
21
21
  *
22
22
  * @param request Объект запроса (Request или IncomingMessage).
23
23
  */
24
- export declare function hasBridgeToNativeDataCookie(request: UniversalRequest): boolean;
24
+ export declare function hasBridgeToNativeDataCookie(cookieHeader: string | null): boolean;
25
+ export declare function parseCookies(cookieHeader: string): Record<string, string>;
package/server/utils.js CHANGED
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getHeaderValue = getHeaderValue;
4
4
  exports.getQueryValues = getQueryValues;
5
5
  exports.hasBridgeToNativeDataCookie = hasBridgeToNativeDataCookie;
6
- const query_and_headers_keys_1 = require("../query-and-headers-keys");
6
+ exports.parseCookies = parseCookies;
7
7
  const regexp_patterns_1 = require("./regexp-patterns");
8
8
  /**
9
9
  * На основе объекта запроса любого типа возвращает
@@ -38,7 +38,16 @@ function getQueryValues(request, queryKeys) {
38
38
  *
39
39
  * @param request Объект запроса (Request или IncomingMessage).
40
40
  */
41
- function hasBridgeToNativeDataCookie(request) {
42
- const cookies = getHeaderValue(request, query_and_headers_keys_1.HEADER_KEY_COOKIE);
43
- return Boolean(cookies && regexp_patterns_1.bridgeToNativeDataCookieExistencePattern.test(cookies));
41
+ function hasBridgeToNativeDataCookie(cookieHeader) {
42
+ return Boolean(cookieHeader && regexp_patterns_1.bridgeToNativeDataCookieExistencePattern.test(cookieHeader));
43
+ }
44
+ function parseCookies(cookieHeader) {
45
+ return cookieHeader.split(';').reduce((acc, part) => {
46
+ const [key, ...rest] = part.split('=');
47
+ const trimmedKey = key.trim();
48
+ if (!trimmedKey)
49
+ return acc;
50
+ acc[trimmedKey] = decodeURIComponent(rest.join('=').trim());
51
+ return acc;
52
+ }, {});
44
53
  }