@jolibox/sdk 1.1.15 → 1.1.16-beta.2
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.
- package/.rush/temp/package-deps_build.json +7 -5
- package/dist/__tests__/index.test.d.ts +1 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/on-custom-event.d.ts +4 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.esm.js +1 -1
- package/dist/index.iife.js +1 -1
- package/dist/index.js +288 -276
- package/package.json +3 -3
- package/sdk.build.log +4 -4
- package/src/__tests__/index.test.ts +63 -0
- package/src/api/index.ts +1 -0
- package/src/api/on-custom-event.ts +9 -0
- package/src/events.ts +43 -24
- package/src/index.ts +19 -1
- package/src/loader/h5.ts +19 -50
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jolibox/sdk",
|
|
3
3
|
"description": "This project is common Jolibox JS-SDk interfere",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.16-beta.2",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
7
7
|
"typings": "dist/index.d.ts",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@jolibox/common": "1.1.
|
|
11
|
-
"@jolibox/types": "1.1.
|
|
10
|
+
"@jolibox/common": "1.1.16-beta.2",
|
|
11
|
+
"@jolibox/types": "1.1.16-beta.2"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"typescript": "5.7.3",
|
package/sdk.build.log
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
Invoking: npm run clean && npm run build:cjs && npm run build:esm && npm run build:iife && tsc
|
|
2
2
|
|
|
3
|
-
> @jolibox/sdk@1.1.
|
|
3
|
+
> @jolibox/sdk@1.1.16-beta.2 clean
|
|
4
4
|
> rimraf ./dist
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
> @jolibox/sdk@1.1.
|
|
7
|
+
> @jolibox/sdk@1.1.16-beta.2 build:cjs
|
|
8
8
|
> BUILD_VERSION=$(node -p "require('./package.json').version") node esbuild.config.js --format=cjs
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
> @jolibox/sdk@1.1.
|
|
11
|
+
> @jolibox/sdk@1.1.16-beta.2 build:esm
|
|
12
12
|
> BUILD_VERSION=$(node -p "require('./package.json').version") node esbuild.config.js --format=esm
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
> @jolibox/sdk@1.1.
|
|
15
|
+
> @jolibox/sdk@1.1.16-beta.2 build:iife
|
|
16
16
|
> BUILD_VERSION=$(node -p "require('./package.json').version") node esbuild.config.js --format=iife
|
|
17
17
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { JoliboxSDK } from '../index';
|
|
2
|
+
|
|
3
|
+
describe('JoliboxSDK Singleton Pattern', () => {
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
//eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
6
|
+
//@ts-ignore
|
|
7
|
+
JoliboxSDK.instance = null;
|
|
8
|
+
|
|
9
|
+
if (window.joliboxsdk) {
|
|
10
|
+
//eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
delete window.joliboxsdk;
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should return the same instance when using new constructor', () => {
|
|
17
|
+
// 创建第一个实例
|
|
18
|
+
const instance1 = new JoliboxSDK();
|
|
19
|
+
|
|
20
|
+
// 创建第二个实例
|
|
21
|
+
const instance2 = new JoliboxSDK();
|
|
22
|
+
|
|
23
|
+
// 验证两个实例是同一个对象
|
|
24
|
+
expect(instance1).toBe(instance2);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should return the same instance when using getInstance method', () => {
|
|
28
|
+
// 使用静态方法创建实例
|
|
29
|
+
const instance1 = JoliboxSDK.getInstance();
|
|
30
|
+
|
|
31
|
+
// 再次使用静态方法获取实例
|
|
32
|
+
const instance2 = JoliboxSDK.getInstance();
|
|
33
|
+
|
|
34
|
+
// 验证两个实例是同一个对象
|
|
35
|
+
expect(instance1).toBe(instance2);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should return the same instance when mixing constructor and getInstance', () => {
|
|
39
|
+
// 使用构造函数创建实例
|
|
40
|
+
const instance1 = new JoliboxSDK();
|
|
41
|
+
|
|
42
|
+
// 使用静态方法获取实例
|
|
43
|
+
const instance2 = JoliboxSDK.getInstance();
|
|
44
|
+
|
|
45
|
+
// 验证两个实例是同一个对象
|
|
46
|
+
expect(instance1).toBe(instance2);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should initialize window.joliboxsdk with SDK instances', () => {
|
|
50
|
+
// 创建实例
|
|
51
|
+
new JoliboxSDK();
|
|
52
|
+
|
|
53
|
+
// 验证全局对象上的引用
|
|
54
|
+
expect(window.joliboxsdk).toBeDefined();
|
|
55
|
+
expect(window.joliboxsdk.runtime).toBeDefined();
|
|
56
|
+
expect(window.joliboxsdk.ads).toBeDefined();
|
|
57
|
+
expect(window.joliboxsdk.lifecycle).toBeDefined();
|
|
58
|
+
expect(window.joliboxsdk.storage).toBeDefined();
|
|
59
|
+
expect(window.joliboxsdk.keyboard).toBeDefined();
|
|
60
|
+
expect(window.joliboxsdk.task).toBeDefined();
|
|
61
|
+
expect(window.joliboxsdk.router).toBeDefined();
|
|
62
|
+
});
|
|
63
|
+
});
|
package/src/api/index.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { hostEmitter } from '@jolibox/common';
|
|
2
|
+
import { HostEventParamsMap } from '@jolibox/types';
|
|
3
|
+
|
|
4
|
+
// Define the custom events we want to support
|
|
5
|
+
type CustomEventType = 'onI18nChanged';
|
|
6
|
+
|
|
7
|
+
export const onCustomEvent = <T extends CustomEventType>(event: T, callback: HostEventParamsMap[T]) => {
|
|
8
|
+
hostEmitter.on(event, callback);
|
|
9
|
+
};
|
package/src/events.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hostEmitter, createCommands, BaseError
|
|
1
|
+
import { hostEmitter, createCommands, BaseError } from '@jolibox/common';
|
|
2
2
|
import { trackError } from './utils/event-tracker';
|
|
3
3
|
document.addEventListener('DOMContentLoaded', () => hostEmitter.emit('onDocumentReady', timeline.startTime));
|
|
4
4
|
|
|
@@ -19,24 +19,46 @@ export const timeline = timelineFactory();
|
|
|
19
19
|
const commands = createCommands();
|
|
20
20
|
|
|
21
21
|
const rejectHandler = (event: PromiseRejectionEvent | ErrorEvent | BaseError) => {
|
|
22
|
+
let errorInfo;
|
|
23
|
+
|
|
24
|
+
if (event instanceof ErrorEvent) {
|
|
25
|
+
errorInfo = {
|
|
26
|
+
message: event.message,
|
|
27
|
+
source: event.filename,
|
|
28
|
+
lineno: event.lineno,
|
|
29
|
+
colno: event.colno,
|
|
30
|
+
error: event.error ? event.error.stack || event.error.toString() : null
|
|
31
|
+
};
|
|
32
|
+
} else if ('reason' in event) {
|
|
33
|
+
// PromiseRejectionEvent
|
|
34
|
+
const reason = event.reason;
|
|
35
|
+
errorInfo = {
|
|
36
|
+
message: reason instanceof Error ? reason.message : String(reason),
|
|
37
|
+
stack: reason instanceof Error ? reason.stack : null,
|
|
38
|
+
type: 'unhandledrejection'
|
|
39
|
+
};
|
|
40
|
+
} else if (event instanceof BaseError) {
|
|
41
|
+
errorInfo = {
|
|
42
|
+
message: event.message,
|
|
43
|
+
name: event.name,
|
|
44
|
+
stack: event.stack
|
|
45
|
+
};
|
|
46
|
+
} else {
|
|
47
|
+
errorInfo = { unknownError: true };
|
|
48
|
+
}
|
|
49
|
+
|
|
22
50
|
commands
|
|
23
51
|
.executeCommand('ReportSDK.traceSystem', {
|
|
24
52
|
event: 'globalJsError',
|
|
25
53
|
info: {
|
|
26
|
-
err: JSON.stringify(
|
|
54
|
+
err: JSON.stringify(errorInfo)
|
|
27
55
|
}
|
|
28
56
|
})
|
|
29
57
|
.catch((e) => {
|
|
30
58
|
// 兜底上报
|
|
31
|
-
console.error(`Fallback report: ${e} ${JSON.stringify(
|
|
59
|
+
console.error(`Fallback report: ${e} ${JSON.stringify(errorInfo)}`);
|
|
32
60
|
trackError({
|
|
33
|
-
err: JSON.stringify(
|
|
34
|
-
message: e.message,
|
|
35
|
-
source: e.source,
|
|
36
|
-
lineno: e.lineno,
|
|
37
|
-
colno: e.colno,
|
|
38
|
-
error: e.error
|
|
39
|
-
})
|
|
61
|
+
err: JSON.stringify(errorInfo)
|
|
40
62
|
});
|
|
41
63
|
});
|
|
42
64
|
};
|
|
@@ -46,29 +68,26 @@ export const reportError = rejectHandler;
|
|
|
46
68
|
window.addEventListener('unhandledrejection', rejectHandler.bind(this));
|
|
47
69
|
window.addEventListener('error', rejectHandler.bind(this));
|
|
48
70
|
window.onerror = (message, source, lineno, colno, error) => {
|
|
71
|
+
const errorInfo = {
|
|
72
|
+
message,
|
|
73
|
+
source,
|
|
74
|
+
lineno,
|
|
75
|
+
colno,
|
|
76
|
+
stack: error?.stack,
|
|
77
|
+
errorString: error?.toString()
|
|
78
|
+
};
|
|
79
|
+
|
|
49
80
|
commands
|
|
50
81
|
.executeCommand('ReportSDK.traceSystem', {
|
|
51
82
|
event: 'globalJsError',
|
|
52
83
|
info: {
|
|
53
|
-
err: JSON.stringify(
|
|
54
|
-
message,
|
|
55
|
-
source,
|
|
56
|
-
lineno,
|
|
57
|
-
colno,
|
|
58
|
-
error
|
|
59
|
-
})
|
|
84
|
+
err: JSON.stringify(errorInfo)
|
|
60
85
|
}
|
|
61
86
|
})
|
|
62
87
|
.catch((e) => {
|
|
63
88
|
// 兜底上报
|
|
64
89
|
trackError({
|
|
65
|
-
err: JSON.stringify(
|
|
66
|
-
message,
|
|
67
|
-
source,
|
|
68
|
-
lineno,
|
|
69
|
-
colno,
|
|
70
|
-
error
|
|
71
|
-
})
|
|
90
|
+
err: JSON.stringify(errorInfo)
|
|
72
91
|
});
|
|
73
92
|
});
|
|
74
93
|
};
|
package/src/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ import './loader';
|
|
|
8
8
|
/**
|
|
9
9
|
* @public Jolibox JS SDK Entry
|
|
10
10
|
*/
|
|
11
|
-
import { getSystemInfoSync, canIUse, login, checkSession, request } from './api';
|
|
11
|
+
import { getSystemInfoSync, canIUse, login, checkSession, request, onCustomEvent } from './api';
|
|
12
12
|
import { RuntimeSDK } from './sdks/runtime';
|
|
13
13
|
import { LifecycleSDK } from './sdks/lifecycle';
|
|
14
14
|
import { StorageSDK } from './sdks/storage';
|
|
@@ -33,6 +33,8 @@ declare global {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export class JoliboxSDK {
|
|
36
|
+
private static instance: JoliboxSDK | null = null;
|
|
37
|
+
|
|
36
38
|
readonly jssdkVersion = '__JOLIBOX_LOCAL_SDK_VERSION__';
|
|
37
39
|
readonly runtime = new RuntimeSDK();
|
|
38
40
|
readonly ads = new JoliboxAds();
|
|
@@ -48,8 +50,13 @@ export class JoliboxSDK {
|
|
|
48
50
|
login = login.bind(this);
|
|
49
51
|
checkSession = checkSession.bind(this);
|
|
50
52
|
request = request.bind(this);
|
|
53
|
+
on = onCustomEvent.bind(this);
|
|
51
54
|
|
|
52
55
|
constructor() {
|
|
56
|
+
if (JoliboxSDK.instance) {
|
|
57
|
+
return JoliboxSDK.instance;
|
|
58
|
+
}
|
|
59
|
+
|
|
53
60
|
window.joliboxsdk = {
|
|
54
61
|
runtime: this.runtime,
|
|
55
62
|
ads: this.ads,
|
|
@@ -59,6 +66,17 @@ export class JoliboxSDK {
|
|
|
59
66
|
task: this.task,
|
|
60
67
|
router: this.router
|
|
61
68
|
};
|
|
69
|
+
|
|
70
|
+
JoliboxSDK.instance = this;
|
|
71
|
+
console.log(`JoliboxSDK ${this.jssdkVersion} init`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 添加静态方法获取实例
|
|
75
|
+
public static getInstance(): JoliboxSDK {
|
|
76
|
+
if (!JoliboxSDK.instance) {
|
|
77
|
+
JoliboxSDK.instance = new JoliboxSDK();
|
|
78
|
+
}
|
|
79
|
+
return JoliboxSDK.instance;
|
|
62
80
|
}
|
|
63
81
|
}
|
|
64
82
|
|
package/src/loader/h5.ts
CHANGED
|
@@ -17,10 +17,7 @@ declare global {
|
|
|
17
17
|
const LOCAL_STORE_KEY = 'jolibox-sdk-loadermeta';
|
|
18
18
|
interface LocalStoreMeta {
|
|
19
19
|
bootstrap_version?: string;
|
|
20
|
-
bootstrap_script?: string;
|
|
21
|
-
|
|
22
20
|
implement_version?: string;
|
|
23
|
-
implement_script?: string;
|
|
24
21
|
timestamp?: number;
|
|
25
22
|
}
|
|
26
23
|
|
|
@@ -32,11 +29,10 @@ const now = Date.now();
|
|
|
32
29
|
const expired = (now: number, prev: number) =>
|
|
33
30
|
now > prev && Math.abs(now - prev) / (1000 * 60 * 60 * 24) >= 7;
|
|
34
31
|
|
|
35
|
-
const isLegalScript = (str: string) =>
|
|
36
|
-
!/^(Failed to fetch version|Couldn't find the requested file)/.test(str);
|
|
37
32
|
async function fetchCurrentRemoteScript() {
|
|
38
33
|
const host = testMode ? 'https://stg-api.jolibox.com' : 'https://api.jolibox.com';
|
|
39
34
|
const path = `${host}/api/fe-configs/js-sdk/version-metadata`;
|
|
35
|
+
|
|
40
36
|
try {
|
|
41
37
|
const response = await fetch(path, {
|
|
42
38
|
method: 'GET',
|
|
@@ -51,13 +47,10 @@ async function fetchCurrentRemoteScript() {
|
|
|
51
47
|
if (currentLocalBootstrapVersion && compare(currentLocalBootstrapVersion, bootstrap_version) >= 0) {
|
|
52
48
|
// no-op
|
|
53
49
|
} else {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
bootstrap_version,
|
|
59
|
-
bootstrap_script: currentBootstrapModuleStr
|
|
60
|
-
});
|
|
50
|
+
currentStore = {
|
|
51
|
+
...currentStore,
|
|
52
|
+
bootstrap_version
|
|
53
|
+
};
|
|
61
54
|
}
|
|
62
55
|
}
|
|
63
56
|
if (implement_version) {
|
|
@@ -65,15 +58,10 @@ async function fetchCurrentRemoteScript() {
|
|
|
65
58
|
if (currentLocalImpelementVersion && compare(currentLocalImpelementVersion, implement_version) >= 0) {
|
|
66
59
|
// no-op
|
|
67
60
|
} else {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
(currentStore = {
|
|
73
|
-
...currentStore,
|
|
74
|
-
implement_version,
|
|
75
|
-
implement_script: currentImplementModuleStr
|
|
76
|
-
});
|
|
61
|
+
currentStore = {
|
|
62
|
+
...currentStore,
|
|
63
|
+
implement_version
|
|
64
|
+
};
|
|
77
65
|
}
|
|
78
66
|
}
|
|
79
67
|
|
|
@@ -89,12 +77,15 @@ export const createLoadImplement = (params: {
|
|
|
89
77
|
bootstrapUrl: string;
|
|
90
78
|
implementUrl: string;
|
|
91
79
|
}) => {
|
|
92
|
-
const { currentMajorVersion, currentVersion
|
|
80
|
+
const { currentMajorVersion, currentVersion } = params;
|
|
81
|
+
|
|
82
|
+
let bootstrapUrl = params.bootstrapUrl;
|
|
83
|
+
let implementUrl = params.implementUrl;
|
|
93
84
|
function fetchCurrentBootstrapModule():
|
|
94
85
|
| { script: string; type: 'localscript' | 'fetch_from_cdn' }
|
|
95
86
|
| undefined {
|
|
96
|
-
const { bootstrap_version,
|
|
97
|
-
if (bootstrap_version &&
|
|
87
|
+
const { bootstrap_version, timestamp } = CURRENT_VERSION_STORE;
|
|
88
|
+
if (bootstrap_version && timestamp) {
|
|
98
89
|
/**
|
|
99
90
|
* if current SDK major version is same && remote version is larget than current, use latest version
|
|
100
91
|
*/
|
|
@@ -103,7 +94,7 @@ export const createLoadImplement = (params: {
|
|
|
103
94
|
major(bootstrap_version) == currentMajorVersion &&
|
|
104
95
|
compare(bootstrap_version, currentVersion) >= 0
|
|
105
96
|
) {
|
|
106
|
-
|
|
97
|
+
bootstrapUrl = getBoostrapModuleUrl(bootstrap_version);
|
|
107
98
|
}
|
|
108
99
|
}
|
|
109
100
|
try {
|
|
@@ -136,31 +127,11 @@ export const createLoadImplement = (params: {
|
|
|
136
127
|
}
|
|
137
128
|
}
|
|
138
129
|
|
|
139
|
-
function evalLocalJSModule(code: string) {
|
|
140
|
-
const evalJS = async (code: string) => {
|
|
141
|
-
try {
|
|
142
|
-
const blob = new Blob([code], { type: 'application/javascript' });
|
|
143
|
-
const blobUrl = URL.createObjectURL(blob);
|
|
144
|
-
const module = await import(blobUrl);
|
|
145
|
-
URL.revokeObjectURL(blobUrl);
|
|
146
|
-
return module;
|
|
147
|
-
} catch (error) {
|
|
148
|
-
const internalError = new InternalJSModuleEvalError(
|
|
149
|
-
`implement module evaluate error in h5:${error} `
|
|
150
|
-
);
|
|
151
|
-
trackError({
|
|
152
|
-
err: `${internalError}`
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
evalJS(code);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
130
|
// load remote impletation
|
|
160
131
|
function loadRemoteImplement() {
|
|
161
132
|
const implStartLoad = Date.now();
|
|
162
|
-
const {
|
|
163
|
-
if (
|
|
133
|
+
const { implement_version, timestamp } = CURRENT_VERSION_STORE;
|
|
134
|
+
if (implement_version && timestamp) {
|
|
164
135
|
/**
|
|
165
136
|
* if current SDK major version is same && remote version is larget than current, use latest version
|
|
166
137
|
*/
|
|
@@ -169,9 +140,7 @@ export const createLoadImplement = (params: {
|
|
|
169
140
|
major(implement_version) == currentMajorVersion &&
|
|
170
141
|
compare(implement_version, currentVersion) >= 0
|
|
171
142
|
) {
|
|
172
|
-
|
|
173
|
-
trackPerformance('implementModuleLoaded', Date.now() - implStartLoad, { type: 'loadscript' });
|
|
174
|
-
return;
|
|
143
|
+
implementUrl = getImplementModuleUrl(implement_version);
|
|
175
144
|
}
|
|
176
145
|
}
|
|
177
146
|
const asyncScript = document.createElement('script');
|