@ 12- apps/ 接线
包对接接线合同. A 类 生产商 软件包的一半——声明 你提供什么,你需要什么,每个能力——和a 消费者 半个 为东道主——通过声明,将每个声明约束在共享的港口,并获得 一份订单, 冲突检查, 报告汇总回.
零从属. 每个能力形状都是 结构双 缩放 所以通过合同就是宣布 已存在, 不重写 .
问题
每个 @12-apps/* 软件包已经遵循相同的隐含插件形状 -
createApi*(config) → { routes }, (中文(简体) ), createWeb*(config) → { page, … }页:1
./hono 适配器,有时是Prisma部分 ./mcp 或者工作蓝图 不过
形状存在于 TypeScript 常规和每包 ADOPTING.md 文件,
因此:
- 每个主机通过阅读重新发现每个包的合同;
- 通过手写表面文件、路由的原始主机线~24包 文件、导航文件、图标文件、徽章文件和政策表格;
- 各个软件包必须保持相同的形状(
JobBlueprint对PaymentsJobBlueprint)在公约中保持不变,没有汇编 检查任何地方; - 添加能力的版本凸起 默默到达 : report-builder 5.x 发送了3个工作复制端点,即它自己的客户呼叫,以及来源 主机从未挂载过它们——编辑器自动保存了404s而无一物为红色;
- 应当通知的软件包或邮件不能说:没有通知端口 而唯一一个电子邮件端口就住在另一个软件包的服务器条目中.
模型
PRODUCER (the package) CONSUMER (the host)
┌──────────────────────────────┐ ┌────────────────────────────────┐
│ <pkg>/manifest │ │ createWiringHost({name, kind, │
│ defineManifest({ │ │ ports}) │
│ name, contract: 1, │ │ │
│ permissions, mcp, │──────▶ .adoptServer({manifest, │
│ notifications, db, e2e, │ │ server, bindings}) │
│ server: ['http','jobs'], │ │ .adoptWeb({manifest, web, │
│ web: ['surface','areas'] │ │ bindings}) │
│ }) │ │ │
│ <pkg>/manifest/server │ │ .assemble() ──▶ routes, jobs, │
│ defineServerManifest(...) │ │ mailers, surfaces, │
│ <pkg>/manifest/web │ │ permissions, notifications, │
│ defineWebManifest(...) │ │ mcpEndpoints, db, areas, │
└──────────────────────────────┘ │ report │
└────────────────────────────────┘
每个运行时间分别显示,因为捆绑是物理:共享的
显示的是每次运行时的数据; 服务器显示的包含
createApi* 工厂和工作蓝图;网络清单载有
createWeb* 工厂; 带有 React 本地屏幕的软件包, 添加本地屏幕
显示(见 反应本地主机) (中文(简体) ). 共同清单
库存 运行时间列表,该清单是
完整性机制: 制作人拒绝一个从
以及一个不答覆的团体
能力得到红色 assemble() 命名软件包和能力.
每个申报的能力必须是: 绑定 或者说 以书面拒绝
原因下降是正当的——没有工人下降的绳子 jobs —— 说
但沉默不是: assemble() 丢弃,而任何问题都得不到答复。 这是
是什么使"撞出一个没有电线的能力" 从一个沉默中
404入为建设失败.
能力
| 类型 | 生产者声明 | 消费者与 | 合计 |
|---|---|---|---|
http | create(config) → { routes } ——现有 createApi* 工厂 | { mountPath, config } | routes、有具体顺序的、经核对的冲突 |
jobs | 名称空间 + 空空 蓝图 (双 @12-apps/jobs' JobBlueprint(中文(简体) ) | { deps } | jobs —— 说 JobDefinition 双胞胎; jobs.map(defineJob) 已经 |
email | createMailer(port) → mailer (单位:千美元) createAuthMailer 模式) | { port? } (对主机端口的默认) | mailers[pkg] |
mcp | WireMcpTool[] —— 说 McpEndpoint 加号 说明 | 收集; 主机构建的词汇工具通过 mcpEndpoints | mcpEndpoints,唯一性检查 |
permissions | rbac形捐款(source, (中文(简体) ), ids, (中文(简体) ), permissions, (中文(简体) ), labels(中文(简体) ) | 已收缴 | permissions ——饲料 composePermissions |
notifications | 蓝图: type,建议 category, (中文(简体) ), generate | 已收缴 | notifications 输入通知挂载 |
db | prisma 部分 + 迁移路径 | 已收缴 | db 输入同步工具 |
surface | create(config) → surface ——现有 createWeb* 工厂 | { config } | surfaces[pkg],一次建造(纪念规则,在一个地方举行) |
surface (内在) | 同样的 create(config) → surface,返回原始组件类型,单位: <pkg>/manifest/native | { config } 编号 native 东道主 | surfaces[pkg],一次建造 |
areas | 每个主机区的路由/导航/出入口建议 | 已收缴 | areas --项目导航数据 |
e2e | 旅行的次路径 | 已收缴 | 报告 |
反应本地主机
反应原生应用程序是第三种主机类型. 船舶筛选的软件包
又有第四项, <pkg>/manifest/native,以
defineNativeManifest 和库存为 native: ["surface"] 共享
显出. 它的表面有网络表面的形状——一个配置对象在,
组件类型外出- 但返回 React 本地组件, 因为本地
捆绑无法导入 react-dom 或网络组件库和网络
捆绑决不能导入 React Industrial .
// package: <pkg>/manifest/native — a plain value; wiring stays a type-only edge
import type { AnyNativeManifest } from "@12-apps/wiring";
export const threadsNativeManifest = {
name: "@12-apps/threads",
surface: { create: createNativeThreads },
} as const satisfies AnyNativeManifest;
// package: its own test suite runs the producer's assertions
expect(defineNativeManifest(threadsManifest, threadsNativeManifest)).toBe(threadsNativeManifest);
// host: the React Native app
const host = createWiringHost({ name: "field-app", kind: "native", ports: { loggerFor } });
const { surface } = host.adoptNative({
manifest: threadsManifest,
native: threadsNativeManifest,
bindings: { surface: { config: { fetch, copy } } },
});
host.assemble();
surface 是两个可能的运行时间之一的能力,所以词汇表
每个软件包的答案不会增长:只检查符合性门
(单位:千美元) native: 库存有其 ./manifest/native 子路径,以及
子路径不会超过清单(assertExportsMirror 检查相同
在包里自己的套房。 本地主机没有 areas (它的导航是
自己的; 载有 areas ),报告服务器
和网络将每个条目的运行时间减半,并回答
已宣布为 scope: "native"。就像网络主机一样,它仍然收集
共享数据能力(许可证、通知、MCP、db)
整体;它根本就没有食物供给它们.
港口(@12-apps/wiring/ports(中文(简体) )
一个主机提供一次,每个包需要: EmailPort (单位:千美元)
EmailDriver 形状,逐字记录, NotifyPort (新的——发出打出的通知)
万事不掷, LoggerPort, (中文(简体) ), JobsEnqueuePort (从来没有扔出),
ClockPort. 用于测试的记忆参考实施船.
何物仍为主机所有
合同故意移动 没有 今天为主机词汇的决定 : 每句用户化的句子(pt-BR 副本以配置方式到达,确切位置是 4.0倒置),警卫和演员解析,计划大门,导航 词汇驱动的MCP操作标识和摘要 工具、RBAC目录组件和数据库本身。 合同规定 每一个a 名称、 键入、 可替换 而不是一项公约.
示例
见 src/__tests__/fixture-package.ts 一个完整的生产者,和
src/__tests__/consumer.test.ts 两种宿主。 简本:
// host (API server)
import { createWiringHost } from "@12-apps/wiring/consumer";
import { defineJob } from "@12-apps/jobs";
const host = createWiringHost({ name: "web", kind: "server", ports: { email } });
host.adoptServer({
manifest: notesManifest,
server: notesServerManifest,
bindings: {
http: { mountPath: "/api/admin/:tenantSlug", config: { store } },
jobs: { deps: { store, ran } },
email: {},
},
});
const wired = host.assemble(); // throws while anything is unanswered
wired.jobs.map((job) => defineJob(job)); // every adopted package's jobs, one line
console.log(renderWiringReport(wired.report));
每个端点必须保存一条路由文件的主机( 覆盖门读取守护符)
) 仍采用:用手和针挂起
unclaimedRoutes(wired.routes, claimedKeys) 改为 [] 单位测试,即
本来能拿到404号工作拷贝的测试.
状态
释放: 软件包在 release-packages.txt (第一——没有)
依赖性)和 @12-apps/report-builder 是第一个由
消费者的绳子。 RFC和每个包装的适应报告均在
docs/wiring/。 。 。 .