Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
__pycache__/
*.pyc
+22
View File
@@ -0,0 +1,22 @@
---
name: dever-language
description: 使用 Dever 语言开发、修改或审查应用与组件。不用于 dever-go 或编译器内部实现。
---
# Dever 开发
先读项目规则和相关领域源码。执行 `dever skill path <项目根>`,使用当前编译器附带的指引;新项目用 `dever new <目录>`。
开发和审查先读 [开发指引](references/development.md),再按任务选择:
| 任务 | 参考 |
| --- | --- |
| 语法与类型 | [语法速查](references/language.md) |
| 说明与调用图 | [文档写法](references/documentation.md) |
| API、Model、权限、事务和 Job | [应用](references/application.md) |
| 标准库、组件和外部程序 | [依赖](references/libraries.md)、[函数签名](references/signatures.md) |
| 完整例子 | [示例](references/examples.md) |
| Python、JavaScript、Go 组件 | [外部库](references/external-lib.md) |
| 安装与环境 | [安装](references/install.md) |
语法或签名不确定时查随包源码,不照搬其他语言写法。修改后运行相关检查、测试并更新调用图;安装、部署和 Git 操作按用户授权执行。
+4
View File
@@ -0,0 +1,4 @@
interface:
display_name: "Dever 语言开发"
short_description: "安装与更新 Dever,创建项目,按语言约束开发、检查和测试应用"
default_prompt: "使用 $dever-language 创建并验证一个符合当前语言规范的 Dever 项目。"
@@ -0,0 +1 @@
{}
@@ -0,0 +1,8 @@
# 集合处理
筛选已发布文章,整理标题并计算总长度。
- [文章汇总命令](doc/api.md)
- [业务操作](doc/app.md)
- [内部规则](doc/domain.md)
- [调用流程](link.md)
@@ -0,0 +1 @@
cmd summarize = app.summarize
@@ -0,0 +1,18 @@
type Article {
title: Text
published: Bool
}
type Summary {
titles: List<Text>
title_length: Int
}
summarize(articles: List<Article>) (summary: Summary) {
published = filter(domain.isPublished, articles)
titles = each(domain.publishedTitle, published)
summary = Summary {
titles = titles
title_length = reduce(domain.addTitleLength, titles, 0)
}
}
@@ -0,0 +1,5 @@
# 文章汇总命令
## content.article.api.summarize
筛选已发布文章并统计标题。
@@ -0,0 +1,13 @@
# 业务操作
## content.article.Article
用于集合处理示例的文章标题和发布标记。
## content.article.Summary
筛选后的标题列表及标题总长度。
## content.article.summarize
选出已发布且标题非空白的文章,去掉标题两端空白,并累计标题长度。
@@ -0,0 +1,13 @@
# 内部规则
## content.article.domain.isPublished
检查文章已发布且标题不是空白。
## content.article.domain.publishedTitle
去掉文章标题两端空白。
## content.article.domain.addTitleLength
把当前标题的字符数加入累计值。
@@ -0,0 +1,11 @@
isPublished(article: app.Article) (selected: Bool) pure {
selected = article.published and text.trim(article.title) != ""
}
publishedTitle(article: app.Article) (title: Text) pure {
title = text.trim(article.title)
}
addTitleLength(title: Text, total: Int) (next: Int) pure {
next = total + length(title)
}
@@ -0,0 +1,234 @@
<!-- Generated by dever inspect --links. Do not edit. -->
<!-- source-sha256: 34a9b8f6199b0fb7fa26e50e29915dd0df8c9c5e142fa23a7f2a5fdd24572ef7 -->
# content.article
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
## 入口
- CMD [content.article.api.summarize](api.dever#L1) → [content.article.summarize](app.dever#L11)
## 外部调用方
当前加载的源码中没有已收录的外部调用。
## 能力与内部调用
### content.article.Article
定义:[app.dever:1:1](app.dever#L1,1)
没有已收录的下游调用。
### content.article.Summary
定义:[app.dever:6:1](app.dever#L6,1)
没有已收录的下游调用。
### content.article.api.summarize
定义:[api.dever:1:1](api.dever#L1,1)
<a id="flow-3c3fded4eed8bf7d-1"></a>
```mermaid
flowchart TD
n0(["content.article.api.summarize"])
click n0 href "api.dever#L1,17" "打开源码"
n1[["content.article.summarize"]]
click n1 href "api.dever#L1,17" "打开源码"
n2(["返回命令结果;未捕获错误终止命令"])
n0 --> n1
n1 --> n2
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| content.article.summarize | [api.dever:1:17](api.dever#L1,17) | [content.article.summarize](link.md#flow-61067b9f346a3099-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [content.article.summarize](app.dever#L11) | 领域内 | CMD 绑定 | [api.dever:1:17](api.dever#L1,17) |
### content.article.domain.addTitleLength
定义:[domain.dever:9:1](domain.dever#L9,1)
<a id="flow-ae94676d0264c9fd-1"></a>
```mermaid
flowchart TD
n0(["addTitleLength"])
click n0 href "domain.dever#L9,1" "打开源码"
n1(["返回"])
n3["赋值 next"]
click n3 href "domain.dever#L10,10" "打开源码"
n0 --> n3
n3 --> n1
```
没有已收录的下游调用。
### content.article.domain.isPublished
定义:[domain.dever:1:1](domain.dever#L1,1)
<a id="flow-424569accb37593c-1"></a>
```mermaid
flowchart TD
n0(["isPublished"])
click n0 href "domain.dever#L1,1" "打开源码"
n1(["返回"])
n3{"article.published"}
click n3 href "domain.dever#L2,14" "打开源码"
n4["求右侧值"]
click n4 href "domain.dever#L2,36" "打开源码"
n5["短路表达式结果"]
click n5 href "domain.dever#L2,14" "打开源码"
n6[["text.trim"]]
click n6 href "domain.dever#L2,36" "打开源码"
n7["赋值 selected"]
click n7 href "domain.dever#L2,14" "打开源码"
n0 --> n3
n3 -->|"true"| n4
n3 -->|"false:跳过右侧"| n5
n4 --> n6
n6 --> n5
n5 --> n7
n7 --> n1
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| text.trim | [domain.dever:2:36](domain.dever#L2,36) | 依赖或生成的分派(本页不展开) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| `text.trim` | 依赖 | 调用 | [domain.dever:2:36](domain.dever#L2,36) |
### content.article.domain.publishedTitle
定义:[domain.dever:5:1](domain.dever#L5,1)
<a id="flow-e1432830e4785e97-1"></a>
```mermaid
flowchart TD
n0(["publishedTitle"])
click n0 href "domain.dever#L5,1" "打开源码"
n1(["返回"])
n3[["text.trim"]]
click n3 href "domain.dever#L6,11" "打开源码"
n4["赋值 title"]
click n4 href "domain.dever#L6,11" "打开源码"
n0 --> n3
n3 --> n4
n4 --> n1
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| text.trim | [domain.dever:6:11](domain.dever#L6,11) | 依赖或生成的分派(本页不展开) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| `text.trim` | 依赖 | 调用 | [domain.dever:6:11](domain.dever#L6,11) |
### content.article.summarize
定义:[app.dever:11:1](app.dever#L11,1)
<a id="flow-61067b9f346a3099-1"></a>
```mermaid
flowchart TD
n0(["summarize"])
click n0 href "app.dever#L11,1" "打开源码"
n1(["返回"])
n3{"还有元素?"}
click n3 href "app.dever#L12,42" "打开源码"
n4["集合操作结果"]
click n4 href "app.dever#L12,15" "打开源码"
n5["取下一元素"]
click n5 href "app.dever#L12,15" "打开源码"
n6[["content.article.domain.isPublished"]]
click n6 href "app.dever#L12,15" "打开源码"
n7["赋值 published"]
click n7 href "app.dever#L12,15" "打开源码"
n8{"还有元素?"}
click n8 href "app.dever#L13,40" "打开源码"
n9["集合操作结果"]
click n9 href "app.dever#L13,12" "打开源码"
n10["取下一元素"]
click n10 href "app.dever#L13,12" "打开源码"
n11[["content.article.domain.publishedTitle"]]
click n11 href "app.dever#L13,12" "打开源码"
n12["赋值 titles"]
click n12 href "app.dever#L13,12" "打开源码"
n13{"还有元素?"}
click n13 href "app.dever#L16,50" "打开源码"
n14["集合操作结果"]
click n14 href "app.dever#L16,20" "打开源码"
n15["取下一元素"]
click n15 href "app.dever#L16,20" "打开源码"
n16[["content.article.domain.addTitleLength"]]
click n16 href "app.dever#L16,20" "打开源码"
n17["赋值 summary"]
click n17 href "app.dever#L14,13" "打开源码"
n0 -->|"开始遍历"| n3
n3 -->|"否(包括空集合)"| n4
n3 -->|"是"| n5
n5 --> n6
n6 -->|"下一元素"| n3
n4 --> n7
n7 -->|"开始遍历"| n8
n8 -->|"否(包括空集合)"| n9
n8 -->|"是"| n10
n10 --> n11
n11 -->|"下一元素"| n8
n9 --> n12
n12 -->|"开始遍历"| n13
n13 -->|"否(包括空集合)"| n14
n13 -->|"是"| n15
n15 --> n16
n16 -->|"下一元素"| n13
n14 --> n17
n17 --> n1
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| content.article.domain.isPublished | [app.dever:12:15](app.dever#L12,15) | [content.article.domain.isPublished](link.md#flow-424569accb37593c-1) |
| content.article.domain.publishedTitle | [app.dever:13:12](app.dever#L13,12) | [content.article.domain.publishedTitle](link.md#flow-e1432830e4785e97-1) |
| content.article.domain.addTitleLength | [app.dever:16:20](app.dever#L16,20) | [content.article.domain.addTitleLength](link.md#flow-ae94676d0264c9fd-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [content.article.domain.isPublished](domain.dever#L1) | 领域内 | 集合回调 | [app.dever:12:22](app.dever#L12,22) |
| [content.article.domain.publishedTitle](domain.dever#L5) | 领域内 | 集合回调 | [app.dever:13:17](app.dever#L13,17) |
| [content.article.domain.addTitleLength](domain.dever#L9) | 领域内 | 集合回调 | [app.dever:16:27](app.dever#L16,27) |
@@ -0,0 +1,43 @@
type State {
Draft
Published
Rejected(reason: Text)
}
clauses() () {
assertEq(label(State.Draft), "draft")
assertEq(label(State.Published), "published")
assertEq(label(State.Rejected("needs revision")), "needs revision")
assertEq(display(null), "missing")
assertEq(display("present"), "present")
assertEq(grade(59), "fail")
assertEq(grade(60), "pass")
}
label(state: State.Published) (text: Text) {
text = "published"
}
label(state: State.Rejected(reason)) (text: Text) {
text = reason
}
label(state: other) (text: Text) {
text = "draft"
}
display(value: Text) (text: Text) {
text = value
}
display(value: null) (text: Text) {
text = "missing"
}
grade(score: Int<60) (text: Text) {
text = "fail"
}
grade(score: Int >= 60) (text: Text) {
text = "pass"
}
@@ -0,0 +1,32 @@
summary() () {
articles = [
app.Article {
title = " First "
published = true
},
app.Article {
title = "Hidden"
published = false
},
app.Article {
title = " "
published = true
},
app.Article {
title = "Second"
published = true
}
]
actual = app.summarize(articles)
assertEq(actual.titles, ["First", "Second"])
assertEq(actual.title_length, 11)
each(assertCleanTitle, actual.titles)
empty = app.summarize([])
assertEq(empty.titles, [])
assertEq(empty.title_length, 0)
}
assertCleanTitle(title: Text) () {
assert(title != "")
assertEq(title, text.trim(title))
}
@@ -0,0 +1 @@
{}
@@ -0,0 +1,9 @@
# 外部命令
通过 Port 调用工具并读取版本。
- [工具适配器](doc/adapter.md)
- [工具版本命令](doc/api.md)
- [业务操作](doc/app.md)
- [外部接口](doc/port.md)
- [调用流程](link.md)
@@ -0,0 +1 @@
external command "bin/tool" {}
@@ -0,0 +1 @@
cmd version = app.version
@@ -0,0 +1,14 @@
type Version {
code: Int
stdout: Text
stderr: Text
}
version() (version: Version) {
output = port.execute(["--version"], null)
version = Version {
code = output.code
stdout = dever.bytes.toText(output.stdout)
stderr = dever.bytes.toText(output.stderr)
}
}
@@ -0,0 +1,3 @@
# 工具适配器
调用受管资源中的 `bin/tool`。程序参数由 Port 传入,不通过 shell 执行。
@@ -0,0 +1,5 @@
# 工具版本命令
## document.tool.api.version
运行工具的 --version 并返回退出码和输出。
@@ -0,0 +1,9 @@
# 业务操作
## document.tool.Version
外部命令的退出码及解码后的标准输出、错误输出。
## document.tool.version
执行工具的 --version,将输出按 UTF-8 解码;命令非零退出码仍作为结果返回。
@@ -0,0 +1,5 @@
# 外部接口
## document.tool.port.execute
执行指定工具并返回退出码和原始输出,stdin 可为空。
@@ -0,0 +1,127 @@
<!-- Generated by dever inspect --links. Do not edit. -->
<!-- source-sha256: c3d45ae2a42beb7d0a9cdab16d131b25e36234ae4dce85b2b66c0086454ba10a -->
# document.tool
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
## 入口
- CMD [document.tool.api.version](api.dever#L1) → [document.tool.version](app.dever#L7)
## 外部调用方
当前加载的源码中没有已收录的外部调用。
## 能力与内部调用
### document.tool.Version
定义:[app.dever:1:1](app.dever#L1,1)
没有已收录的下游调用。
### document.tool.adapter.execute
定义:[adapter.dever:1:1](adapter.dever#L1,1)
<a id="flow-82fa6bebec059055-1"></a>
```mermaid
flowchart TD
n0(["execute"])
click n0 href "adapter.dever#L1,1" "打开源码"
n1(["返回"])
n3[["外部命令边界:execute(受检参数、退出状态与输出)"]]
click n3 href "adapter.dever#L1,1" "打开源码"
n4(["声明的业务错误或 Worker 故障向调用方传播"])
n0 --> n3
n3 -->|"成功"| n1
n3 -->|"失败"| n4
```
没有已收录的下游调用。
### document.tool.api.version
定义:[api.dever:1:1](api.dever#L1,1)
<a id="flow-437acd584ec674f9-1"></a>
```mermaid
flowchart TD
n0(["document.tool.api.version"])
click n0 href "api.dever#L1,15" "打开源码"
n1[["document.tool.version"]]
click n1 href "api.dever#L1,15" "打开源码"
n2(["返回命令结果;未捕获错误终止命令"])
n0 --> n1
n1 --> n2
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| document.tool.version | [api.dever:1:15](api.dever#L1,15) | [document.tool.version](link.md#flow-d23463cc55069c99-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [document.tool.version](app.dever#L7) | 领域内 | CMD 绑定 | [api.dever:1:15](api.dever#L1,15) |
### document.tool.port.execute
定义:[port.dever:1:1](port.dever#L1,1)
没有已收录的下游调用。
### document.tool.version
定义:[app.dever:7:1](app.dever#L7,1)
<a id="flow-d23463cc55069c99-1"></a>
```mermaid
flowchart TD
n0(["version"])
click n0 href "app.dever#L7,1" "打开源码"
n1(["返回"])
n3[["document.tool.adapter.execute"]]
click n3 href "app.dever#L8,12" "打开源码"
n4["赋值 output"]
click n4 href "app.dever#L8,12" "打开源码"
n5[["dever.bytes.toText"]]
click n5 href "app.dever#L11,14" "打开源码"
n6[["dever.bytes.toText"]]
click n6 href "app.dever#L12,14" "打开源码"
n7["赋值 version"]
click n7 href "app.dever#L9,13" "打开源码"
n0 --> n3
n3 --> n4
n4 --> n5
n5 --> n6
n6 --> n7
n7 --> n1
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| document.tool.adapter.execute | [app.dever:8:12](app.dever#L8,12) | [document.tool.adapter.execute](link.md#flow-82fa6bebec059055-1) |
| dever.bytes.toText | [app.dever:11:14](app.dever#L11,14) | 依赖或生成的分派(本页不展开) |
| dever.bytes.toText | [app.dever:12:14](app.dever#L12,14) | 依赖或生成的分派(本页不展开) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [document.tool.adapter.execute](adapter.dever#L1) | 领域内 | 调用 | [app.dever:8:12](app.dever#L8,12) |
| `dever.bytes.toText` | 依赖 | 调用 | [app.dever:11:14](app.dever#L11,14) |
| `dever.bytes.toText` | 依赖 | 调用 | [app.dever:12:14](app.dever#L12,14) |
@@ -0,0 +1 @@
execute(args: List<Text>, stdin: Bytes?) (output: dever.process.Output) fails dever.process.Error
@@ -0,0 +1,16 @@
version() () {
output = app.version()
assertEq(output.code, 0)
assertEq(output.stdout, "tool 1.0\n")
assertEq(output.stderr, "")
}
port.execute(args: List<Text>, stdin: Bytes?) (output: dever.process.Output) {
assertEq(args, ["--version"])
assertEq(stdin, null)
output = dever.process.Output {
code = 0
stdout = dever.bytes.fromText("tool 1.0\n")
stderr = dever.bytes.fromText("")
}
}
@@ -0,0 +1,4 @@
{
"lib": ["pip:markdown@3.8.2"],
"log": { "level": "info" }
}
@@ -0,0 +1,9 @@
# Markdown 渲染
用外部 Markdown 库实现渲染,通过 fake 测试成功与拒绝。
- [Markdown 适配器](doc/adapter.md)
- [Markdown 命令](doc/api.md)
- [业务操作](doc/app.md)
- [外部接口](doc/port.md)
- [调用流程](link.md)
@@ -0,0 +1,3 @@
external pip "python/render.py" {
lib "markdown@3.8.2"
}
@@ -0,0 +1 @@
cmd render = app.render
@@ -0,0 +1,8 @@
type RenderResult {
Rendered(html: Text)
error Rejected(message: Text)
}
render(source: Text) (html: Text) {
html = port.render(source)
}
@@ -0,0 +1,3 @@
# Markdown 适配器
使用 `python/render.py` 和锁定的 Markdown 库实现渲染。第三方库与 Worker 资源须先准备,测试中的 fake 不依赖它们。
@@ -0,0 +1,5 @@
# Markdown 命令
## document.markup.api.render
调用外部组件把 Markdown 转为 HTML。
@@ -0,0 +1,9 @@
# 业务操作
## document.markup.RenderResult
Markdown 渲染成功或业务拒绝;拒绝包含原因。
## document.markup.render
调用外部渲染器把 Markdown 转为 HTML;渲染器可拒绝输入。
@@ -0,0 +1,5 @@
# 外部接口
## document.markup.port.render
外部 Markdown 渲染操作,失败类型为 RenderResult。
@@ -0,0 +1,17 @@
package main
import (
"bytes"
"context"
"github.com/yuin/goldmark"
)
func Render(ctx context.Context, payload any, setting any) (any, error) {
var output bytes.Buffer
source := payload.(map[string]any)["source"].(string)
if err := goldmark.Convert([]byte(source), &output); err != nil {
return nil, err
}
return map[string]any{"html": output.String()}, nil
}
@@ -0,0 +1,5 @@
import { marked } from 'marked';
export async function render(payload, setting, signal) {
return { html: await marked.parse(payload.source) };
}
@@ -0,0 +1,114 @@
<!-- Generated by dever inspect --links. Do not edit. -->
<!-- source-sha256: 43bec2fc499db07b0488aa4e6d1775a75b4d28f1a575072696eba0c215ae34a6 -->
# document.markup
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
## 入口
- CMD [document.markup.api.render](api.dever#L1) → [document.markup.render](app.dever#L6)
## 外部调用方
当前加载的源码中没有已收录的外部调用。
## 能力与内部调用
### document.markup.RenderResult
定义:[app.dever:1:1](app.dever#L1,1)
没有已收录的下游调用。
### document.markup.adapter.render
定义:[adapter.dever:1:1](adapter.dever#L1,1)
<a id="flow-97bf2a6af81a831b-1"></a>
```mermaid
flowchart TD
n0(["render"])
click n0 href "adapter.dever#L1,1" "打开源码"
n1(["返回"])
n3[["外部 pip Worker 边界:render(受检协议与结果)"]]
click n3 href "adapter.dever#L1,1" "打开源码"
n4(["声明的业务错误或 Worker 故障向调用方传播"])
n0 --> n3
n3 -->|"成功"| n1
n3 -->|"失败"| n4
```
没有已收录的下游调用。
### document.markup.api.render
定义:[api.dever:1:1](api.dever#L1,1)
<a id="flow-b479961e7bdb93bd-1"></a>
```mermaid
flowchart TD
n0(["document.markup.api.render"])
click n0 href "api.dever#L1,14" "打开源码"
n1[["document.markup.render"]]
click n1 href "api.dever#L1,14" "打开源码"
n2(["返回命令结果;未捕获错误终止命令"])
n0 --> n1
n1 --> n2
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| document.markup.render | [api.dever:1:14](api.dever#L1,14) | [document.markup.render](link.md#flow-1e0e90ddee897072-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [document.markup.render](app.dever#L6) | 领域内 | CMD 绑定 | [api.dever:1:14](api.dever#L1,14) |
### document.markup.port.render
定义:[port.dever:1:1](port.dever#L1,1)
没有已收录的下游调用。
### document.markup.render
定义:[app.dever:6:1](app.dever#L6,1)
<a id="flow-1e0e90ddee897072-1"></a>
```mermaid
flowchart TD
n0(["render"])
click n0 href "app.dever#L6,1" "打开源码"
n1(["返回"])
n3[["document.markup.adapter.render"]]
click n3 href "app.dever#L7,10" "打开源码"
n4["赋值 html"]
click n4 href "app.dever#L7,10" "打开源码"
n0 --> n3
n3 --> n4
n4 --> n1
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| document.markup.adapter.render | [app.dever:7:10](app.dever#L7,10) | [document.markup.adapter.render](link.md#flow-97bf2a6af81a831b-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [document.markup.adapter.render](adapter.dever#L1) | 领域内 | 调用 | [app.dever:7:10](app.dever#L7,10) |
@@ -0,0 +1 @@
render(source: Text) (html: Text) fails app.RenderResult
@@ -0,0 +1,5 @@
import markdown
async def render(payload, setting):
return {"html": markdown.markdown(payload["source"])}
@@ -0,0 +1,16 @@
rejected() () {
verify(result(app.render("rejected input")))
}
port.render(source: Text) (html: Text) {
assertEq(source, "rejected input")
fail(app.RenderResult.Rejected("invalid document"))
}
verify(value: app.RenderResult.Rejected(message)) () recover("test verifies declared rejection") {
assertEq(message, "invalid document")
}
verify(value: app.RenderResult.Rendered(html)) () recover("test verifies declared rejection") {
assert(false)
}
@@ -0,0 +1,8 @@
rendered() () {
assertEq(app.render("# Report"), "<h1>Report</h1>")
}
port.render(source: Text) (html: Text) {
assertEq(source, "# Report")
html = "<h1>Report</h1>"
}
@@ -0,0 +1,22 @@
{
"database": {
"default": {
"type": "sqlite",
"path": "data/db/platform.db",
"tenant_directory": "data/tenants"
}
},
"tenant": { "database": "default" },
"auth": {
"providers": {
"session": {
"verify": "access.session.verify",
"jwtSecret": "replace-this-example-secret-before-deployment",
"ttlSeconds": 3600
}
}
},
"sites": { "admin": { "path": "admin", "auth": "session" } },
"http": { "host": "127.0.0.1", "port": 8080 },
"log": { "level": "info" }
}
@@ -0,0 +1,8 @@
# 会话认证
验证站点会话、用户、租户和成员关系。
- [业务操作](doc/app.md)
- [成员关系](doc/model-membership.md)
- [数据结构](doc/model.md)
- [调用流程](link.md)
@@ -0,0 +1,60 @@
type Identity {
id: Text
user_id: access.user.model.id?
tenant_id: access.tenant.model.id?
}
verify(claims: dever.auth.Claims) (identity: Identity) {
identity = findSession(uuid.parse(claims.session), claims.tenant, claims)
}
findSession(session_id: Uuid, selected_key: Text, claims: dever.auth.Claims) (
identity: Identity
) {
stored = model.first(
{
where = uuid == session_id and subject == claims.subject and site == claims.site and tenant_key == selected_key and revoked == false and expires_at > dever.time.now(
)
}
)
identity = verified(stored)
}
findSession(session_id: null, selected_key: Text?, claims: dever.auth.Claims) (
identity: Identity
) {
fail(dever.api.Error.Unauthorized)
}
findSession(session_id: Uuid, selected_key: null, claims: dever.auth.Claims) (
identity: Identity
) {
fail(dever.api.Error.Unauthorized)
}
verified(stored: model.Session) (identity: Identity) {
access.user.requireActive(stored.user_id)
access.tenant.requireActive(stored.tenant_id, stored.tenant_key)
requireMembership(
model.membership.exists(
{
where = user_id == stored.user_id and tenant_id == stored.tenant_id and active == true
}
)
)
identity = Identity {
id = stored.subject
user_id = stored.user_id
tenant_id = stored.tenant_id
}
}
verified(stored: null) (identity: Identity) {
fail(dever.api.Error.Unauthorized)
}
requireMembership(active: true) () {}
requireMembership(active: false) () {
fail(dever.api.Error.Unauthorized)
}
@@ -0,0 +1,21 @@
# 业务操作
## access.session.Identity
认证后的主体、用户编号和租户编号。
## access.session.verify
检查会话声明以及用户、租户和成员状态,返回可信身份。
## access.session.findSession
按 UUID、主体、站点和租户查找未撤销且未过期的会话;缺失或无效时返回未授权。
## access.session.verified
验证已找到会话的用户、租户和成员状态;空记录返回未授权。
## access.session.requireMembership
成员关系启用时通过,否则返回未授权。
@@ -0,0 +1,5 @@
# 成员关系
## access.session.model.membership.Membership
平台库中的用户与租户成员关系,每对用户和租户唯一。
@@ -0,0 +1,5 @@
# 数据结构
## access.session.model.Session
平台库中的站点会话,保存用户、租户、有效期和撤销状态。
@@ -0,0 +1,231 @@
<!-- Generated by dever inspect --links. Do not edit. -->
<!-- source-sha256: 1e9b28fb1ca00f29b41bebdf0b358f4e1a1b787fb2a524aea6377dd8b98ab87b -->
# access.session
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
## 入口
没有已收录的入口。
## 外部调用方
| 能力 | 调用方 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.session.verify](app.dever#L7) | [content.article.api.admin.entries.rest](../../content/article/api/admin/entries.dever#L1) | 身份验证 | [../../content/article/api/admin/entries.dever:1:1](../../content/article/api/admin/entries.dever#L1,1) |
| [access.session.verify](app.dever#L7) | [system.health.api.admin.status.ready](../../system/health/api/admin/status.dever#L1) | 身份验证 | [../../system/health/api/admin/status.dever:1:20](../../system/health/api/admin/status.dever#L1,20) |
## 能力与内部调用
### access.session.Identity
定义:[app.dever:1:1](app.dever#L1,1)
没有已收录的下游调用。
### access.session.findSession
定义:[app.dever:11:1](app.dever#L11,1)、[app.dever:23:1](app.dever#L23,1)、[app.dever:29:1](app.dever#L29,1)
<a id="flow-390194c3c1297291-1"></a>
```mermaid
flowchart TD
n0(["findSession"])
click n0 href "app.dever#L11,1" "打开源码"
n1(["返回"])
n2(["传播错误"])
n3{"按实参匹配分句(互斥)"}
n4["session_id: Uuid 且 selected_key: Text"]
click n4 href "app.dever#L11,1" "打开源码"
n5[["dever.time.now"]]
click n5 href "app.dever#L16,157" "打开源码"
n6["access.session.model.first"]
click n6 href "app.dever#L14,12" "打开源码"
n7["赋值 stored"]
click n7 href "app.dever#L14,12" "打开源码"
n8[["access.session.verified"]]
click n8 href "app.dever#L20,14" "打开源码"
n9["赋值 identity"]
click n9 href "app.dever#L20,14" "打开源码"
n10["session_id: null"]
click n10 href "app.dever#L23,1" "打开源码"
n11["fail dever.api.Error.Unauthorized"]
click n11 href "app.dever#L26,8" "打开源码"
n12["session_id: Uuid 且 selected_key: null"]
click n12 href "app.dever#L29,1" "打开源码"
n13["fail dever.api.Error.Unauthorized"]
click n13 href "app.dever#L32,8" "打开源码"
n0 --> n3
n3 -->|"session_id: Uuid 且 selected_key: Text"| n4
n4 --> n5
n5 --> n6
n6 --> n7
n7 --> n8
n8 --> n9
n9 --> n1
n3 -->|"session_id: null"| n10
n10 --> n11
n11 --> n2
n3 -->|"session_id: Uuid 且 selected_key: null"| n12
n12 --> n13
n13 --> n2
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| dever.time.now | [app.dever:16:157](app.dever#L16,157) | 依赖或生成的分派(本页不展开) |
| access.session.verified | [app.dever:20:14](app.dever#L20,14) | [access.session.verified](link.md#flow-d703972fda2ee383-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.session.model.first](model.dever#L1) | 领域内 | 内建操作 | [app.dever:14:12](app.dever#L14,12) |
| `dever.time.now` | 依赖 | 调用 | [app.dever:16:157](app.dever#L16,157) |
| [access.session.verified](app.dever#L35) | 领域内 | 调用 | [app.dever:20:14](app.dever#L20,14) |
### access.session.model.Session
定义:[model.dever:1:1](model.dever#L1,1)
没有已收录的下游调用。
### access.session.model.membership.Membership
定义:[model/membership.dever:1:1](model/membership.dever#L1,1)
没有已收录的下游调用。
### access.session.requireMembership
定义:[app.dever:56:1](app.dever#L56,1)、[app.dever:58:1](app.dever#L58,1)
<a id="flow-322031245e905aab-1"></a>
```mermaid
flowchart TD
n0(["requireMembership"])
click n0 href "app.dever#L56,1" "打开源码"
n1(["返回"])
n2(["传播错误"])
n3{"按实参匹配分句(互斥)"}
n4["active: true"]
click n4 href "app.dever#L56,1" "打开源码"
n5["active: false"]
click n5 href "app.dever#L58,1" "打开源码"
n6["fail dever.api.Error.Unauthorized"]
click n6 href "app.dever#L59,8" "打开源码"
n0 --> n3
n3 -->|"active: true"| n4
n4 --> n1
n3 -->|"active: false"| n5
n5 --> n6
n6 --> n2
```
没有已收录的下游调用。
### access.session.verified
定义:[app.dever:35:1](app.dever#L35,1)、[app.dever:52:1](app.dever#L52,1)
<a id="flow-d703972fda2ee383-1"></a>
```mermaid
flowchart TD
n0(["verified"])
click n0 href "app.dever#L35,1" "打开源码"
n1(["返回"])
n2(["传播错误"])
n3{"按实参匹配分句(互斥)"}
n4["stored: model.Session"]
click n4 href "app.dever#L35,1" "打开源码"
n5[["access.user.requireActive"]]
click n5 href "app.dever#L36,3" "打开源码"
n6[["access.tenant.requireActive"]]
click n6 href "app.dever#L37,3" "打开源码"
n7["access.session.model.membership.exists"]
click n7 href "app.dever#L39,5" "打开源码"
n8[["access.session.requireMembership"]]
click n8 href "app.dever#L38,3" "打开源码"
n9["赋值 identity"]
click n9 href "app.dever#L45,14" "打开源码"
n10["stored: null"]
click n10 href "app.dever#L52,1" "打开源码"
n11["fail dever.api.Error.Unauthorized"]
click n11 href "app.dever#L53,8" "打开源码"
n0 --> n3
n3 -->|"stored: model.Session"| n4
n4 --> n5
n5 --> n6
n6 --> n7
n7 --> n8
n8 --> n9
n9 --> n1
n3 -->|"stored: null"| n10
n10 --> n11
n11 --> n2
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| access.user.requireActive | [app.dever:36:3](app.dever#L36,3) | [access.user.requireActive](../user/link.md#flow-f368d51d0e19702c-1) |
| access.tenant.requireActive | [app.dever:37:3](app.dever#L37,3) | [access.tenant.requireActive](../tenant/link.md#flow-8fb2bcc151c4208d-1) |
| access.session.requireMembership | [app.dever:38:3](app.dever#L38,3) | [access.session.requireMembership](link.md#flow-322031245e905aab-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.user.requireActive](../user/app.dever#L1) | 跨领域 [access.user](../user/link.md) | 调用 | [app.dever:36:3](app.dever#L36,3) |
| [access.tenant.requireActive](../tenant/app.dever#L1) | 跨领域 [access.tenant](../tenant/link.md) | 调用 | [app.dever:37:3](app.dever#L37,3) |
| [access.session.requireMembership](app.dever#L56) | 领域内 | 调用 | [app.dever:38:3](app.dever#L38,3) |
| [access.session.model.membership.exists](model/membership.dever#L1) | 领域内 | 内建操作 | [app.dever:39:5](app.dever#L39,5) |
### access.session.verify
定义:[app.dever:7:1](app.dever#L7,1)
<a id="flow-01943b8d6356f307-1"></a>
```mermaid
flowchart TD
n0(["verify"])
click n0 href "app.dever#L7,1" "打开源码"
n1(["返回"])
n3[["uuid.parse"]]
click n3 href "app.dever#L8,26" "打开源码"
n4[["access.session.findSession"]]
click n4 href "app.dever#L8,14" "打开源码"
n5["赋值 identity"]
click n5 href "app.dever#L8,14" "打开源码"
n0 --> n3
n3 --> n4
n4 --> n5
n5 --> n1
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| uuid.parse | [app.dever:8:26](app.dever#L8,26) | 依赖或生成的分派(本页不展开) |
| access.session.findSession | [app.dever:8:14](app.dever#L8,14) | [access.session.findSession](link.md#flow-390194c3c1297291-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.session.findSession](app.dever#L11) | 领域内 | 调用 | [app.dever:8:14](app.dever#L8,14) |
| `uuid.parse` | 依赖 | 调用 | [app.dever:8:26](app.dever#L8,26) |
@@ -0,0 +1,10 @@
global type Session {
uuid: Uuid generated unique
subject: Text
user_id: access.user.model.id
tenant_id: access.tenant.model.id
tenant_key: Text
site: Text
expires_at: DateTime
revoked: Bool default false
}
@@ -0,0 +1,7 @@
global type Membership {
user_id: access.user.model.id
tenant_id: access.tenant.model.id
active: Bool default true
}
unique(user_id, tenant_id)
@@ -0,0 +1,8 @@
# 租户状态
检查租户编号、唯一键和启用状态。
- [业务操作](doc/app.md)
- [内部规则](doc/domain.md)
- [数据结构](doc/model.md)
- [调用流程](link.md)
@@ -0,0 +1,9 @@
requireActive(tenant_id: model.id, tenant_key: Text) () {
domain.requireActive(
model.exists(
{
where = id == tenant_id and key == tenant_key and active == true
}
)
)
}
@@ -0,0 +1,5 @@
# 业务操作
## access.tenant.requireActive
要求租户编号、键和启用状态同时匹配。
@@ -0,0 +1,5 @@
# 内部规则
## access.tenant.domain.requireActive
租户已启用时通过,否则返回未授权。
@@ -0,0 +1,5 @@
# 数据结构
## access.tenant.model.Tenant
平台租户的唯一键和启用状态。
@@ -0,0 +1,5 @@
requireActive(active: true) () {}
requireActive(active: false) () {
fail(dever.api.Error.Unauthorized)
}
@@ -0,0 +1,89 @@
<!-- Generated by dever inspect --links. Do not edit. -->
<!-- source-sha256: 6ab5ca6ad85fdbfe6452f6aaf1909ecf5a0ea5b89b5e50d80d0325d23097bd03 -->
# access.tenant
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
## 入口
没有已收录的入口。
## 外部调用方
| 能力 | 调用方 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.tenant.requireActive](app.dever#L1) | [access.session.verified](../session/app.dever#L35) | 调用 | [../session/app.dever:37:3](../session/app.dever#L37,3) |
## 能力与内部调用
### access.tenant.domain.requireActive
定义:[domain.dever:1:1](domain.dever#L1,1)、[domain.dever:3:1](domain.dever#L3,1)
<a id="flow-4bf621ec21992ef9-1"></a>
```mermaid
flowchart TD
n0(["requireActive"])
click n0 href "domain.dever#L1,1" "打开源码"
n1(["返回"])
n2(["传播错误"])
n3{"按实参匹配分句(互斥)"}
n4["active: true"]
click n4 href "domain.dever#L1,1" "打开源码"
n5["active: false"]
click n5 href "domain.dever#L3,1" "打开源码"
n6["fail dever.api.Error.Unauthorized"]
click n6 href "domain.dever#L4,8" "打开源码"
n0 --> n3
n3 -->|"active: true"| n4
n4 --> n1
n3 -->|"active: false"| n5
n5 --> n6
n6 --> n2
```
没有已收录的下游调用。
### access.tenant.model.Tenant
定义:[model.dever:1:1](model.dever#L1,1)
没有已收录的下游调用。
### access.tenant.requireActive
定义:[app.dever:1:1](app.dever#L1,1)
<a id="flow-8fb2bcc151c4208d-1"></a>
```mermaid
flowchart TD
n0(["requireActive"])
click n0 href "app.dever#L1,1" "打开源码"
n1(["返回"])
n3["access.tenant.model.exists"]
click n3 href "app.dever#L3,5" "打开源码"
n4[["access.tenant.domain.requireActive"]]
click n4 href "app.dever#L2,3" "打开源码"
n0 --> n3
n3 --> n4
n4 --> n1
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| access.tenant.domain.requireActive | [app.dever:2:3](app.dever#L2,3) | [access.tenant.domain.requireActive](link.md#flow-4bf621ec21992ef9-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.tenant.domain.requireActive](domain.dever#L1) | 领域内 | 调用 | [app.dever:2:3](app.dever#L2,3) |
| [access.tenant.model.exists](model.dever#L1) | 领域内 | 内建操作 | [app.dever:3:5](app.dever#L3,5) |
@@ -0,0 +1,4 @@
global type Tenant {
key: Text unique
active: Bool default true
}
@@ -0,0 +1,8 @@
# 用户状态
检查用户是否存在且已启用。
- [业务操作](doc/app.md)
- [内部规则](doc/domain.md)
- [数据结构](doc/model.md)
- [调用流程](link.md)
@@ -0,0 +1,9 @@
requireActive(user_id: model.id) () {
domain.requireActive(
model.exists(
{
where = id == user_id and active == true
}
)
)
}
@@ -0,0 +1,5 @@
# 业务操作
## access.user.requireActive
要求指定用户存在且已启用。
@@ -0,0 +1,5 @@
# 内部规则
## access.user.domain.requireActive
用户已启用时通过,否则返回未授权。
@@ -0,0 +1,5 @@
# 数据结构
## access.user.model.User
平台用户的 UUID 和启用状态。
@@ -0,0 +1,5 @@
requireActive(active: true) () {}
requireActive(active: false) () {
fail(dever.api.Error.Unauthorized)
}
@@ -0,0 +1,89 @@
<!-- Generated by dever inspect --links. Do not edit. -->
<!-- source-sha256: 8c879374d859fd5ed0f03587252b2d7be479f6a1198251f23907f88f8d0c2b39 -->
# access.user
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
## 入口
没有已收录的入口。
## 外部调用方
| 能力 | 调用方 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.user.requireActive](app.dever#L1) | [access.session.verified](../session/app.dever#L35) | 调用 | [../session/app.dever:36:3](../session/app.dever#L36,3) |
## 能力与内部调用
### access.user.domain.requireActive
定义:[domain.dever:1:1](domain.dever#L1,1)、[domain.dever:3:1](domain.dever#L3,1)
<a id="flow-fa5889ca969e0acf-1"></a>
```mermaid
flowchart TD
n0(["requireActive"])
click n0 href "domain.dever#L1,1" "打开源码"
n1(["返回"])
n2(["传播错误"])
n3{"按实参匹配分句(互斥)"}
n4["active: true"]
click n4 href "domain.dever#L1,1" "打开源码"
n5["active: false"]
click n5 href "domain.dever#L3,1" "打开源码"
n6["fail dever.api.Error.Unauthorized"]
click n6 href "domain.dever#L4,8" "打开源码"
n0 --> n3
n3 -->|"active: true"| n4
n4 --> n1
n3 -->|"active: false"| n5
n5 --> n6
n6 --> n2
```
没有已收录的下游调用。
### access.user.model.User
定义:[model.dever:1:1](model.dever#L1,1)
没有已收录的下游调用。
### access.user.requireActive
定义:[app.dever:1:1](app.dever#L1,1)
<a id="flow-f368d51d0e19702c-1"></a>
```mermaid
flowchart TD
n0(["requireActive"])
click n0 href "app.dever#L1,1" "打开源码"
n1(["返回"])
n3["access.user.model.exists"]
click n3 href "app.dever#L3,5" "打开源码"
n4[["access.user.domain.requireActive"]]
click n4 href "app.dever#L2,3" "打开源码"
n0 --> n3
n3 --> n4
n4 --> n1
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| access.user.domain.requireActive | [app.dever:2:3](app.dever#L2,3) | [access.user.domain.requireActive](link.md#flow-fa5889ca969e0acf-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.user.domain.requireActive](domain.dever#L1) | 领域内 | 调用 | [app.dever:2:3](app.dever#L2,3) |
| [access.user.model.exists](model.dever#L1) | 领域内 | 内建操作 | [app.dever:3:5](app.dever#L3,5) |
@@ -0,0 +1,4 @@
global type User {
uuid: Uuid generated unique
active: Bool default true
}
@@ -0,0 +1,7 @@
# 文章 REST
用 Model 和 REST 提供文章增删改查,正文保留原文。
- [文章 REST](doc/api-admin-entries.md)
- [数据结构](doc/model.md)
- [调用流程](link.md)
@@ -0,0 +1,5 @@
# 文章 REST
## content.article.api.admin.entries.rest
按 Article Model 提供增删改查,由站点认证和角色权限保护。
@@ -0,0 +1,5 @@
# 数据结构
## content.article.model.Article
文章标题和正文;标题自动去首尾空白,正文使用 raw 保留原文。
@@ -0,0 +1,254 @@
<!-- Generated by dever inspect --links. Do not edit. -->
<!-- source-sha256: 09e55892630b724d8c2e599fc3b266e774d5c3576ea2741220017ee9246fa910 -->
# content.article
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
## 入口
- REST [content.article.api.admin.entries.rest](api/admin/entries.dever#L1)
## 外部调用方
当前加载的源码中没有已收录的外部调用。
## 能力与内部调用
### content.article.api.admin.entries.rest
定义:[api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1)
<a id="flow-96f9bcd93be27ba3-1"></a>
```mermaid
flowchart TD
n0(["content.article.api.admin.entries.rest"])
click n0 href "api/admin/entries.dever#L1,1" "打开源码"
n1{"自动 REST /content/article/admin/entries 或其正整数 id 路径;topic #34;admin/entries#34;"}
n2(["返回 HTTP 404"])
n3{"集合允许 GET / POST;详情允许 GET / PUT / DELETE"}
n4(["返回 HTTP 405 + Allow"])
n5["取得认证时间"]
n6(["返回 HTTP 500"])
n7["按 topic #34;admin/entries#34; 选择站点与 Provider"]
n8(["返回 HTTP 403"])
n9{"读取 Bearer 或站点 Cookie;两者互斥"}
n10(["返回 HTTP 401"])
n11["认证上下文就绪"]
n12(["返回 HTTP 401"])
n13["校验 Token 的签名、有效期及站点"]
n14{"Cookie 且非 GET / HEAD / OPTIONS?"}
n15["检查站点 Origin"]
n16(["返回 HTTP 403"])
n17(["返回 HTTP 401"])
n18{"按 Provider.verify 选择验证函数"}
n19[["access.session.verify"]]
click n19 href "api/admin/entries.dever#L1,1" "打开源码"
n20(["返回 HTTP 业务错误映射状态;其他故障 500"])
n21["用验证结果建立身份"]
n22(["返回 HTTP 认证错误状态"])
n23(["返回 HTTP 500"])
n24["建立请求数据库上下文"]
n25{"有已验证租户?"}
n26["数据库上下文就绪"]
n27{"部署启用了租户存储?"}
n28(["返回 HTTP 403"])
n29["按已验证租户选择数据库"]
n30(["返回 HTTP 503"])
n31{"检查租户组件启用状态:content"}
n32(["返回 HTTP 403"])
n33(["返回 HTTP 503"])
n34{"写请求(非 GET)?"}
n35["进入写入口事务"]
n36["开始 REST 操作"]
n37["失败:保留 HTTP 错误状态"]
n38{"此请求拥有写入口事务?"}
n39["回滚入口拥有的事务"]
n40(["返回 HTTP 500"])
n41["事务边界结束"]
n42(["返回错误响应"])
n43{"权限检查:content.article.{site}.{read/create/replace/delete}"}
n44["权限通过"]
n45["取得目标 Model 的数据库执行器"]
n46{"按方法与 id 选择一个操作"}
n47["REST 响应已编码"]
n48["解码分页和可选搜索参数"]
click n48 href "api/admin/entries.dever#L1,1" "打开源码"
n49[["content.article.model.list:COUNT + 分页 SELECT"]]
click n49 href "model.dever#L1,1" "打开源码"
n50["按 REST 读字段编码结果(不含私有字段)"]
n51["检查空参数合同"]
click n51 href "api/admin/entries.dever#L1,1" "打开源码"
n52[["content.article.model.get:SELECT 单条"]]
click n52 href "model.dever#L1,1" "打开源码"
n53["按 REST 读字段编码结果(不含私有字段)"]
n54["解码创建字段"]
click n54 href "api/admin/entries.dever#L1,1" "打开源码"
n55["取得已解码字段 title"]
n56["规范化字段 title:去首尾空白;null 保持不变"]
click n56 href "model.dever#L2,3" "打开源码"
n57["检查字段 title 的长度 / 有限数合同"]
click n57 href "model.dever#L2,3" "打开源码"
n58["取得已解码字段 body"]
n59["填入 created_at"]
n60[["content.article.model.create:INSERT RETURNING"]]
click n60 href "model.dever#L1,1" "打开源码"
n61["按 REST 读字段编码结果(不含私有字段)"]
n62["解码替换字段"]
click n62 href "api/admin/entries.dever#L1,1" "打开源码"
n63["取得已解码字段 title"]
n64["规范化字段 title:去首尾空白;null 保持不变"]
click n64 href "model.dever#L2,3" "打开源码"
n65["检查字段 title 的长度 / 有限数合同"]
click n65 href "model.dever#L2,3" "打开源码"
n66["取得已解码字段 body"]
n67[["content.article.model.update:UPDATE RETURNING"]]
click n67 href "model.dever#L1,1" "打开源码"
n68["按 REST 读字段编码结果(不含私有字段)"]
n69["检查空参数合同"]
click n69 href "api/admin/entries.dever#L1,1" "打开源码"
n70[["content.article.model.delete:DELETE"]]
click n70 href "model.dever#L1,1" "打开源码"
n71["编码空结果"]
n72{"此请求拥有写入口事务?"}
n73["提交入口拥有的事务"]
n74(["返回 HTTP 500"])
n75["事务边界结束"]
n76["发布暂存的认证 Header / Cookie"]
n77(["返回成功响应"])
n0 --> n1
n1 -->|"站点未匹配或 id 无效"| n2
n1 --> n3
n3 -->|"不允许的方法"| n4
n3 --> n5
n5 -->|"失败"| n6
n5 --> n7
n7 -->|"无站点绑定"| n8
n7 --> n9
n9 -->|"凭据格式错误或冲突"| n10
n9 -->|"无凭据"| n12
n9 -->|"有凭据"| n14
n14 -->|"否"| n13
n14 -->|"是"| n15
n15 -->|"不符合"| n16
n15 -->|"通过"| n13
n13 -->|"无 Provider 或 Token 无效"| n17
n13 --> n18
n18 -->|"access.session.verify"| n19
n19 -->|"验证函数失败"| n20
n19 --> n21
n21 -->|"身份不合法"| n22
n21 -->|"通过"| n11
n18 -->|"无匹配验证函数"| n23
n11 --> n24
n24 --> n25
n25 -->|"否"| n27
n27 -->|"是:缺少租户身份"| n28
n27 -->|"否"| n26
n25 -->|"是"| n29
n29 -->|"选择失败"| n30
n29 --> n31
n31 -->|"未启用"| n32
n31 -->|"查询失败"| n33
n31 -->|"允许"| n26
n26 --> n34
n34 -->|"是"| n35
n34 -->|"否"| n36
n35 --> n36
n37 --> n38
n38 -->|"是"| n39
n39 -->|"事务结束失败"| n40
n38 -->|"否"| n41
n39 -->|"完成"| n41
n41 --> n42
n36 --> n43
n43 -->|"拒绝:Access 状态;查询故障:500"| n37
n43 -->|"允许"| n44
n44 --> n45
n45 -->|"数据库错误:404 / 400 / 409 / 500"| n37
n45 --> n46
n46 -->|"GET 集合"| n48
n48 -->|"输入或解码错误:400 等对应状态"| n37
n48 --> n49
n49 -->|"数据库错误:404 / 400 / 409 / 500"| n37
n49 --> n50
n50 -->|"编码失败:500"| n37
n50 --> n47
n46 -->|"GET 详情"| n51
n51 -->|"输入或解码错误:400 等对应状态"| n37
n51 --> n52
n52 -->|"数据库错误:404 / 400 / 409 / 500"| n37
n52 -->|"无记录或删除影响 0 行:404"| n37
n52 --> n53
n53 -->|"编码失败:500"| n37
n53 --> n47
n46 -->|"POST 集合"| n54
n54 -->|"输入或解码错误:400 等对应状态"| n37
n54 --> n55
n55 --> n56
n56 --> n57
n57 -->|"长度不符合:400;非有限数:500"| n37
n57 --> n58
n58 --> n59
n59 -->|"值生成失败:数据库错误映射"| n37
n59 --> n60
n60 -->|"数据库错误:404 / 400 / 409 / 500"| n37
n60 --> n61
n61 -->|"编码失败:500"| n37
n61 --> n47
n46 -->|"PUT 详情"| n62
n62 -->|"输入或解码错误:400 等对应状态"| n37
n62 --> n63
n63 --> n64
n64 --> n65
n65 -->|"长度不符合:400;非有限数:500"| n37
n65 --> n66
n66 --> n67
n67 -->|"数据库错误:404 / 400 / 409 / 500"| n37
n67 -->|"无记录或删除影响 0 行:404"| n37
n67 --> n68
n68 -->|"编码失败:500"| n37
n68 --> n47
n46 -->|"DELETE 详情"| n69
n69 -->|"输入或解码错误:400 等对应状态"| n37
n69 --> n70
n70 -->|"数据库错误:404 / 400 / 409 / 500"| n37
n70 -->|"无记录或删除影响 0 行:404"| n37
n70 --> n71
n71 -->|"编码失败:500"| n37
n71 --> n47
n47 --> n72
n72 -->|"是"| n73
n73 -->|"事务结束失败"| n74
n72 -->|"否"| n75
n73 -->|"完成"| n75
n75 --> n76
n76 --> n77
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| access.session.verify | [api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1) | [access.session.verify](../../access/session/link.md#flow-01943b8d6356f307-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.session.verify](../../access/session/app.dever#L7) | 跨领域 [access.session](../../access/session/link.md) | 身份验证 | [api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1) |
| [content.article.model.create](model.dever#L1) | 领域内 | REST Model 操作 | [api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1) |
| [content.article.model.delete](model.dever#L1) | 领域内 | REST Model 操作 | [api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1) |
| [content.article.model.get](model.dever#L1) | 领域内 | REST Model 操作 | [api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1) |
| [content.article.model.list](model.dever#L1) | 领域内 | REST Model 操作 | [api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1) |
| [content.article.model.update](model.dever#L1) | 领域内 | REST Model 操作 | [api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1) |
### content.article.model.Article
定义:[model.dever:1:1](model.dever#L1,1)
没有已收录的下游调用。
@@ -0,0 +1,4 @@
type Article {
title: Text(1, 200)
body: Text raw
}
@@ -0,0 +1,7 @@
# 就绪检查
提供公开 HTTP 就绪接口。
- [就绪接口](doc/api-admin-status.md)
- [业务操作](doc/app.md)
- [调用流程](link.md)
@@ -0,0 +1 @@
public get ready = app.status
@@ -0,0 +1,3 @@
status() (healthy: Bool) {
healthy = true
}
@@ -0,0 +1,5 @@
# 就绪接口
## system.health.api.admin.status.ready
公开就绪检查,返回 true。
@@ -0,0 +1,5 @@
# 业务操作
## system.health.status
返回 true,供就绪检查使用。
@@ -0,0 +1,122 @@
<!-- Generated by dever inspect --links. Do not edit. -->
<!-- source-sha256: bda5697651e3fc58a69923938754c00177ae31286d9b8d63f2bce96eb2a9620c -->
# system.health
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
## 入口
- API [system.health.api.admin.status.ready](api/admin/status.dever#L1) → [system.health.status](app.dever#L1)
## 外部调用方
当前加载的源码中没有已收录的外部调用。
## 能力与内部调用
### system.health.api.admin.status.ready
定义:[api/admin/status.dever:1:1](api/admin/status.dever#L1,1)
<a id="flow-b6734ecb2e8b0bd8-1"></a>
```mermaid
flowchart TD
n0(["system.health.api.admin.status.ready"])
click n0 href "api/admin/status.dever#L1,20" "打开源码"
n1{"路由 GET /system/health/admin/status/ready,topic #34;admin/status#34; 接受请求?"}
n2(["返回 HTTP 404"])
n3["进入 API"]
n4["取得认证时间"]
n5(["返回 HTTP 500"])
n6["按 topic #34;admin/status#34; 选择站点与 Provider"]
n7(["返回 HTTP 403"])
n8{"读取显式 Authorization;public 忽略自动 Cookie"}
n9(["返回 HTTP 401"])
n10["认证上下文就绪"]
n11["校验 Token 的签名、有效期及站点"]
n12(["返回 HTTP 401"])
n13{"按 Provider.verify 选择验证函数"}
n14[["access.session.verify"]]
click n14 href "api/admin/status.dever#L1,20" "打开源码"
n15(["返回 HTTP 业务错误映射状态;其他故障 500"])
n16["用验证结果建立身份"]
n17(["返回 HTTP 认证错误状态"])
n18(["返回 HTTP 500"])
n19["失败:保留 HTTP 错误状态"]
n20(["返回错误响应"])
n21["读取 API 输入"]
n22["解码具体参数并检查字段合同"]
n23[["system.health.status"]]
click n23 href "api/admin/status.dever#L1,20" "打开源码"
n24["按输出类型检查可序列化值并编码响应"]
n25["发布暂存的认证 Header / Cookie"]
n26(["返回成功响应"])
n0 --> n1
n1 -->|"未匹配此入口:继续路由;均未匹配"| n2
n1 --> n3
n3 --> n4
n4 -->|"失败"| n5
n4 --> n6
n6 -->|"无站点绑定"| n7
n6 --> n8
n8 -->|"凭据格式错误或冲突"| n9
n8 -->|"没有显式凭据:匿名"| n10
n8 -->|"有 Bearer"| n11
n11 -->|"无 Provider 或 Token 无效"| n12
n11 --> n13
n13 -->|"access.session.verify"| n14
n14 -->|"验证函数失败"| n15
n14 --> n16
n16 -->|"身份不合法"| n17
n16 -->|"通过"| n10
n13 -->|"无匹配验证函数"| n18
n19 --> n20
n10 --> n21
n21 -->|"失败:输入错误状态"| n19
n21 --> n22
n22 -->|"失败:400"| n19
n22 --> n23
n23 -->|"业务错误映射状态;其他故障:500"| n19
n23 --> n24
n24 -->|"编码失败:500"| n19
n24 --> n25
n25 --> n26
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| access.session.verify | [api/admin/status.dever:1:20](api/admin/status.dever#L1,20) | [access.session.verify](../../access/session/link.md#flow-01943b8d6356f307-1) |
| system.health.status | [api/admin/status.dever:1:20](api/admin/status.dever#L1,20) | [system.health.status](link.md#flow-3f8f51687b91cbe2-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.session.verify](../../access/session/app.dever#L7) | 跨领域 [access.session](../../access/session/link.md) | 身份验证 | [api/admin/status.dever:1:20](api/admin/status.dever#L1,20) |
| [system.health.status](app.dever#L1) | 领域内 | API 绑定 | [api/admin/status.dever:1:20](api/admin/status.dever#L1,20) |
### system.health.status
定义:[app.dever:1:1](app.dever#L1,1)
<a id="flow-3f8f51687b91cbe2-1"></a>
```mermaid
flowchart TD
n0(["status"])
click n0 href "app.dever#L1,1" "打开源码"
n1(["返回"])
n3["赋值 healthy"]
click n3 href "app.dever#L2,13" "打开源码"
n0 --> n3
n3 --> n1
```
没有已收录的下游调用。
@@ -0,0 +1,3 @@
title() () {
assertEq(domain.normalize(" Draft "), "Draft")
}
@@ -0,0 +1,12 @@
{
"database": { "default": { "type": "sqlite", "path": "data/jobs.db" } },
"runtime": { "mode": "worker", "shutdown_ms": 30000 },
"job": {
"workers": 1,
"poll_ms": 1000,
"lease_ms": 60000,
"retry_base_ms": 1000,
"retry_max_ms": 300000
},
"log": { "level": "info" }
}
@@ -0,0 +1,9 @@
# 归档任务
按请求键入队任务并保存归档记录。
- [归档命令](doc/api.md)
- [业务操作](doc/app.md)
- [任务](doc/job.md)
- [数据结构](doc/model.md)
- [调用流程](link.md)
@@ -0,0 +1,3 @@
cmd submit = app.submit
cmd total = app.total
@@ -0,0 +1,21 @@
type Request {
key: Text
title: Text
}
submit(input: Request) (id: Id) {
id = dever.job.enqueue(job.store, input, input.key)
}
store(input: Request) () {
stored = model.create(
{
request_key = input.key
title = input.title
}
)
}
total() (count: Int) {
count = model.count()
}
@@ -0,0 +1,9 @@
# 归档命令
## report.archive.api.submit
使用请求键入队归档任务。
## report.archive.api.total
读取归档记录数。
@@ -0,0 +1,17 @@
# 业务操作
## report.archive.Request
归档请求的幂等键和标题。
## report.archive.submit
使用 input.key 入队归档任务,返回任务编号;相同键不会重复入队。
## report.archive.store
保存请求键和标题,供归档 Job 调用。
## report.archive.total
返回已保存的归档记录数。
@@ -0,0 +1,5 @@
# 任务
## report.archive.job.store
执行归档,最多执行 3 次(含首次),单次期限 30 秒。
@@ -0,0 +1,5 @@
# 数据结构
## report.archive.model.Archive
归档记录,请求键唯一。
@@ -0,0 +1,5 @@
database default
job store(input: app.Request) () retry(3) timeout(30000) {
app.store(input)
}
@@ -0,0 +1,206 @@
<!-- Generated by dever inspect --links. Do not edit. -->
<!-- source-sha256: eb439b9c77c2bf6e0d55e26f5ad1b2a024bc2783baebddb3756d892621724e0c -->
# report.archive
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
## 入口
- CMD [report.archive.api.submit](api.dever#L1) → [report.archive.submit](app.dever#L6)
- CMD [report.archive.api.total](api.dever#L3) → [report.archive.total](app.dever#L19)
- Job [report.archive.job.store](job.dever#L3)
## 外部调用方
当前加载的源码中没有已收录的外部调用。
## 能力与内部调用
### report.archive.Request
定义:[app.dever:1:1](app.dever#L1,1)
没有已收录的下游调用。
### report.archive.api.submit
定义:[api.dever:1:1](api.dever#L1,1)
<a id="flow-e67830c6d0f7d77d-1"></a>
```mermaid
flowchart TD
n0(["report.archive.api.submit"])
click n0 href "api.dever#L1,14" "打开源码"
n1[["report.archive.submit"]]
click n1 href "api.dever#L1,14" "打开源码"
n2(["返回命令结果;未捕获错误终止命令"])
n0 --> n1
n1 --> n2
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| report.archive.submit | [api.dever:1:14](api.dever#L1,14) | [report.archive.submit](link.md#flow-efc65eed6c9089ab-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [report.archive.submit](app.dever#L6) | 领域内 | CMD 绑定 | [api.dever:1:14](api.dever#L1,14) |
### report.archive.api.total
定义:[api.dever:3:1](api.dever#L3,1)
<a id="flow-72cad6bf19cb8c40-1"></a>
```mermaid
flowchart TD
n0(["report.archive.api.total"])
click n0 href "api.dever#L3,13" "打开源码"
n1[["report.archive.total"]]
click n1 href "api.dever#L3,13" "打开源码"
n2(["返回命令结果;未捕获错误终止命令"])
n0 --> n1
n1 --> n2
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| report.archive.total | [api.dever:3:13](api.dever#L3,13) | [report.archive.total](link.md#flow-b00d0e9bba4b635b-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [report.archive.total](app.dever#L19) | 领域内 | CMD 绑定 | [api.dever:3:13](api.dever#L3,13) |
### report.archive.job.store
定义:[job.dever:3:1](job.dever#L3,1)
<a id="flow-3208caa5c17a4976-1"></a>
```mermaid
flowchart TD
n0(["store"])
click n0 href "job.dever#L3,1" "打开源码"
n1(["返回"])
n3[["report.archive.store"]]
click n3 href "job.dever#L4,3" "打开源码"
n0 --> n3
n3 --> n1
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| report.archive.store | [job.dever:4:3](job.dever#L4,3) | [report.archive.store](link.md#flow-73a6995d7ee43f96-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [report.archive.store](app.dever#L10) | 领域内 | 调用 | [job.dever:4:3](job.dever#L4,3) |
### report.archive.model.Archive
定义:[model.dever:1:1](model.dever#L1,1)
没有已收录的下游调用。
### report.archive.store
定义:[app.dever:10:1](app.dever#L10,1)
<a id="flow-73a6995d7ee43f96-1"></a>
```mermaid
flowchart TD
n0(["store"])
click n0 href "app.dever#L10,1" "打开源码"
n1(["返回"])
n3["report.archive.model.create"]
click n3 href "app.dever#L11,12" "打开源码"
n4["赋值 stored"]
click n4 href "app.dever#L11,12" "打开源码"
n0 --> n3
n3 --> n4
n4 --> n1
```
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [report.archive.model.create](model.dever#L1) | 领域内 | 内建操作 | [app.dever:11:12](app.dever#L11,12) |
### report.archive.submit
定义:[app.dever:6:1](app.dever#L6,1)
<a id="flow-efc65eed6c9089ab-1"></a>
```mermaid
flowchart TD
n0(["submit"])
click n0 href "app.dever#L6,1" "打开源码"
n1(["返回"])
n3["持久化任务入队"]
click n3 href "app.dever#L7,8" "打开源码"
n4[["Job:report.archive.job.store"]]
click n4 href "app.dever#L7,8" "打开源码"
n5["赋值 id"]
click n5 href "app.dever#L7,8" "打开源码"
n0 --> n3
n3 -.->|"由 Worker 后续调度,不等待执行"| n4
n3 --> n5
n5 --> n1
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| Job:report.archive.job.store | [app.dever:7:8](app.dever#L7,8) | [report.archive.job.store](link.md#flow-3208caa5c17a4976-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [report.archive.job.store](job.dever#L3) | 领域内 | 入队 | [app.dever:7:26](app.dever#L7,26) |
### report.archive.total
定义:[app.dever:19:1](app.dever#L19,1)
<a id="flow-b00d0e9bba4b635b-1"></a>
```mermaid
flowchart TD
n0(["total"])
click n0 href "app.dever#L19,1" "打开源码"
n1(["返回"])
n3["report.archive.model.count"]
click n3 href "app.dever#L20,11" "打开源码"
n4["赋值 count"]
click n4 href "app.dever#L20,11" "打开源码"
n0 --> n3
n3 --> n4
n4 --> n1
```
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [report.archive.model.count](model.dever#L1) | 领域内 | 内建操作 | [app.dever:20:11](app.dever#L20,11) |
@@ -0,0 +1,4 @@
type Archive {
request_key: Text unique
title: Text
}
@@ -0,0 +1,12 @@
queued() () {
request = app.Request {
key = "test-report"
title = "Report"
}
first_id = app.submit(request)
assertEq(app.submit(request), first_id)
assertEq(dever.test.drainJobs(5), 1)
assertEq(app.total(), 1)
dever.test.advanceClock(1000)
assertEq(dever.test.drainJobs(5), 0)
}
+1
View File
@@ -0,0 +1 @@
7889e0edaf5fbf1060bef4ad6e27106128485c8412a6f6cd4457dc67e112986e
+2
View File
@@ -0,0 +1,2 @@
/data/
/hello-app
+13
View File
@@ -0,0 +1,13 @@
<!-- dever-language:start -->
# Dever 开发
先执行 `dever skill path .`,读取随包 SKILL.md、references/development.md 和任务相关指引。
- 源码使用 `.dever`。函数用 lowerCamelCase,字段和目录用 snake_case;业务角色按实际需要创建。
- 修改前读相关领域 README、doc、link.md 和源码。说明写具体用途,不复制签名、调用方和行号,不填占位文字。
- 复用标准库、Model 和现有组件,只实现当前需求。不写无意义包装、重复检查、假调用或静默兜底,不弱化测试。
- Model Text 默认去首尾空白;保留原文用 raw,大小写转换用 lower/upper,不重复清洗。
- 配置放 config/setting.json;身份和归属来自可信上下文,真实凭据不提交。
- 修改后运行 fmt --check、check、相关 test 和 inspect --links,再用 inspect --links --check 检查调用图。不手改生成关系。
- 保留无关修改;安装、部署、数据操作及 Git 操作按授权执行。
<!-- dever-language:end -->
+17
View File
@@ -0,0 +1,17 @@
# Dever 应用
这是最小命令行项目,无需数据库或第三方依赖。
```sh
dever check .
dever test .
dever run . -- hello.greeting.greet '{"name":"Dever"}'
dever build . --output hello-app
./hello-app hello.greeting.greet '{"name":"World"}'
```
源码在 module/,测试在 test/,配置在 config/setting.json。领域 README 介绍职责,doc 解释行为,link.md 展示调用流程。
修改后运行 `dever fmt . --check` 和相关测试,再执行 `dever inspect . --links` 更新流程;`--links --check` 检查是否过期。
AI 从 AGENTS.md 进入,项目不设置 dever.version。安装与完整示例见 [Dever 仓库](https://git.dever.cc/dever/core)。
@@ -0,0 +1,5 @@
{
"log": {
"level": "info"
}
}
@@ -0,0 +1,6 @@
# 问候
接收姓名并返回问候语,无持久化或外部调用。
- [行为说明](doc/greeting.md)
- [调用流程](link.md):运行 `dever inspect . --links` 生成。
@@ -0,0 +1 @@
cmd greet = app.greet
@@ -0,0 +1,3 @@
greet(name: Text) (message: Text) {
message = "Hello, " + name
}
Loaded 100 of 1479 files, more files were not shown because too many files have changed in this diff. Show more