webloom-framework 0.1.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,267 @@
1
+ import { i as PluginSetup, aL as RuntimeUnitImplementationRegistry, R as RuntimeKind, ae as PluginStateKind, r as RemoteServiceReference, s as RemoteServiceMessageCodec, ah as PluginUnitState, au as RemoteServiceProxy, m as PluginHost, L as LifecycleDisposeResult, M as MessageBus, K as LifecycleScopeKind, o as LifecycleScopeIdentity, n as LifecycleScope, a9 as PluginIntentSnapshot, a7 as PluginIntentController, ax as RemoteServiceTransport, am as RemoteServiceBridge, ao as RemoteServiceCallContext, aC as ResourceRegistry, aA as ResourceDefinition } from './createPluginHost-BVsUCDKN.js';
2
+
3
+ interface RuntimeUnitImplementation {
4
+ /** 产品标识。 */
5
+ pluginId: string;
6
+ /** 稳定运行单元标识。 */
7
+ unitId: string;
8
+ /** 当前环境的可执行入口。 */
9
+ setup: PluginSetup;
10
+ }
11
+ /** 创建一个拒绝重复注册、按产品和单元查找实现的运行时注册表。 */
12
+ declare function createRuntimeUnitImplementationRegistry(implementations?: readonly RuntimeUnitImplementation[]): RuntimeUnitImplementationRegistry & {
13
+ register(implementation: RuntimeUnitImplementation): void;
14
+ unregister(pluginId: string, unitId: string): void;
15
+ };
16
+
17
+ declare const RUNTIME_PROTOCOL_VERSION = "webloom.runtime.v2";
18
+ declare const RUNTIME_SNAPSHOT_TYPE = "webloom.runtime.snapshot";
19
+ declare const RUNTIME_ERROR_TYPE = "webloom.runtime.error";
20
+ interface RuntimeErrorMessage {
21
+ type: typeof RUNTIME_ERROR_TYPE;
22
+ protocolVersion: string;
23
+ code: "runtime_initialization_failed" | "protocol_mismatch" | "transport_unavailable";
24
+ message: string;
25
+ pluginId?: string;
26
+ unitId?: string;
27
+ phase?: "startup" | "snapshot";
28
+ }
29
+ interface RuntimeSnapshot {
30
+ type: typeof RUNTIME_SNAPSHOT_TYPE;
31
+ protocolVersion: string;
32
+ runtimeId: string;
33
+ runtimeKind: RuntimeKind;
34
+ runtimeInstanceId: string;
35
+ revision: number;
36
+ state: "starting" | "ready" | "stopping" | "failed" | "disposed";
37
+ units: readonly RuntimeSnapshotUnit[];
38
+ services: readonly RemoteServiceReference[];
39
+ }
40
+ interface RuntimeSnapshotUnit {
41
+ pluginId: string;
42
+ unitId: string;
43
+ runtime: RuntimeKind;
44
+ instanceId?: string;
45
+ state: PluginStateKind;
46
+ }
47
+ declare function createRuntimeMessageCodec(): RemoteServiceMessageCodec;
48
+ /** 只验证外层结构;协议版本接受由 RuntimeHandle 单独处理。 */
49
+ declare function isRuntimeSnapshot(input: unknown): input is RuntimeSnapshot;
50
+ declare function isRuntimeSnapshotProtocol(input: RuntimeSnapshot): boolean;
51
+ declare function isRuntimeError(input: unknown): input is RuntimeErrorMessage;
52
+ /** 将 Host 的 PluginUnitState 映射成不会泄露领域配置的运行时快照。 */
53
+ declare function unitSnapshotFromState(state: PluginUnitState, runtime: RuntimeKind): RuntimeSnapshotUnit;
54
+
55
+ type RuntimeAppState = "starting" | "ready" | "stopping" | "failed" | "disposed" | "disconnected";
56
+ interface RuntimeStatusSnapshot {
57
+ runtimeId: string;
58
+ runtimeKind: RuntimeKind;
59
+ runtimeInstanceId: string;
60
+ state: RuntimeAppState;
61
+ revision: number;
62
+ units: readonly RuntimeSnapshotUnit[];
63
+ services: readonly RemoteServiceReference[];
64
+ error?: string;
65
+ }
66
+ type RuntimeStatusListener = (snapshot: RuntimeStatusSnapshot) => void;
67
+ interface WindowApp {
68
+ readonly runtimeKind: "window-main";
69
+ readonly runtimeId: string;
70
+ readonly runtimeInstanceId: string;
71
+ /** 已由 createWindowApp 装配并注册完成的本地 Host。 */
72
+ readonly host: PluginHost;
73
+ state(): RuntimeStatusSnapshot;
74
+ /** 获取当前本地 capability;停止或销毁后同步抛错。 */
75
+ capability<T>(capabilityId: string): T;
76
+ subscribe(listener: RuntimeStatusListener): () => void;
77
+ dispose(reason?: string): Promise<LifecycleDisposeResult>;
78
+ }
79
+ interface RuntimeHandle {
80
+ readonly runtimeKind: "shared-worker";
81
+ readonly runtimeId: string;
82
+ /** 当前已观察到的 Worker 启动身份;首份快照前为 undefined。 */
83
+ readonly runtimeInstanceId?: string;
84
+ state(): RuntimeStatusSnapshot;
85
+ capability<T = unknown>(capabilityId: string, options?: {
86
+ contractVersion?: string;
87
+ }): RemoteServiceProxy & {
88
+ readonly serviceType?: T;
89
+ };
90
+ subscribe(listener: RuntimeStatusListener): () => void;
91
+ dispose(reason?: string): Promise<void>;
92
+ }
93
+ /** 跨 realm 初始化失败;不把 required 插件错误包装成成功状态。 */
94
+ interface RuntimeInitializationErrorDetails {
95
+ pluginId?: string;
96
+ unitId?: string;
97
+ phase: "validate" | "register" | "startup" | "snapshot";
98
+ error: string;
99
+ }
100
+ declare class RuntimeInitializationError extends Error {
101
+ readonly code: "runtime_initialization_failed";
102
+ readonly details: RuntimeInitializationErrorDetails;
103
+ constructor(details: RuntimeInitializationErrorDetails);
104
+ }
105
+ declare class RuntimeUnavailableError extends Error {
106
+ readonly code: "transport_unavailable";
107
+ constructor(message?: string);
108
+ }
109
+
110
+ interface SharedWorkerLike {
111
+ readonly port: MessagePort;
112
+ onerror?: (event: Event) => void;
113
+ addEventListener?(type: "error", listener: (event: Event) => void): void;
114
+ removeEventListener?(type: "error", listener: (event: Event) => void): void;
115
+ }
116
+ type SharedWorkerFactory = (url: string | URL, options: {
117
+ type: "module";
118
+ name?: string;
119
+ credentials?: RequestCredentials;
120
+ }) => SharedWorkerLike;
121
+ /**
122
+ * 临时迁移接缝:下游 typed transfer API 完成前允许领域代码在同一
123
+ * 物理端口安装 listener。它不参与 Runtime 授权,也不传 connectionId。
124
+ */
125
+ interface SharedWorkerConnectionContext {
126
+ readonly worker: SharedWorkerLike;
127
+ readonly port: MessagePort;
128
+ }
129
+ interface ConnectSharedWorkerOptions {
130
+ id: string;
131
+ url: string | URL;
132
+ name?: string;
133
+ credentials?: RequestCredentials;
134
+ defaultCallTimeoutMs?: number;
135
+ /** SWCF-009 完成前的临时同端口迁移接缝。 */
136
+ onConnection?: (context: SharedWorkerConnectionContext) => void;
137
+ }
138
+ /** 创建生产 RuntimeHandle;测试工厂不属于生产公共选项。 */
139
+ declare function connectSharedWorker(options: ConnectSharedWorkerOptions): RuntimeHandle;
140
+ /** 仅由 `webloom-framework/testing` 暴露的 SharedWorker 工厂注入入口。 */
141
+ declare function connectSharedWorkerForTesting(options: ConnectSharedWorkerOptions, workerFactory: SharedWorkerFactory): RuntimeHandle;
142
+
143
+ declare function createMessageBus(): MessageBus;
144
+
145
+ interface CreateResourceScopeOptions {
146
+ /** 可选固定作用域标识;生产代码通常省略,让实现生成不可复用 ID。 */
147
+ scopeId?: string;
148
+ /** 运行实例标识;省略时与 scopeId 同源生成。 */
149
+ instanceId?: string;
150
+ /** 作用域类型。 */
151
+ kind: LifecycleScopeKind;
152
+ /** 绑定插件和宿主只读属性。 */
153
+ metadata?: Omit<Partial<LifecycleScopeIdentity>, "scopeId" | "instanceId" | "kind">;
154
+ /** 测试或宿主诊断使用的状态变更回调。 */
155
+ onChange?: (scope: LifecycleScope) => void;
156
+ /** 子作用域生成最终清理结果后的内部通知;用于更新父级登记。 */
157
+ onDisposeResult?: (result: LifecycleDisposeResult) => void;
158
+ }
159
+ /**
160
+ * 创建一个可嵌套的生命周期作用域。
161
+ *
162
+ * `createResourceScope` 是 `createLifecycleScope` 的同义入口,方便不同
163
+ * 模块按“作用域”或“资源”语义调用;两者不代表两套实现。
164
+ */
165
+ declare function createLifecycleScope(options: CreateResourceScopeOptions): LifecycleScope;
166
+ /** 语义别名:资源管理代码通常以 ResourceScope 称呼同一原语。 */
167
+ declare const createResourceScope: typeof createLifecycleScope;
168
+ type ResourceScopeOptions = CreateResourceScopeOptions;
169
+
170
+ interface CreatePluginIntentControllerOptions {
171
+ /** 当前控制面启动身份;Worker 重启时应传入新值。 */
172
+ authorityInstanceId?: string;
173
+ /** Worker 恢复后读取的平台意图。 */
174
+ initial?: Partial<PluginIntentSnapshot>;
175
+ /** 将候选快照原子写入平台存储;缺省表示内存控制面(测试用)。 */
176
+ persist?: (snapshot: PluginIntentSnapshot) => Promise<void>;
177
+ /** 有界去重记录数量,避免 commandId 永久增长。 */
178
+ maxCommandRecords?: number;
179
+ }
180
+ /** 创建单一控制面上的插件意图控制器。 */
181
+ declare function createPluginIntentController(options?: CreatePluginIntentControllerOptions): PluginIntentController;
182
+
183
+ interface CreateServiceBridgeOptions {
184
+ /** 要接受的精确服务协议版本。 */
185
+ protocolVersion: string;
186
+ /** 实际端口 RPC 传输;桥不负责重放请求。 */
187
+ transport: RemoteServiceTransport;
188
+ /** 默认总 deadline;必须有限且大于零。 */
189
+ defaultCallTimeoutMs?: number;
190
+ }
191
+ /** 创建一个无连接握手、无增量 revision 状态机的服务桥。 */
192
+ declare function createServiceBridge(options: CreateServiceBridgeOptions): RemoteServiceBridge;
193
+
194
+ interface RemoteServicePortCallMessage {
195
+ type: string;
196
+ protocolVersion: string;
197
+ callId: string;
198
+ capabilityId: string;
199
+ contractVersion: string;
200
+ serviceInstanceId: string;
201
+ operationId?: string;
202
+ grantId?: string;
203
+ request: unknown;
204
+ }
205
+ interface RemoteServicePortResultMessage {
206
+ type: string;
207
+ protocolVersion: string;
208
+ callId: string;
209
+ serviceInstanceId: string;
210
+ result: unknown;
211
+ }
212
+ interface RemoteServicePortErrorMessage {
213
+ type: string;
214
+ protocolVersion: string;
215
+ callId: string;
216
+ serviceInstanceId: string;
217
+ error: {
218
+ name?: string;
219
+ message: string;
220
+ code: string;
221
+ details?: Readonly<Record<string, unknown>>;
222
+ };
223
+ }
224
+ interface RemoteServicePortCancelMessage {
225
+ type: string;
226
+ protocolVersion: string;
227
+ callId: string;
228
+ serviceInstanceId: string;
229
+ }
230
+ type RemoteServicePortResponseMessage = RemoteServicePortResultMessage | RemoteServicePortErrorMessage;
231
+ interface CreateMessagePortServiceTransportOptions {
232
+ /** 已由实际 Worker / Window 连接产生的双工端口。 */
233
+ port: MessagePort;
234
+ /** 可选 transferable 提取器;默认只发送结构化克隆数据。 */
235
+ transferForRequest?: (request: unknown, context: RemoteServiceCallContext) => readonly Transferable[];
236
+ /** dispose 时是否关闭端口;默认不关闭,由端口所有者决定。 */
237
+ closeOnDispose?: boolean;
238
+ /** wire 消息 codec;默认 webloom.remote-service.v2。 */
239
+ codec?: RemoteServiceMessageCodec;
240
+ /** 直接使用 transport 时的默认总 deadline。 */
241
+ defaultCallTimeoutMs?: number;
242
+ }
243
+ /** 创建一条不携带连接身份、不会自动重放的 MessagePort transport。 */
244
+ declare function createMessagePortServiceTransport(options: CreateMessagePortServiceTransportOptions): RemoteServiceTransport & {
245
+ dispose(): void;
246
+ };
247
+ /** Provider 侧调用消息的结构化输入。 */
248
+ interface MessagePortServiceCallInput {
249
+ message: RemoteServicePortCallMessage;
250
+ /** Provider 的权威目录命中的服务引用。 */
251
+ reference: RemoteServiceReference;
252
+ signal: AbortSignal;
253
+ }
254
+
255
+ /**
256
+ * 资源注册表实现
257
+ *
258
+ * 设计缘由:管理资源定义的注册和查询,支持 owner-aware 生命周期。
259
+ * 重复 id 抛错,disable 时可回收。
260
+ */
261
+
262
+ /** 资源注册表实现 */
263
+ declare function createResourceRegistry(): ResourceRegistry;
264
+ /** Internal runtime hook; deliberately absent from ResourceRegistry's public type. */
265
+ declare function registerOwnedResource(registry: ResourceRegistry, ownerId: string, definition: ResourceDefinition<any, any>): void;
266
+
267
+ export { createMessageBus as A, createMessagePortServiceTransport as B, type ConnectSharedWorkerOptions as C, createPluginIntentController as D, createResourceRegistry as E, createResourceScope as F, createRuntimeMessageCodec as G, createRuntimeUnitImplementationRegistry as H, createServiceBridge as I, isRuntimeError as J, isRuntimeSnapshot as K, isRuntimeSnapshotProtocol as L, type MessagePortServiceCallInput as M, registerOwnedResource as N, unitSnapshotFromState as O, type SharedWorkerFactory as P, connectSharedWorkerForTesting as Q, type RuntimeHandle as R, type SharedWorkerConnectionContext as S, type WindowApp as W, type RuntimeStatusSnapshot as a, type RuntimeStatusListener as b, type CreateMessagePortServiceTransportOptions as c, type CreatePluginIntentControllerOptions as d, type CreateResourceScopeOptions as e, type CreateServiceBridgeOptions as f, RUNTIME_ERROR_TYPE as g, RUNTIME_PROTOCOL_VERSION as h, RUNTIME_SNAPSHOT_TYPE as i, type RemoteServicePortCallMessage as j, type RemoteServicePortCancelMessage as k, type RemoteServicePortErrorMessage as l, type RemoteServicePortResponseMessage as m, type RemoteServicePortResultMessage as n, type ResourceScopeOptions as o, type RuntimeAppState as p, type RuntimeErrorMessage as q, RuntimeInitializationError as r, type RuntimeInitializationErrorDetails as s, type RuntimeSnapshot as t, type RuntimeSnapshotUnit as u, RuntimeUnavailableError as v, type RuntimeUnitImplementation as w, type SharedWorkerLike as x, connectSharedWorker as y, createLifecycleScope as z };
package/dist/testing.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { an as RemoteServiceCallContext, o as CreatePluginHostOptions, a0 as PluginHost, au as RemoteServiceTransport } from './createPluginHost-CcW9sPNs.js';
2
- export { j as CapabilityRegistry, k as ContextExtensionInput, l as ContributionAdapter, m as ContributionAdapterInput, n as ContributionHandle, T as PermissionPolicyInput, V as PermissionPolicyResult, Y as PluginConfigStore, aH as RuntimeUnitSnapshot, aJ as ScopeResolution, aK as ScopeResolver, aL as ScopeResolverInput, aO as StartupCapabilityError, aQ as StartupPluginError, a_ as createCapabilityRegistry, a$ as createInMemoryPluginConfigStore, b0 as createPluginHost, b2 as createResourceStore } from './createPluginHost-CcW9sPNs.js';
3
- export { C as CreateMessagePortServiceTransportOptions, a as CreatePluginIntentControllerOptions, b as CreateResourceScopeOptions, c as CreateServiceBridgeOptions, R as RemoteServicePortCallMessage, d as RemoteServicePortCancelMessage, e as RemoteServicePortErrorMessage, f as RemoteServicePortResponseMessage, g as RemoteServicePortResultMessage, h as ResourceScopeOptions, i as RuntimeUnitImplementation, j as createLifecycleScope, k as createMessageBus, l as createMessagePortServiceTransport, m as createPluginIntentController, n as createRemoteServiceBridge, o as createResourceRegistry, p as createResourceScope, q as createRuntimeUnitImplementationRegistry, r as createServiceBridge } from './resourceRegistry-BAnqKcp7.js';
1
+ import { ao as RemoteServiceCallContext, C as CreatePluginHostOptions, m as PluginHost, ax as RemoteServiceTransport } from './createPluginHost-BVsUCDKN.js';
2
+ export { t as CapabilityRegistry, u as ContextExtensionInput, v as ContributionAdapter, w as ContributionAdapterInput, x as ContributionHandle, a0 as PermissionPolicyInput, a1 as PermissionPolicyResult, a3 as PluginConfigStore, aG as RuntimeUnitAttributesInput, aH as RuntimeUnitAvailabilityInput, aM as RuntimeUnitParentScopeInput, aN as RuntimeUnitSnapshot, aR as StartupCapabilityError, aT as StartupPluginError, b1 as createCapabilityRegistry, b2 as createInMemoryPluginConfigStore, b3 as createPluginHost, b5 as createResourceStore } from './createPluginHost-BVsUCDKN.js';
3
+ export { c as CreateMessagePortServiceTransportOptions, d as CreatePluginIntentControllerOptions, e as CreateResourceScopeOptions, f as CreateServiceBridgeOptions, M as MessagePortServiceCallInput, j as RemoteServicePortCallMessage, k as RemoteServicePortCancelMessage, l as RemoteServicePortErrorMessage, m as RemoteServicePortResponseMessage, n as RemoteServicePortResultMessage, o as ResourceScopeOptions, w as RuntimeUnitImplementation, P as SharedWorkerFactory, Q as connectSharedWorkerForTesting, z as createLifecycleScope, A as createMessageBus, B as createMessagePortServiceTransport, D as createPluginIntentController, E as createResourceRegistry, F as createResourceScope, H as createRuntimeUnitImplementationRegistry, I as createServiceBridge } from './resourceRegistry-BFnjmeGE.js';
4
4
 
5
5
  interface FakeTransportCall {
6
6
  /** 调用请求体。 */
package/dist/testing.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createPluginHost } from './chunk-KGNF36DZ.js';
2
- export { StartupCapabilityError, StartupPluginError, createCapabilityRegistry, createInMemoryPluginConfigStore, createLifecycleScope, createMessageBus, createMessagePortServiceTransport, createPluginHost, createPluginIntentController, createRemoteServiceBridge, createResourceRegistry, createResourceScope, createResourceStore, createRuntimeUnitImplementationRegistry, createServiceBridge } from './chunk-KGNF36DZ.js';
1
+ import { createPluginHost } from './chunk-76BGPI6M.js';
2
+ export { StartupCapabilityError, StartupPluginError, connectSharedWorkerForTesting, createCapabilityRegistry, createInMemoryPluginConfigStore, createLifecycleScope, createMessageBus, createMessagePortServiceTransport, createPluginHost, createPluginIntentController, createResourceRegistry, createResourceScope, createResourceStore, createRuntimeUnitImplementationRegistry, createServiceBridge } from './chunk-76BGPI6M.js';
3
3
 
4
4
  // src/testing/fakes.ts
5
5
  function createFakeRemoteServiceTransport(options = {}) {
package/docs/api.md CHANGED
@@ -1,29 +1,172 @@
1
1
  # WebLoom API 说明
2
2
 
3
- ## 依赖和运行单元
3
+ ## 浏览器 Runtime
4
4
 
5
- `PluginManifest` 描述一个产品,`RuntimeUnitDescriptor` 描述该产品在某个 `execution` 环境中的运行单元。`pluginId` 是产品级稳定身份,`unitId` 是单元级稳定身份,`instanceId` 是每次启动新生成的实例身份。运行单元依赖必须同时声明 `capability`、`contractVersion`、`sourceExecution` `scope`,Host 不根据 capability 名猜测远端服务。可执行 setup 必须由宿主的 `runtimeUnitImplementationRegistry` 按 `pluginId + unitId` 提供,静态清单不携带函数。
5
+ WebLoom 0.3.0 只支持两个真实 JavaScript realm:`window-main` 和
6
+ `shared-worker`。`runtime` 是受限的 `RuntimeKind`,不是可自由填写的环境标签;
7
+ 不支持的 runtime 在装配边界 fail closed。不实现 Server、Service Worker 或
8
+ 其它服务端 PluginHost。
6
9
 
7
- `meta.defaultEnabled` 是初始启用意图,`PluginState.kind` 是实际运行状态。两者必须分开读取:依赖缺失时可以得到 `blocked`,同时保留 `desiredEnabled: true`。
10
+ 一次 Runtime 启动会生成不可复用的 `runtimeInstanceId`。每个插件运行单元启动
11
+ 会生成不可复用的运行单元 `instanceId`。同一 SharedWorker 接收多个 Window 连接
12
+ 时,Worker 单元仍只有一个实例;端口本身隔离请求和取消空间,物理端口身份不进入
13
+ 公共 Runtime 或 RemoteService wire。
8
14
 
9
- ## Context 和 Scope
15
+ ## 普通插件 API
10
16
 
11
- `PluginContext` 的基础字段只包含插件身份、实例身份、Scope、取消信号、权限租约、MessageBus、配置和 capability 访问。产品服务通过 `contextExtension` 注入,扩展属性按只读对象处理。
17
+ ```ts
18
+ import { createWindowApp, definePlugin } from "webloom-framework";
12
19
 
13
- `LifecycleScope.revoke()` 是同步安全边界:它先阻止新资源、撤销权限租约并触发 `AbortSignal`;`dispose()` 再等待清理。清理失败和超时通过 `LifecycleDisposeResult` 暴露,不能被包装成成功。异步创建在撤权后才返回时,资源会立即释放且不会进入旧实例。
20
+ const hello = definePlugin({
21
+ id: "hello",
22
+ provides: ["hello.service"],
23
+ setup(ctx) {
24
+ ctx.provide("hello.service", { value: "world" });
25
+ ctx.onDispose(() => {
26
+ // 释放本插件登记的资源。
27
+ });
28
+ },
29
+ });
14
30
 
15
- ## 权限
31
+ const app = await createWindowApp({ plugins: [hello] });
32
+ const service = app.capability<{ value: string }>("hello.service");
33
+ ```
16
34
 
17
- 权限租约把插件申请、可信批准和会话约束求交集。`permissions` 只是 Context 视图,最终远端调用或持久化写入仍需使用 `verifyPermissionLease()` `assertBinding()` fail-closed 检查。`attributes` 参与租约身份比较,但不作为任意数据仓库。
35
+ `definePlugin()` 将静态 `manifest/descriptor` 与当前 realm `setup` 分开保存。
36
+ 静态 descriptor 可用于验证和快照,不携带函数。`createWindowApp()` 自动固定
37
+ `window-main`、生成实例身份、创建内部 Implementation Registry、批量注册并
38
+ 等待初始启动;使用者不需要手工 `register()`。
18
39
 
19
- ## 服务桥和 wire codec
40
+ `createWindowApp()` Promise 只有在必需插件成功后才成功。失败会抛出包含
41
+ `pluginId`、`unitId` 和 `phase` 的 `RuntimeInitializationError`。非必需插件的
42
+ 失败保留在 Runtime 快照中,不会被伪装为 running。
43
+
44
+ ## SharedWorker
45
+
46
+ Worker 入口:
47
+
48
+ ```ts
49
+ import { definePlugin, startSharedWorkerApp } from "webloom-framework";
50
+
51
+ const storage = definePlugin({
52
+ id: "storage",
53
+ provides: ["storage.service"],
54
+ setup(ctx) {
55
+ ctx.provide("storage.service", {
56
+ handle(request: { key: string }) {
57
+ return { key: request.key };
58
+ },
59
+ });
60
+ },
61
+ });
62
+
63
+ startSharedWorkerApp({ id: "coordinator", plugins: [storage] });
64
+ ```
65
+
66
+ Window 入口:
67
+
68
+ ```ts
69
+ // Vite emits a hashed JavaScript SharedWorker asset from this importer.
70
+ import coordinatorWorkerUrl from "./coordinator.worker.ts?sharedworker&url";
71
+ import { connectSharedWorker } from "webloom-framework";
72
+
73
+ const runtime = connectSharedWorker({
74
+ id: "coordinator",
75
+ url: coordinatorWorkerUrl,
76
+ });
77
+
78
+ const storage = runtime.capability("storage.service");
79
+ await storage.call({ key: "hello" });
80
+ ```
81
+
82
+ 连接入口必须创建真实的 `new SharedWorker(url, { type: "module" })`,并同步返回
83
+ 本地 `RuntimeHandle`。Worker 只发布完整 `RuntimeSnapshot`;`capability()` 总是
84
+ 返回惰性代理,第一次 `call()` 在同一个有限 deadline 内等待目录和远程执行。断线、
85
+ 协议不兼容、Worker 重启或 Provider 实例变化都会同步撤销旧代理。显式重建只建立
86
+ 新句柄和新代理,不会重放可能产生外部副作用的调用,也不会静默替换旧代理的绑定。
87
+
88
+ 这里的 `url` 必须是 Bundler 产出的 JavaScript Worker URL。Vite 使用
89
+ `?sharedworker&url` 或等价的独立 Rollup entry;不要把
90
+ `new URL("./coordinator.worker.ts", import.meta.url)` 作为普通参数传入框架,
91
+ 因为框架内部的 `new SharedWorker()` 不会让 Vite 重新发现调用方源码入口。
20
92
 
21
- `createServiceBridge()` 只接受同一连接、权威身份、连续快照和精确契约版本。旧 revision、revision gap、Provider 实例重建和断线都会使旧代理永久失效。传输层每次调用生成独立 `callId`;业务 `operationId` 可以重用,两者不混淆。
93
+ `RuntimeHandle` 提供:
22
94
 
23
- `createRemoteServiceMessageCodec()` 默认生成 `webloom.remote-service.*` 消息名。产品迁移旧协议时可以传入旧前缀,编码、解码和版本仍由 codec 集中负责。
95
+ | 成员 | 语义 |
96
+ | --- | --- |
97
+ | `runtimeId` | Worker 的逻辑标识 |
98
+ | `runtimeInstanceId` | 当前 Worker 物理启动身份 |
99
+ | `state()` | `starting / ready / disconnected / failed / stopping / disposed` 快照 |
100
+ | `capability()` | 立即获取惰性代理;第一次 `call()` 精确绑定 Runtime/service instance |
101
+ | `subscribe()` | 观察 Runtime/Unit/服务快照 |
102
+ | `dispose()` | 同步撤销本句柄,异步关闭连接资源 |
24
103
 
25
- ## 宿主扩展点
104
+ ### Window 投影 Worker capability
105
+
106
+ Window Host 不会根据 Worker manifest 创建假运行单元。需要使用 Worker capability
107
+ 的页面插件应把已经 `ready` 的句柄传给 Window App:
108
+
109
+ ```ts
110
+ const app = await createWindowApp({
111
+ remoteRuntime: runtime,
112
+ plugins: [definePlugin({
113
+ id: "window-consumer",
114
+ dependencies: [{
115
+ capability: "coordinator.service",
116
+ contractVersion: "coordinator.service.v1",
117
+ sourceRuntime: "shared-worker",
118
+ }],
119
+ setup(ctx) {
120
+ const coordinator = ctx.serviceBridge?.requireProxy({
121
+ capabilityId: "coordinator.service",
122
+ contractVersion: "coordinator.service.v1",
123
+ runtime: "shared-worker",
124
+ }, ctx.scope);
125
+ if (!coordinator) throw new Error("Remote service bridge is unavailable");
126
+ ctx.provide("window.coordinator", coordinator);
127
+ },
128
+ })],
129
+ });
130
+ ```
131
+
132
+ `remoteRuntime` 只向 Host 投影当前完整快照中的服务和单元状态;setup 函数不会进入
133
+ Worker。这里的 `coordinator` 是惰性代理,业务在调用边界执行
134
+ `await coordinator.call({ type: "health" })`。断线时 Host 同步撤销页面插件的 Scope
135
+ 和远程代理,显式重建后的新代理必须重新取得,不会静默重绑旧引用。
136
+
137
+ ## 生命周期和 Scope
138
+
139
+ RuntimeUnit 实例存在的条件是:目标 Runtime 存活、插件启用意图为 true、硬依赖
140
+ 已就绪并且当前 Runtime 已装配实现。每个实例仍由框架创建一个内部
141
+ `ResourceScope`,用于 `AbortSignal`、capability ownership、task/subscription
142
+ ownership 和 cleanup callbacks;这个 Scope 不再从用户声明的生命周期分类推导。
143
+
144
+ 停用顺序固定为:同步阻止新 capability 和调用、撤销旧引用、触发 Scope
145
+ `AbortSignal`、执行 setup teardown 与 `ctx.onDispose()`,最后发布停止/清理状态。
146
+ `revoke()` 先形成安全边界,`dispose()` 再等待异步收尾;清理失败和超时通过
147
+ `LifecycleDisposeResult` 暴露。
148
+
149
+ 领域状态(例如 owner、session epoch、Vault lock/unlock、桶世代和最终 I/O
150
+ fence)不属于 WebLoom Runtime 生命周期。应用自己的 Coordinator/服务控制器
151
+ 负责推进领域状态,再通过新的服务快照让旧代理失效。
152
+
153
+ ## 依赖和契约
154
+
155
+ 跨 Runtime 依赖必须声明精确的 `contractVersion` 和 `sourceRuntime`。本地
156
+ capability 可以使用 `runtimeCapabilityContractVersion()` 或
157
+ `defineRuntimeUnitProvidedContracts()` 生成默认 v1 版本;框架不会根据 capability
158
+ 名称猜测远端服务。`providedContracts`、Provider 实例身份、Runtime 启动身份和
159
+ 快照 revision 都参与代理绑定。
160
+
161
+ ## 服务桥和 wire codec
26
162
 
27
- `CreatePluginHostOptions` 提供 `capabilities`、`contextExtension`、`manifestValidator`、`scopeResolver`、`permissionPolicy`、`configStore`、`pluginIntentCoordinator`、`runtimeSnapshots`、`contributionAdapters` 和 `serviceBridgeForPlugin`。WebLoom 不创建产品 Registry、日志、存储或身份状态机。
163
+ 低层 `createServiceBridge()` 仍可用于复杂打包和协议扩展。它只接受同一连接、
164
+ 权威身份、连续快照和精确契约版本;传输层每次调用生成独立 `callId`,业务
165
+ `operationId` 可以复用但不参与响应关联。默认消息 codec 生成
166
+ `webloom.remote-service.*`;迁移旧协议时可传入显式前缀。
28
167
 
29
- 贡献适配器返回的 `ContributionHandle` 支持 `revoke()` 和 `dispose()`:前者用于同步撤下入口,后者用于等待异步收尾。任何贡献都必须绑定当前 `instanceId` 和 Scope。
168
+ 普通插件不需要接触 `MessagePort`、握手或 codec。测试中的 `MessageChannel`
169
+ 证明 transport simulation;真实浏览器验收仍需确认 `Window` 与
170
+ `SharedWorkerGlobalScope` 的 realm marker、setup 次数和多页面连接行为。
171
+ 仓库提供 `scripts/browser-runtime-fixture/` 与 `pnpm run test:browser`;缺少
172
+ Playwright/Chromium 时脚本明确报告 unsupported,不回退为 Node 或同页面模拟。
@@ -8,25 +8,33 @@
8
8
  | WebLoom 起始提交 | `f1c801655c3132c57b66045ff6d5e642fba4a8ea` |
9
9
  | Node | `v22.13.1` |
10
10
  | pnpm | `11.5.1` |
11
- | 首发包版本 | `0.1.0` |
11
+ | 下一发布包版本 | `0.3.0` |
12
+ | npm 包名 | `webloom-framework` |
13
+ | 发布来源提交 | `a5ace48` |
14
+ | 已发布基线标签 | `v0.1.0` |
12
15
 
13
16
  迁入范围是通用 Manifest、依赖图、Host 调度、MessageBus、生命周期 Scope、权限租约、服务桥、MessagePort 传输、升级门禁、意图控制、任务调度、资源 Store 和 React 绑定。产品 Registry、业务配置、产品日志、身份状态机和视觉组件不属于 WebLoom。
14
17
 
15
- 本文件只记录代码迁移基线。npm 包名占用查询、许可证归属确认、provenance、发布和部署由发布责任人在发布批次执行;施工代理不自动发布。
18
+ 本文件记录代码迁移基线和首发包发布 provenance。npm 包名占用查询、许可证归属确认、
19
+ 发布和部署由发布责任人在发布批次执行;施工代理不自动发布。
16
20
 
17
21
  ## 当前工作区发布状态
18
22
 
19
- 当前拆分源码仍在工作树中,尚未形成包含全部实现的正式提交;上表的
20
- `WebLoom 起始提交` 只是独立仓库初始化提交,不能充当 `0.1.0` 的源码 provenance。
23
+ 当前工作区正在准备 `webloom-framework@0.3.0`,但该版本尚未发布到 npm;下游在发布
24
+ 责任人完成正式发布前继续使用本地工作区依赖。`v0.1.0` / npm `0.1.0` 是已发布基线,
25
+ 不是本轮未发布代码的验收替代品。
21
26
 
22
- 正式发布前必须由负责人完成以下确认:
27
+ WebLoom 已形成包含全部实现和测试的正式发布提交 `a5ace48`,并以 `v0.1.0` 标签发布
28
+ `webloom-framework@0.1.0`。上表的 `WebLoom 起始提交` 仍只是独立仓库初始化提交,
29
+ 不作为 `0.1.0` 的源码 provenance。
23
30
 
24
- - 提交 WebLoom 全部实现和测试,并把该提交记录为发布 provenance;
25
- - 确认 `AGPL-3.0-only` 源码归属和发布责任;
26
- - 使用 `pnpm run pack:consumer` 验证 tarball 的 core-only、React 和 Worker 三类消费者;
27
- - 每个临时消费者都生成独立的 `smoke.ts`,使用临时项目自己的 `tsc --noEmit`
31
+ 发布记录确认如下:
32
+
33
+ - `AGPL-3.0-only` 源码归属和发布责任已按包元数据记录;
34
+ - `pnpm run pack:consumer` 已验证 tarball 的 core-only、React 和 Worker 三类消费者;
35
+ - 每个临时消费者均使用独立的 `smoke.ts` 和临时项目自己的 `tsc --noEmit`
28
36
  编译公开声明;Worker 消费者使用 `lib: ["ES2022", "WebWorker"]` 且不安装 React;
29
- - `npm pack --json` 必须通过发布文件白名单、tarball/解包体积上限和必需入口文件检查;
30
- - 从 tarball 解包后扫描全部 `.d.ts` 与 `.js.map`,禁止本地绝对路径、不可发布的
37
+ - `npm pack --json` 已通过发布文件白名单、tarball/解包体积上限和必需入口文件检查;
38
+ - 从 tarball 解包后扫描全部 `.d.ts` 与 `.js.map`,未发现本地绝对路径、不可发布的
31
39
  source map 路径以及产品领域字段;
32
- - 在负责人确认后创建 `v0.1.0` tag 并发布 npm 包。
40
+ - 已创建 `v0.1.0` tag 并发布 npm 包 `webloom-framework@0.1.0`。