@jolibox/sdk 1.1.4-beta.1 → 1.1.4-beta.11

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.
@@ -39,29 +39,29 @@ describe('canIUse', () => {
39
39
 
40
40
  it('should return true for supported API without properties', () => {
41
41
  mockGetCanIUseConfig.mockReturnValue({ version: '0.9.0' });
42
- expect(canIUse('supportedApi')).toBe(true);
42
+ expect(canIUse('supportedApi')).toBe(false);
43
43
  });
44
44
 
45
45
  it('should check H5 platform configuration', () => {
46
46
  //eslint-disable-next-line @typescript-eslint/ban-ts-comment
47
47
  //@ts-ignore
48
48
  mockEnv.mockReturnValue({ data: { deviceInfo: { platform: 'h5' } } });
49
- mockGetCanIUseConfig.mockReturnValue({ version: '0.9.0' });
50
- expect(canIUse('webApi')).toBe(true);
49
+ mockGetCanIUseConfig.mockReturnValue({ version: '1.0.0' });
50
+ expect(canIUse('webApi')).toBe(false);
51
51
  expect(mockGetCanIUseConfig).toHaveBeenCalledWith('h5', 'webApi');
52
52
  });
53
53
 
54
54
  it('should handle nested property checks', () => {
55
55
  mockGetCanIUseConfig.mockReturnValue({
56
- version: '0.9.0',
56
+ version: '1.0.0',
57
57
  properties: {
58
58
  feature: {
59
- subFeature: '0.9.0'
59
+ subFeature: '1.0.0'
60
60
  }
61
61
  }
62
62
  });
63
63
 
64
- expect(canIUse('api:feature:subFeature')).toBe(true);
64
+ expect(canIUse('api:feature:subFeature')).toBe(false);
65
65
  });
66
66
 
67
67
  it('should return false for unsupported property version', () => {
@@ -2,7 +2,7 @@ import { env as getEnv } from './get-system-info';
2
2
  import { isObject, isString, compareVersion } from '@jolibox/common';
3
3
  import { getCanIUseConfig, get } from '../utils/can-i-use';
4
4
 
5
- const __VERSION__ = '1.0.0'; // mock
5
+ const __VERSION__ = '__JOLIBOX_LOCAL_SDK_VERSION__'; // mock
6
6
 
7
7
  export function canIUse(schema: string): boolean {
8
8
  const env = getEnv();
@@ -1,9 +1,6 @@
1
1
  import { createCommands } from '@jolibox/common';
2
2
 
3
3
  const commands = createCommands();
4
- export async function getSystemInfo() {
5
- return await commands.executeCommand('API.getSystemInfo');
6
- }
7
4
 
8
5
  export function getSystemInfoSync() {
9
6
  return commands.excuteCommandSync('API.getSystemInfoSync');
package/src/api/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './get-system-info';
2
2
  export * from './can-i-use';
3
+ export * from './login';
@@ -0,0 +1,24 @@
1
+ import { createCommands } from '@jolibox/common';
2
+ import { canIUse } from './can-i-use';
3
+ import { ResponseType } from '@jolibox/types';
4
+
5
+ const commands = createCommands();
6
+ export async function login() {
7
+ if (!canIUse('login')) {
8
+ return {
9
+ code: 'FAILURE' as ResponseType,
10
+ message: '[Jolibox SDK]login is not supported in this platform'
11
+ };
12
+ }
13
+ return await commands.executeCommand('API.login');
14
+ }
15
+
16
+ export async function checkSession() {
17
+ if (!canIUse('checkSession')) {
18
+ return {
19
+ code: 'FAILURE' as ResponseType,
20
+ message: '[Jolibox SDK]checkSession is not supported in this platform'
21
+ };
22
+ }
23
+ return await commands.executeCommand('API.checkSession');
24
+ }
package/src/events.ts ADDED
@@ -0,0 +1,82 @@
1
+ import { hostEmitter, createCommands, BaseError, EProject } from '@jolibox/common';
2
+ import { trackError } from './utils/event-tracker';
3
+ document.addEventListener('DOMContentLoaded', () => hostEmitter.emit('onDocumentReady', timeline.startTime));
4
+
5
+ const timelineFactory = () => {
6
+ let _beginLoad = 0;
7
+ return {
8
+ set startTime(time: number) {
9
+ _beginLoad = time;
10
+ },
11
+ get startTime(): number {
12
+ return _beginLoad;
13
+ }
14
+ };
15
+ };
16
+
17
+ export const timeline = timelineFactory();
18
+
19
+ const commands = createCommands();
20
+
21
+ const rejectHandler = (event: PromiseRejectionEvent | ErrorEvent | BaseError) => {
22
+ commands
23
+ .executeCommand('ReportSDK.traceSystem', {
24
+ event: 'globalJsError',
25
+ info: {
26
+ err: JSON.stringify(event)
27
+ }
28
+ })
29
+ .catch((e) => {
30
+ // 兜底上报
31
+ console.error(`Fallback report: ${e} ${JSON.stringify(event)}`);
32
+ trackError(
33
+ 'globalJsError',
34
+ {
35
+ err: JSON.stringify({
36
+ message: e.message,
37
+ source: e.source,
38
+ lineno: e.lineno,
39
+ colno: e.colno,
40
+ error: e.error
41
+ })
42
+ },
43
+ EProject.WebSDK
44
+ );
45
+ });
46
+ };
47
+
48
+ export const reportError = rejectHandler;
49
+
50
+ window.addEventListener('unhandledrejection', rejectHandler.bind(this));
51
+ window.addEventListener('error', rejectHandler.bind(this));
52
+ window.onerror = (message, source, lineno, colno, error) => {
53
+ commands
54
+ .executeCommand('ReportSDK.traceSystem', {
55
+ event: 'globalJsError',
56
+ info: {
57
+ err: JSON.stringify({
58
+ message,
59
+ source,
60
+ lineno,
61
+ colno,
62
+ error
63
+ })
64
+ }
65
+ })
66
+ .catch((e) => {
67
+ // 兜底上报
68
+ trackError(
69
+ 'globalJsError',
70
+ {
71
+ err: JSON.stringify({
72
+ message,
73
+ source,
74
+ lineno,
75
+ colno,
76
+ error
77
+ })
78
+ },
79
+ EProject.WebSDK
80
+ );
81
+ });
82
+ };
package/src/index.ts CHANGED
@@ -2,28 +2,40 @@
2
2
  /**
3
3
  * global error catch
4
4
  */
5
- import './errors';
5
+ import './events';
6
6
  import './loader';
7
7
 
8
8
  /**
9
9
  * @public Jolibox JS SDK Entry
10
10
  */
11
- import { getSystemInfo, getSystemInfoSync, canIUse } from './api';
11
+ import { getSystemInfoSync, canIUse, login, checkSession } from './api';
12
12
  import { RuntimeSDK } from './sdks/runtime';
13
13
  import { LifecycleSDK } from './sdks/lifecycle';
14
14
  import { StorageSDK } from './sdks/storage';
15
15
  import { JoliboxAds } from './sdks/ads';
16
16
  import { KeyboardSDK } from './sdks/keyboard';
17
+ import { TaskTrackerSDK } from './sdks/task';
18
+
19
+ declare global {
20
+ interface Window {
21
+ JoliboxSDK: typeof JoliboxSDK; // TODO: code review this @Dengxue
22
+ }
23
+ }
17
24
 
18
25
  export class JoliboxSDK {
26
+ readonly jssdkVersion = '__JOLIBOX_LOCAL_SDK_VERSION__';
19
27
  readonly runtime = new RuntimeSDK();
20
28
  readonly ads = new JoliboxAds();
21
29
  readonly lifecycle = new LifecycleSDK();
22
30
  readonly storage = new StorageSDK();
23
31
  readonly keyboard = new KeyboardSDK();
32
+ readonly task = new TaskTrackerSDK();
24
33
 
25
34
  //global API
26
- getSystemInfo = getSystemInfo.bind(this);
27
35
  getSystemInfoSync = getSystemInfoSync.bind(this);
28
36
  canIUse = canIUse.bind(this);
37
+ login = login.bind(this);
38
+ checkSession = checkSession.bind(this);
29
39
  }
40
+
41
+ window.JoliboxSDK = JoliboxSDK; // TODO: code review this @Dengxue
package/src/loader/h5.ts CHANGED
@@ -2,7 +2,8 @@ import { compare, major } from './version';
2
2
  import { track, trackPerformance } from '../utils/report';
3
3
  import { InternalJSModuleEvalError, InternalJSModuleFetchError } from '@jolibox/common';
4
4
  import { getBoostrapModuleUrl, getImplementModuleUrl } from './index';
5
- import { reportError } from '../errors';
5
+ import { reportError, timeline } from '../events';
6
+ import { testMode } from '@/utils/env';
6
7
 
7
8
  const LOCAL_STORE_KEY = 'jolibox-sdk-loadermeta';
8
9
  interface LocalStoreMeta {
@@ -23,8 +24,8 @@ const expired = (now: number, prev: number) =>
23
24
  const isLegalScript = (str: string) =>
24
25
  !/^(Failed to fetch version|Couldn't find the requested file)/.test(str);
25
26
  async function fetchCurrentRemoteScript() {
26
- // TODO: adjust host
27
- const path = `https://stg-api.jolibox.com/api/fe-configs/js-sdk/version_metadata`;
27
+ const host = testMode ? 'https://stg-api.jolibox.com' : 'https://api.jolibox.com';
28
+ const path = `${host}/api/fe-configs/js-sdk/version_metadata`;
28
29
  try {
29
30
  const response = await fetch(path, {
30
31
  method: 'GET',
@@ -36,7 +37,7 @@ async function fetchCurrentRemoteScript() {
36
37
  let currentStore: LocalStoreMeta = { ...CURRENT_VERSION_STORE, timestamp: now };
37
38
  if (bootstrap_version) {
38
39
  const { bootstrap_version: currentLocalBootstrapVersion } = CURRENT_VERSION_STORE;
39
- if (currentLocalBootstrapVersion && compare(currentLocalBootstrapVersion, bootstrap_version)) {
40
+ if (currentLocalBootstrapVersion && compare(currentLocalBootstrapVersion, bootstrap_version) >= 0) {
40
41
  // no-op
41
42
  } else {
42
43
  const currentBootstrapModuleStr = await (await fetch(getBoostrapModuleUrl(bootstrap_version))).text();
@@ -50,7 +51,7 @@ async function fetchCurrentRemoteScript() {
50
51
  }
51
52
  if (implement_version) {
52
53
  const { implement_version: currentLocalImpelementVersion } = CURRENT_VERSION_STORE;
53
- if (currentLocalImpelementVersion && compare(currentLocalImpelementVersion, implement_version)) {
54
+ if (currentLocalImpelementVersion && compare(currentLocalImpelementVersion, implement_version) >= 0) {
54
55
  // no-op
55
56
  } else {
56
57
  const currentImplementModuleStr = await (
@@ -89,7 +90,7 @@ export const createLoadImplement = (params: {
89
90
  if (
90
91
  !expired(now, timestamp) &&
91
92
  major(bootstrap_version) == currentMajorVersion &&
92
- compare(bootstrap_version, currentVersion) > 0
93
+ compare(bootstrap_version, currentVersion) >= 0
93
94
  ) {
94
95
  return { script: bootstrap_script, type: 'localscript' };
95
96
  }
@@ -106,9 +107,10 @@ export const createLoadImplement = (params: {
106
107
 
107
108
  function loadBootstrapModule() {
108
109
  const startToLoad = Date.now();
109
- track('js_sdk_begin', {
110
+ track('jsSdkBegin', {
110
111
  t: startToLoad
111
112
  });
113
+ timeline.startTime = startToLoad;
112
114
 
113
115
  const { script: currentBootstrapModule, type } = fetchCurrentBootstrapModule() ?? {};
114
116
  if (currentBootstrapModule) {
@@ -136,7 +138,7 @@ export const createLoadImplement = (params: {
136
138
  }
137
139
 
138
140
  // load remote impletation
139
- function loadRemoteImplementation() {
141
+ function loadRemoteImplement() {
140
142
  const implStartLoad = Date.now();
141
143
  const { implement_script, implement_version, timestamp } = CURRENT_VERSION_STORE;
142
144
  if (implement_script && implement_version && timestamp) {
@@ -146,7 +148,7 @@ export const createLoadImplement = (params: {
146
148
  if (
147
149
  !expired(now, timestamp) &&
148
150
  major(implement_version) == currentMajorVersion &&
149
- compare(implement_version, currentVersion) > 0
151
+ compare(implement_version, currentVersion) >= 0
150
152
  ) {
151
153
  evalLocalJSModule(implement_script);
152
154
  trackPerformance('implementModuleLoaded', Date.now() - implStartLoad, { type: 'loadscript' });
@@ -166,7 +168,7 @@ export const createLoadImplement = (params: {
166
168
  return () => {
167
169
  try {
168
170
  loadBootstrapModule();
169
- loadRemoteImplementation();
171
+ loadRemoteImplement();
170
172
  fetchCurrentRemoteScript();
171
173
  } catch (e) {
172
174
  reportError(new InternalJSModuleEvalError(`module evaluate error: ${e}`));
@@ -3,7 +3,7 @@ import { major } from './version';
3
3
  import { createLoadImplement as createH5LoadImplement } from './h5';
4
4
  import { createLoadImplement as createNativeLoadImplement } from './native';
5
5
  //TODO: temp
6
- const BUILD_VERSION = '1.0.0';
6
+ const BUILD_VERSION = '__JOLIBOX_LOCAL_SDK_VERSION__';
7
7
  const currentVersion = BUILD_VERSION;
8
8
  const currentMajorVersion = major(currentVersion);
9
9
 
@@ -15,15 +15,27 @@ const currentMajorVersion = major(currentVersion);
15
15
 
16
16
  declare global {
17
17
  interface Window {
18
- // WebBridge: AndroidBridge | null;
18
+ JoliAndroidSDKBridge?: unknown;
19
19
  webkit?: unknown;
20
20
  }
21
21
  }
22
- const loadScript = window.webkit
23
- ? (params: { path: string }) => {
24
- return window.prompt('loadScript', JSON.stringify(params));
25
- }
26
- : undefined;
22
+ const loadScript =
23
+ window.webkit || window.JoliAndroidSDKBridge
24
+ ? (params: { path: string }) => {
25
+ const res = window.prompt(
26
+ 'invoke',
27
+ JSON.stringify({
28
+ event: 'loadScriptSync',
29
+ paramsString: JSON.stringify(params)
30
+ })
31
+ );
32
+ if (res) {
33
+ const { data } = JSON.parse(res);
34
+ return data;
35
+ }
36
+ return undefined;
37
+ }
38
+ : undefined;
27
39
 
28
40
  const isNative = typeof loadScript !== 'undefined';
29
41
 
@@ -32,16 +44,18 @@ const isNative = typeof loadScript !== 'undefined';
32
44
  * as this logic needs to remain self-contained within the SDK.
33
45
  */
34
46
  export const getBoostrapModuleUrl = (version: string | number) =>
35
- `http://cdn.jsdelivr.net/npm/@jolibox/bootstrap@${version}/dist/index.js`;
47
+ `https://cdn.jsdelivr.net/npm/@jolibox/bootstrap@${version}/dist/index.js`;
36
48
  export const getImplementModuleUrl = (version: string | number) =>
37
- `http://cdn.jsdelivr.net/npm/@jolibox/implementation@${version}/dist/index.js`;
38
- /**
39
- * localTest
40
- */
41
- // export const getBoostrapModuleUrl = (version: string | number) => `http://localhost:3000/get-bootstrap`;
42
- // export const getImplementModuleUrl = (version: string | number) => `http://localhost:3000/get-implement`;
43
- const IMMEDIATE_SCRIPT_URL = getBoostrapModuleUrl(currentMajorVersion);
44
- const REMOTE_IMPLEMENT_URL = getImplementModuleUrl(currentMajorVersion);
49
+ `https://cdn.jsdelivr.net/npm/@jolibox/implement@${version}/dist/index.js`;
50
+
51
+ const checkReleaseVersion = (version: string): boolean => {
52
+ const semverRegex = /^\d+\.\d+\.\d+$/;
53
+ return semverRegex.test(version);
54
+ };
55
+
56
+ const fetchVersion = !checkReleaseVersion(currentVersion) ? currentVersion : currentMajorVersion;
57
+ const IMMEDIATE_SCRIPT_URL = getBoostrapModuleUrl(fetchVersion);
58
+ const REMOTE_IMPLEMENT_URL = getImplementModuleUrl(fetchVersion);
45
59
 
46
60
  /**
47
61
  * load entry
@@ -1,6 +1,6 @@
1
1
  import { track, trackPerformance } from '@/utils/report';
2
2
  import { InternalJSModuleEvalError } from '@jolibox/common';
3
- import { reportError } from '../errors';
3
+ import { reportError, timeline } from '../events';
4
4
 
5
5
  export const createLoadImplement = (
6
6
  loadScript: (params: { path: string }) => string | null,
@@ -34,9 +34,10 @@ export const createLoadImplement = (
34
34
  return () => {
35
35
  try {
36
36
  const startToLoad = Date.now();
37
- track('js_sdk_begin', {
37
+ track('jsSdkBegin', {
38
38
  t: startToLoad
39
39
  });
40
+ timeline.startTime = startToLoad;
40
41
  /**
41
42
  * bootstrap module
42
43
  */
@@ -8,14 +8,10 @@ export class KeyboardSDK extends BaseSDK implements Keyboard {
8
8
  * @returns
9
9
  */
10
10
  showKeyboard(params: { defaultValue?: string; maxLength?: number; multiple?: boolean }) {
11
- const canUse = this.canUse('keyboard.showKeyboard');
12
- if (!canUse) {
13
- return {
14
- code: 'FAILURE' as ResponseType,
15
- message: `[Jolibox SDK] keyboard.showKeyboard is not supported in this platform`
16
- };
11
+ const errMsg = this.canIUseIfThrow('keyboard.showKeyboard');
12
+ if (errMsg) {
13
+ return errMsg;
17
14
  }
18
-
19
15
  this.commands.executeCommand('KeyboardSDK.showKeyboard', params);
20
16
  }
21
17
  /**
@@ -24,26 +20,20 @@ export class KeyboardSDK extends BaseSDK implements Keyboard {
24
20
  * @returns
25
21
  */
26
22
  updateKeyboard(value: string) {
27
- const canUse = this.canUse('keyboard.updateKeyboard');
28
- if (!canUse) {
29
- return {
30
- code: 'FAILURE' as ResponseType,
31
- message: `[Jolibox SDK] keyboard.updateKeyboard is not supported in this platform`
32
- };
23
+ const errMsg = this.canIUseIfThrow('keyboard.updateKeyboard');
24
+ if (errMsg) {
25
+ return errMsg;
33
26
  }
34
- this.commands.executeCommand('KeyboardSDK.updateKeyboard', value);
27
+ this.commands.executeCommand('KeyboardSDK.updateKeyboard', { value });
35
28
  }
36
29
  /**
37
30
  * 隐藏键盘
38
31
  * @returns
39
32
  */
40
33
  hideKeyboard() {
41
- const canUse = this.canUse('keyboard.hideKeyboard');
42
- if (!canUse) {
43
- return {
44
- code: 'FAILURE' as ResponseType,
45
- message: `[Jolibox SDK] keyboard.hideKeyboard is not supported in this platform`
46
- };
34
+ const errMsg = this.canIUseIfThrow('keyboard.hideKeyboard');
35
+ if (errMsg) {
36
+ return errMsg;
47
37
  }
48
38
  this.commands.executeCommand('KeyboardSDK.hideKeyboard');
49
39
  }
@@ -19,14 +19,18 @@ export class LifecycleSDK extends BaseSDK<LifecycleSDKEventMap> implements Lifec
19
19
  this.commands.executeCommand('LifecycleSDK.onReady', wrappedOnReady.bind(this));
20
20
  }
21
21
 
22
- exitGame(params: { onBeforeExit: () => void }) {
23
- this.commands.executeCommand('LifecycleSDK.exitGame', params.onBeforeExit);
22
+ exit(params: { onBeforeExit: () => void }) {
23
+ const errMsg = this.canIUseIfThrow('lifeCycle.exit');
24
+ if (errMsg) {
25
+ return errMsg;
26
+ }
27
+ this.commands.executeCommand('LifecycleSDK.exit', params.onBeforeExit);
24
28
  }
25
29
 
26
- onGameHide(params: () => void) {
27
- this.commands.executeCommand('LifecycleSDK.onGameHide', params.bind(this));
30
+ onJoliboxHide(params: () => void) {
31
+ this.commands.executeCommand('LifecycleSDK.onJoliboxHide', params.bind(this));
28
32
  }
29
- onGameShow(params: () => void) {
30
- this.commands.executeCommand('LifecycleSDK.onGameShow', params.bind(this));
33
+ onJoliboxShow(params: () => void) {
34
+ this.commands.executeCommand('LifecycleSDK.onJoliboxShow', params.bind(this));
31
35
  }
32
36
  }
package/src/sdks/sdk.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { canIUse } from '@/api';
2
2
  import { createCommands, EventEmitter } from '@jolibox/common';
3
+ import { ResponseType } from '@jolibox/types';
3
4
  export interface BaseSDKEventMap {
4
5
  _baseSDKMarker?: never;
5
6
  }
@@ -19,4 +20,13 @@ export abstract class BaseSDK<T = BaseSDKEventMap> {
19
20
  canUse(command: string) {
20
21
  return canIUse(command);
21
22
  }
23
+
24
+ canIUseIfThrow(command: string) {
25
+ if (!this.canUse(command)) {
26
+ return {
27
+ code: 'FAILURE' as ResponseType,
28
+ message: `[Jolibox SDK] ${command} is not supported in this platform`
29
+ };
30
+ }
31
+ }
22
32
  }
@@ -0,0 +1,65 @@
1
+ import { BaseSDK } from './sdk';
2
+ import { TaskTracker, TaskResponse } from '@jolibox/types';
3
+
4
+ export class TaskTrackerSDK extends BaseSDK implements TaskTracker {
5
+ async onLevelFinished(levelId: string | number, result: boolean, duration: number): Promise<TaskResponse> {
6
+ const errMsg = this.canIUseIfThrow('TaskTrackerSDK.onLevelFinished');
7
+ if (errMsg) {
8
+ return Promise.resolve(errMsg);
9
+ }
10
+ return await this.commands.executeCommand('TaskTrackerSDK.levelFinished', levelId, { result, duration });
11
+ }
12
+ async onTaskFinished(taskId: string | number, duration: number): Promise<TaskResponse> {
13
+ const errMsg = this.canIUseIfThrow('TaskTrackerSDK.onTaskFinished');
14
+ if (errMsg) {
15
+ return errMsg;
16
+ }
17
+ return await this.commands.executeCommand('TaskTrackerSDK.taskFinished', taskId, { duration });
18
+ }
19
+ async onTaskEvent(
20
+ taskId: string | number,
21
+ params: {
22
+ tools?: {
23
+ id: string | number;
24
+ name: string;
25
+ count?: number;
26
+ description?: string;
27
+ price?: {
28
+ amount: number;
29
+ unit: string;
30
+ };
31
+ }[];
32
+ awards?: {
33
+ id: string | number;
34
+ name: string;
35
+ }[];
36
+ }
37
+ ): Promise<TaskResponse> {
38
+ const errMsg = this.canIUseIfThrow('TaskTrackerSDK.onTaskEvent');
39
+ if (errMsg) {
40
+ return errMsg;
41
+ }
42
+ return await this.commands.executeCommand('TaskTrackerSDK.taskEvent', taskId, params);
43
+ }
44
+ async onLevelUpgrade(levelId: string | number, name: string): Promise<TaskResponse> {
45
+ const errMsg = this.canIUseIfThrow('TaskTrackerSDK.onLevelUpgrade');
46
+ if (errMsg) {
47
+ return errMsg;
48
+ }
49
+ return await this.commands.executeCommand('TaskTrackerSDK.levelUpgrade', levelId, name);
50
+ }
51
+ async onHistoryUserLevel(level: number): Promise<TaskResponse> {
52
+ const errMsg = this.canIUseIfThrow('TaskTrackerSDK.onHistoryUserLevel');
53
+ if (errMsg) {
54
+ return errMsg;
55
+ }
56
+ return await this.commands.executeCommand('TaskTrackerSDK.historyUserLevel', level);
57
+ }
58
+ async onHistoryUserScore(score: number): Promise<TaskResponse> {
59
+ const errMsg = this.canIUseIfThrow('TaskTrackerSDK.onHistoryUserScore');
60
+ if (errMsg) {
61
+ return errMsg;
62
+ }
63
+ return await this.commands.executeCommand('TaskTrackerSDK.historyUserScore', score);
64
+ }
65
+ }
@@ -0,0 +1 @@
1
+ export const testMode = new URLSearchParams(window.location.search).get('joliboxEnv') === 'staging';