@alfalab/bridge-to-native 0.0.8 → 0.0.9-beta-fa134e1

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.
@@ -6,25 +6,26 @@ import type { Environment, HandleRedirect, NativeFeatureKey, NativeParams, Theme
6
6
  * для использования в вебвью окружении.
7
7
  */
8
8
  export declare class BridgeToNative {
9
+ private readonly handleRedirect;
10
+ private readonly blankPagePath;
11
+ private readonly nativeParams?;
9
12
  readonly AndroidBridge: {
10
13
  setPageSettings: (params: string) => void;
11
14
  } | undefined;
12
15
  readonly environment: Environment;
13
16
  readonly nativeFallbacks: NativeFallbacks;
14
17
  private nextPageId;
18
+ constructor(handleRedirect: HandleRedirect, blankPagePath: string, nativeParams?: NativeParams | undefined);
15
19
  private _nativeNavigationAndTitle;
20
+ get nativeNavigationAndTitle(): NativeNavigationAndTitle;
16
21
  private _originalWebviewParams;
22
+ get originalWebviewParams(): string;
17
23
  private _appVersion;
24
+ get appVersion(): string;
18
25
  private _iosAppId?;
26
+ get iosAppId(): string | undefined;
19
27
  private _theme;
20
- private readonly _blankPagePath;
21
- private readonly _handleRedirect;
22
- constructor(handleRedirect: HandleRedirect, blankPagePath: string, nativeParams?: NativeParams);
23
28
  get theme(): Theme;
24
- get appVersion(): string;
25
- get iosAppId(): string | undefined;
26
- get nativeNavigationAndTitle(): NativeNavigationAndTitle;
27
- get originalWebviewParams(): string;
28
29
  /**
29
30
  * Метод, проверяющий, можно ли использовать нативную функциональность в текущей версии приложения.
30
31
  *
@@ -43,6 +44,7 @@ export declare class BridgeToNative {
43
44
  * `false` – текущая версия ниже.
44
45
  */
45
46
  isCurrentVersionHigherOrEqual(versionToCompare: string): boolean;
47
+ checkAndroidAllowOpenInNewWebview(): boolean;
46
48
  /**
47
49
  * Сохраняет текущее состояние BridgeToNative в sessionStorage.
48
50
  * Так же сохраняет текущее состояние nativeNavigationAndTitle.
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- /* eslint-disable no-underscore-dangle */
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.BridgeToNative = void 0;
5
4
  const constants_1 = require("./constants");
@@ -12,6 +11,9 @@ const utils_1 = require("./utils");
12
11
  */
13
12
  class BridgeToNative {
14
13
  constructor(handleRedirect, blankPagePath, nativeParams) {
14
+ this.handleRedirect = handleRedirect;
15
+ this.blankPagePath = blankPagePath;
16
+ this.nativeParams = nativeParams;
15
17
  // Webview, запущенное в Android окружении имеет объект `Android` в window.
16
18
  this.AndroidBridge = window.Android;
17
19
  this.environment = this.AndroidBridge ? 'android' : 'ios';
@@ -19,7 +21,6 @@ class BridgeToNative {
19
21
  if (previousState) {
20
22
  this.restorePreviousState();
21
23
  this.nativeFallbacks = new native_fallbacks_1.NativeFallbacks(this);
22
- this._blankPagePath = blankPagePath;
23
24
  return;
24
25
  }
25
26
  this._appVersion =
@@ -29,14 +30,15 @@ class BridgeToNative {
29
30
  this._iosAppId = this.getIosAppId(nativeParams === null || nativeParams === void 0 ? void 0 : nativeParams.iosAppId);
30
31
  this._theme = (nativeParams === null || nativeParams === void 0 ? void 0 : nativeParams.theme) === 'dark' ? 'dark' : 'light';
31
32
  this._originalWebviewParams = (nativeParams === null || nativeParams === void 0 ? void 0 : nativeParams.originalWebviewParams) || '';
32
- this._nativeNavigationAndTitle = new native_navigation_and_title_1.NativeNavigationAndTitle(this, nativeParams ? nativeParams.nextPageId : null, nativeParams === null || nativeParams === void 0 ? void 0 : nativeParams.title, handleRedirect);
33
- this._handleRedirect = handleRedirect;
33
+ this._nativeNavigationAndTitle = new native_navigation_and_title_1.NativeNavigationAndTitle(this, (nativeParams === null || nativeParams === void 0 ? void 0 : nativeParams.nextPageId) || null, nativeParams === null || nativeParams === void 0 ? void 0 : nativeParams.title, handleRedirect);
34
34
  this.nextPageId = nativeParams ? nativeParams.nextPageId : null;
35
35
  this.nativeFallbacks = new native_fallbacks_1.NativeFallbacks(this);
36
- this._blankPagePath = blankPagePath;
37
36
  }
38
- get theme() {
39
- return this._theme;
37
+ get nativeNavigationAndTitle() {
38
+ return this._nativeNavigationAndTitle;
39
+ }
40
+ get originalWebviewParams() {
41
+ return this._originalWebviewParams;
40
42
  }
41
43
  get appVersion() {
42
44
  return this._appVersion;
@@ -44,11 +46,8 @@ class BridgeToNative {
44
46
  get iosAppId() {
45
47
  return this._iosAppId;
46
48
  }
47
- get nativeNavigationAndTitle() {
48
- return this._nativeNavigationAndTitle;
49
- }
50
- get originalWebviewParams() {
51
- return this._originalWebviewParams;
49
+ get theme() {
50
+ return this._theme;
52
51
  }
53
52
  /**
54
53
  * Метод, проверяющий, можно ли использовать нативную функциональность в текущей версии приложения.
@@ -89,6 +88,10 @@ class BridgeToNative {
89
88
  }
90
89
  return true;
91
90
  }
91
+ checkAndroidAllowOpenInNewWebview() {
92
+ const comparisonResult = this.isCurrentVersionHigherOrEqual(constants_1.START_VERSION_ANDROID_ALLOW_OPEN_NEW_WEBVIEW);
93
+ return this.environment === 'android' && comparisonResult;
94
+ }
92
95
  /**
93
96
  * Сохраняет текущее состояние BridgeToNative в sessionStorage.
94
97
  * Так же сохраняет текущее состояние nativeNavigationAndTitle.
@@ -102,7 +105,7 @@ class BridgeToNative {
102
105
  appVersion: this._appVersion,
103
106
  theme: this._theme,
104
107
  nextPageId: this.nextPageId,
105
- originalWebviewParams: this._originalWebviewParams || '',
108
+ originalWebviewParams: this.originalWebviewParams,
106
109
  iosAppId: this._iosAppId,
107
110
  };
108
111
  sessionStorage.setItem(constants_1.PREVIOUS_B2N_STATE_STORAGE_KEY, JSON.stringify(currentState));
@@ -135,7 +138,7 @@ class BridgeToNative {
135
138
  this._theme = previousState.theme;
136
139
  this._originalWebviewParams = previousState.originalWebviewParams;
137
140
  this.nextPageId = previousState.nextPageId;
138
- this._nativeNavigationAndTitle = new native_navigation_and_title_1.NativeNavigationAndTitle(this, previousState.nextPageId, '', this._handleRedirect);
141
+ this._nativeNavigationAndTitle = new native_navigation_and_title_1.NativeNavigationAndTitle(this, previousState.nextPageId, '', this.handleRedirect);
139
142
  sessionStorage.removeItem(constants_1.PREVIOUS_B2N_STATE_STORAGE_KEY);
140
143
  }
141
144
  }
package/constants.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { NativeFeaturesFromVersion } from './types';
2
+ export declare const START_VERSION_ANDROID_ALLOW_OPEN_NEW_WEBVIEW = "10.35.0";
3
+ export declare const ANDROID_APP_ID = "YWxmYWJhbms=";
2
4
  export declare const CLOSE_WEBVIEW_SEARCH_KEY = "closeWebView";
3
5
  export declare const CLOSE_WEBVIEW_SEARCH_VALUE = "true";
4
6
  export declare const PREVIOUS_B2N_STATE_STORAGE_KEY = "previousBridgeToNativeState";
package/constants.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEEP_LINK_PATTERN = exports.nativeFeaturesFromVersion = exports.versionToIosAppId = exports.PREVIOUS_NATIVE_NAVIGATION_AND_TITLE_STATE_STORAGE_KEY = exports.PREVIOUS_B2N_STATE_STORAGE_KEY = exports.CLOSE_WEBVIEW_SEARCH_VALUE = exports.CLOSE_WEBVIEW_SEARCH_KEY = void 0;
3
+ exports.DEEP_LINK_PATTERN = exports.nativeFeaturesFromVersion = exports.versionToIosAppId = exports.PREVIOUS_NATIVE_NAVIGATION_AND_TITLE_STATE_STORAGE_KEY = exports.PREVIOUS_B2N_STATE_STORAGE_KEY = exports.CLOSE_WEBVIEW_SEARCH_VALUE = exports.CLOSE_WEBVIEW_SEARCH_KEY = exports.ANDROID_APP_ID = exports.START_VERSION_ANDROID_ALLOW_OPEN_NEW_WEBVIEW = void 0;
4
+ exports.START_VERSION_ANDROID_ALLOW_OPEN_NEW_WEBVIEW = '10.35.0';
5
+ exports.ANDROID_APP_ID = 'YWxmYWJhbms=';
4
6
  exports.CLOSE_WEBVIEW_SEARCH_KEY = 'closeWebView';
5
7
  exports.CLOSE_WEBVIEW_SEARCH_VALUE = 'true';
6
8
  exports.PREVIOUS_B2N_STATE_STORAGE_KEY = 'previousBridgeToNativeState';
@@ -1,10 +1,10 @@
1
- import { PdfType } from './types';
1
+ import { ExternalNavigationOptions, PdfType } from './types';
2
2
  import type { BridgeToNative } from './bridge-to-native';
3
3
  /**
4
4
  * Класс содержит реализацию обходных путей для веб-фич, которые не работают в нативном-вебвью.
5
5
  */
6
6
  export declare class NativeFallbacks {
7
- private b2n;
7
+ private readonly b2n;
8
8
  constructor(b2n: BridgeToNative);
9
9
  /**
10
10
  * Метод, возвращающий пропсы для ссылок, ведущих на ВНЕШНИЙ ресурс. Которые просто
@@ -21,7 +21,7 @@ export declare class NativeFallbacks {
21
21
  * ```
22
22
  * В разных OS и разных версиях приложения, открытие ресурса будет работать по-разному:
23
23
  *
24
- * - Если текущая версия приложения может открыть ссылку в браузере,
24
+ * - Если текущая версия приложения может открыть ссылку в браузере и не задан параметр `forceOpenInWebview`,
25
25
  * обогащаем URL специальным query-параметром (`target=_blank` в приложении не работает).
26
26
  * - Если это iOS, меняем URL на диплинк, который откроет ссылку в новом вебвью, поверх текущего.
27
27
  * К первому-вебвью, пользователь вернётся, когда закроет второе вебвью с внешним ресурсом.
@@ -29,11 +29,14 @@ export declare class NativeFallbacks {
29
29
  * навигации с приложением. Это «фолбэк-сценарий» с плохим UX (сайт полностью выпадает из истории), но другого способа нет.
30
30
  *
31
31
  * @param link Строка - валидный урл.
32
- * @param onClick Дополнительный обработчик на клик, например, для отправки метрики.
32
+ * @param options - опции
33
+ * @param options.forceOpenInWebview Boolean - по умолчанию = false, если передать true,
34
+ * все ссылки будут открываться в рамках webview, иначе открытие по возможности будет происходить в браузере.
35
+ * @param options.onClick Дополнительный обработчик на клик, например, для отправки метрики.
33
36
  * Внимание! Не факт, что в «фолбэк-сценарии» асинхронная операция будет выполнена (метрика отправлена)!
34
37
  * @returns Пропсы для ссылки в вебвью окружении.
35
38
  */
36
- getExternalLinkProps(link: string, onClick?: () => void): {
39
+ getExternalLinkProps(link: string, options?: ExternalNavigationOptions): {
37
40
  href: string;
38
41
  onClick: (() => void) | undefined;
39
42
  };
@@ -54,6 +57,8 @@ export declare class NativeFallbacks {
54
57
  * См. описание в `getExternalLinkProps`, чтобы узнать, как выбирается способ для перехода.
55
58
  *
56
59
  * @param link Строка - валидный урл.
60
+ * @param forceOpenInWebview Boolean - по умолчанию = false, если передать true,
61
+ * все ссылки будут открываться в рамках webview, иначе открытие по возможности будет происходить в браузере.
57
62
  */
58
- visitExternalResource(link: string): void;
63
+ visitExternalResource(link: string, forceOpenInWebview?: boolean): void;
59
64
  }
@@ -24,7 +24,7 @@ class NativeFallbacks {
24
24
  * ```
25
25
  * В разных OS и разных версиях приложения, открытие ресурса будет работать по-разному:
26
26
  *
27
- * - Если текущая версия приложения может открыть ссылку в браузере,
27
+ * - Если текущая версия приложения может открыть ссылку в браузере и не задан параметр `forceOpenInWebview`,
28
28
  * обогащаем URL специальным query-параметром (`target=_blank` в приложении не работает).
29
29
  * - Если это iOS, меняем URL на диплинк, который откроет ссылку в новом вебвью, поверх текущего.
30
30
  * К первому-вебвью, пользователь вернётся, когда закроет второе вебвью с внешним ресурсом.
@@ -32,21 +32,26 @@ class NativeFallbacks {
32
32
  * навигации с приложением. Это «фолбэк-сценарий» с плохим UX (сайт полностью выпадает из истории), но другого способа нет.
33
33
  *
34
34
  * @param link Строка - валидный урл.
35
- * @param onClick Дополнительный обработчик на клик, например, для отправки метрики.
35
+ * @param options - опции
36
+ * @param options.forceOpenInWebview Boolean - по умолчанию = false, если передать true,
37
+ * все ссылки будут открываться в рамках webview, иначе открытие по возможности будет происходить в браузере.
38
+ * @param options.onClick Дополнительный обработчик на клик, например, для отправки метрики.
36
39
  * Внимание! Не факт, что в «фолбэк-сценарии» асинхронная операция будет выполнена (метрика отправлена)!
37
40
  * @returns Пропсы для ссылки в вебвью окружении.
38
41
  */
39
- getExternalLinkProps(link, onClick) {
40
- const { iosAppId } = this.b2n;
42
+ getExternalLinkProps(link, options = {}) {
43
+ const { onClick, forceOpenInWebview } = options;
44
+ const { iosAppId, environment, checkAndroidAllowOpenInNewWebview } = this.b2n;
41
45
  const url = (0, utils_1.getUrlInstance)(link);
42
- if (this.b2n.canUseNativeFeature('linksInBrowser')) {
46
+ const appId = (0, utils_1.getAppId)(environment, iosAppId);
47
+ if (!forceOpenInWebview && this.b2n.canUseNativeFeature('linksInBrowser')) {
43
48
  url.searchParams.append('openInBrowser', 'true');
44
49
  return { href: url.href, onClick };
45
50
  }
46
- if (iosAppId) {
51
+ if (iosAppId || checkAndroidAllowOpenInNewWebview()) {
47
52
  return {
48
- href: `${iosAppId}://webFeature?type=recommendation&url=${encodeURIComponent(url.href)}`,
49
- onClick,
53
+ href: `${appId}://webFeature?type=recommendation&url=${encodeURIComponent(url.href)}`,
54
+ onClick: options === null || options === void 0 ? void 0 : options.onClick,
50
55
  };
51
56
  }
52
57
  return {
@@ -95,17 +100,20 @@ class NativeFallbacks {
95
100
  * См. описание в `getExternalLinkProps`, чтобы узнать, как выбирается способ для перехода.
96
101
  *
97
102
  * @param link Строка - валидный урл.
103
+ * @param forceOpenInWebview Boolean - по умолчанию = false, если передать true,
104
+ * все ссылки будут открываться в рамках webview, иначе открытие по возможности будет происходить в браузере.
98
105
  */
99
- visitExternalResource(link) {
106
+ visitExternalResource(link, forceOpenInWebview = false) {
100
107
  var _a;
101
- const { iosAppId } = this.b2n;
108
+ const { iosAppId, environment, checkAndroidAllowOpenInNewWebview } = this.b2n;
102
109
  const url = (0, utils_1.getUrlInstance)(link);
103
- if (this.b2n.canUseNativeFeature('linksInBrowser')) {
110
+ const appId = (0, utils_1.getAppId)(environment, iosAppId);
111
+ if (!forceOpenInWebview && this.b2n.canUseNativeFeature('linksInBrowser')) {
104
112
  url.searchParams.append('openInBrowser', 'true');
105
113
  window.location.replace(url.href);
106
114
  }
107
- else if (iosAppId) {
108
- window.location.replace(`${iosAppId}://webFeature?type=recommendation&url=${encodeURIComponent(url.href)}`);
115
+ else if (iosAppId || checkAndroidAllowOpenInNewWebview()) {
116
+ window.location.replace(`${appId}://webFeature?type=recommendation&url=${encodeURIComponent(url.href)}`);
109
117
  }
110
118
  else {
111
119
  (_a = this.b2n.nativeNavigationAndTitle) === null || _a === void 0 ? void 0 : _a.setInitialView('');
@@ -4,12 +4,14 @@ import { BridgeToNative } from './bridge-to-native';
4
4
  * Класс, отвечающий за взаимодействие с нативными элементами в приложении – заголовком и нативной кнопкой назад.
5
5
  */
6
6
  export declare class NativeNavigationAndTitle {
7
- private b2n;
7
+ private readonly b2n;
8
+ private readonly pageId;
9
+ private readonly initialNativeTitle;
10
+ private readonly handleWindowRedirect;
8
11
  private nativeHistoryStack;
9
12
  private numOfBackSteps;
10
13
  private lastSetPageSettingsParams;
11
- private readonly _handleWindowRedirect;
12
- constructor(b2n: BridgeToNative, pageId: number | null, initialNativeTitle: string | undefined, handleWindowRedirect: HandleRedirect);
14
+ constructor(b2n: BridgeToNative, pageId: number | null, initialNativeTitle: string, handleWindowRedirect: HandleRedirect);
13
15
  /**
14
16
  * Метод, вызывающий `history.back()` или закрывающий вебвью, если нет записей
15
17
  * в истории переходов.
@@ -9,13 +9,15 @@ const utils_1 = require("./utils");
9
9
  class NativeNavigationAndTitle {
10
10
  constructor(b2n, pageId, initialNativeTitle = '', handleWindowRedirect) {
11
11
  this.b2n = b2n;
12
+ this.pageId = pageId;
13
+ this.initialNativeTitle = initialNativeTitle;
14
+ this.handleWindowRedirect = handleWindowRedirect;
12
15
  this.nativeHistoryStack = [''];
13
16
  this.numOfBackSteps = 1;
14
17
  // Тут сохраняются параметры, которые в последний раз были отправлены в приложение.
15
18
  // Просто, чтобы не слать одинаковые сигналы в приложение.
16
19
  this.lastSetPageSettingsParams = '';
17
20
  this.handleBack = this.handleBack.bind(this);
18
- this._handleWindowRedirect = handleWindowRedirect;
19
21
  const previousState = !!sessionStorage.getItem(constants_1.PREVIOUS_NATIVE_NAVIGATION_AND_TITLE_STATE_STORAGE_KEY);
20
22
  if (pageId) {
21
23
  this.supportSharedSession(pageId, initialNativeTitle);
@@ -63,17 +65,17 @@ class NativeNavigationAndTitle {
63
65
  window.history.go(-this.numOfBackSteps);
64
66
  }
65
67
  /**
66
- * Метод вызывает `src/shared/utils/handle-redirect` из `newclick-host-ui`
67
- * и регистрирует этот переход в приложении, чтобы кнопка «Назад» в Нативе вызывала
68
+ * Метод вызывает handle-redirect и регистрирует этот переход в
69
+ * приложении, чтобы кнопка «Назад» в Нативе вызывала
68
70
  * переход назад в вебе.
69
71
  */
70
72
  handleRedirect(pageTitleOrPath, appName, path, params) {
71
73
  if (appName) {
72
- this._handleWindowRedirect(appName, path, params);
74
+ this.handleWindowRedirect(appName, path, params);
73
75
  }
74
76
  else {
75
77
  const { appName: extractedAppName, path: extractedPath, query: extractedQuery, } = (0, utils_1.extractAppNameRouteAndQuery)(pageTitleOrPath);
76
- this._handleWindowRedirect(extractedAppName, extractedPath, extractedQuery);
78
+ this.handleWindowRedirect(extractedAppName, extractedPath, extractedQuery);
77
79
  }
78
80
  const title = appName ? pageTitleOrPath : '';
79
81
  this.nativeHistoryStack.push(title);
@@ -127,7 +129,7 @@ class NativeNavigationAndTitle {
127
129
  // В b2n этот метод отмечен модификатором доступа private, но тут его нужно вызвать
128
130
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
129
131
  // @ts-ignore
130
- this.handleRedirect(this.b2n._blankPagePath);
132
+ this.handleRedirect(this.b2n.blankPagePath);
131
133
  this.goBack();
132
134
  }
133
135
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "license": "UNLICENSED",
3
3
  "name": "@alfalab/bridge-to-native",
4
- "version": "0.0.8",
4
+ "version": "0.0.9-beta-fa134e1",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/core-ds/bridge-to-native.git"
package/types.d.ts CHANGED
@@ -19,7 +19,6 @@ export declare type WebViewWindow = Window & {
19
19
  Android?: {
20
20
  setPageSettings: (params: string) => void;
21
21
  };
22
- handleRedirect?: (appName: string, path?: string, params?: Record<string, string>) => VoidFunction;
23
22
  };
24
23
  export declare type PdfType = 'pdfFile' | 'base64' | 'binary';
25
24
  export declare type PreviousBridgeToNativeState = Omit<NativeParams, 'title' | 'theme'> & {
@@ -32,4 +31,8 @@ export declare type PreviousNativeNavigationAndTitleState = {
32
31
  export declare type SyncPurpose = 'initialization' | 'navigation' | 'title-replacing';
33
32
  export declare type HandleRedirect = (appName: string, path?: string, params?: Record<string, string>) => void;
34
33
  export declare type Theme = 'light' | 'dark';
34
+ export declare type ExternalNavigationOptions = {
35
+ onClick?: () => void;
36
+ forceOpenInWebview?: boolean;
37
+ };
35
38
  export {};
package/utils.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Environment } from './types';
1
2
  /**
2
3
  * Разделяет веб ссылку на компоненты
3
4
  * @param route внутренний путь для навигации
@@ -19,3 +20,4 @@ export declare const getUrlInstance: (link: string) => URL;
19
20
  * @returns Правильный формат или нет.
20
21
  */
21
22
  export declare const isValidVersionFormat: (version?: string | undefined) => boolean;
23
+ export declare const getAppId: (environment: Environment, iosAppId?: string | undefined) => string | null;
package/utils.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isValidVersionFormat = exports.getUrlInstance = exports.extractAppNameRouteAndQuery = void 0;
3
+ exports.getAppId = exports.isValidVersionFormat = exports.getUrlInstance = exports.extractAppNameRouteAndQuery = void 0;
4
+ const constants_1 = require("./constants");
4
5
  /**
5
6
  * Разделяет веб ссылку на компоненты
6
7
  * @param route внутренний путь для навигации
@@ -57,3 +58,13 @@ const isValidVersionFormat = (version) => {
57
58
  return versionPattern.test(version);
58
59
  };
59
60
  exports.isValidVersionFormat = isValidVersionFormat;
61
+ const getAppId = (environment, iosAppId) => {
62
+ if (environment === 'android') {
63
+ return atob(constants_1.ANDROID_APP_ID);
64
+ }
65
+ if (environment === 'ios' && iosAppId && typeof iosAppId === 'string') {
66
+ return iosAppId;
67
+ }
68
+ return null;
69
+ };
70
+ exports.getAppId = getAppId;