文档
工程会议
用于域计算、模式验证、效果服务、数据库访问和验证的模式。
从英文自动翻译。 查看英文原文
源码结构
使用英文标识符、注释、文档和提交消息。 TypeScript 项目使用严格的检查;公共接口应该保留显式类型并解码外部值,而不是传播未经检查的数据。包清单公开源入口点,应用程序模块使用适合其构建工具的相对导入。
桌面具有组屏幕、组件、商店和助手 apps/desktop/src/features。路由适配器保留在 src/pages,而应用程序 shell 和客户端设置位于 src/app。核心几何体独立于 UI 状态和数据库访问。
几何和输出单位
核心矢量和解析实体几何使用毫米和弧度。场景放置、功能控制和 CAM 旋转轴字段具有特定于合同的角度约定,包括度数。在它们之间进行调整时遵循每个模式。 UI显示转换属于显示边界。加工操作还使用进给、主轴、深度和间隙参数,其含义由其模式定义;裸数字永远不应该仅从标签获取隐式单位。
G 代码发布自己的特定于控制器的输出转换和数字格式。共享发射器暴露 formatNumber,四舍五入到请求的十进制计数,标准化负零,并跟踪发射位置。 Feed 格式可能会省略整数值的小数部分。使用发射器而不是在每个呼叫者中独立格式化动作词。
保持模型证据和近似显示几何形状不同。细分曲面对于渲染和分析很有用,但其本身并不能证明精确的 B-rep 操作已成功。
效果服务
服务延伸 Context.Service 并公开类型化的效果返回方法。层构建实现并提供其依赖项。 apps/sidecar/src/layers.ts 是存储库、服务、处理程序、授权和运行时配置的集成点。
具有生命周期的资源(例如 SQLite 句柄)是在所属层的范围内获取和释放的。使用 Effect.try 或 Effect.tryPromise 将外部异常转化为有意义的错误。错误和跟踪中的稳定操作名称使得可以诊断故障,而无需将调用者耦合到存储驱动程序。
层组合很重要:合并在一起的兄弟层不会自动相互提供服务。 Sidecar 工作流程步骤注册在其需要的运行程序之后分层; AI步骤在AI服务可用的地方注册。
验证和错误
HTTP 输入和输出声明于 packages/api/src/groups。验证这些边界处的未知输入。合约包括纯消息错误、成功/错误信封和一些带密钥的错误主体;保留端点声明的状态和响应表示。
面向 HTTP 的错误使用效果模式和状态注释。例如, BadRequest, Unauthorized, NotFound, 和 Conflict 被声明于 packages/api/src/errors.ts。内部数据库故障携带操作并导致 DatabaseError。将内部故障映射到 HTTP 边界,而不是向客户端公开驱动程序详细信息。
合同变更后重新生成客户 bun run generate。检查提交的输出 bun run generate:check。生成的文件夹是输出;生成明确排除目标无法表示的端点。
持久化和配置
应用程序存储库使用 Database 服务来自 @cadcamfun/db 和它的 query(operation, run) 帮手。改变 packages/db/src/schema.ts 在生成迁移之前。一起查看生成的 SQL 和元数据;不要针对用户的数据库运行破坏性模式实验。
sidecar 读取配置 apps/sidecar/src/config.ts 并通过它 AppConfigService。凭证在服务需要时使用经过编辑的值。将令牌和提供商机密值保留在日志、文档和故障排除输出中。本机 shell 提供自己的启动令牌和存储路径。
文档工作室是一个单独的本地工具,有自己的路径选择 packages/web/script/studio-server.ts。不要假设其数据库路径或登录会话与正在运行的桌面匹配。
确认
bun test packages/core
bun run --filter @cadcamfun/core typecheck
bun run generate:check
# Documentation content and build
bun test packages/web/test
bun run --filter @cadcamfun/web typecheck
bun run --filter @cadcamfun/web build 选择行使受影响边界的检查。纯粹的内核更改需要数值和几何回归情况。 HTTP 更改需要处理程序和模式覆盖。文档更改需要有效的路线、锚点、示例和成功的站点构建。完整的工作区测试运行是 bun run test,其中包括包、应用程序和脚本。
可选字段、空值和传输值
可选字段可以被省略。可为空的字段可以显式包含 null。它们是独立的属性:项目合同要求 description 在返回的行中,同时允许 null,并且创建请求允许省略该字段。在构建客户端或修补保存的文档时保留这些区别。
传输验证也不同于业务验证。项目创建有效负载允许在架构级别省略名称,但处理程序返回 400 Project name is required 如果它丢失或为空。单独生成的类型无法确定现有资源或当前帐户是否允许执行操作。
HTTP 查询参数是编码文本。使用客户端声明的查询字段或 URL 编码,而不是将任意用户输入连接到 URL 中。将密钥或项目标识符编码为单个路径段,并在标头中而不是在复制的链接中发送不记名令牌。
维护可执行文档
针对其命名的确切公共表面编写一个示例。 Promise SDK 和完全基于合约的 Effect 客户端具有不同的方法/输入形状和覆盖范围。设置驱动的生成器请求不同于本机内核请求,生成的程序也不同于保存的刀具路径记录。
API 参考快照是从应用程序合约生成的。更改端点后,在 Web 工作区中重新生成它 bun run generate:api-reference。跑步 bun run check:api-reference 将其与当前源进行比较。资源分组、文章文本、导航和辅助检索仍然必须忠实地描述该契约。
记录从隔离运行捕获的响应时,请识别任何标准化 ID、时间戳或省略的字段。将请求模板标记为模板。不要将发明的标识符或未执行的模式示例作为观察到的生产响应提供。
贯穿始终的效果模式
import { Context, Data, Effect, Layer } from "effect"
import { AppConfigService } from "../config.ts"
export class StorageError extends Data.TaggedError("StorageError")<{
readonly operation: string
readonly cause: unknown
}> {}
export class Storage extends Context.Service<Storage, {
readonly read: (key: string) => Effect.Effect<Uint8Array, StorageError>
}>()("cadcamfun/Storage") {
static readonly layer = Layer.effect(Storage, Effect.gen(function* () {
const config = yield* AppConfigService // dependencies come from the context
return Storage.of({
read: (key) => Effect.tryPromise({
try: () => Bun.file(`${config.dataDir}/storage/${key}`).bytes(),
catch: (cause) => new StorageError({ operation: "read", cause }),
}),
})
}))
} - 服务键是命名空间的:
"cadcamfun/Database","cadcamfun/AppConfig","cadcamfun/ApiClient". - 错误是
Data.TaggedError内部服务和Schema.TaggedError与一个httpApiStatus在 HTTP 边界。处理程序将第一个映射到第二个。 - 终生使用的资源
Effect.acquireRelease因此当图层范围关闭时它们也会关闭。 - 命名函数使用
Effect.fn("area.name")因此跟踪带有可读的跨度名称,例如cam.runKernelRequest. - 配置被读取一次,在
AppConfigService;没有其他模块读取process.env在边车里。
命名和布局规则
| 事物 | 习俗 | 例子 |
|---|---|---|
| 文件 | kebab-case,每个文件一个概念,在它旁边进行测试 | cam-program.ts, cam-program.test.ts |
| 操作 ID | <group>.<method> | projects.create, health.check |
| 查询标签 | 点状,资源优先 | projects.list, toolpaths.update |
| 单位 | 毫米、度、秒;进给为 mm/min,Vc 为 m/min | feed: 5696 |
| 坐标 | Z向上,右手;程序在工作坐标中移动 | 原点位于库存中心顶部 |
| 识别号 | UUID 字符串 | crypto.randomUUID() |