webloom-framework 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,567 @@
1
+ # WebLoom SharedWorker call-first 消融施工单
2
+
3
+ ## 1. 文档状态
4
+
5
+ - 状态:SWCF-001~008、SWCF-010 的代码与本地/真实 Chromium 验收已完成;npm registry 发布待完成,SWCF-009 已正式拆为后续 typed-transfer 版本
6
+ - 施工性质:破坏性协议与 API 精简
7
+ - 目标发布:`webloom-framework@0.3.0`
8
+ - 涉及仓库:WebLoom、DemoWebLoom、keymaster.cc
9
+ - 本文是施工要求;当前实现与验收结果以各仓库工作树、验证记录和最终交付报告为准。
10
+ 由于 `webloom-framework@0.3.0` 尚未发布到 npm,公开 registry 消费者验收仍未完成;
11
+ Keymaster 的 `onConnection/onPortConnect` 按 SWCF-009 保留为迁移接缝;它们不是本版本
12
+ 的完成项,必须在后续 typed capability/transfer API 完成并通过安全回归后删除。
13
+
14
+ 本文覆盖《浏览器双运行时施工单》中与 SharedWorker `hello`、服务桥
15
+ `handshake`、`baseline/resync`、握手超时和自动重连有关的旧要求。其余
16
+ PluginHost、RuntimeUnit、ResourceScope、真实浏览器 realm 和下游安全要求继续有效。
17
+
18
+ ## 2. 施工目标
19
+
20
+ 把 SharedWorker 从“连接握手成功后才允许取得代理”改成 call-first 模型:
21
+
22
+ ```text
23
+ 创建 SharedWorker → 立即返回 RuntimeHandle → capability() 返回惰性代理
24
+
25
+ proxy.call() 统一返回结果或错误
26
+ ```
27
+
28
+ 连接建立不再证明后续调用可成功。Runtime 状态和服务目录只用于观察、路由和
29
+ 实例绑定;协议不兼容、服务未就绪、Provider 已撤销、断线和超时统一从
30
+ `proxy.call()` 的 Promise 返回。
31
+
32
+ ## 3. 不可退让的边界
33
+
34
+ ### 3.1 必须删除
35
+
36
+ - `webloom.runtime.hello`;
37
+ - RemoteService `handshake` 控制消息;
38
+ - Runtime 连接阶段的 handshake timer 和 `handshakeTimeoutMs`;
39
+ - `baseline` 字段、连续 revision 要求和 `webloom.runtime.resync`;
40
+ - Runtime Host 与 MessagePort Provider 重复发送的服务目录;
41
+ - `connectSharedWorker()` 内建的 `autoReconnect`、`reconnectDelayMs`;
42
+ - `RuntimeHandle.ready()`;
43
+ - 因握手或 baseline 未完成而由 `capability()` 同步抛错的行为;
44
+ - 仅为旧 WebLoom wire/API 存在的兼容分支。
45
+
46
+ ### 3.2 必须保留
47
+
48
+ - 真实 module `SharedWorker` 和真实 `SharedWorkerGlobalScope`;
49
+ - 每次 Worker 启动唯一的 `runtimeInstanceId`;
50
+ - 每次服务实例唯一且不可复用的 `serviceInstanceId`;
51
+ - 精确 `contractVersion`;
52
+ - 旧代理永久失效,禁止静默换绑;
53
+ - Provider 端每次调用重新验证实例、契约和授权状态;
54
+ - 同步 revoke 后异步 drain/teardown 的顺序;
55
+ - `AbortSignal`、调用 deadline、取消消息和 pending call 清理;
56
+ - 禁止框架自动重放调用;
57
+ - Keymaster 的 owner、session epoch、bucket generation、grant、authority CAS、
58
+ handover generation、durable/final-I/O lease;
59
+ - Local 桶继续使用 Window `localStorage` 页面桥,不改成 IndexedDB 或 Worker 存储。
60
+
61
+ ### 3.3 名称相同但不得删除的机制
62
+
63
+ 以下属于领域安全协议,不属于本次删除的“连接可用性握手”:
64
+
65
+ - `UpgradeGate.handshake()`;
66
+ - Keymaster 发布接管、世代检查和 I/O lease;
67
+ - Sat/Channel/P2P 领域自己的连接身份;
68
+ - 需要显式认证或签发授权会话的业务握手。
69
+
70
+ 不得通过全仓机械删除 `handshake` 或 `connectionId` 完成本施工单。
71
+
72
+ ## 4. 目标公共 API
73
+
74
+ ### 4.1 `connectSharedWorker()`
75
+
76
+ 目标 API 同步建立本地句柄:
77
+
78
+ ```ts
79
+ const runtime = connectSharedWorker({
80
+ id: "keymaster-coordinator",
81
+ url: coordinatorWorkerUrl,
82
+ defaultCallTimeoutMs: 30_000,
83
+ });
84
+
85
+ const crypto = runtime.capability("keymaster.crypto", {
86
+ contractVersion: "keymaster.crypto.v1",
87
+ });
88
+
89
+ const result = await crypto.call(request, {
90
+ signal,
91
+ timeoutMs: 10_000,
92
+ operationId,
93
+ });
94
+ ```
95
+
96
+ `connectSharedWorker()` 可以因调用参数非法、环境不支持 SharedWorker 或构造器同步
97
+ 失败而抛错;它不得等待 Worker 回包,也不得因远端协议、启动状态或服务目录拒绝。
98
+
99
+ 目标 `ConnectSharedWorkerOptions`:
100
+
101
+ ```ts
102
+ interface ConnectSharedWorkerOptions {
103
+ id: string;
104
+ url: string | URL;
105
+ name?: string;
106
+ credentials?: RequestCredentials;
107
+ defaultCallTimeoutMs?: number;
108
+ }
109
+ ```
110
+
111
+ 删除 `autoReconnect`、`reconnectDelayMs`、`handshakeTimeoutMs` 和迁移钩子。
112
+ 测试注入若需要 `workerFactory`,把它移入 testing 入口,不扩散到普通 API。
113
+
114
+ ### 4.2 `RuntimeHandle`
115
+
116
+ ```ts
117
+ interface RuntimeHandle {
118
+ readonly runtimeKind: "shared-worker";
119
+ readonly runtimeId: string;
120
+ readonly runtimeInstanceId?: string;
121
+ state(): RuntimeStatusSnapshot;
122
+ capability<T = unknown>(
123
+ capabilityId: string,
124
+ options?: { contractVersion?: string },
125
+ ): RemoteServiceProxy & { readonly serviceType?: T };
126
+ subscribe(listener: RuntimeStatusListener): () => void;
127
+ dispose(reason?: string): Promise<void>;
128
+ }
129
+ ```
130
+
131
+ - 删除 `ready()`;
132
+ - 删除公开 `connectionId`;调试需要时可以在内部诊断对象中使用,但不得成为调用授权;
133
+ - `state()/subscribe()` 是观测接口,不是调用成功承诺;
134
+ - `capability()` 总是返回一个惰性代理,不检查当前 `ready`;
135
+ - `dispose()` 同步撤销代理和拒绝 pending calls,再异步完成端口/资源清理。
136
+
137
+ ### 4.3 惰性代理绑定规则
138
+
139
+ 1. 代理创建时可以尚无服务目录。
140
+ 2. 第一次调用等待一个精确匹配的 `capabilityId + contractVersion`,等待时间计入本次
141
+ 调用的 deadline。
142
+ 3. 第一次获得匹配服务时,代理原子地绑定该 `runtimeInstanceId + serviceInstanceId`。
143
+ 4. 多个并发首次调用共享同一个绑定结果,不能分别绑定不同实例。
144
+ 5. 一旦绑定,代理终身不换绑;实例撤销、Worker 重启或显式 dispose 后只会失败。
145
+ 6. 调用方需要恢复时必须创建新 RuntimeHandle 和新代理。
146
+
147
+ ## 5. 目标 wire 协议
148
+
149
+ ### 5.1 单向完整状态快照
150
+
151
+ Worker 在端口接入后以及 Runtime 状态变化后发送一类完整快照:
152
+
153
+ ```ts
154
+ interface RuntimeSnapshot {
155
+ type: "webloom.runtime.snapshot";
156
+ protocolVersion: "webloom.runtime.v2";
157
+ runtimeId: string;
158
+ runtimeKind: "shared-worker";
159
+ runtimeInstanceId: string;
160
+ revision: number;
161
+ state: "starting" | "ready" | "failed" | "stopping" | "disposed";
162
+ units: readonly RuntimeSnapshotUnit[];
163
+ services: readonly RemoteServiceReference[];
164
+ }
165
+ ```
166
+
167
+ 规则:
168
+
169
+ - 每一份快照都是完整真值,不存在 baseline/incremental 两种语义;
170
+ - 同一 `runtimeInstanceId` 只接受严格大于当前值的 revision,重复或回退直接忽略;
171
+ - 新 `runtimeInstanceId` 先同步撤销全部旧代理,再接受其任意非负首 revision;
172
+ - 无 revision-gap 和 resync;丢包后下一份完整快照自然收敛;
173
+ - 外层结构解析和版本接受必须分开;`protocolVersion` 可解析但不匹配时记录状态,
174
+ 实际调用返回 `protocol_mismatch`;
175
+ - 完全无法解析对端协议时,由调用 deadline 收敛为 `call_timeout`。
176
+
177
+ ### 5.2 服务引用
178
+
179
+ 把 `providerInstanceId` 统一命名为 `serviceInstanceId`。本次快照 revision 只放在
180
+ 快照顶层,不复制到每条服务引用。物理端口身份不进入引用。
181
+
182
+ ```ts
183
+ interface RemoteServiceReference {
184
+ capabilityId: string;
185
+ contractVersion: string;
186
+ runtime: RuntimeKind;
187
+ runtimeInstanceId: string;
188
+ serviceInstanceId: string;
189
+ status: "starting" | "ready" | "unavailable" | "failed";
190
+ attributes: Readonly<Record<string, unknown>>;
191
+ grantId?: string;
192
+ authorizationRevision?: number;
193
+ }
194
+ ```
195
+
196
+ `scopeId`、`handoverGeneration` 等领域值不再作为通用传输的一级寻址字段;需要公开
197
+ 观察的放入 `attributes`,需要安全判断的仍由 Keymaster 权威状态和最终 I/O fence
198
+ 校验。`grantId` 不是授权事实,Provider 不得因客户端回显它就放行。
199
+
200
+ ### 5.3 调用消息
201
+
202
+ ```ts
203
+ interface RemoteServiceCallMessage {
204
+ type: "webloom.remote-service.call";
205
+ protocolVersion: "webloom.remote-service.v2";
206
+ callId: string;
207
+ capabilityId: string;
208
+ contractVersion: string;
209
+ serviceInstanceId: string;
210
+ operationId?: string;
211
+ grantId?: string;
212
+ request: unknown;
213
+ }
214
+ ```
215
+
216
+ `result`、`error`、`cancel` 只需回显 `protocolVersion + callId + serviceInstanceId`。
217
+ 删除这些消息中的 `connectionId` 和完整 `reference` 回传。Provider 用接收消息的
218
+ MessagePort 找到端点,用自己的权威目录重建和校验引用,不能信任客户端提交的
219
+ `attributes`、scope、世代或 status。
220
+
221
+ 结构化错误至少稳定覆盖:
222
+
223
+ - `transport_unavailable`;
224
+ - `call_timeout`;
225
+ - `runtime_initialization_failed`;
226
+ - `protocol_mismatch`;
227
+ - `capability_unavailable`;
228
+ - `contract_version_mismatch`;
229
+ - `service_stale`;
230
+ - `service_revoked`;
231
+ - `permission_denied`;
232
+ - `request_cancelled`;
233
+ - `handler_failed`。
234
+
235
+ 错误必须保留 `code` 和可序列化 `message`;不得依赖远端 Error 原型或 stack。
236
+
237
+ ## 6. 工单拆分
238
+
239
+ ### SWCF-001:锁定旧行为和删除清单
240
+
241
+ 修改范围:
242
+
243
+ - `src/runtime/runtime.test.ts`
244
+ - `src/transport/serviceBridge.test.ts`
245
+ - `src/transport/messagePortService*.test.ts`
246
+ - `docs/proposals/browser-runtime-v1/*`
247
+
248
+ 工作:
249
+
250
+ - 固化多页面共享一个 Worker/Unit、单端口隔离、撤销、取消、旧代理失效和不重放;
251
+ - 为当前 hello、Provider handshake、重复 snapshot、baseline/resync 和自动重连建立
252
+ 仅用于证明删除前现状的测试/计数;
253
+ - 把旧需求文档相应条目标为被本文覆盖,不能继续存在两份相反真值。
254
+
255
+ 验收:测试明确区分“必须保留的安全行为”和“将删除的连接仪式”。
256
+
257
+ ### SWCF-002:定义 v2 协议和调用错误
258
+
259
+ 修改范围:
260
+
261
+ - `src/runtime/runtimeProtocol.ts`
262
+ - `src/runtime/runtimeTypes.ts`
263
+ - `src/contracts/lifecycle.ts`
264
+ - `src/index.ts`
265
+
266
+ 工作:
267
+
268
+ - 升级 Runtime/RemoteService wire 版本;
269
+ - 删除 hello/resync/handshake/baseline 类型和导出;
270
+ - 引入 `serviceInstanceId` 和精简消息结构;
271
+ - 删除 `RemoteServiceHandshake`、`RemoteServicePortControlMessage` 中的握手/目录分支;
272
+ - Bridge 状态收敛为 `empty | ready | stale | disposed`,不得保留 handshaking;
273
+ - 定义稳定错误基类/错误码和序列化规则;
274
+ - 更新中文字段注释和生成声明。
275
+
276
+ 验收:公开 `.d.ts` 不再包含已删除符号,`UpgradeHandshake` 等领域安全类型仍存在。
277
+
278
+ ### SWCF-003:把 ServiceBridge 改为惰性单次绑定
279
+
280
+ 修改范围:
281
+
282
+ - `src/transport/serviceBridge.ts`
283
+ - `src/transport/serviceBridge.test.ts`
284
+
285
+ 工作:
286
+
287
+ - 用 `applySnapshot(snapshot)` 同时建立 Runtime authority 和完整服务目录;
288
+ - 删除 `handshake()`、baseline 和连续 revision 状态机;
289
+ - 实现未绑定代理、原子首次绑定和永久实例绑定;
290
+ - 快照删除/替换服务时同步 revoke 已绑定旧代理;
291
+ - Runtime instance 改变时同步 revoke 全部旧代理;
292
+ - 同一 Runtime 的旧/重复 revision 只忽略,不清空当前新目录;
293
+ - `disconnect()`/`dispose()` 拒绝所有等待绑定和 pending call;
294
+ - 不创建隐式重试或请求重放。
295
+
296
+ 必测:调用早于首快照、并发首次调用、契约不匹配、服务永远不存在、乱序快照、
297
+ Provider 更换、Runtime 重启、dispose 与首次绑定竞争。
298
+
299
+ ### SWCF-004:精简 MessagePort Transport/Provider
300
+
301
+ 修改范围:
302
+
303
+ - `src/transport/messagePortServiceTransport.ts`
304
+ - `src/transport/messagePortServiceProvider.ts`
305
+ - 对应测试和 legacy codec 测试
306
+
307
+ 工作:
308
+
309
+ - codec 只保留 `call/result/error/cancel`;
310
+ - Provider 创建后不主动发送 handshake 或 snapshot;
311
+ - Runtime Host 成为 RuntimeSnapshot 的唯一发布者;
312
+ - 端口本身作为连接隔离边界,wire 删除 `connectionId`;
313
+ - Provider 根据本地端点目录验证 capability、contract、service instance 和 grant;
314
+ - Transport 为每次调用安装 deadline 和 AbortSignal;
315
+ - timeout/abort/dispose 必须删除 pending 项并尽力发送 cancel;
316
+ - Result/Error 必须匹配 callId 和 serviceInstanceId;迟到响应直接丢弃;
317
+ - Provider revoke 先同步停止新调用并 abort pending,再发布新完整 RuntimeSnapshot。
318
+
319
+ deadline 从调用开始计时,包含等待首快照和远端执行的总时间;默认值必须有限且大于零。
320
+
321
+ ### SWCF-005:简化 SharedWorker Host
322
+
323
+ 修改范围:
324
+
325
+ - `src/runtime/sharedWorkerHost.ts`
326
+ - `src/runtime/runtime.test.ts`
327
+
328
+ 工作:
329
+
330
+ - `onconnect` 收到端口即创建 Endpoint 和 Provider;
331
+ - connection identity 只保存在 Endpoint 内部,不由 Window 生成、回显或用于授权;
332
+ - 删除 hello 门禁、重复 connectionId 检查、resync handler 和握手 phase;
333
+ - 每次连接只发送一份完整 RuntimeSnapshot;
334
+ - Runtime/Unit/Service 状态变化只发布下一份完整快照;
335
+ - fail/stopping/disposed 尽力发布末态后关闭端口;
336
+ - 对每条端口分别维护 callId、cancel 和 pending 集合;
337
+ - 一个端口释放不得停止共享 RuntimeUnit。
338
+
339
+ 验收:新连接零客户端控制消息即可收到首快照;ready 时每次端点初始发布只有一份目录。
340
+
341
+ ### SWCF-006:简化 Window RuntimeHandle
342
+
343
+ 修改范围:
344
+
345
+ - `src/runtime/connectSharedWorker.ts`
346
+ - `src/runtime/runtimeTypes.ts`
347
+ - `src/runtime/windowRuntime.ts`
348
+ - `src/runtime/runtime.test.ts`
349
+
350
+ 工作:
351
+
352
+ - `connectSharedWorker()` 改为同步返回 handle;
353
+ - 先安装监听器、Transport 和本地状态,再 `port.start()`;
354
+ - 删除 readiness generation、handshake timer、hello、resync 和 reconnect 状态机;
355
+ - `capability()` 改为惰性代理;
356
+ - Worker error/messageerror 立即标记 disconnected、撤销代理并拒绝 pending calls;
357
+ - 断线后不自行创建新 Worker;
358
+ - 显式 dispose 幂等,且不会被任何 timer 复活;
359
+ - Window Host 的 remote dependency 继续依据快照投影 blocked/ready,但不得调用
360
+ `RuntimeHandle.ready()`。
361
+
362
+ 测试必须证明 connect 返回不依赖任何 Worker 回包,错误只在构造或 call 边界出现。
363
+
364
+ ### SWCF-007:迁移 DemoWebLoom
365
+
366
+ 修改范围:
367
+
368
+ - `/home/david/Workspaces/DemoWebLoom/examples/07-remote/*`
369
+ - Demo 浏览器 smoke 和说明文档
370
+
371
+ 工作:
372
+
373
+ - 删除 `await connectSharedWorker()` 的就绪含义;
374
+ - 删除 `autoReconnect`、`reconnectDelayMs`、`handshakeTimeoutMs`;
375
+ - UI 文案从 handshake/baseline 改成 snapshot/call;
376
+ - 不再展示公开 connectionId;改为展示 runtimeInstanceId、serviceInstanceId、revision;
377
+ - “重连”按钮显式 dispose 旧 handle,再创建新 handle 和新代理;
378
+ - 协议不匹配场景通过 `proxy.call()` 得到错误,不再期待连接 Promise 拒绝。
379
+
380
+ 浏览器验收继续证明:真实 SharedWorker realm、两个页面共享 Runtime/Unit、setup=1、
381
+ 两个页面调用相互隔离、显式重建产生新代理、不可解析对端最终 call timeout。
382
+
383
+ ### SWCF-008:迁移 Keymaster RemoteService
384
+
385
+ 修改范围:
386
+
387
+ - `/home/david/Workspaces/keymaster.cc/apps/web/src/keymasterSessionCoordinatorClient.ts`
388
+ - `/home/david/Workspaces/keymaster.cc/apps/web/src/keymasterSessionCoordinator.worker.ts`
389
+ - `/home/david/Workspaces/keymaster.cc/packages/contracts/src/keymasterLifecycle.ts`
390
+ - Keymaster RemoteService/Coordinator 定向测试
391
+
392
+ 工作:
393
+
394
+ - 删除 Keymaster 对 `RemoteServiceHandshake` 和 `baseline` 的消费;
395
+ - Coordinator Provider 改为无握手创建,服务变化发布完整目录;
396
+ - 页面侧 ServiceBridge 从首份完整目录直接建立 authority;
397
+ - 删除 RemoteService wire 的 connectionId,端点仍在 Worker 内用不可见 identity 管理;
398
+ - `providerInstanceId` 迁移为 `serviceInstanceId`;
399
+ - 客户端 reconnect/backoff 仍由 Keymaster 单一领域连接管理器负责,但每次重连必须
400
+ 创建全新的 WebLoom handle 和代理;不得迁回 WebLoom 核心;
401
+ - owner/session/bucket/grant/authorization/final-I/O 校验在调用前后保持原顺序;
402
+ - A → B → A、lock/unlock 和 Worker 重启后旧代理永久失败。
403
+
404
+ 禁止把 Keymaster 领域 `connectionId`(例如 Sat inbound lane)与已删除的 WebLoom
405
+ RemoteService connectionId 混为一谈。
406
+
407
+ ### SWCF-009:收口 Keymaster 端口扩展钩子
408
+
409
+ 目标:最终删除 WebLoom 公共 `onConnection` / `onPortConnect`,使 RuntimeHandle 不泄漏
410
+ SharedWorker/MessagePort。
411
+
412
+ 工作分两步:
413
+
414
+ 1. 先完成 SWCF-001 至 SWCF-008;这一步允许钩子作为同版本内的临时迁移接缝。
415
+ 2. 将 Keymaster 主 Coordinator 请求、事件和需要转移 MessagePort 的操作迁入明确的
416
+ typed capability/transfer API;完成真实浏览器压力和安全回归后,删除两个钩子。
417
+
418
+ transfer API 必须显式列出 request/result transferables,普通 capability 默认仍只允许
419
+ 结构化克隆;不得暴露裸 Runtime MessagePort。若本阶段无法证明所有 Keymaster 领域
420
+ 通道已迁移,停止删除钩子,不得用 `any` 或全局消息监听绕过。
421
+
422
+ ### SWCF-010:文档、包与三仓原子切换
423
+
424
+ 修改范围:
425
+
426
+ - WebLoom README、`docs/api.md`、migration 文档、consumer smoke;
427
+ - Demo README/课程;
428
+ - Keymaster 依赖、锁文件、release-boundary;
429
+ - 三仓残留符号检查。
430
+
431
+ 工作:
432
+
433
+ - 文档主路径只展示 call-first API;
434
+ - 标明 `0.3.0` 是破坏性升级,不提供 v1 wire 双栈;
435
+ - 先以 WebLoom tarball 在 Demo/Keymaster 完成消费者验证;
436
+ - WebLoom 正式发布后,下游只安装 registry 精确版本并重跑门禁;
437
+ - 禁止同时接受 v1/v2 消息,避免协议降级和双真值;
438
+ - npm 发布仍由用户手工执行,施工者不索取 OTP、不代替用户发布。
439
+
440
+ 残留搜索至少覆盖:
441
+
442
+ ```text
443
+ RUNTIME_HELLO_TYPE
444
+ RUNTIME_RESYNC_TYPE
445
+ RemoteServiceHandshake
446
+ handshakeTimeoutMs
447
+ autoReconnect
448
+ reconnectDelayMs
449
+ baseline
450
+ bridge.handshake
451
+ codec.type("handshake")
452
+ connectionId(只审查 WebLoom transport 含义,不能盲删领域字段)
453
+ ```
454
+
455
+ ## 7. 测试矩阵
456
+
457
+ | 场景 | 单元/模拟 | 真实浏览器 | Keymaster 集成 |
458
+ |---|---:|---:|---:|
459
+ | connect 不等待 Worker 回包 | 必须 | 必须 | 必须 |
460
+ | call 等待首份快照且受 deadline 限制 | 必须 | 必须 | 必须 |
461
+ | 协议可解析但版本不兼容 | 必须 | 必须 | 必须 |
462
+ | 对端静默/完全不可解析 | 必须 | 必须 | 必须 |
463
+ | 两页面共享一个 Worker/Unit | 模拟辅助 | 必须 | 必须 |
464
+ | 两端口 callId/cancel 隔离 | 必须 | 必须 | 必须 |
465
+ | Provider 更换后旧代理失败 | 必须 | 必须 | 必须 |
466
+ | Worker 重启后旧代理失败 | 必须 | 必须 | 必须 |
467
+ | 显式重连不重放旧调用 | 必须 | 必须 | 必须 |
468
+ | dispose 与迟到 result 竞争 | 必须 | 必须 | 定向 |
469
+ | lock/unlock、A → B → A | 不适用 | 不适用 | 必须 |
470
+ | grant/owner generation/final-I/O fence | 不适用 | 不适用 | 必须 |
471
+ | Local localStorage 页面桥 | 不适用 | 不适用 | 必须 |
472
+
473
+ 每项测试还必须断言负面事实:没有 hello、没有 handshake 控制包、没有重复目录、
474
+ 没有 resync、没有自动创建第二个 Worker、没有自动重放 call。
475
+
476
+ ## 8. 执行顺序与门禁
477
+
478
+ ```text
479
+ SWCF-001 行为基线
480
+ → SWCF-002 v2 类型
481
+ → SWCF-003 Bridge
482
+ → SWCF-004 Transport/Provider
483
+ → SWCF-005 Worker Host
484
+ → SWCF-006 Window Handle
485
+ → SWCF-007 Demo
486
+ → SWCF-008 Keymaster RemoteService
487
+ → SWCF-009 端口扩展收口
488
+ → SWCF-010 文档/打包/发布准备
489
+ ```
490
+
491
+ 每个阶段先跑 targeted tests 和 typecheck。跨仓切换前至少执行:
492
+
493
+ ### WebLoom
494
+
495
+ ```bash
496
+ pnpm typecheck
497
+ pnpm test
498
+ pnpm lint:boundaries
499
+ pnpm build
500
+ pnpm run pack:consumer
501
+ pnpm run test:browser
502
+ git diff --check
503
+ ```
504
+
505
+ ### DemoWebLoom
506
+
507
+ ```bash
508
+ pnpm typecheck
509
+ pnpm build
510
+ pnpm run test:browser
511
+ git diff --check
512
+ ```
513
+
514
+ ### Keymaster
515
+
516
+ - typecheck;
517
+ - boundaries/final-I/O/release-boundary 门禁;
518
+ - Coordinator Client/Worker、Runtime adapter、bootstrap、owner storage、crypto 定向测试;
519
+ - 完整 Vitest 分批执行;
520
+ - production build 并确认发出真实 SharedWorker chunk;
521
+ - 真实浏览器多 Tab、断线、显式重连、lock/unlock、A → B → A、Local 桶回归;
522
+ - `git diff --check`。
523
+
524
+ 测试结果必须分成三层报告:代码完成、本地/自动化验收、真实浏览器或公网生产验收。
525
+ 本地 build、MessageChannel 和 Playwright fixture 不能代替公网发布验收。
526
+
527
+ ## 9. 停止条件
528
+
529
+ 出现任一情况立即停止当前批次,不继续删除旧路径:
530
+
531
+ - `capability()` 仍因未 ready 同步抛错;
532
+ - call 可能无限 pending;
533
+ - 代理在 service/runtime instance 改变后自动换绑;
534
+ - reconnect 自动重放任何请求;
535
+ - 两个页面产生两个 Worker RuntimeUnit 实例;
536
+ - 旧/迟到响应可以完成新调用;
537
+ - Provider 依赖客户端回传的完整 reference 决定授权;
538
+ - Keymaster lock 后旧调用仍能进入 handler 或最终 I/O;
539
+ - owner/session/bucket/grant/handover/final-I/O fence 被弱化;
540
+ - Local 桶离开页面 localStorage 桥;
541
+ - 为通过测试同时保留 v1/v2 两套协议真值;
542
+ - 真实浏览器不支持时退回 Node 模拟并报告通过。
543
+
544
+ ## 10. 完成定义
545
+
546
+ 只有同时满足以下条件,施工单才可标记完成:
547
+
548
+ 1. WebLoom v2 call-first 代码、类型、文档和 tarball 一致;
549
+ 2. 精确搜索无预期外旧 handshake/baseline/resync/reconnect API;
550
+ 3. WebLoom 全部门禁和真实 Chromium fixture 通过;
551
+ 4. Demo 使用新 API,真实浏览器课程通过;
552
+ 5. Keymaster 使用新 API,安全门禁、构建和真实浏览器关键路径通过;
553
+ 6. 旧代理、超时、取消、断线、Worker 重启和 Provider 重建均有负面断言;
554
+ 7. `UpgradeGate` 及 Keymaster 最终 I/O 安全边界未被删除或降级;
555
+ 8. 发布状态如实记录;未发布 npm 或未做公网验证时必须明确标为未完成项。
556
+
557
+ ## 11. 交付物
558
+
559
+ - WebLoom v2 Runtime/RemoteService wire;
560
+ - 无 handshake 的 `connectSharedWorker()` 与惰性 capability proxy;
561
+ - 单一完整 RuntimeSnapshot 发布链;
562
+ - 有 deadline/AbortSignal 的 MessagePort call;
563
+ - 显式重连、永不自动重放的调用模型;
564
+ - DemoWebLoom call-first 课程与浏览器证据;
565
+ - Keymaster 同版本迁移和安全回归证据;
566
+ - v1 → v2 破坏性迁移说明;
567
+ - 三仓独立验收记录和剩余公网发布边界。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webloom-framework",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "通用前端插件生命周期框架",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {