449 lines
26 KiB
Markdown
449 lines
26 KiB
Markdown
# Dever 语言规范
|
||
|
||
Dever 使用静态类型、命名输出和互斥分句。应用由 API、CMD、Job 声明入口,编译为原生程序。
|
||
|
||
- 开始项目:[开发约定](skills/references/development.md)
|
||
- 源码格式:[源码与说明](DOCUMENTATION.md)
|
||
- AI 开发:[随包 Skill](skills/SKILL.md)
|
||
- 安装:[安装指引](skills/references/install.md)
|
||
|
||
本文代码块是语法片段,放入对应角色的 `.dever` 文件。完整项目见 [示例](skills/references/examples.md)。
|
||
|
||
## 项目与命名
|
||
|
||
```text
|
||
config/setting.json
|
||
module/<组件>/<领域>/<角色>.dever
|
||
test/<组件>/<领域>/<主题>.dever
|
||
data/
|
||
```
|
||
|
||
配置来自 `config/setting.json`。run 读取项目配置,独立程序读取可执行文件旁的 `config/setting.json`。
|
||
|
||
| 名称 | 格式 |
|
||
| --- | --- |
|
||
| 组件、领域、主题、字段、局部值 | ASCII lower_snake_case |
|
||
| 函数、SQL 方法、标准库方法 | ASCII lowerCamelCase |
|
||
| 类型、choice 分支 | UpperCamelCase |
|
||
|
||
源码使用 UTF-8 和 `.dever` 后缀,不写分号。说明放领域 README 和 doc,Markdown 不参与编译。词法支持 `#` 行注释,项目源码不写说明注释;`//` 表示整数除法,不支持块注释。
|
||
|
||
| 角色 | 职责与可见性 |
|
||
| --- | --- |
|
||
| app | 公开领域能力;可调用本领域 Domain、Model、Port 和其他领域 App |
|
||
| domain | 本领域业务规则;不能调用其他领域或执行外部 I/O |
|
||
| model | 字段、查询和迁移,只由本领域 App 操作 |
|
||
| api | 绑定本领域 App,或声明自动 REST |
|
||
| port | 外部操作的输入、输出和错误 |
|
||
| adapter | 实现 Port,可调用本文件函数和标准能力 |
|
||
| job | 绑定本领域 App 的持久任务 |
|
||
|
||
应用不声明 package、import、exposes、internal 或 main。App 函数按 `组件.领域.函数` 调用;同组件可省略组件,同领域使用 `app.函数`。跨领域只访问 App,公开参数和输出不能暴露私有类型。领域之间不得循环依赖。
|
||
|
||
角色可使用 `app/publish.dever` 等主题文件,主题名不进入调用名,目录允许一个文件。App/Domain/Port/Adapter/Job 的主文件与目录二选一;Model/API 主文件可与目录共存。只有 API 目录可递归,其路径参与 URL。目录不能命名为 `common/shared/utils/helper/base/service`。
|
||
|
||
## 类型与值
|
||
|
||
| 类型 | 语义 |
|
||
| --- | --- |
|
||
| Bool | true / false |
|
||
| Int | 有符号 64 位整数,检查溢出 |
|
||
| Decimal | decimal128,最多 34 位有效数字,银行家舍入 |
|
||
| Float | IEEE 754 binary64 |
|
||
| Text / Bytes | UTF-8 文本 / 字节序列 |
|
||
| Id / Uuid | 不透明标识 / UUID |
|
||
| DateTime / Date / Time / Duration | UTC 时间点、日期、日内时间、时长;毫秒精度 |
|
||
| Json | 不透明 JSON 值,保留数字文本 |
|
||
| Secret | 仅在规定的输入、密码和摘要操作中使用的敏感值 |
|
||
| T? | T 或 null |
|
||
| List<T> / Map<K,V> | 同类型列表 / 保留插入顺序的映射 |
|
||
|
||
整数字面量默认 Int,小数字面量默认 Decimal;明确 Float 上下文可接收小数。科学计数形式为 Float。Decimal 与 Float 不隐式混合。Text 支持反斜杠、引号、换行、回车、Tab 和 `\u{HEX}` 转义。
|
||
|
||
字段型 type:
|
||
|
||
```dever
|
||
type User {
|
||
name: Text
|
||
phone: Text?
|
||
}
|
||
|
||
rename(user: User, name: Text) (result: User) {
|
||
result = user
|
||
result.name = name
|
||
}
|
||
```
|
||
|
||
构造使用 `User { name = "Lin" phone = null }`,必须一次给全字段。字段型、choice、List、Map 使用值语义,修改局部副本不改变原值。
|
||
|
||
选择型 type:
|
||
|
||
```dever
|
||
type State {
|
||
Draft
|
||
Published
|
||
Rejected(reason: Text)
|
||
}
|
||
```
|
||
|
||
构造为 `State.Published` 或 `State.Rejected("原因")`,载荷按位置传入。
|
||
|
||
List 写为 `[1, 2]`,Map 写为 `{"name" = "Lin"}`。空集合需由上下文确定类型。Map 键允许 Text、Int、Bool、Id 和无载荷 choice。Map 相等比较包含插入顺序;List 按位置比较。Float 的 NaN 与自身不相等。
|
||
|
||
## 函数与分句
|
||
|
||
```dever
|
||
greet(name: Text) (message: Text) {
|
||
message = "Hello, " + name
|
||
}
|
||
```
|
||
|
||
函数格式为 `名称(输入) (命名输出)`,空输入或输出仍写 `()`。调用按位置传参;单输出直接得到值,多输出按名称访问。输出必须在正常结束前赋值。
|
||
|
||
`=` 可初始化或修改当前函数的参数、局部值和输出;变量类型保持不变,读取前须初始化。字段只能在完整值上读取或修改。
|
||
|
||
局部值首次声明可写 `input: CreateInput = dever.json.decode(payload)`,为解码、空集合或 null 提供具体类型;类型省略时仍由表达式推断。后续赋值只写 `input = ...`,不重复声明类型,也不改变原类型。
|
||
|
||
同名、同参数数量的分句须相邻,输出类型和错误声明一致。分句互不重叠并覆盖全部输入,没有先后优先级:
|
||
|
||
```dever
|
||
label(state: State.Published) (text: Text) {
|
||
text = "published"
|
||
}
|
||
|
||
label(state: State.Rejected(reason)) (text: Text) {
|
||
text = reason
|
||
}
|
||
|
||
label(state: other) (text: Text) {
|
||
text = "draft"
|
||
}
|
||
```
|
||
|
||
模式支持类型、choice 分支、null、常量、Int/Decimal 范围和 other。other 是该参数位置未被明确模式覆盖的补集;多个参数按全部组合检查。choice 载荷按位置绑定,不使用的载荷可写 `_`。
|
||
|
||
可空值用 `value: Text` 与 `value: null` 两个分句处理。范围可写 `score: Int >= 60 and < 90`。没有 if/else、switch/match、return、条件表达式、用户递归、lambda、运行时闭包或用户泛型。
|
||
|
||
## 运算与范围
|
||
|
||
| 运算 | 规则 |
|
||
| --- | --- |
|
||
| + - * | 同类数值;Int 可提升为 Decimal;Text 支持 + |
|
||
| / | Int/Int 和 Decimal 运算返回 Decimal;Float/Float 返回 Float |
|
||
| // % | 仅 Int,整数除法向零截断 |
|
||
| == != | 同类型且可比较的值 |
|
||
| < <= > >= | Int、Decimal、Float |
|
||
| and or not | Bool;and/or 短路 |
|
||
|
||
优先级依次为调用/字段、一元运算、乘除取余、加减、比较、and、or。同级从左到右。
|
||
|
||
字段、choice 载荷、函数及 handler 参数都可声明 Int、Decimal 范围:
|
||
|
||
```dever
|
||
type Port {
|
||
value: Int >= 1 and <= 65535
|
||
}
|
||
```
|
||
|
||
构造、赋值和返回都须满足范围。未知 Int 先经范围分句确认;不支持任意用户谓词。静态可知的数值故障在检查时拒绝,运行时故障报告源码位置并终止当前执行。
|
||
|
||
## 集合与 handler
|
||
|
||
使用具名函数处理集合,不提供 for/while、break/continue。
|
||
|
||
| 操作 | 行为 |
|
||
| --- | --- |
|
||
| each(handler, values[, context]) | 零输出时遍历;单输出时生成 List |
|
||
| filter(handler, values[, context]) | Bool handler 筛选 List |
|
||
| find(handler, values[, context]) | 返回 List 的首个匹配项或 null |
|
||
| sum(values) / sum(handler, values[, context]) | 数值 List 求和 |
|
||
| reduce(handler, values, initial) | handler 输入为元素、当前状态,返回下一状态 |
|
||
| reduceUntil(handler, values, initial) | 另输出 stop: Bool,处理当前项后停止 |
|
||
| parallelEach(handler, values, limit[, context]) | 并行执行零输出 handler,返回前收尾全部工作 |
|
||
| contains(values, target) | 判断 List 中是否存在相等项,直接返回 Bool |
|
||
|
||
List/Bytes 支持 each、reduce、reduceUntil;Bytes 元素是 0–255 的 Int。Stream/AsyncStream 的 each 只接受零输出 handler。filter/find/sum 只处理 List。除 parallelEach 外按顺序处理,空集合求和为零,reduce 返回初始状态。
|
||
|
||
context 最多一个,需要多项数据时用 record;reduce 的共享数据放 State。parallelEach 的非挂起并发数为 1–256,挂起任务容量为 1–65536,并受运行时容量限制。同步 Stream 不接受挂起 handler。
|
||
|
||
handler 是签名匹配的具名函数或 handler 参数,不是可存储、比较或返回的运行时值:
|
||
|
||
```dever
|
||
transformAll(
|
||
transform: handler(value: Int) (result: Text),
|
||
values: List<Int>
|
||
) (results: List<Text>) {
|
||
results = each(transform, values)
|
||
}
|
||
```
|
||
|
||
`append`、`put`、`remove` 返回新集合;`get`、`first`、`find` 返回可空值。entries 返回有序 MapEntry 列表。length 接受 List、Map、Text、Bytes,Text 按 Unicode 码点计数。
|
||
|
||
## 失败、纯函数与敏感值
|
||
|
||
choice 的错误分支显式标记 error:
|
||
|
||
```dever
|
||
type DeliveryError {
|
||
error Unavailable(message: Text)
|
||
}
|
||
```
|
||
|
||
普通调用返回成功值,失败自动向调用方传播。`fail(DeliveryError.Unavailable("原因"))` 主动失败;`result(call)` 捕获后交给覆盖所有结果的分句处理。
|
||
|
||
捕获按错误类型及变体身份匹配,不按消息或载荷形状猜测。结果 Choice 的 error 分支可包装原错误 Choice,实际构造的仍是原失败变体;原错误 Choice 含有成功分支不妨碍捕获。参数求值的失败继续向外传播。
|
||
|
||
错误必须传播、返回或明确恢复。读取消息、记录日志、覆盖变量不算处理;恢复函数写 `recover("具体原因")`,全部分句理由一致。失败义务跟随 record、choice 和集合,局部查找不能代表处理了其余错误。程序故障不属于可捕获业务失败。
|
||
|
||
`pure` 写在输出声明后,禁止 I/O、时钟、随机源、并发等效果;检查沿调用链和 handler 传播。pure 函数仍可能有数值故障。
|
||
|
||
private 字段只能由所属源码边界访问,含私有字段的值不能直接用于公开 wire 输出。Model 私有字段仅供同领域 App 操作,跨边界返回显式 View。
|
||
|
||
Secret 不可比较、打印、拼接、转为普通文本、作为 Model 列或输出。密码使用 dever.crypto;哈希和验证属于阻塞操作。测试源可用 `secret(Text)` 构造固定输入,生产源码没有此能力。
|
||
|
||
## 并发与资源
|
||
|
||
挂起由调用图推断,不写 async/await。普通调用等待完成,显式 run 才启动子任务。
|
||
|
||
| 操作 | 合同 |
|
||
| --- | --- |
|
||
| run(call) | 启动 Task |
|
||
| wait(task) | 等待并消费 Task |
|
||
| stop(task) | 请求取消并等待退出;仅接受零输出 Task |
|
||
| timeout(task, ms, fallback) | 到期先清理任务,再调用输出签名相同的零输入 fallback |
|
||
| race(task, task, ...) | 取首个完成结果,清理其余任务;输出签名须相同 |
|
||
| group(limit) / run(group, call) | 有界零输出任务组,满时等待;最终 wait/stop |
|
||
| parallel(pure_call) | 在线程池执行非挂起纯计算 |
|
||
| blocking(call) | 在线程池执行明确的非挂起阻塞调用 |
|
||
| channel(T, capacity) | 有界通道;send/receive 挂起,close 唤醒等待者 |
|
||
|
||
Task/Group 不能复制、嵌套、传参或逃逸,必须消费一次。异常路径也会收尾子任务;取消不撤销已经发生的外部副作用。程序故障不会变成 timeout 的正常 fallback。
|
||
|
||
Group、Channel 容量为 1–65536。Channel 关闭后仍可读完缓存,之后 receive 返回 null。Channel 元素不能为可空类型;需要传递空值时,用含可空字段的消息 Choice 区分消息与关闭。
|
||
|
||
跨线程或挂起点存活的普通值须可传递。挂起调用链中的同步文件、睡眠、密码计算等操作用 blocking;网络等待和 dever.task.sleep 自动挂起。
|
||
|
||
File、Socket、Listener、Stream、AsyncStream 等资源的副本共享状态。普通数据的值语义不适用于这些句柄。
|
||
|
||
- Stream 按需单次消费,别名共享读取位置;reduceUntil 停止后可继续读取同步 Stream。
|
||
- AsyncStream 的消费结束、提前停止、故障或取消会关闭流;别名不能继续消费。
|
||
- `stream(channel/list/bytes)` 创建 AsyncStream;`dever.task.ticks(ms)` 创建周期流。
|
||
- 关闭流释放其持有的资源别名,不关闭调用者另外持有的 File、Socket 或 Channel。
|
||
|
||
## API、身份与权限
|
||
|
||
API 文件只声明绑定:
|
||
|
||
```dever
|
||
get profile = app.profile
|
||
public post login = app.login
|
||
authenticated get current = app.current
|
||
long post generate = app.generate
|
||
cmd export = app.export
|
||
rest model
|
||
```
|
||
|
||
HTTP 支持 get/post/put/delete。URL 为组件、领域、API 主题路径和 action;CMD 为 `组件.领域.命令`。API 绑定本领域 App 的一个可序列化输出。
|
||
|
||
GET/DELETE 从 query 读取标量;POST/PUT 从 JSON 对象读取;CMD 接受单个 JSON 对象。未知、重复和缺少必填字段失败。成功包裹为 `{"code":0,"message":"ok","data":...}`,失败的 data 为 null。dever.api.Error 映射 400/401/403/404/409/429,其他内部失败返回安全的 500。
|
||
|
||
| 声明 | 访问要求 |
|
||
| --- | --- |
|
||
| 默认 HTTP | 可信身份与精确角色权限 |
|
||
| public HTTP | 匿名入口 |
|
||
| authenticated HTTP | 可信身份;对象所有权与业务权限由 App 检查 |
|
||
|
||
public/authenticated 不用于 CMD 或 REST。权限键为 `component.domain.site.action`,REST 的 action 为 read/create/replace/delete。HTTP 方法不进入权限键。
|
||
|
||
`sites.<key>.path` 匹配 API 父目录,每条接口须恰好匹配一个站点。例如 admin 匹配 api/admin/**,不匹配 api/admin.dever,也不改写 URL。
|
||
|
||
认证 Provider 绑定项目的 verify App。它接收 dever.auth.Claims,返回仅含 `id: Text`、`user_id: UserModelId?`、`tenant_id: TenantModelId?` 的身份 record。verify 校验账号、会话、租户和成员关系,只读 global Model,不返回权限列表。
|
||
|
||
身份读取使用 dever.auth 和 dever.site,ID 类型须匹配 verify。客户端参数不能建立身份。public、CMD、Job 和普通测试不能直接读取 HTTP 身份。
|
||
|
||
角色与权限由核心管理。授权读取允许受保护或 authenticated 入口;修改只允许普通受保护写入口。Owner 为受保护角色,不按特定用户 ID 判断。业务 capability 在可信租户和入口命名空间内注册,撤销同时清理 grants,重新注册不恢复授权。
|
||
|
||
HTTP 元数据用 dever.api 读取和设置。敏感头不能读作普通 Text;Cookie 登录/退出用 issueCookie/clearCookie。Cookie 写入及 Cookie 认证的写请求要求 Origin、Host 精确匹配站点 origin。响应元数据仅在处理与编码成功后发布。
|
||
|
||
Upload 仅用于 POST 的顶层 multipart 参数,不能复制、嵌套、输出或进入 CMD。显式关闭,或一次性传给本领域 Port,由 Adapter 使用 storage;失败和取消清理临时文件。
|
||
|
||
完整配置与调用边界见 [应用指引](skills/references/application.md)和 [签名索引](skills/references/signatures.md)。
|
||
|
||
## Model 与查询
|
||
|
||
主文件的记录名对应领域,主题文件对应主题名:
|
||
|
||
```dever
|
||
type Article {
|
||
title: Text(1, 200)
|
||
body: Text
|
||
published: Bool default false index
|
||
}
|
||
```
|
||
|
||
自动生成只读 id 和 created_at。每个 Model 的 id 是不同的名义类型,不能用 Int 或其他 Model 的 ID 替代。
|
||
|
||
字段支持存储标量、无载荷 choice、Model ID 及可空形式。Text/Bytes 可指定长度,Decimal(P,S) 指定精度;generated 用于非空 Uuid。default 为静态值,index/unique 可用于单字段或顶层复合索引。外键自动建立索引,不自动加载关联。结构化数据显式使用 Json。
|
||
|
||
Model 的 `Text` 字段写入时默认去除首尾 Unicode 空白,再检查长度;不处理内部空白。`raw` 保留原文,`lower`/`upper` 在去除首尾空白后转换大小写,三者互斥。可空字段的 null 保持不变,空串不转为 null。普通函数中的 Text、查询条件、读取结果和原生 SQL 不自动转换。
|
||
|
||
```dever
|
||
type Category {
|
||
name: Text(1, 80)
|
||
code: Text(1, 40) lower unique
|
||
description: Text?
|
||
content: Text raw
|
||
active: Bool default true
|
||
}
|
||
```
|
||
|
||
create、update、条件更新、createMany、upsert 和 REST 使用同一写入规则。default、Seed 在编译期按该规则处理。富文本、代码、签名原文等需要逐字保留的字段使用 raw;不会依据字段名称猜测策略。去除空白不负责 HTML 安全过滤。
|
||
|
||
同领域 App 使用 model.create/createMany/get/first/list/cursor/count/exists/stream/update/delete/upsert。多 Model 使用 model.topic.operation。查询块只出现在调用位置,不是可保存的动态 Map:
|
||
|
||
```dever
|
||
page = model.list({
|
||
where = published == true
|
||
order = [created_at.desc, id.desc]
|
||
page = 1
|
||
size = 20
|
||
})
|
||
```
|
||
|
||
where 支持布尔组合、比较、null、in、between、contains、startsWith、endsWith;结构静态确定,值参数绑定。list 返回 items/page/size/total/pages;cursor 返回 items/next/has_more,不做 count。max_page_size 限制结果规模。
|
||
|
||
update(id, values) 返回记录,不存在时失败;条件 update/delete 返回影响行数,必须明确 where。delete(id) 返回影响行数。关系用 with 显式加载,返回 Related 的 Unloaded/Loaded 状态;仅 first/list/cursor 支持一层关系。
|
||
|
||
### REST 字段
|
||
|
||
create/replace/search 表达式写在 Model 字段上:引用 input 时转换客户端值,否则由服务端提供并拒绝客户端同名输入。表达式只允许纯计算及指定的只读请求能力。search 最多一个,不能叫 page/size。
|
||
|
||
`private owner user_id: ... = dever.auth.userId()` 创建时绑定可信用户,全部读写 SQL 均包含 owner 条件。private 字段不出现在 REST 输出;没有 replace 来源时 PUT 保留其原值。
|
||
|
||
普通 CRUD 直接使用 rest model;关联编排、状态转换或不同输出合同由 App 实现。
|
||
|
||
### 事务
|
||
|
||
```dever
|
||
transaction publish(id: model.id) (article: model.Article) {
|
||
article = model.update(id, { published = true })
|
||
}
|
||
```
|
||
|
||
普通写 HTTP、CMD、Job 建立整体事务,成功编码后提交。嵌套 transaction 复用同一连接的事务;失败或取消回滚后传播原错误。GET 不写库或入队。
|
||
|
||
long 入口不建立整体事务,写库和入队必须进入显式 transaction。外部 I/O、Worker、等待、并发和密码计算放在事务外,写入依赖的状态在事务内重新确认。long 已提交阶段不会因后续失败撤销。
|
||
|
||
事务不能跨物理数据库或混用 global/tenant 存储,不暴露 begin/commit/rollback。数据库失败保留 dever.database.Error。
|
||
|
||
### 数据库、迁移与租户
|
||
|
||
Model 优先使用组件同名连接,否则 database.default;显式 `database report` 绑定固定连接,缺失时报错。SQLite/PostgreSQL 配置在 setting.json;PostgreSQL 明确选择 TLS system/disabled。
|
||
|
||
当前 Model 是目标 schema。空库建最终表;安全变更自动应用,收紧约束先检查数据。字段重命名用 `from old_name`;破坏性变化用命名 migrate。已执行迁移的内容与顺序不可修改。
|
||
|
||
migrate 可含 before/after SQL,每块提供 sqlite、postgres 和参数列表,只允许单条参数化 INSERT/UPDATE/DELETE。全新数据库不执行历史数据变换。seed 按唯一键补缺失记录,不覆盖既有数据;迁移原子性限于所属 Model。
|
||
|
||
复杂查询在 Model 声明 `sql name(inputs) (output)`,两种方言均为静态字符串;结果是具体 Model、私有 SQL record 或本领域 App View。只有可证明只读的 SQL 能用于 GET。
|
||
|
||
启用 tenant 后,global type 使用控制库,其余 Model/Job 使用租户库。只支持 database 隔离。租户库须先显式迁移;缺失、未就绪或 schema 过期时报错,不回退控制库。
|
||
|
||
运维入口为 `dever tenant migrate`、`dever tenant owner`、`dever tenant component enable|disable`。数据库权限和真实数据操作由部署侧管理。
|
||
|
||
## Job
|
||
|
||
```dever
|
||
database default
|
||
|
||
job store(input: app.Request) () retry(3) timeout(30000) {
|
||
app.store(input)
|
||
}
|
||
```
|
||
|
||
Job 输入为零个或一个本领域 App record,无输出,只绑定本领域 App。retry 为 1–100 次,包含首次执行;timeout 为 1–3600000 毫秒。定时声明为 `schedule name = "0 * * * *"`,使用 UTC。
|
||
|
||
App 使用 dever.job.enqueue/enqueueAt,传入任务、可选 payload 和幂等 key。key 为 1–1024 字节;同租户、任务身份和 key 的活跃任务复用 Id。入队与业务写入可共享事务。
|
||
|
||
任务至少执行一次,外部副作用须业务幂等。受保护 HTTP 入队保存可信身份与精确入口权限键,执行时重验;public/authenticated 不能入队角色权限绑定的任务。CMD、schedule、测试使用 System 身份。身份失效、坏 payload 或不兼容 schema 进入 blocked。
|
||
|
||
runtime.mode 为 api/worker/all。任务配置包括 workers、poll_ms、lease_ms 和重试间隔;SQLite 的 lease 必须长于 Job timeout,worker 连接池需留出监管连接。停止时先停止接收和 claim,再按 shutdown_ms 排空。
|
||
|
||
测试使用临时数据库和用例独立时钟,可用 dever.test.advanceClock、drainJobs 验证队列。
|
||
|
||
## Port、Adapter 与依赖
|
||
|
||
Port 声明输入、命名输出和明确失败集合:
|
||
|
||
```dever
|
||
send(message: Text) () fails app.DeliveryError
|
||
```
|
||
|
||
Adapter 用 `port.send(...) (...) { ... }` 实现。每个文件完整实现一个 Port;单实现自动绑定,多实现由 setting.json 的 adapter 配置选择。实现只能抛出 Port 声明的错误。Port 不接受 handler 或数值范围参数。
|
||
|
||
Adapter 可声明 setting,选中的实现从配置读取同类型字段。外部程序用 external command/exec/pip/npm/go;代码可信性、网络、文件和设备权限由运行环境负责,allow 声明不是 OS 沙箱。
|
||
|
||
| 方式 | 用途 |
|
||
| --- | --- |
|
||
| external command | 普通 ELF;argv/stdin 输入,code/stdout/stderr 输出,无 shell |
|
||
| external exec | 遵循 Port Worker 协议的程序 |
|
||
| external pip/npm/go | 由相应生态实现 Port,使用锁定资源 |
|
||
|
||
外部调用和 fake 用法见 [依赖](skills/references/libraries.md)、[外部库示例](skills/references/external-lib.md)。协议与运行资源见 [SDK](sdk/README.md)。
|
||
|
||
Dever Package 是 module 下的可复用组件,使用 `dever package add/update/remove/list/doctor/export` 管理。dever.lock 固定版本、来源和摘要,不能覆盖本地同名组件。已有锁通过 `dever lib install` 恢复资源;run/build 消费锁定资产。当前 CLI 不提供 Lib add/update 或 target add。
|
||
|
||
## 标准库与数据格式
|
||
|
||
标准库按包名直接调用,无需 import。功能目录见 [标准库](lib/README.md),参数和输出见 [签名索引](skills/references/signatures.md)。
|
||
|
||
### JSON 数据
|
||
|
||
API/CMD 根据参数和输出类型编解码。Decimal/Id/Uuid 使用字符串,ModelId 为整数,日期时间使用规范字符串,Duration 为整数毫秒。支持 List、可空、可公开传输的 record 和无载荷、无 error 分支的 Choice。此类 Choice 按声明的分支名编码为 JSON 字符串,也可作 query 或 multipart 标量;未知分支报错。带载荷或 error 分支的 Choice、Map、资源不用于普通 wire。缺少可空字段得到 null,未知、重复和缺少必填字段失败。
|
||
|
||
已有 Json 需要转换成具体类型时,使用 `dever.json.decode(value)`;目标类型由局部声明、命名输出、字段或参数类型确定。直接从已有原文解码,不转为 Text 或 Document。解码失败传播 `dever.json.JsonResult.Failed`。Text 不在此处清洗。
|
||
|
||
需要处理解码失败时,直接写 `decoded: DecodeResult = result(dever.json.decode(payload))`。DecodeResult 必须有一个含单个目标类型字段的成功分支,以及包装 dever.json.JsonResult 的 error 分支,例如 `Parsed(value: CreateInput)` 和 `error Invalid(cause: dever.json.JsonResult)`。不需要另写解码函数;无类型上下文时不会猜测目标类型。
|
||
|
||
单个布尔条件失败使用 `dever.api.require(condition, dever.api.Error.Invalid)`;false 传播指定错误,true 继续。该函数不代替业务状态分支和执行时的权限校验。
|
||
|
||
日期时间使用 RFC3339,输出 UTC,最多毫秒精度;Date 为 YYYY-MM-DD,Time 为 HH:MM:SS[.sss]。无效日期、非有限 Float、越界整数失败。
|
||
|
||
Json 提供 fromText/toText、object/array、propertyValue/setProperty 等操作;encode 按实参具体类型输出 Json。原始数字不经过 Float。Json 限制为 16 MiB、64 层、65536 个值。
|
||
|
||
需要遍历节点时使用 parse/stringify 和 Document:root 为根 ID,nodes 为连续节点列表,子 ID 小于父 ID。Number 保留原文,Object 保留键顺序。node/property/element 缺失返回 null;JSON null 是实际节点。Document 的解析深度上限为 256,stringify 检查手工节点引用。
|
||
|
||
### 文件、HTTP 与流
|
||
|
||
文件 create 只建新文件,read 可能短读。I/O 错误通过声明失败传播;流的终止错误最多产生一次,之后结束。TCP 支持同时读写,每个方向串行;部分写入后失败关闭连接,不重发整条消息。
|
||
|
||
HTTP 支持 HTTP/1、HTTP/2、TLS、连接池、流式响应、SSE 与 HTTP/1 WebSocket。普通应用使用 API 声明,底层服务和客户端放 Adapter。
|
||
|
||
- Limits 控制头、正文、期限和连接数;HTTP/2 另限制流数与接收窗口。
|
||
- serve/send 缓冲完整正文;serveLive/open/openStream 按块处理。
|
||
- 池和会话归创建它们的任务作用域,响应须消费或关闭后释放名额。
|
||
- TLS 校验证书和名称,不自动降级;HTTP/2 使用 ALPN 或明文 prior knowledge。
|
||
- HTTP 协议头和正文定界由引擎管理;应用不写 Content-Length/Transfer-Encoding/Upgrade。
|
||
- 关闭 Listener 停止接收并排空;stop 取消服务并等待清理。
|
||
- 不支持 CONNECT、非空 trailer、服务器推送、WebSocket-over-HTTP/2、WebSocket 子协议或压缩扩展。
|
||
|
||
## 检查、测试与构建
|
||
|
||
```sh
|
||
dever new my_app
|
||
dever fmt my_app --check
|
||
dever check my_app
|
||
dever test my_app --filter hello.greeting
|
||
dever run my_app -- hello.greeting.greet '{"name":"Dever"}'
|
||
dever build my_app --output my_app/program
|
||
```
|
||
|
||
check 检查源码、配置、类型规则和生产入口可达性,不启动应用。重复实现和业务合理性仍需代码审查。
|
||
|
||
测试入口把文件主题转成 lowerCamelCase,例如 account_login.dever 对应 accountLogin() (),可调用本领域 App/Domain。外部 Port 使用用例独立 fake;Model 使用临时 SQLite。筛选不跳过源码检查,每例独立进程,默认 60 秒;任一失败返回非零。
|
||
|
||
`dever api <root> --output <新文件>` 导出接口快照。已有 dever.api 时,check/run/build 检查基线;变更需审查新快照,工具不自动覆盖。
|
||
|
||
构建输出须是新文件。生成的程序可脱离开发包运行,仍需配套配置和系统运行库。
|
||
|
||
安装环境与交叉编译见 [安装说明](build/README.md)和 [编译器开发](rust/README.md)。
|