Skip to content

Commit 9d2928c

Browse files
authored
Merge pull request #150 from budou0608/main
新增PageSpy 的调试 SDK 用于 Lynx 应用
2 parents b9d1d61 + 2924360 commit 9d2928c

33 files changed

Lines changed: 4759 additions & 0 deletions

packages/page-spy-lynx/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2022-present Blucas
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

packages/page-spy-lynx/README.md

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
[npm-image]: https://img.shields.io/npm/v/@huolala-tech/page-spy-lynx?logo=npm&label=version
2+
[npm-url]: https://www.npmjs.com/package/@huolala-tech/page-spy-lynx
3+
[minified-image]: https://img.shields.io/bundlephobia/min/@huolala-tech/page-spy-lynx
4+
[minified-url]: https://unpkg.com/browse/@huolala-tech/page-spy-lynx/dist/esm/index.min.js
5+
6+
[English](./README.md) | [Chinese](./README_ZH.md)
7+
8+
# `@huolala-tech/page-spy-lynx`
9+
10+
[![SDK version][npm-image]][npm-url]
11+
[![SDK size][minified-image]][minified-url]
12+
13+
> PageSpy SDK for Lynx apps.
14+
15+
Lynx does not provide browser `window` or `document` objects. This SDK does not depend on DOM APIs, and its built-in plugins enable themselves only when the Lynx runtime exposes the corresponding global capability.
16+
17+
## Usage
18+
19+
Initialize PageSpy from background-only code, such as an entry module imported only by background logic or inside a Lynx `useEffect`.
20+
21+
```ts
22+
import PageSpy from '@huolala-tech/page-spy-lynx';
23+
24+
const pageSpy = new PageSpy({
25+
api: 'example.com',
26+
});
27+
```
28+
29+
## `InitConfig`
30+
31+
The `api` option is required. Other options follow the shared PageSpy SDK config.
32+
33+
```ts
34+
const pageSpy = new PageSpy(config);
35+
36+
interface InitConfig {
37+
api: string;
38+
project?: string;
39+
title?: string;
40+
enableSSL?: boolean | null;
41+
disabledPlugins?: (InternalPlugins | string)[];
42+
}
43+
44+
type InternalPlugins =
45+
| 'ConsolePlugin'
46+
| 'ErrorPlugin'
47+
| 'NetworkPlugin'
48+
| 'SystemPlugin'
49+
| 'WebSocketPlugin';
50+
```
51+
52+
## Built-in plugins
53+
54+
- `ConsolePlugin`: proxies `console.log/info/error/warn/debug`.
55+
- `SystemPlugin`: reports PageSpy client and available Lynx system information.
56+
- `NetworkPlugin`: proxies `fetch` first, and proxies `XMLHttpRequest` only when the runtime exposes a compatible implementation.
57+
- `WebSocketPlugin`: proxies WebSocket traffic only when `globalThis.WebSocket` is constructable.
58+
- `ErrorPlugin`: listens to global error and unhandled rejection hooks when the runtime exposes them.
59+
60+
Use `disabledPlugins` to turn off any built-in plugin.
61+
62+
## Native WebSocket module
63+
64+
PageSpy prefers Lynx `NativeModules.LynxNativeWebSocketModule` for its own WebSocket connection. Host apps should register this native module before initializing PageSpy in real Lynx native runtimes. In web preview runtimes, the SDK falls back to a constructable `globalThis.WebSocket`; if neither capability exists, initialization throws a clear error.
65+
66+
The JavaScript contract is:
67+
68+
```ts
69+
declare let NativeModules: {
70+
LynxNativeWebSocketModule: {
71+
connect(socketId: string, url: string): void;
72+
send(socketId: string, data: string): void;
73+
close(socketId: string): void;
74+
drainEvents(
75+
socketId: string,
76+
callback: (events: NativeWebSocketEvent[]) => void,
77+
): void;
78+
};
79+
};
80+
81+
type NativeWebSocketEvent =
82+
| { type: 'open'; socketId: string }
83+
| { type: 'message'; socketId: string; data: string }
84+
| { type: 'close'; socketId: string; code?: number; reason?: string }
85+
| { type: 'error'; socketId: string; message?: string };
86+
```
87+
88+
Native implementations should enqueue `open/message/close/error` events and return them through `drainEvents`. Do not depend on invoking the same NativeModule callback multiple times.
89+
90+
Native examples are included in `native-examples/android`, `native-examples/ios`, and `native-examples/harmony`.
91+
92+
Android registration:
93+
94+
```java
95+
LynxEnv.inst().registerModule(
96+
"LynxNativeWebSocketModule",
97+
LynxNativeWebSocketModule.class
98+
);
99+
```
100+
101+
The Android example uses OkHttp's `WebSocketListener`; add OkHttp to the host app or adapt the sample to the app's existing WebSocket client.
102+
103+
iOS registration:
104+
105+
```objc
106+
[globalConfig registerModule:LynxNativeWebSocketModule.class];
107+
```
108+
109+
Harmony registration:
110+
111+
```ts
112+
import { LynxNativeWebSocketModule } from './module/LynxNativeWebSocketModule';
113+
114+
this.modules.set('LynxNativeWebSocketModule', {
115+
moduleClass: LynxNativeWebSocketModule,
116+
});
117+
```
118+
119+
The Harmony example uses NetworkKit's WebSocket API. If your Harmony SDK uses a different WebSocket import path, adapt only the import and keep the NativeModule contract unchanged.
120+
121+
`WebSocketPlugin` is separate from this module. It still proxies app WebSocket traffic only when the Lynx runtime exposes a constructable `globalThis.WebSocket`.
122+
123+
## Native Console module
124+
125+
In Lynx native runtimes, `console` messages are routed through the Lynx DevTool inspector pipeline and cannot be reliably intercepted by overwriting `console.log` in JavaScript. PageSpy automatically detects `NativeModules.PageSpyConsoleModule` and polls `drainMessages` for JSON strings received by `LynxInspectorConsoleDelegate.onConsoleMessage(msg)`.
126+
127+
The JavaScript contract is:
128+
129+
```ts
130+
declare let NativeModules: {
131+
PageSpyConsoleModule: {
132+
drainMessages(
133+
callback: (messages: string[]) => void,
134+
): void;
135+
};
136+
};
137+
```
138+
139+
Native examples are included in `native-examples/android`, `native-examples/ios`, and `native-examples/harmony`:
140+
141+
- `PageSpyConsoleModule`: stores console messages and returns them to JS via `drainMessages`.
142+
- `PageSpyConsoleDelegate`: implements `LynxInspectorConsoleDelegate` and queues messages from `onConsoleMessage(msg)`.
143+
144+
Android registration and setup:
145+
146+
```java
147+
LynxEnv.inst().registerModule(
148+
"PageSpyConsoleModule",
149+
PageSpyConsoleModule.class
150+
);
151+
152+
LynxBaseInspectorOwner owner = lynxView.getBaseInspectorOwner();
153+
if (owner != null) {
154+
owner.setLynxInspectorConsoleDelegate(new PageSpyConsoleDelegate());
155+
}
156+
```
157+
158+
iOS registration and setup:
159+
160+
```objc
161+
[globalConfig registerModule:PageSpyConsoleModule.class];
162+
163+
id<LynxBaseInspectorOwner> owner = lynxView.baseInspectorOwner;
164+
if (owner) {
165+
[PageSpyConsoleDelegate installWithInspectorOwner:owner];
166+
}
167+
```
168+
169+
Harmony registration and setup:
170+
171+
```ts
172+
import { LynxView, LynxContext } from '@lynx/lynx';
173+
import { PageSpyConsoleModule } from './module/PageSpyConsoleModule';
174+
import { PageSpyConsoleDelegate } from './module/PageSpyConsoleDelegate';
175+
176+
this.modules.set('PageSpyConsoleModule', {
177+
moduleClass: PageSpyConsoleModule,
178+
});
179+
180+
LynxView({
181+
onCreate: (context: LynxContext) => {
182+
const owner = context.getBaseInspectorOwner();
183+
if (owner) {
184+
PageSpyConsoleDelegate.installWithInspectorOwner(owner);
185+
}
186+
},
187+
});
188+
```
189+
190+
Without this delegate integration, PageSpy still works in Web or JS-overwritable console environments, but iOS/Android/Harmony native console messages may not be captured because they stay inside Lynx's native inspector pipeline.
191+
192+
Troubleshooting: if iOS logs only show `LynxNativeWebSocketModule.drainEvents` and never show `PageSpyConsoleModule.drainMessages`, the current LynxView does not expose `NativeModules.PageSpyConsoleModule`. Registering only the WebSocket module makes the PageSpy connection work, but it does not capture native console messages.

0 commit comments

Comments
 (0)