资源包与调用装配边界
@weiliang0121/capix-resources 独立提供资源定义、登记、固定接线和本次访问。Runtime 使用资源包打开端点,再把端点交给 Work;阶段选择、恢复和调用退出属于 Runtime。
Resource + ResourceOffer
│ register
▼
Resources
│ bind(deps) 固定登记与申请的对应关系
▼
ResourceBinding
│ open(owner) 每次建立访问
▼
ResourceAccess ───────────→ endpoints
▲ │
│ 检查本次窗口 │ 操作委托
└──── ResourceActivity ←──┘
│
作者资源方法对象与归属
| 对象 | 所属 | 职责与寿命 |
|---|---|---|
| Resource | resources | 包裹宿主持有的原实体,不冻结实体状态、不接管实体生命周期 |
| ResourceOffer | resources | 固定资源操作方法、捕获、收窄及描述;签发申请 |
| ResourceRequest | resources | 类型化身份引用,不查找供给或保存端点 |
| Resources | resources | 登记 Resource 与 Offer,按 deps 建立固定接线 |
| ResourceBinding | resources | 固定申请与供给入口;每次 open 建立独立访问,不回查可变注册表 |
| ResourceProvider | resources | 从宿主来源取得材料,固定选取函数 |
| ResourceSupply / ResourceSupplyEntry | resources | 持有已选定的材料入口;同一捕获可供不同子树或窗口使用 |
| ResourceAccess | resources | 持有一个不可重新打开的访问窗口,以及本窗口的端点缓存 |
| ResourceActivity | resources | 一个窗口共享的操作执行机制;操作状态各自局部保存 |
| ResourceEndpointActivity | resources | 为精确 expose 固定当前资源身份,委托同一 Activity,不另建窗口 |
| ResourceOwner | resources 定义,宿主实现 | 调用来源、有效性检查、故障策略、信号与事实出口 |
| Resources / CapturedResources | runtime | 组合资源登记与结构作用域、成组捕获、收窄和 Interaction 接线 |
| ResourceInstallation | runtime | 解算结构供给路线,为本次调用建立固定材料供给 |
| InvocationResources | runtime | 选择调用阶段与轮次的窗口,连接 owner,关闭阶段和调用访问 |
| ResourceRecord | runtime/recording | 资源事实的可选记录投影 |
资源包不导入 Scene、Callable、Invocation、Context、CP、Recorder 或 Interaction。访问窗口不递归查询祖先调用,不因父层状态改变重新解算申请。
独立使用
import { Resource, ResourceOffer, Resources } from '@weiliang0121/capix-resources';
const lookup = new ResourceOffer({
methods: {
translate(dictionary, word) {
return dictionary.get(word) ?? word;
},
},
});
const resources = new Resources();
resources.register(new Resource(new Map([['hello', '你好']])), lookup);
const binding = resources.bind({ dictionary: lookup.request() });
const access = binding.open();
try {
const result = await access.endpoints.dictionary.translate('hello');
} finally {
access.close();
}bind 核对全部 deps 并固定登记快照,不捕获材料或执行资源方法。由登记建立的 Binding 每次 open 取得新的捕获;ResourceBinding.fromSupply(deps, supply) 则显式复用已经建立的供给,适合调用的不同阶段或已收窄子树。两者建立相同的 ResourceAccess。
ResourceOffer 有独立 UUID id;name 用于解释和既有资源观测,不承担唯一性约束。申请和供给仍按准确的 Offer 身份匹配,复制 UUID 或名称不能伪装为原申请。同一 Offer 的不同 deps 别名在一个窗口中取得相同端点;不同窗口不共享端点。
ResourceSupplyEntry.readMaterial() 只返回材料。端点由 entry.offer.createEndpoint(material, activity) 建立,ResourceRequest 不再通过同名 get/read 混合材料和端点。普通 methods 的建立仅生成包装,首次调用才执行作者方法;精确 expose 仍支持同步值、订阅及固定写入凭据。
操作与访问窗口
普通方法只按需接收第三参数 { signal, emit }。signal 供资源适配协作取消,emit 发布本次操作增量;方法返回后 emit 永久失效。业务端通过 delta/result 接收,不取得原始实体或 Activity。
检查窗口 → 发布 started → 再检查 → 执行资源方法
│
固定 Outcome
│
发布真实 returned / threw
│
检查访问资格
│
succeeded / failed 接收
│
再检查 → 返回接收结果方法失败和结果接收失败有独立边界:回调错误不再次进入同一个 failed。业务成功处理普通失败时,不把共享窗口标记为失败;取消、必要捕获、窗口关闭、严格观测等机制故障不可通过 fallback 转成成功。
窗口关闭以后,已开始的操作仍可能真实完成。保留真实返回事实,然后拒绝新接收;如果方法失败同时窗口失效,保留两个原因。异步接收回调正在等待时关闭,也会拒绝它后续的资源访问和最终结果。增量消费者是被动预览,失败仅停用本次消费。
close() 结束访问资格并通知活动操作清理,幂等且不可重新打开;不调用原实体的 close,不回滚外部效果,不自动取消独立 Interaction,也不保证排空未 await 的资源操作。宿主应等待必要异步工作完成,再关闭访问;close 自身可能抛错,需要分别保留正文与关闭结果,不能让 finally 中的清理覆盖正文失败。构造/启动失败关闭部分资源时也保留两条因果。
资源适配作者可以用 operation.onClose(cleanup) 登记同步清理,返回解除登记函数。操作真正返回、访问窗口关闭或本次调用信号取消时耗尽清理;用于解除订阅、关闭 reader 并唤醒其挂起的本地读取。关闭不会用 Promise.race 冒充原操作已经退出,适配器仍需让自己的等待真正结束。operation.signal 保持原调用信号;窗口关闭传 ResourceAccessClosed,调用取消传原 signal.reason,不能把二者互相冒充。正常操作结束也清理,但不因此把正常结果变成失败。清理回调必须同步返回 undefined;一个回调故障不能阻止其他清理,非法异步回调的迟到拒绝会被消费。普通节点仍使用 delta/result,无需管理这个资源适配端口。
Runtime 接入
场景宿主仍从 @weiliang0121/capix-runtime 取得 Resources 和 Runner。这个 Resources 负责结构范围的组合登记,并使用资源包的 Provider、Supply、Binding 与 Access;业务结构不接触这些装配对象。
import { Resource, ResourceOffer } from '@weiliang0121/capix-resources';
import { Scene, Node, Resources, Runner } from '@weiliang0121/capix-runtime';
const dictionary = new ResourceOffer({
methods: {
translate(origin, word) {
return origin.get(word) ?? word;
},
},
});
const translate = new Node({
deps: { dictionary: dictionary.request() },
run: (work) => work.resources.dictionary.translate(work.input),
});
const scene = new Scene().add({ translate });
scene.run = (work) => work.children.translate.invoke(work.input);
const resources = new Resources();
resources.register(new Resource(new Map([['hello', '你好']])), dictionary);
const runner = new Runner(scene, resources).build();
await runner.run('hello');可运行版本见 dictionary.mjs。
InvocationResources 持有调用侧阶段选择,依据已有供给打开 ResourceAccess;切换阶段、CP 恢复和调用退出明确关闭相应窗口。供给与窗口分开:恢复或收窄仍依据固定材料,不能借父层供给扩大 Work 的 deps。观测处理也取得本次窗口,处理返回后关闭,不复用已失效端点。
调用侧 closeAllResult() 同步耗尽所有窗口并返回 OperationOutcome,供 Invocation 退出使用;closeAll() 保持原有抛错接口。单错误保持身份,多错误聚合,失败值允许 undefined。两个入口都不等待未 await 的普通资源 Promise。完整时序见 调用退出契约。
@weiliang0121/capix-resources/operation 提供 OperationOutcome、Reception、OperationOptions、OperationResult 和 receiveOperation。子调用共用这组正常/异常接收契约,实际子接纳、关系归属和 CP 留在 Runtime。
宿主故障接缝
资源包不能识别任意宿主的错误类。有 owner 的访问必须提供完整策略:
interface ResourceFailurePolicy {
isProtected(error: unknown): boolean;
isCancellation(error: unknown, signal: AbortSignal): boolean;
describe(error: unknown): unknown;
}打开访问前校验并固定策略函数和 receiver;失效 owner 不能先触发材料捕获。策略判定抛错或返回非法值属于机制失败,保留原操作错误;非法异步判定的迟到拒绝也被消费。validate、故障报告、接纳来源和观测端口同样要求同步完成;保留 void 回调的普通同步返回值,异步返回会被拒绝并消费迟到拒绝。ResourceAccessClosed 和 ResourcePortError 在资源包及 Runtime 中均属于受保护故障,包含在 AggregateError 中也不能进入业务 fallback。Runtime 提供自己的策略,Runner 使用者不必配置它。
无 owner 的独立访问没有调用归属,仍受本地窗口控制;它不能替其他宿主判断控制错误。ResourceOwner.facts 只转交 ResourceFact,不认识消息、记忆或业务事件的字段;资源包依赖 contracts/changes,不再依赖 protocol。
领域事实与准确操作
实体拥有领域事件,changes 的 FactType<Event> 只提供进程内类型身份与固定正文。名称可省略,且只作说明;两个同名类型不会互相匹配。create 用 stableValue 固定普通数据,match 仅向准确类型的同步回调交付 Stable<Event>。它没有订阅表、全局注册、独立生命周期或持久身份。载体带有私有 symbol 标记,stableValue 会拒绝将它用于正式交付或 CP state,不能把它直接当作 wire DTO;需要保存时应由领域适配明确取出正文并编码。
const changed = new FactType<{ revision: number; content: string }>('note.changed');
const offer = new ResourceOffer({
methods: {
replace(origin: Notes, input: string, operation: ResourceOperation) {
// Notes 自己检查、提交并发布事实;资源方法不从返回值猜测效果。
return origin.replace(input, {
origin: operation.origin,
observation: operation.facts(changed),
});
},
},
});operation.facts(type) 返回准确类型的同步 consumer;没有观察方时返回 undefined。它固定本次操作,而非端点上的可变“当前操作”。低层 activity.runSync(action, detail, execute) 的 execute 也取得同一类操作端口。正常结束在结果接收前关闭;窗口关闭、信号取消或结束后发布均被拒绝。宿主校验函数重入关闭时也不能在返回后放行发布。
传出的 ResourceFact 包含 contextId、offerId、supplyId、access、本次 operationId、序号、时间及不透明事实。operationId 对应普通 ResourceObservation 的 interactionId;此名称不表示独立 Interaction。它证明事实经该操作转交,不自动证明该操作造成事件所描述的变化。确认旧效果、读取旧事实与新变化仍由生产者及适配解释;不会把 contextId 注入领域正文。
实体在变化发生处调用自己的 publishFact,必要捕获与观察各自尝试。因此方法后来抛错,已经转交的真实变化仍保留;记录失败也不回滚实体。固定数据失败、异步必要端口及 strict 观察失败属于受保护故障,不能由普通 failed 回调接收。isolate 会停用坏消费者,继续向健康同伴转交。资源 Promise 没有因此变成子调用或事务。
TranscriptFacts/MemoryFacts 是各自生产者公开的类型身份,专用资源适配在内部使用上述同一机制。新增业务实体无需修改资源、调用或 CP 的领域联合。
验证范围
验证包括:独立包链路与类型拒绝、固定登记、每次捕获、同一窗口别名、不同窗口隔离、关闭后晚到返回、接收回调重入与关闭、并发增量、严格观测、同步端点、跨作用域收窄,以及 Runtime CP、固定效果和交互生命周期回归。资源包与生成声明无 Runtime 反向依赖;不把未 await 操作的访问关闭误称为后台任务完成。