Metadata-Version: 2.4
Name: ruyijs
Version: 0.1.2
Summary: Firefox SpiderMonkey JavaScript contexts and native browser APIs for Python
Author: ruyijs contributors
License-Expression: MPL-2.0
Keywords: javascript,spidermonkey,firefox,browser,dom
Classifier: Development Status :: 4 - Beta
Classifier: Operating System :: Microsoft :: Windows :: Windows 10
Classifier: Operating System :: Microsoft :: Windows :: Windows 11
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Dynamic: license-file

# ruyiJS

**基于 Firefox / SpiderMonkey 原生组件的 Python JavaScript 运行库。**
在 Python 中执行 JavaScript，使用 DOM、Navigator、定时器和网络 API，无需启动浏览器窗口。

[![Python](https://img.shields.io/badge/Python-%3E%3D3.10-3776AB?style=flat-square&logo=python&logoColor=white)](https://pypi.org/project/ruyijs/)
[![Platform](https://img.shields.io/badge/platform-Windows%20x64-0078D4?style=flat-square)](https://pypi.org/project/ruyijs/)

每个 `Context` 拥有独立进程和持续保存的 JavaScript 上下文；wheel 自带原生运行时，安装后可离线执行本地脚本。

```python
from ruyijs import Context

with Context() as context:
    context.eval("let count = 40")
    print(context.eval("count += 2"))  # 42
    print(context.eval("navigator.userAgent"))
```

[快速开始](#快速开始) · [常用功能](#常用功能) · [更多用法](#更多用法) · [运行示例](#运行示例)

## 快速开始

当前版本 **0.1.2**，适用于 **Windows 10 / 11 x64**，需要 **64 位 Python 3.10 或更高版本**。已在 Python 3.14.6 下验证安装与运行。

```powershell
python -m pip install ruyijs
python -c "import ruyijs; print(ruyijs.__version__)"
```

wheel 自带原生运行时，安装后运行本地脚本无需再次下载。

## 常用功能

| 功能 | 用法 |
| --- | --- |
| JavaScript 执行 | `Context.eval()`，同一上下文保留变量和函数 |
| Python / JS 数据交换 | `set()`、数组与对象转换、`UNDEFINED` |
| JS 函数调用 | `eval(..., to_py=False)` 返回可调用的 `JSFunction` |
| Python 回调 | `expose()` 将 Python 函数提供给 JavaScript |
| 页面与 DOM | `load_page(url, html)`、DOM 查询、经典脚本执行 |
| 环境配置 | `configure_fingerprint()`，配置 Navigator、Screen、时区等 |
| 时间与调度 | `advance()`、`tick()`、系统时钟与逻辑时钟 |
| 网络与代理 | 页面内 `fetch` / XHR，`Context(proxy=...)` |
| 离线资源 | `put_resource()` 提供脚本或请求响应 |
| 错误与日志 | `JSError`、`console_log()`、`net_log()` |

## 更多用法

### 传入数据、调用 JavaScript 函数

```python
from ruyijs import Context

with Context() as context:
    context.set("config", {"offset": 2})
    add = context.eval("(value) => value + config.offset", to_py=False)
    print(add(40))  # 42
```

`JSFunction` / `JSObject` 引用随所属 `Context` 关闭而失效。需要普通 Python 数据时，可直接让 `eval()` 返回数组、对象或基本值。

### 调用 Python 函数

```python
from ruyijs import Context

with Context() as context:
    context.expose(lambda a, b: a + b, "add")
    print(context.eval("add(20, 22)"))  # 42
```

回调中请使用传入的数据；同一个 `Context` 的嵌套调用会抛出 `ReentrantError`。

### 加载 HTML 与脚本

```python
from ruyijs import Context

with Context() as context:
    context.put_resource("https://example.test/app.js", "window.answer = 42")
    context.load_page("https://example.test/", '''
        <script src="/app.js"></script><p id="message">hello</p>
    ''')
    print(context.eval("document.querySelector('#message').textContent"))  # hello
    print(context.eval("answer"))  # 42
```

`load_page()` 接收页面地址和 HTML 文本；相对资源地址按页面地址解析。上面的资源由 `put_resource()` 提供，整个示例可离线运行。

### 配置浏览器环境

```python
from ruyijs import Context

with Context() as context:
    context.configure_fingerprint({
        "navigator": {"language": "zh-CN", "hardwareConcurrency": 8},
        "screen": {"width": 1920, "height": 1080},
        "viewport": {"width": 1280, "height": 720},
        "locale": {"timezone": "Asia/Shanghai"},
    })
    print(context.eval("[navigator.language, screen.width]"))  # ['zh-CN', 1920]
```

在首次执行页面脚本或加载 HTML 前提交配置。`configure_fingerprint()` 与 `configure_environment()` 使用同一套字段校验。

### 执行定时任务

```python
from ruyijs import Context

with Context() as context:
    context.configure_clock("logical", epoch_ms=1700000000000)
    context.eval("globalThis.result = 0; setTimeout(() => { result = 42 }, 10)")
    context.advance(10)
    print(context.eval("result"))  # 42
```

定时器由宿主显式推进。逻辑时钟直接前进；默认系统时钟下，`advance(10)` 会实际等待相应时间。

### 使用 fetch 与代理

```python
from time import monotonic
from ruyijs import Context

with Context(proxy="http://127.0.0.1:7890", timeout=30) as context:
    context.load_page("https://example.com/", "")
    context.eval('''
        globalThis.reply = null;
        fetch('/').then(async r => ({status: r.status, text: await r.text()}))
          .then(value => { reply = value })
          .catch(error => { reply = {error: String(error)} });
        undefined
    ''')
    deadline = monotonic() + 30
    while context.eval("reply") is None:
        if monotonic() >= deadline:
            raise TimeoutError("等待 fetch 超时")
        context.advance(25)
    print(context.eval("reply"))
```

将代理地址替换为实际使用的 HTTP(S) 代理；传入 `proxy=None` 使用直连。代理仅作用于当前上下文。页面请求需要网络连接，可用 `net_log()` 查看请求记录。

### 处理 JavaScript 错误

```python
from ruyijs import Context, JSError

with Context(timeout=10) as context:
    try:
        context.eval("throw new TypeError('bad input')", filename="demo.js")
    except JSError as error:
        print(error.name, error.message)  # TypeError bad input
        print(error.stack)
```

`timeout` 限制启动及每次请求的等待时间；超时会关闭该上下文，后续工作需新建 `Context`。

## 运行示例

示例随包安装，无需下载仓库：

```powershell
python -m ruyijs.examples                      # 列出全部示例及其安装路径
python -m ruyijs.examples google --query python --pages 1
python -m ruyijs.examples boss --query ai --city 101210100
python -m ruyijs.examples fangdi               # 网上房地产（瑞数 6）
python -m ruyijs.examples cebwm --pages 3      # 光大理财（瑞数定制）
python -m ruyijs.examples toutiao --pages 3    # 今日头条
python -m ruyijs.examples geetest              # 极验一键通过
python -m ruyijs.examples yidun                # 网易易盾
python -m ruyijs.examples shumei               # 数美滑块
python -m ruyijs.examples tianyu               # 腾讯天御
```

示例均支持 `--direct`（直连）和 `--proxy http://127.0.0.1:7890`，在名称后加 `--help` 查看全部参数，`--output result.json` 可保存结果。各示例目录内附 README。线上结果受网络与站点当前响应影响。

## 使用范围与反馈

本版本提供 Windows x64 上的 JavaScript 执行及部分 Web API 环境，没有浏览器窗口和完整页面布局、渲染能力。

相关项目：[ruyiHttp](https://gitee.com/losenine888/ruyiHttp)，基于 Firefox 原生网络组件的 Requests 风格 Python 请求库。
