Files

4.8 KiB
Raw Permalink Blame History

用 Python、JavaScript 或 Go 转换 Markdown

复制 external-lib 项目 后,就能检查源码并运行 fake 测试。无需安装 Python/Node/Go 或下载依赖。

项目用现成 Markdown 库转换内容,App 只调用 Port。生成的 HTML 没有做安全清洗;展示不可信输入前,需要按产品要求清洗。外部语言只承接需要复用的能力,不转移普通业务逻辑。

先读 module/document/markup/app.dever、port.dever、adapter.dever 和两个测试。Port 的成功输出必须带名字 html,失败合同来自公开 App 的 RenderResult;同一份 Dever 声明供三种实现复用。

默认选择 pip。切换生态时只替换 adapter.dever 中的 external 声明、同一文件的 lib 值及 config/setting.json 的根 lib 数组;不要同时复制三个 Adapter 到同一角色,也不要改 Port/App/Test:

生态 external 声明 lib 声明 setting.json 根 lib 中的值 已附实现
Python external pip "python/render.py" lib "markdown@3.8.2" pip:markdown@3.8.2 module/document/markup/python/render.py
JavaScript external npm "javascript/render.mjs" lib "marked@15.0.12" npm:marked@15.0.12 module/document/markup/javascript/render.mjs
Go external go "go/render.go" lib "github.com/yuin/goldmark@v1.7.8" go:github.com/yuin/goldmark@v1.7.8 module/document/markup/go/render.go

项目默认 Python 的完整声明如下,JS/Go 按表替换:

源码:外部 Adapter

external pip "python/render.py" {
  lib "markdown@3.8.2"
}

版本是示例固定输入,不表示“最新版本”;显式依赖准备仍须验证来源、许可证和产品需求。库的 Markdown 方言与尾部换行不同,切换生态不保证输出逐字节等价,真实输出验收需针对所选库进行。普通 fake 用例只验证 App/Port 合同。

外部函数怎么写

Python 导出 async def render(payload, setting);JS 导出 async function render(payload, setting, signal);Go 导出 func Render(context.Context, any, any) (any, error),使用 package main,但不手写 main。操作名 render 在 Go 映射为 Render,蛇形名按段转 PascalCase。

编译器从 Port 生成 contract.json,写入参数、返回值、业务错误和接口标识。运行器自动找到同名处理函数,不需要项目复制 SDK、维护注册表、计算 hash 或编写 stdin 协议。

Python 使用 Worker.from_manifest,JS 使用 Worker.fromManifest,Go 由生成的入口调用 ServeManifest。SDK 来自匹配版本的运行资源。

三种实现共用的是 Port 的输入、输出、错误和操作合同。schema 身份还包含所选生态与 Lib 声明,因此切换生态或依赖后会变化,不能复用旧 manifest 或要求三种 hash 相等。

本例输入是 {"source":"# Report"},成功返回 {"html":"..."}。即使只有一个输出,也不能返回裸字符串。

没有 setting 声明时,setting 为 null/nil。需要配置再增加 Adapter setting 类型和对应配置绑定,不能读取环境变量。

业务拒绝使用 SDK 的 BusinessError:本例的 identity 是 document.markup.RenderResult.Rejected,payload 是 {"message":"..."}。未知异常属于运行失败,不自动变成业务拒绝,也不返回假成功。

stdout 专用于协议,日志写 stderr。Python 处理函数要响应取消,JS 使用 signal,Go 使用 context。需要网络、文件、子进程或 GPU 时,通过 allow 声明效果;实际权限继承容器用户,由部署侧管理,不再使用 Dever 内层沙箱或 Adapter 挂载授权。真实文件路径通过业务 setting 传入。只运行可信 Worker,本例纯文档转换不需要这些能力。

准备与测试

dever check <root>
dever test <root> --filter document.markup --timeout-ms 5000

rendered 与 rejected 各自替换 port.render,分别检查成功和业务拒绝。fake 只对当前用例有效,不会影响其他测试。编译器检查参数、返回值、允许的错误和调用能力。普通测试不加载真实库、不启动 Worker,也不要求依赖锁或生产配置。

需要真实运行时,按用户授权准备依赖:

dever lib add <root> pip:markdown@3.8.2
dever run <root> -- document.markup.render '{"source":"# Report"}'

JS/Go 改用表中的依赖声明。已有锁迁移机器时使用 dever lib install <root>。run/build 离线;缺资源就报错,不借用宿主解释器。切换依赖使用 Lib 命令并核对锁,不手工伪造 dever.lock。

静态示例与 fake 检查不证明第三方依赖已下载、真实库输出已验收或跨架构 Worker 已可运行。