@jolibox/sdk 1.1.12 → 1.1.13-beta.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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ type StorageIsolationOptions = {
2
+ namespace?: string;
3
+ debug?: boolean;
4
+ };
5
+ declare global {
6
+ interface Window {
7
+ __joliboxLocalStorage__: typeof window.localStorage;
8
+ }
9
+ }
10
+ export declare function createStorageIsolation(options?: StorageIsolationOptions): {
11
+ isolateFrame: (frame: HTMLIFrameElement) => void;
12
+ isolateWindowStorage: () => void;
13
+ };
14
+ export {};
@@ -0,0 +1 @@
1
+ export declare function getOriginalLocalStorage(): Storage;
@@ -9,6 +9,7 @@ export declare class LifecycleSDK extends BaseSDK<LifecycleSDKEventMap> implemen
9
9
  onReady(callback: (info?: Env['hostUserInfo']) => void): void;
10
10
  exit(params: {
11
11
  onBeforeExit: () => void;
12
+ shouldStay?: boolean;
12
13
  }): {
13
14
  code: import("@jolibox/types").ResponseType;
14
15
  message: string;
@@ -0,0 +1,15 @@
1
+ import { BaseSDK } from './sdk';
2
+ import { Router, ResponseType, StandardResponse } from '@jolibox/types';
3
+ export declare class RouterSDK extends BaseSDK implements Router {
4
+ openSchema(schema: string): Promise<{
5
+ code: ResponseType;
6
+ message: string;
7
+ }>;
8
+ openPage(url: string): Promise<StandardResponse<{
9
+ webviewId: number;
10
+ }>>;
11
+ closePage(webviewId: number): Promise<{
12
+ code: ResponseType;
13
+ message: string;
14
+ }>;
15
+ }
package/esbuild.config.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const esbuild = require('esbuild');
2
2
  const fs = require('fs');
3
+ const babel = require('@babel/core');
3
4
 
4
5
  function versionPlugin(version) {
5
6
  return {
@@ -52,9 +53,34 @@ function build(format) {
52
53
  '.js': 'jsx'
53
54
  },
54
55
  plugins: [versionPlugin(process.env.BUILD_VERSION || '1.0.0')],
56
+ inject: ['../../polyfills/abort-controller-polyfill.js'],
55
57
  });
56
58
  }
57
59
 
60
+ // Step 1: Bundle with esbuild
61
+ esbuild.buildSync({
62
+ entryPoints: ['src/index.js'],
63
+ bundle: true,
64
+ outfile: 'dist/temp.js',
65
+ format: 'iife',
66
+ });
67
+
68
+ // Step 2: Transform with Babel
69
+ const code = fs.readFileSync('dist/temp.js', 'utf8');
70
+ const result = babel.transformSync(code, {
71
+ presets: [
72
+ ['@babel/preset-env', {
73
+ targets: {
74
+ safari: '14'
75
+ }
76
+ }]
77
+ ]
78
+ });
79
+
80
+ // Write the final output
81
+ fs.writeFileSync('dist/index.js', result.code);
82
+ fs.unlinkSync('dist/temp.js'); // Clean up temp file
83
+
58
84
  (async ()=>{
59
85
  try{
60
86
  await build(options.format);
package/package.json CHANGED
@@ -1,21 +1,24 @@
1
1
  {
2
2
  "name": "@jolibox/sdk",
3
3
  "description": "This project is common Jolibox JS-SDk interfere",
4
- "version": "1.1.12",
4
+ "version": "1.1.13-beta.1",
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.12",
11
- "@jolibox/types": "1.1.12"
10
+ "@jolibox/common": "1.1.13-beta.1",
11
+ "@jolibox/types": "1.1.13-beta.1"
12
12
  },
13
13
  "devDependencies": {
14
14
  "typescript": "5.7.3",
15
15
  "@jolibox/eslint-config": "1.0.0",
16
16
  "@types/jest": "28.1.1",
17
17
  "rimraf": "6.0.1",
18
- "esbuild": "0.24.2"
18
+ "esbuild": "0.24.2",
19
+ "@babel/core": "7.23.3",
20
+ "@babel/preset-env": "7.23.3",
21
+ "@babel/preset-typescript": "7.23.3"
19
22
  },
20
23
  "scripts": {
21
24
  "clean": "rimraf ./dist",
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.12 clean
3
+ > @jolibox/sdk@1.1.13-beta.1 clean
4
4
  > rimraf ./dist
5
5
 
6
6
 
7
- > @jolibox/sdk@1.1.12 build:cjs
7
+ > @jolibox/sdk@1.1.13-beta.1 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.12 build:esm
11
+ > @jolibox/sdk@1.1.13-beta.1 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.12 build:iife
15
+ > @jolibox/sdk@1.1.13-beta.1 build:iife
16
16
  > BUILD_VERSION=$(node -p "require('./package.json').version") node esbuild.config.js --format=iife
17
17
 
package/src/api/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './get-system-info';
2
2
  export * from './can-i-use';
3
3
  export * from './login';
4
+ export * from './request';
@@ -0,0 +1,15 @@
1
+ import { RequestParams } from '@jolibox/types';
2
+ import { canIUse } from './can-i-use';
3
+ import { createCommands } from '@jolibox/common';
4
+
5
+ const commands = createCommands();
6
+
7
+ export async function request(params: RequestParams) {
8
+ if (!canIUse('request')) {
9
+ return {
10
+ code: 'FAILURE' as ResponseType,
11
+ message: '[Jolibox SDK]request is not supported in this platform'
12
+ };
13
+ }
14
+ return await commands.executeCommand('HttpSDK.request', params);
15
+ }
package/src/index.ts CHANGED
@@ -8,13 +8,14 @@ import './loader';
8
8
  /**
9
9
  * @public Jolibox JS SDK Entry
10
10
  */
11
- import { getSystemInfoSync, canIUse, login, checkSession } from './api';
11
+ import { getSystemInfoSync, canIUse, login, checkSession, request } 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
17
  import { TaskTrackerSDK } from './sdks/task';
18
+ import { RouterSDK } from './sdks/router';
18
19
 
19
20
  declare global {
20
21
  interface Window {
@@ -26,6 +27,7 @@ declare global {
26
27
  storage: InstanceType<typeof StorageSDK>;
27
28
  keyboard: InstanceType<typeof KeyboardSDK>;
28
29
  task: InstanceType<typeof TaskTrackerSDK>;
30
+ router: InstanceType<typeof RouterSDK>;
29
31
  };
30
32
  }
31
33
  }
@@ -38,12 +40,14 @@ export class JoliboxSDK {
38
40
  readonly storage = new StorageSDK();
39
41
  readonly keyboard = new KeyboardSDK();
40
42
  readonly task = new TaskTrackerSDK();
43
+ readonly router = new RouterSDK();
41
44
 
42
45
  //global API
43
46
  getSystemInfoSync = getSystemInfoSync.bind(this);
44
47
  canIUse = canIUse.bind(this);
45
48
  login = login.bind(this);
46
49
  checkSession = checkSession.bind(this);
50
+ request = request.bind(this);
47
51
 
48
52
  constructor() {
49
53
  window.joliboxsdk = {
@@ -52,7 +56,8 @@ export class JoliboxSDK {
52
56
  lifecycle: this.lifecycle,
53
57
  storage: this.storage,
54
58
  keyboard: this.keyboard,
55
- task: this.task
59
+ task: this.task,
60
+ router: this.router
56
61
  };
57
62
  }
58
63
  }
@@ -0,0 +1,36 @@
1
+ import { createStorageIsolation } from './localstorage-protector';
2
+ import { getOriginalLocalStorage } from '../local-storage';
3
+
4
+ createStorageIsolation({
5
+ namespace: 'jolibox-sdk-test'
6
+ });
7
+
8
+ describe('getOriginalLocalStorage', () => {
9
+ it('should return localStorage object', () => {
10
+ const originalStorage = getOriginalLocalStorage();
11
+
12
+ expect(originalStorage).toBeDefined();
13
+ expect(typeof originalStorage.getItem).toBe('function');
14
+ expect(typeof originalStorage.setItem).toBe('function');
15
+ expect(typeof originalStorage.removeItem).toBe('function');
16
+ });
17
+
18
+ it('should be able to store and get data', () => {
19
+ const originalStorage = getOriginalLocalStorage();
20
+ const testKey = '__test_key__';
21
+ const testValue = 'test-value';
22
+
23
+ originalStorage.removeItem(testKey);
24
+
25
+ originalStorage.setItem(testKey, testValue);
26
+
27
+ // 验证数据是否正确存储
28
+ expect(originalStorage.getItem(testKey)).toBe(testValue);
29
+ expect(originalStorage.getItem('jolibox-sdk-test:__test_key__')).toBeNull();
30
+
31
+ // 清理测试数据
32
+ originalStorage.removeItem(testKey);
33
+ expect(originalStorage.getItem(testKey)).toBeNull();
34
+ expect(originalStorage.getItem('jolibox-sdk-test:__test_key__')).toBeNull();
35
+ });
36
+ });
@@ -0,0 +1,191 @@
1
+ type StorageIsolationOptions = {
2
+ namespace?: string;
3
+ debug?: boolean;
4
+ };
5
+
6
+ declare global {
7
+ interface Window {
8
+ __joliboxLocalStorage__: typeof window.localStorage;
9
+ }
10
+ }
11
+
12
+ export function createStorageIsolation(options: StorageIsolationOptions = {}) {
13
+ const namespace = options.namespace || getJoliMpId();
14
+ const debug = options.debug || true;
15
+ const observedFrames = new WeakSet<HTMLIFrameElement>();
16
+
17
+ function getJoliMpId() {
18
+ const urlParams = new URLSearchParams(window.location.search);
19
+ return urlParams.get('mpId') ?? urlParams.get('appId') ?? urlParams.get('gameId') ?? '';
20
+ }
21
+
22
+ function _log(message: string) {
23
+ if (debug) {
24
+ console.log(`[StorageIsolation] ${message}`);
25
+ }
26
+ }
27
+
28
+ function createIsolatedStorage() {
29
+ const originalStorage = window.localStorage;
30
+
31
+ return {
32
+ setItem(key: string, value: string) {
33
+ const namespacedKey = key.startsWith(`${namespace}:`) ? key : `${namespace}:${key}`;
34
+ originalStorage.setItem(namespacedKey, value);
35
+ },
36
+ getItem(key: string) {
37
+ const namespacedKey = key.startsWith(`${namespace}:`) ? key : `${namespace}:${key}`;
38
+ let value = originalStorage.getItem(namespacedKey);
39
+ if (value === null) {
40
+ // 如果命名空间不存在,则尝试直接获取原始键
41
+ const originalKey = key.startsWith(`${namespace}:`) ? key.slice(namespace.length + 1) : key;
42
+ value = originalStorage.getItem(originalKey);
43
+ }
44
+ return value;
45
+ },
46
+ removeItem(key: string) {
47
+ const namespacedKey = key.startsWith(`${namespace}:`) ? key : `${namespace}:${key}`;
48
+ originalStorage.removeItem(namespacedKey);
49
+ },
50
+ clear() {
51
+ const keys = Object.keys(originalStorage);
52
+ for (const key of keys) {
53
+ if (key.startsWith(`${namespace}:`)) {
54
+ originalStorage.removeItem(key);
55
+ }
56
+ }
57
+ },
58
+ key(index: number) {
59
+ const keys = Object.keys(originalStorage)
60
+ .filter((key) => key.startsWith(`${namespace}:`))
61
+ .map((key) => key.slice(namespace.length + 1));
62
+ return keys[index] || null;
63
+ },
64
+ get length() {
65
+ return Object.keys(originalStorage).filter((key) => key.startsWith(`${namespace}:`)).length;
66
+ }
67
+ };
68
+ }
69
+
70
+ function injectIsolatedStorage(
71
+ frameWindow: Window,
72
+ isolatedStorage: ReturnType<typeof createIsolatedStorage>
73
+ ) {
74
+ try {
75
+ const originalStorage = frameWindow.localStorage;
76
+ window.__joliboxLocalStorage__ = originalStorage;
77
+
78
+ type StorageProp = keyof typeof isolatedStorage;
79
+ const storageProxy = new Proxy(originalStorage, {
80
+ get: (target, prop: StorageProp) => {
81
+ if (prop in isolatedStorage) {
82
+ return isolatedStorage[prop];
83
+ }
84
+ if (typeof target[prop] === 'function') {
85
+ return target[prop].bind(target);
86
+ }
87
+ return target[prop];
88
+ },
89
+ set: (target, prop: StorageProp, value) => {
90
+ if (prop === 'length') {
91
+ return true;
92
+ }
93
+ if (prop in isolatedStorage) {
94
+ isolatedStorage[prop] = value;
95
+ return true;
96
+ }
97
+ target[prop] = value;
98
+ return true;
99
+ }
100
+ });
101
+
102
+ Object.defineProperty(frameWindow, 'localStorage', {
103
+ get: () => storageProxy,
104
+ configurable: true
105
+ });
106
+ } catch (e) {
107
+ _log(`Failed to inject storage proxy: ${e}`);
108
+ }
109
+ }
110
+
111
+ function injectIsolation(frame: HTMLIFrameElement) {
112
+ try {
113
+ const frameWindow = frame.contentWindow;
114
+ const frameDocument = frame.contentDocument;
115
+
116
+ if (!frameWindow || !frameDocument) {
117
+ _log('Cannot access frame content - likely due to same-origin policy');
118
+ return;
119
+ }
120
+
121
+ const isolatedStorage = createIsolatedStorage();
122
+ injectIsolatedStorage(frameWindow, isolatedStorage);
123
+ _log(`Successfully isolated storage for frame: ${frame.src}`);
124
+ } catch (e) {
125
+ _log(`Injection failed: ${e}`);
126
+ }
127
+ }
128
+
129
+ function isolateFrame(frame: HTMLIFrameElement) {
130
+ if (observedFrames.has(frame)) {
131
+ return;
132
+ }
133
+
134
+ try {
135
+ frame.addEventListener('load', () => {
136
+ injectIsolation(frame);
137
+ });
138
+ if (frame.contentDocument && frame.contentDocument.readyState === 'complete') {
139
+ injectIsolation(frame);
140
+ }
141
+
142
+ observedFrames.add(frame);
143
+ } catch (e) {
144
+ _log(`Failed to isolate frame: ${e}`);
145
+ }
146
+ }
147
+
148
+ function handleExistingFrames() {
149
+ document.querySelectorAll('iframe').forEach((frame) => {
150
+ injectIsolation(frame);
151
+ });
152
+ }
153
+
154
+ function startObserving() {
155
+ handleExistingFrames();
156
+
157
+ const observer = new MutationObserver((mutations) => {
158
+ mutations.forEach((mutation) => {
159
+ mutation.addedNodes.forEach((node) => {
160
+ if (node instanceof HTMLIFrameElement) {
161
+ isolateFrame(node);
162
+ }
163
+ });
164
+ });
165
+ });
166
+
167
+ observer.observe(document.documentElement, {
168
+ childList: true,
169
+ subtree: true
170
+ });
171
+
172
+ _log('Started observing iframe creation');
173
+ }
174
+
175
+ function isolateWindowStorage() {
176
+ const isolatedStorage = createIsolatedStorage();
177
+ injectIsolatedStorage(window, isolatedStorage);
178
+ }
179
+
180
+ // Initialize
181
+ startObserving();
182
+ isolateWindowStorage();
183
+
184
+ // Return public API if needed
185
+ return {
186
+ isolateFrame,
187
+ isolateWindowStorage
188
+ };
189
+ }
190
+
191
+ createStorageIsolation();
package/src/loader/h5.ts CHANGED
@@ -5,6 +5,7 @@ import { getBoostrapModuleUrl, getImplementModuleUrl } from './index';
5
5
  import { timeline } from '../events';
6
6
  import { testMode } from '@/utils/env';
7
7
  import { trackError } from '@/utils/event-tracker';
8
+ import { getOriginalLocalStorage } from './local-storage';
8
9
 
9
10
  const LOCAL_STORE_KEY = 'jolibox-sdk-loadermeta';
10
11
  interface LocalStoreMeta {
@@ -16,7 +17,11 @@ interface LocalStoreMeta {
16
17
  timestamp?: number;
17
18
  }
18
19
 
19
- const CURRENT_VERSION_STORE: LocalStoreMeta = JSON.parse(localStorage.getItem(LOCAL_STORE_KEY) ?? '{}');
20
+ getOriginalLocalStorage();
21
+
22
+ const CURRENT_VERSION_STORE: LocalStoreMeta = JSON.parse(
23
+ window.__joliboxLocalStorage__.getItem(LOCAL_STORE_KEY) ?? '{}'
24
+ );
20
25
  const now = Date.now();
21
26
 
22
27
  const expired = (now: number, prev: number) =>
@@ -67,7 +72,7 @@ async function fetchCurrentRemoteScript() {
67
72
  }
68
73
  }
69
74
 
70
- localStorage.setItem(LOCAL_STORE_KEY, JSON.stringify(currentStore));
75
+ window.__joliboxLocalStorage__.setItem(LOCAL_STORE_KEY, JSON.stringify(currentStore));
71
76
  } catch (error) {
72
77
  console.warn('Failed to fetch loader metadata: ', error);
73
78
  }
@@ -0,0 +1,33 @@
1
+ export function getOriginalLocalStorage(): Storage {
2
+ try {
3
+ if (window.__joliboxLocalStorage__) {
4
+ return window.__joliboxLocalStorage__;
5
+ }
6
+
7
+ let originalDescriptor = Object.getOwnPropertyDescriptor(window, 'localStorage');
8
+ if (originalDescriptor && originalDescriptor.get) {
9
+ const originalLocalStorage = originalDescriptor.get.call(window);
10
+ window.__joliboxLocalStorage__ = originalLocalStorage;
11
+ return originalLocalStorage;
12
+ }
13
+
14
+ originalDescriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(window), 'localStorage');
15
+ if (originalDescriptor && originalDescriptor.get) {
16
+ const originalLocalStorage = originalDescriptor.get.call(window);
17
+ return originalLocalStorage;
18
+ }
19
+
20
+ const windowProto = Window.prototype;
21
+ const protoDescriptor = Object.getOwnPropertyDescriptor(windowProto, 'localStorage');
22
+ if (protoDescriptor && protoDescriptor.get) {
23
+ const protoLocalStorage = protoDescriptor.get.call(window);
24
+ window.__joliboxLocalStorage__ = protoLocalStorage;
25
+ return protoLocalStorage;
26
+ }
27
+
28
+ return localStorage;
29
+ } catch (e) {
30
+ console.warn('Failed to get original localStorage, fallback to default implementation', e);
31
+ return localStorage;
32
+ }
33
+ }
@@ -19,12 +19,12 @@ export class LifecycleSDK extends BaseSDK<LifecycleSDKEventMap> implements Lifec
19
19
  this.commands.executeCommand('LifecycleSDK.onReady', wrappedOnReady.bind(this));
20
20
  }
21
21
 
22
- exit(params: { onBeforeExit: () => void }) {
22
+ exit(params: { onBeforeExit: () => void; shouldStay?: boolean }) {
23
23
  const errMsg = this.canIUseIfThrow('lifeCycle.exit');
24
24
  if (errMsg) {
25
25
  return errMsg;
26
26
  }
27
- this.commands.executeCommand('LifecycleSDK.exit', params.onBeforeExit);
27
+ this.commands.executeCommand('LifecycleSDK.exit', params.onBeforeExit, params.shouldStay);
28
28
  }
29
29
 
30
30
  onJoliboxHide(params: () => void) {
@@ -0,0 +1,30 @@
1
+ import { BaseSDK } from './sdk';
2
+ import { Router, ResponseType, StandardResponse } from '@jolibox/types';
3
+
4
+ export class RouterSDK extends BaseSDK implements Router {
5
+ async openSchema(schema: string) {
6
+ const errMsg = this.canIUseIfThrow('router.openSchema');
7
+ if (errMsg) {
8
+ return errMsg;
9
+ }
10
+ return await this.commands.executeCommand('RouterSDK.openSchema', schema);
11
+ }
12
+ async openPage(url: string): Promise<
13
+ StandardResponse<{
14
+ webviewId: number;
15
+ }>
16
+ > {
17
+ const errMsg = this.canIUseIfThrow('router.openPage');
18
+ if (errMsg) {
19
+ return errMsg;
20
+ }
21
+ return await this.commands.executeCommand('RouterSDK.openPage', url);
22
+ }
23
+ async closePage(webviewId: number) {
24
+ const errMsg = this.canIUseIfThrow('router.closePage');
25
+ if (errMsg) {
26
+ return errMsg;
27
+ }
28
+ return await this.commands.executeCommand('RouterSDK.closePage', webviewId);
29
+ }
30
+ }