@proteus-vue/render-backend 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,4291 @@
1
+ // src/conformance.ts
2
+ var LAYOUT_VALUES = ["yoga", "native", "none"];
3
+ var GLASS_VALUES = ["L3", "L2", "L1", "none"];
4
+ var BLUR_VALUES = ["true", "approximate", "none"];
5
+ var ANIMATION_VALUES = ["native", "js", "none"];
6
+ var INPUT_VALUES = ["touch", "cursor", "remote", "dial", "voice"];
7
+ var REQUIRED_METHODS = ["createElement", "insert", "remove", "patchProp", "setText"];
8
+ var OPTIONAL_METHODS = [
9
+ "querySelector",
10
+ "measure",
11
+ "layout",
12
+ "scheduleFrame",
13
+ "flush",
14
+ "dispatchInput",
15
+ "onMount",
16
+ "onUnmount",
17
+ "registerExternalTexture",
18
+ "unregisterExternalTexture"
19
+ ];
20
+ function runBackendConformance(backend) {
21
+ const checks = [];
22
+ function check(name, pass, detail) {
23
+ checks.push({ name, pass, detail });
24
+ }
25
+ check("id", typeof backend.id === "string" && backend.id.length > 0, `id=${String(backend.id)}`);
26
+ check("version", typeof backend.version === "string" && backend.version.length > 0, `version=${String(backend.version)}`);
27
+ for (const m of REQUIRED_METHODS) {
28
+ const fn = backend[m];
29
+ check(`method.${String(m)}`, typeof fn === "function", typeof fn === "function" ? void 0 : `\u7F3A\u5931\u65B9\u6CD5 ${String(m)}`);
30
+ }
31
+ let unique = true;
32
+ try {
33
+ const a = backend.createElement({ type: "view", props: {}, children: [] });
34
+ const b = backend.createElement({ type: "view", props: {}, children: [] });
35
+ unique = a !== b;
36
+ } catch (e) {
37
+ unique = false;
38
+ check("createElement.unique", false, "createElement \u629B\u9519: " + e.message);
39
+ }
40
+ if (!checks.some((c) => c.name === "createElement.unique")) {
41
+ check("createElement.unique", unique, unique ? void 0 : "\u4E24\u6B21\u8C03\u7528\u8FD4\u56DE\u76F8\u540C\u53E5\u67C4\uFF08\u975E\u552F\u4E00\uFF09");
42
+ }
43
+ const caps = backend.capabilities;
44
+ if (!caps) {
45
+ check("capabilities", false, "\u7F3A\u5931 capabilities \u58F0\u660E");
46
+ } else {
47
+ check("capabilities.layout", LAYOUT_VALUES.indexOf(caps.layout) >= 0, `layout=${String(caps.layout)}`);
48
+ check("capabilities.glass", GLASS_VALUES.indexOf(caps.glass) >= 0, `glass=${String(caps.glass)}`);
49
+ check("capabilities.blur", BLUR_VALUES.indexOf(caps.blur) >= 0, `blur=${String(caps.blur)}`);
50
+ check("capabilities.animation", ANIMATION_VALUES.indexOf(caps.animation) >= 0, `animation=${String(caps.animation)}`);
51
+ const badInput = (caps.input || []).filter((i) => INPUT_VALUES.indexOf(i) < 0);
52
+ check("capabilities.input", badInput.length === 0, badInput.length ? `\u975E\u6CD5 input \u7C7B\u578B: ${badInput.join(",")}` : void 0);
53
+ }
54
+ for (const m of OPTIONAL_METHODS) {
55
+ const fn = backend[m];
56
+ if (fn !== void 0 && fn !== null && typeof fn !== "function") {
57
+ check(`optional.${String(m)}`, false, `\u53EF\u9009\u65B9\u6CD5 ${String(m)} \u975E\u51FD\u6570`);
58
+ }
59
+ }
60
+ return { ok: checks.every((c) => c.pass), checks };
61
+ }
62
+
63
+ // src/headless.ts
64
+ function toPlainTree(root) {
65
+ return {
66
+ id: root.id,
67
+ type: root.type,
68
+ props: { ...root.props },
69
+ text: root.text,
70
+ children: root.children.map(toPlainTree)
71
+ };
72
+ }
73
+ var SEMANTIC_HEADLESS_MAP = {
74
+ "layout.box": "box",
75
+ "layout.stack": "stack",
76
+ "layout.grid": "grid",
77
+ "layout.fluid": "fluid",
78
+ "layout.adaptive": "adaptive",
79
+ "layout.fit": "fit",
80
+ "layout.split": "split",
81
+ "layout.safe": "safe",
82
+ "layout.sidebar": "sidebar",
83
+ "ui.text": "text",
84
+ "ui.button": "button",
85
+ "ui.image": "image",
86
+ "ui.input": "input",
87
+ "ui.list": "list",
88
+ "ui.nav": "nav",
89
+ "capability.scan-qr": "scan-qr",
90
+ "capability.pick-photo": "pick-photo",
91
+ "capability.location": "location",
92
+ // ★G-32 B1:新增 implemented 语义(与 component-ir SEMANTIC_BACKEND_MAP headless 列同源)
93
+ "layout.inline": "inline",
94
+ "layout.spacer": "spacer",
95
+ "layout.divider": "divider",
96
+ "layout.scroll": "scroll",
97
+ "layout.virtual-list": "virtual-list",
98
+ "layout.masonry": "masonry",
99
+ "ui.heading": "heading",
100
+ "ui.icon": "icon",
101
+ "ui.textarea": "textarea",
102
+ "ui.switch": "switch",
103
+ "ui.slider": "slider",
104
+ "shell.nav": "nav",
105
+ "shell.tabbar": "tabbar",
106
+ "shell.drawer": "drawer",
107
+ "shell.modal": "modal",
108
+ // ★G-32 B4:Shell 补齐 + UI 补齐(与 SEMANTIC_BACKEND_MAP headless 列同源)
109
+ "shell.page": "page",
110
+ "shell.segment": "segment",
111
+ "shell.popover": "popover",
112
+ "shell.action-sheet": "action-sheet",
113
+ "ui.rich-text": "rich-text",
114
+ "ui.avatar": "avatar",
115
+ "ui.media": "media",
116
+ "ui.canvas": "canvas",
117
+ "ui.svg": "svg",
118
+ "ui.select": "select",
119
+ "ui.checkbox": "checkbox",
120
+ "ui.radio": "radio",
121
+ "ui.picker": "picker",
122
+ "ui.form": "form",
123
+ "gesture.draggable": "draggable",
124
+ "gesture.scrollable": "scrollable",
125
+ // ★G-32 B5 尾巴:E18 声明式导航(p-router-link)
126
+ "engineering.router-link": "router-link",
127
+ // ★G-32 B5 续二:工程原语动画组件形态(E19/E20)
128
+ "engineering.transition": "transition",
129
+ "engineering.animate": "animate"
130
+ };
131
+ var HEADLESS_CAPABILITIES = {
132
+ layout: "none",
133
+ // 框架 IR 求解(headless 无布局器)
134
+ glass: "none",
135
+ blur: "none",
136
+ animation: "js",
137
+ textureSharing: false,
138
+ remoteRendering: false,
139
+ ssr: true,
140
+ input: ["touch"]
141
+ };
142
+ function createHeadlessBackend() {
143
+ let nextId = 1;
144
+ const nodes = /* @__PURE__ */ new Map();
145
+ function ensureNode(handle) {
146
+ const n = handle;
147
+ if (!n || typeof n.id !== "number") throw new Error("HeadlessBackend: \u975E\u6CD5\u53E5\u67C4");
148
+ return n;
149
+ }
150
+ return {
151
+ id: "headless",
152
+ version: "0.1.0",
153
+ capabilities: HEADLESS_CAPABILITIES,
154
+ createElement(node) {
155
+ const viewType = node.semantic ? SEMANTIC_HEADLESS_MAP[node.semantic] ?? node.type : node.type;
156
+ const n = {
157
+ id: nextId++,
158
+ type: viewType,
159
+ props: { ...node.props },
160
+ children: [],
161
+ parent: null,
162
+ text: ""
163
+ };
164
+ nodes.set(n.id, n);
165
+ return n;
166
+ },
167
+ insert(child, parent, anchor) {
168
+ const c = ensureNode(child);
169
+ const p = ensureNode(parent);
170
+ if (c.parent) {
171
+ const oldIdx = c.parent.children.indexOf(c);
172
+ if (oldIdx >= 0) c.parent.children.splice(oldIdx, 1);
173
+ }
174
+ if (anchor) {
175
+ const a = ensureNode(anchor);
176
+ const idx = p.children.indexOf(a);
177
+ p.children.splice(idx >= 0 ? idx : p.children.length, 0, c);
178
+ } else {
179
+ p.children.push(c);
180
+ }
181
+ c.parent = p;
182
+ },
183
+ remove(child) {
184
+ const c = ensureNode(child);
185
+ if (c.parent) {
186
+ const idx = c.parent.children.indexOf(c);
187
+ if (idx >= 0) c.parent.children.splice(idx, 1);
188
+ c.parent = null;
189
+ }
190
+ nodes.delete(c.id);
191
+ },
192
+ patchProp(el, key, prev, next) {
193
+ const n = ensureNode(el);
194
+ if (next === null || next === void 0) {
195
+ delete n.props[key];
196
+ } else {
197
+ n.props[key] = next;
198
+ }
199
+ },
200
+ setText(el, text) {
201
+ ensureNode(el).text = text;
202
+ },
203
+ measure() {
204
+ return { width: 0, height: 0 };
205
+ }
206
+ };
207
+ }
208
+
209
+ // src/vue-dom.ts
210
+ function defaultDocument() {
211
+ const g = globalThis;
212
+ if (g.document && typeof g.document.createElement === "function") {
213
+ return g.document;
214
+ }
215
+ throw new Error("VueDomBackend: \u65E0 document \u73AF\u5883\uFF08SSR/Node\u2014\u2014\u8BF7\u6CE8\u5165 documentLike \u6216\u6539\u7528 HeadlessBackend\uFF09");
216
+ }
217
+ var VUE_DOM_CAPABILITIES = {
218
+ layout: "native",
219
+ // 浏览器原生布局
220
+ glass: "L1",
221
+ // backdrop-filter(基础玻璃)
222
+ blur: "approximate",
223
+ animation: "js",
224
+ textureSharing: false,
225
+ remoteRendering: false,
226
+ ssr: false,
227
+ input: ["touch", "cursor"]
228
+ };
229
+ var SEMANTIC_WEB_MAP = {
230
+ "layout.box": { tag: "div", className: "proteus-box" },
231
+ "layout.stack": { tag: "div", className: "proteus-stack" },
232
+ "layout.grid": { tag: "div", className: "proteus-grid" },
233
+ "layout.fluid": { tag: "div", className: "proteus-fluid" },
234
+ "layout.adaptive": { tag: "div", className: "proteus-adaptive" },
235
+ "layout.fit": { tag: "div", className: "proteus-fit" },
236
+ "layout.split": { tag: "div", className: "proteus-split" },
237
+ "layout.safe": { tag: "div", className: "proteus-safe" },
238
+ "layout.sidebar": { tag: "div", className: "proteus-sidebar" },
239
+ "ui.text": { tag: "span" },
240
+ "ui.button": { tag: "button" },
241
+ "ui.image": { tag: "img" },
242
+ "ui.input": { tag: "input" },
243
+ "ui.list": { tag: "div", className: "proteus-list" },
244
+ "ui.nav": { tag: "nav" },
245
+ // ★G-31 B5:能力入口(G-28 组件化)——Web 端用标准元素承载,能力实现由 useNative 注入
246
+ "capability.scan-qr": { tag: "button", className: "proteus-scan-qr" },
247
+ "capability.pick-photo": { tag: "input", className: "proteus-pick-photo" },
248
+ "capability.location": { tag: "button", className: "proteus-location" },
249
+ // ★G-32 B1:新增 implemented 语义(与 component-ir SEMANTIC_BACKEND_MAP vue-dom 列同源)
250
+ "layout.inline": { tag: "div", className: "proteus-inline" },
251
+ "layout.spacer": { tag: "div", className: "proteus-spacer" },
252
+ "layout.divider": { tag: "hr", className: "proteus-divider" },
253
+ "layout.scroll": { tag: "div", className: "proteus-scroll" },
254
+ "layout.virtual-list": { tag: "div", className: "proteus-virtual-list" },
255
+ "layout.masonry": { tag: "div", className: "proteus-masonry" },
256
+ "ui.heading": { tag: "div", className: "proteus-heading" },
257
+ "ui.icon": { tag: "span", className: "proteus-icon" },
258
+ "ui.textarea": { tag: "textarea" },
259
+ "ui.switch": { tag: "div", className: "proteus-switch" },
260
+ "ui.slider": { tag: "div", className: "proteus-slider" },
261
+ "shell.nav": { tag: "nav", className: "proteus-nav" },
262
+ "shell.tabbar": { tag: "nav", className: "proteus-tabbar" },
263
+ "shell.drawer": { tag: "aside", className: "proteus-drawer" },
264
+ "shell.modal": { tag: "div", className: "proteus-modal" },
265
+ // ★G-32 B4:Shell 补齐 + UI 补齐(与 SEMANTIC_BACKEND_MAP vue-dom 列同源)
266
+ "shell.page": { tag: "div", className: "proteus-page" },
267
+ "shell.segment": { tag: "div", className: "proteus-segment" },
268
+ "shell.popover": { tag: "div", className: "proteus-popover" },
269
+ "shell.action-sheet": { tag: "div", className: "proteus-action-sheet" },
270
+ "ui.rich-text": { tag: "div", className: "proteus-rich-text" },
271
+ "ui.avatar": { tag: "div", className: "proteus-avatar" },
272
+ "ui.media": { tag: "div", className: "proteus-media" },
273
+ "ui.canvas": { tag: "canvas" },
274
+ "ui.svg": { tag: "svg" },
275
+ "ui.select": { tag: "div", className: "proteus-select" },
276
+ "ui.checkbox": { tag: "div", className: "proteus-checkbox" },
277
+ "ui.radio": { tag: "div", className: "proteus-radio" },
278
+ "ui.picker": { tag: "div", className: "proteus-picker" },
279
+ "ui.form": { tag: "form" },
280
+ // ★G-32 B4 ④ Gesture 组件形态
281
+ "gesture.draggable": { tag: "div", className: "proteus-draggable" },
282
+ "gesture.scrollable": { tag: "div", className: "proteus-scrollable" },
283
+ // ★G-32 B5 尾巴:E18 声明式导航(p-router-link——a 元素语义)
284
+ "engineering.router-link": { tag: "a", className: "proteus-router-link" },
285
+ // ★G-32 B5 续二:工程原语动画组件形态(E19/E20——纯 CSS 声明)
286
+ "engineering.transition": { tag: "div", className: "proteus-transition" },
287
+ "engineering.animate": { tag: "div", className: "proteus-animate" }
288
+ };
289
+ function createVueDomBackend(doc) {
290
+ const documentLike = doc ?? defaultDocument();
291
+ function ensureEl(handle) {
292
+ const el = handle;
293
+ if (!el || typeof el.tagName !== "string") throw new Error("VueDomBackend: \u975E\u6CD5\u53E5\u67C4");
294
+ return el;
295
+ }
296
+ return {
297
+ id: "vue-dom",
298
+ version: "0.1.0",
299
+ capabilities: VUE_DOM_CAPABILITIES,
300
+ createElement(node) {
301
+ if (node.semantic) {
302
+ const mapped = SEMANTIC_WEB_MAP[node.semantic];
303
+ if (mapped) {
304
+ const el = documentLike.createElement(mapped.tag);
305
+ if (mapped.className) el.setAttribute("class", mapped.className);
306
+ return el;
307
+ }
308
+ }
309
+ return documentLike.createElement(node.type);
310
+ },
311
+ insert(child, parent, anchor) {
312
+ const c = ensureEl(child);
313
+ const p = ensureEl(parent);
314
+ if (anchor) {
315
+ const a = ensureEl(anchor);
316
+ if (a.parentNode === p) {
317
+ p.insertBefore(c, a);
318
+ return;
319
+ }
320
+ }
321
+ p.appendChild(c);
322
+ },
323
+ remove(child) {
324
+ const c = ensureEl(child);
325
+ if (c.parentNode) c.parentNode.removeChild(c);
326
+ },
327
+ patchProp(el, key, prev, next) {
328
+ const target = ensureEl(el);
329
+ if (key.startsWith("on") && key.length > 2) {
330
+ const eventName = key.slice(2).toLowerCase();
331
+ if (typeof next === "function") {
332
+ if (prev !== next) target.addEventListener(eventName, next);
333
+ } else {
334
+ target.removeEventListener(eventName, prev);
335
+ }
336
+ return;
337
+ }
338
+ if (key === "style" && next && typeof next === "object") {
339
+ const style = next;
340
+ for (const k of Object.keys(style)) {
341
+ ;
342
+ target.style[k] = style[k];
343
+ }
344
+ return;
345
+ }
346
+ if (next === null || next === void 0) {
347
+ target.removeAttribute(key);
348
+ } else {
349
+ target.setAttribute(key, String(next));
350
+ }
351
+ },
352
+ setText(el, text) {
353
+ ensureEl(el).textContent = text;
354
+ },
355
+ measure(node, constraints) {
356
+ const el = ensureEl(node);
357
+ const w = constraints?.width ?? el.clientWidth ?? 0;
358
+ const h = constraints?.height ?? el.clientHeight ?? 0;
359
+ return { width: w, height: h };
360
+ }
361
+ };
362
+ }
363
+
364
+ // src/native.ts
365
+ function createMockNativeAdapter() {
366
+ const ops = [];
367
+ return {
368
+ ops,
369
+ createView(descriptor) {
370
+ ops.push(`create:${descriptor.type}`);
371
+ return descriptor;
372
+ },
373
+ updateView(_handle, key, _prev, next) {
374
+ ops.push(`update:${key}=${String(next)}`);
375
+ },
376
+ insertView(child, _parent, _anchor) {
377
+ ops.push(`insert:${String(child.type)}`);
378
+ },
379
+ removeView(child) {
380
+ ops.push(`remove:${String(child.type)}`);
381
+ },
382
+ setViewText(_handle, text) {
383
+ ops.push(`setText:${text}`);
384
+ }
385
+ };
386
+ }
387
+ var NATIVE_CAPABILITIES = {
388
+ layout: "native",
389
+ // 原生布局(AutoLayout / ConstraintLayout / ArkUI 约束)
390
+ glass: "L3",
391
+ // iOS UIGlassEffect / 鸿蒙 fractal(G-07 系统级玻璃语义)
392
+ blur: "true",
393
+ animation: "native",
394
+ // 系统原生转场/动画
395
+ textureSharing: true,
396
+ // PlatformView / TextureView 混合
397
+ remoteRendering: false,
398
+ ssr: false,
399
+ input: ["touch", "cursor", "remote"]
400
+ };
401
+ var SEMANTIC_NATIVE_MAPS = {
402
+ ios: {
403
+ "layout.box": "UIView",
404
+ "layout.stack": "UIStackView",
405
+ "layout.grid": "UICollectionView",
406
+ "layout.fluid": "UIView.fluid",
407
+ "layout.adaptive": "UISheet",
408
+ "layout.fit": "UIView.fit",
409
+ "layout.split": "UISplitViewController",
410
+ "layout.safe": "safeAreaLayoutGuide",
411
+ "layout.sidebar": "UISplitViewController.side",
412
+ "ui.text": "UILabel",
413
+ "ui.button": "UIButton",
414
+ "ui.image": "UIImageView",
415
+ "ui.input": "UITextField",
416
+ "ui.list": "UITableView",
417
+ "ui.nav": "UINavigationController",
418
+ "capability.scan-qr": "AVCaptureSession",
419
+ "capability.pick-photo": "UIImagePicker",
420
+ "capability.location": "CLLocationManager",
421
+ // ★G-32 B1:新增 implemented 语义
422
+ "layout.inline": "UITextAttachment",
423
+ "layout.spacer": "UILayoutGuide",
424
+ "layout.divider": "UIView.divider",
425
+ "layout.scroll": "UIScrollView",
426
+ "layout.virtual-list": "UICollectionView",
427
+ "layout.masonry": "UICollectionView.masonry",
428
+ "ui.heading": "UILabel.heading",
429
+ "ui.icon": "UIImageView.icon",
430
+ "ui.textarea": "UITextView",
431
+ "ui.switch": "UISwitch",
432
+ "ui.slider": "UISlider",
433
+ "shell.nav": "UINavigationBar",
434
+ "shell.tabbar": "UITabBar",
435
+ "shell.drawer": "UIView.drawer",
436
+ "shell.modal": "UIAlertController",
437
+ // ★G-32 B4:Shell 补齐 + UI 补齐
438
+ "shell.page": "UIViewController",
439
+ "shell.segment": "UISegmentedControl",
440
+ "shell.popover": "UIPopoverController",
441
+ "shell.action-sheet": "UIAlertController.actionSheet",
442
+ "ui.rich-text": "UITextView.attributed",
443
+ "ui.avatar": "UIImageView.avatar",
444
+ "ui.media": "AVPlayerView",
445
+ "ui.canvas": "UIView.canvas",
446
+ "ui.svg": "UIView.svg",
447
+ "ui.select": "UIPickerView",
448
+ "ui.checkbox": "UIButton.checkbox",
449
+ "ui.radio": "UIButton.radio",
450
+ "ui.picker": "UIDatePicker",
451
+ "ui.form": "UIView.form",
452
+ "gesture.draggable": "UIPanGestureRecognizer",
453
+ "gesture.scrollable": "UIScrollView.gesture",
454
+ // ★G-32 B5 尾巴:E18 声明式导航(p-router-link)
455
+ "engineering.router-link": "UIButton.link",
456
+ // ★G-32 B5 续二:工程原语动画组件形态(E19/E20)
457
+ "engineering.transition": "UIView.transition",
458
+ "engineering.animate": "CAKeyframeAnimation"
459
+ },
460
+ android: {
461
+ "layout.box": "FrameLayout",
462
+ "layout.stack": "LinearLayout",
463
+ "layout.grid": "GridLayoutManager",
464
+ "layout.fluid": "ConstraintLayout",
465
+ "layout.adaptive": "BottomSheetDialog",
466
+ "layout.fit": "wrapContent",
467
+ "layout.split": "SlidingPaneLayout",
468
+ "layout.safe": "WindowInsets",
469
+ "layout.sidebar": "NavigationRail",
470
+ "ui.text": "TextView",
471
+ "ui.button": "Button",
472
+ "ui.image": "ImageView",
473
+ "ui.input": "EditText",
474
+ "ui.list": "RecyclerView",
475
+ "ui.nav": "NavigationRail",
476
+ "capability.scan-qr": "CameraX",
477
+ "capability.pick-photo": "PhotoPicker",
478
+ "capability.location": "FusedLocation",
479
+ // ★G-32 B1:新增 implemented 语义
480
+ "layout.inline": "TextView.inline",
481
+ "layout.spacer": "Space",
482
+ "layout.divider": "View.divider",
483
+ "layout.scroll": "ScrollView",
484
+ "layout.virtual-list": "RecyclerView",
485
+ "layout.masonry": "StaggeredGridLayoutManager",
486
+ "ui.heading": "TextView.heading",
487
+ "ui.icon": "ImageView.icon",
488
+ "ui.textarea": "EditText.multiline",
489
+ "ui.switch": "Switch",
490
+ "ui.slider": "SeekBar",
491
+ "shell.nav": "Toolbar",
492
+ "shell.tabbar": "BottomNavigationView",
493
+ "shell.drawer": "DrawerLayout",
494
+ "shell.modal": "Dialog",
495
+ // ★G-32 B4:Shell 补齐 + UI 补齐
496
+ "shell.page": "Activity",
497
+ "shell.segment": "TabLayout",
498
+ "shell.popover": "PopupWindow",
499
+ "shell.action-sheet": "BottomSheet",
500
+ "ui.rich-text": "TextView.html",
501
+ "ui.avatar": "ImageView.avatar",
502
+ "ui.media": "VideoView",
503
+ "ui.canvas": "SurfaceView",
504
+ "ui.svg": "VectorDrawable",
505
+ "ui.select": "Spinner",
506
+ "ui.checkbox": "CheckBox",
507
+ "ui.radio": "RadioButton",
508
+ "ui.picker": "DatePicker",
509
+ "ui.form": "LinearLayout.form",
510
+ "gesture.draggable": "GestureDetector",
511
+ "gesture.scrollable": "NestedScrollView",
512
+ // ★G-32 B5 尾巴:E18 声明式导航(p-router-link)
513
+ "engineering.router-link": "TextView.link",
514
+ // ★G-32 B5 续二:工程原语动画组件形态(E19/E20)
515
+ "engineering.transition": "View.animate.transition",
516
+ "engineering.animate": "ValueAnimator"
517
+ },
518
+ harmony: {
519
+ "layout.box": "Stack",
520
+ "layout.stack": "Flex",
521
+ "layout.grid": "Grid",
522
+ "layout.fluid": "Flex.fluid",
523
+ "layout.adaptive": "Sheet",
524
+ "layout.fit": "fitContent",
525
+ "layout.split": "SideBarContainer",
526
+ "layout.safe": "getAvoidArea",
527
+ "layout.sidebar": "SideBarContainer",
528
+ "ui.text": "Text",
529
+ "ui.button": "Button",
530
+ "ui.image": "Image",
531
+ "ui.input": "TextInput",
532
+ "ui.list": "List",
533
+ "ui.nav": "Navigation",
534
+ "capability.scan-qr": "ScanKit",
535
+ "capability.pick-photo": "PhotoViewPicker",
536
+ "capability.location": "geoLocationManager",
537
+ // ★G-32 B1:新增 implemented 语义
538
+ "layout.inline": "Span",
539
+ "layout.spacer": "Blank",
540
+ "layout.divider": "Divider",
541
+ "layout.scroll": "Scroll",
542
+ "layout.virtual-list": "List",
543
+ "layout.masonry": "WaterFlow",
544
+ "ui.heading": "Text.heading",
545
+ "ui.icon": "SymbolGlyph",
546
+ "ui.textarea": "TextArea",
547
+ "ui.switch": "Toggle",
548
+ "ui.slider": "Slider",
549
+ "shell.nav": "NavigationBar",
550
+ "shell.tabbar": "Tabs",
551
+ "shell.drawer": "Panel",
552
+ "shell.modal": "CustomDialog",
553
+ // ★G-32 B4:Shell 补齐 + UI 补齐
554
+ "shell.page": "Page",
555
+ "shell.segment": "Segmented",
556
+ "shell.popover": "Popup",
557
+ "shell.action-sheet": "ActionSheet",
558
+ "ui.rich-text": "RichText",
559
+ "ui.avatar": "Image.avatar",
560
+ "ui.media": "Video",
561
+ "ui.canvas": "Canvas",
562
+ "ui.svg": "Shape",
563
+ "ui.select": "Select",
564
+ "ui.checkbox": "Checkbox",
565
+ "ui.radio": "Radio",
566
+ "ui.picker": "DatePicker",
567
+ "ui.form": "FormComponent",
568
+ "gesture.draggable": "PanGesture",
569
+ "gesture.scrollable": "Scroll.gesture",
570
+ // ★G-32 B5 尾巴:E18 声明式导航(p-router-link)
571
+ "engineering.router-link": "Text.link",
572
+ // ★G-32 B5 续二:工程原语动画组件形态(E19/E20)
573
+ "engineering.transition": "animateTo.transition",
574
+ "engineering.animate": "Animator.transition"
575
+ }
576
+ };
577
+ function createNativeBackend(adapter, platform = "ios") {
578
+ const viewAdapter = adapter ?? createMockNativeAdapter();
579
+ const semanticMap = SEMANTIC_NATIVE_MAPS[platform];
580
+ const id = platform === "ios" ? "native-ios" : platform === "android" ? "native-android" : "native-harmony";
581
+ let nextId = 1;
582
+ const nodes = /* @__PURE__ */ new Map();
583
+ function ensureNode(handle) {
584
+ const n = handle;
585
+ if (!n || typeof n.id !== "number") throw new Error("NativeBackend: \u975E\u6CD5\u53E5\u67C4");
586
+ return n;
587
+ }
588
+ return {
589
+ id,
590
+ version: "0.1.0",
591
+ capabilities: NATIVE_CAPABILITIES,
592
+ createElement(node) {
593
+ const viewType = node.semantic ? semanticMap[node.semantic] ?? node.type : node.type;
594
+ const descriptor = {
595
+ id: nextId++,
596
+ type: viewType,
597
+ props: { ...node.props },
598
+ children: [],
599
+ parent: null,
600
+ text: "",
601
+ handle: null
602
+ };
603
+ descriptor.handle = viewAdapter.createView(descriptor);
604
+ nodes.set(descriptor.id, descriptor);
605
+ return descriptor;
606
+ },
607
+ insert(child, parent, anchor) {
608
+ const c = ensureNode(child);
609
+ const p = ensureNode(parent);
610
+ if (c.parent) {
611
+ const oldIdx = c.parent.children.indexOf(c);
612
+ if (oldIdx >= 0) c.parent.children.splice(oldIdx, 1);
613
+ }
614
+ if (anchor) {
615
+ const a = ensureNode(anchor);
616
+ const idx = p.children.indexOf(a);
617
+ p.children.splice(idx >= 0 ? idx : p.children.length, 0, c);
618
+ } else {
619
+ p.children.push(c);
620
+ }
621
+ c.parent = p;
622
+ viewAdapter.insertView(c.handle, p.handle, anchor ? ensureNode(anchor).handle : void 0);
623
+ },
624
+ remove(child) {
625
+ const c = ensureNode(child);
626
+ if (c.parent) {
627
+ const idx = c.parent.children.indexOf(c);
628
+ if (idx >= 0) c.parent.children.splice(idx, 1);
629
+ c.parent = null;
630
+ }
631
+ nodes.delete(c.id);
632
+ viewAdapter.removeView(c.handle);
633
+ },
634
+ patchProp(el, key, prev, next) {
635
+ const n = ensureNode(el);
636
+ if (next === null || next === void 0) {
637
+ delete n.props[key];
638
+ } else {
639
+ n.props[key] = next;
640
+ }
641
+ viewAdapter.updateView(n.handle, key, prev, next);
642
+ },
643
+ setText(el, text) {
644
+ const n = ensureNode(el);
645
+ n.text = text;
646
+ viewAdapter.setViewText(n.handle, text);
647
+ },
648
+ measure() {
649
+ return { width: 0, height: 0 };
650
+ }
651
+ };
652
+ }
653
+
654
+ // src/flutter.ts
655
+ var WIDGET_MAP = {
656
+ view: "Container",
657
+ text: "Text",
658
+ button: "FilledButton",
659
+ image: "Image",
660
+ input: "TextField",
661
+ textarea: "TextField",
662
+ "scroll-view": "SingleChildScrollView",
663
+ switch: "Switch",
664
+ slider: "Slider",
665
+ icon: "Icon",
666
+ progress: "LinearProgressIndicator",
667
+ navigator: "GestureDetector",
668
+ "p-grid": "Wrap",
669
+ "p-stack": "Flex",
670
+ "p-split": "Row"
671
+ };
672
+ var SEMANTIC_FLUTTER_MAP = {
673
+ "layout.box": "Container",
674
+ "layout.stack": "Flex",
675
+ "layout.grid": "GridView",
676
+ "layout.fluid": "Wrap",
677
+ "layout.adaptive": "showModal",
678
+ "layout.fit": "IntrinsicWidth",
679
+ "layout.split": "Row",
680
+ "layout.safe": "SafeArea",
681
+ "layout.sidebar": "NavigationRail",
682
+ "ui.text": "Text",
683
+ "ui.button": "FilledButton",
684
+ "ui.image": "Image",
685
+ "ui.input": "TextField",
686
+ "ui.list": "ListView",
687
+ "ui.nav": "Navigator",
688
+ "capability.scan-qr": "scanQR",
689
+ "capability.pick-photo": "pickPhoto",
690
+ "capability.location": "getLocation",
691
+ // ★G-32 B1:新增 implemented 语义
692
+ "layout.inline": "InlineSpan",
693
+ "layout.spacer": "Spacer",
694
+ "layout.divider": "Divider",
695
+ "layout.scroll": "ScrollView",
696
+ "layout.virtual-list": "ListView",
697
+ "layout.masonry": "SliverMasonryGrid",
698
+ "ui.heading": "Text.heading",
699
+ "ui.icon": "Icon",
700
+ "ui.textarea": "TextField.multiline",
701
+ "ui.switch": "Switch",
702
+ "ui.slider": "Slider",
703
+ "shell.nav": "AppBar",
704
+ "shell.tabbar": "BottomNavigationBar",
705
+ "shell.drawer": "Drawer",
706
+ "shell.modal": "showDialog",
707
+ // ★G-32 B4:Shell 补齐 + UI 补齐
708
+ "shell.page": "Scaffold",
709
+ "shell.segment": "SegmentedButton",
710
+ "shell.popover": "showMenu",
711
+ "shell.action-sheet": "showModalBottomSheet",
712
+ "ui.rich-text": "RichText",
713
+ "ui.avatar": "CircleAvatar",
714
+ "ui.media": "VideoPlayer",
715
+ "ui.canvas": "CustomPaint",
716
+ "ui.svg": "SvgPicture",
717
+ "ui.select": "DropdownButton",
718
+ "ui.checkbox": "Checkbox",
719
+ "ui.radio": "Radio",
720
+ "ui.picker": "showDatePicker",
721
+ "ui.form": "Form",
722
+ "gesture.draggable": "Draggable",
723
+ "gesture.scrollable": "Scrollable",
724
+ // ★G-32 B5 尾巴:E18 声明式导航(p-router-link)
725
+ "engineering.router-link": "TextButton",
726
+ // ★G-32 B5 续二:工程原语动画组件形态(E19/E20)
727
+ "engineering.transition": "AnimatedOpacity",
728
+ "engineering.animate": "AnimationController"
729
+ };
730
+ function mapWidgetType(type) {
731
+ return WIDGET_MAP[type] ?? type;
732
+ }
733
+ function toWidgetTree(root) {
734
+ return {
735
+ id: root.id,
736
+ widget: root.widget,
737
+ props: { ...root.props },
738
+ text: root.text,
739
+ children: root.children.map(toWidgetTree)
740
+ };
741
+ }
742
+ var FLUTTER_CAPABILITIES = {
743
+ layout: "yoga",
744
+ // Flutter 自带布局引擎(Yoga/C++——后端可选,spike 标注)
745
+ glass: "L3",
746
+ // Skia/Impeller 绘制玻璃(系统级渲染语义)
747
+ blur: "true",
748
+ animation: "native",
749
+ // Flutter 动画管线
750
+ textureSharing: true,
751
+ // Texture/PlatformView 混合
752
+ remoteRendering: false,
753
+ ssr: false,
754
+ input: ["touch", "cursor", "remote"]
755
+ };
756
+ function createFlutterBackend() {
757
+ let nextId = 1;
758
+ const nodes = /* @__PURE__ */ new Map();
759
+ function ensureNode(handle) {
760
+ const n = handle;
761
+ if (!n || typeof n.id !== "number") throw new Error("FlutterBackend: \u975E\u6CD5\u53E5\u67C4");
762
+ return n;
763
+ }
764
+ return {
765
+ id: "flutter",
766
+ version: "0.1.0",
767
+ capabilities: FLUTTER_CAPABILITIES,
768
+ createElement(node) {
769
+ const widget = node.semantic ? SEMANTIC_FLUTTER_MAP[node.semantic] ?? mapWidgetType(node.type) : mapWidgetType(node.type);
770
+ const descriptor = {
771
+ id: nextId++,
772
+ widget,
773
+ props: { ...node.props },
774
+ children: [],
775
+ parent: null,
776
+ text: ""
777
+ };
778
+ nodes.set(descriptor.id, descriptor);
779
+ return descriptor;
780
+ },
781
+ insert(child, parent, anchor) {
782
+ const c = ensureNode(child);
783
+ const p = ensureNode(parent);
784
+ if (c.parent) {
785
+ const oldIdx = c.parent.children.indexOf(c);
786
+ if (oldIdx >= 0) c.parent.children.splice(oldIdx, 1);
787
+ }
788
+ if (anchor) {
789
+ const a = ensureNode(anchor);
790
+ const idx = p.children.indexOf(a);
791
+ p.children.splice(idx >= 0 ? idx : p.children.length, 0, c);
792
+ } else {
793
+ p.children.push(c);
794
+ }
795
+ c.parent = p;
796
+ },
797
+ remove(child) {
798
+ const c = ensureNode(child);
799
+ if (c.parent) {
800
+ const idx = c.parent.children.indexOf(c);
801
+ if (idx >= 0) c.parent.children.splice(idx, 1);
802
+ c.parent = null;
803
+ }
804
+ nodes.delete(c.id);
805
+ },
806
+ patchProp(el, key, prev, next) {
807
+ const n = ensureNode(el);
808
+ if (next === null || next === void 0) {
809
+ delete n.props[key];
810
+ } else {
811
+ n.props[key] = next;
812
+ }
813
+ },
814
+ setText(el, text) {
815
+ ensureNode(el).text = text;
816
+ },
817
+ measure() {
818
+ return { width: 0, height: 0 };
819
+ }
820
+ };
821
+ }
822
+
823
+ // src/conformance-component.ts
824
+ function createControlReader(backendId) {
825
+ switch (backendId) {
826
+ case "vue-dom":
827
+ return (h) => {
828
+ const el = h;
829
+ const cls = el.getAttribute?.("class") ?? "";
830
+ return el.tagName.toLowerCase() + (cls ? "." + cls : "");
831
+ };
832
+ case "native-ios":
833
+ case "native-android":
834
+ case "native-harmony":
835
+ return (h) => h.type;
836
+ case "flutter":
837
+ return (h) => h.widget;
838
+ case "headless":
839
+ return (h) => h.type;
840
+ default:
841
+ return (h) => String(h?.type ?? h);
842
+ }
843
+ }
844
+ function renderComponentSnapshot(backend, ir, readControl) {
845
+ const snapOf = /* @__PURE__ */ new Map();
846
+ function visit(node) {
847
+ const handle = backend.createElement(node);
848
+ for (const key of Object.keys(node.props)) {
849
+ backend.patchProp(handle, key, void 0, node.props[key]);
850
+ }
851
+ const snap = {
852
+ type: node.type,
853
+ semantic: node.semantic ?? node.type,
854
+ control: readControl(handle),
855
+ props: { ...node.props },
856
+ text: "",
857
+ children: []
858
+ };
859
+ snapOf.set(handle, snap);
860
+ for (const child of node.children) {
861
+ const childHandle = visit(child);
862
+ backend.insert(childHandle, handle);
863
+ const childSnap = snapOf.get(childHandle);
864
+ if (childSnap) snap.children.push(childSnap);
865
+ }
866
+ return handle;
867
+ }
868
+ const rootHandle = visit(ir);
869
+ return snapOf.get(rootHandle);
870
+ }
871
+
872
+ // src/hybrid.ts
873
+ function createHybridRenderer(options) {
874
+ const { defaultBackend } = options;
875
+ const regions = options.regions ?? [];
876
+ const devtools = options.devtools !== false;
877
+ const nodeBackend = /* @__PURE__ */ new WeakMap();
878
+ const traceList = [];
879
+ function routeFor(node) {
880
+ for (const r of regions) {
881
+ if (r.match(node)) return r.backend;
882
+ }
883
+ return defaultBackend;
884
+ }
885
+ function regionName(node) {
886
+ for (const r of regions) {
887
+ if (r.match(node)) return r.name ?? "region";
888
+ }
889
+ return "default";
890
+ }
891
+ function backendOf(handle) {
892
+ if (typeof handle !== "object" || handle === null) return void 0;
893
+ return nodeBackend.get(handle)?.backend;
894
+ }
895
+ function textureBackends() {
896
+ const sharing = regions.map((r) => r.backend).concat([defaultBackend]).filter((b) => b.capabilities.textureSharing === true);
897
+ return sharing.length ? sharing : [defaultBackend];
898
+ }
899
+ const defaultId = "hybrid";
900
+ const hybrid = {
901
+ id: defaultId,
902
+ version: "0.1.0",
903
+ capabilities: {
904
+ layout: "none",
905
+ // 无自带布局器——委托各后端(框架 IR 求解)
906
+ glass: "L1",
907
+ blur: "none",
908
+ animation: "js",
909
+ textureSharing: true,
910
+ remoteRendering: false,
911
+ ssr: true,
912
+ input: ["touch", "cursor", "remote"]
913
+ },
914
+ createElement(node) {
915
+ const backend = routeFor(node);
916
+ const handle = backend.createElement(node);
917
+ if (typeof handle === "object" && handle !== null) {
918
+ nodeBackend.set(handle, { backend, region: regionName(node) });
919
+ }
920
+ if (devtools) {
921
+ traceList.push({ semantic: node.semantic, type: node.type, backendId: backend.id, region: regionName(node) });
922
+ }
923
+ return handle;
924
+ },
925
+ insert(child, parent, anchor) {
926
+ const b = backendOf(child);
927
+ if (!b) return;
928
+ b.insert(child, parent, anchor);
929
+ },
930
+ remove(child) {
931
+ const b = backendOf(child);
932
+ if (b) b.remove(child);
933
+ },
934
+ patchProp(el, key, prev, next) {
935
+ const b = backendOf(el);
936
+ if (b) b.patchProp(el, key, prev, next);
937
+ },
938
+ setText(el, text) {
939
+ const b = backendOf(el);
940
+ if (b) b.setText(el, text);
941
+ },
942
+ // —— 可选方法:委托归属后端(存在时) ——
943
+ querySelector(selector) {
944
+ return defaultBackend.querySelector ? defaultBackend.querySelector(selector) : null;
945
+ },
946
+ measure(node, constraints) {
947
+ const b = backendOf(node);
948
+ if (b && b.measure) return b.measure(node, constraints);
949
+ return { width: 0, height: 0 };
950
+ },
951
+ layout(root, constraints) {
952
+ const b = backendOf(root);
953
+ if (b && b.layout) b.layout(root, constraints);
954
+ },
955
+ scheduleFrame(task) {
956
+ defaultBackend.scheduleFrame ? defaultBackend.scheduleFrame(task) : task();
957
+ },
958
+ flush() {
959
+ defaultBackend.flush ? defaultBackend.flush() : void 0;
960
+ },
961
+ dispatchInput(event) {
962
+ const b = backendOf(event.target);
963
+ if (b && b.dispatchInput) b.dispatchInput(event);
964
+ },
965
+ onMount(root) {
966
+ const b = backendOf(root);
967
+ if (b && b.onMount) b.onMount(root);
968
+ },
969
+ onUnmount(root) {
970
+ const b = backendOf(root);
971
+ if (b && b.onUnmount) b.onUnmount(root);
972
+ },
973
+ // —— ★B6 纹理共享:广播注册/注销(跨后端全局 id) ——
974
+ registerExternalTexture(id, texture) {
975
+ for (const b of textureBackends()) {
976
+ if (b.registerExternalTexture) b.registerExternalTexture(id, texture);
977
+ }
978
+ },
979
+ unregisterExternalTexture(id) {
980
+ for (const b of textureBackends()) {
981
+ if (b.unregisterExternalTexture) b.unregisterExternalTexture(id);
982
+ }
983
+ },
984
+ // —— 混合专属 ——
985
+ routeFor,
986
+ backendOf,
987
+ traces: () => traceList.slice(),
988
+ clearTraces: () => {
989
+ traceList.length = 0;
990
+ }
991
+ };
992
+ return hybrid;
993
+ }
994
+ function textureRef(textureId) {
995
+ return { textureId };
996
+ }
997
+ function runHybridConformance(renderer) {
998
+ const checks = [];
999
+ function check(name, pass, detail) {
1000
+ checks.push({ name, pass, detail });
1001
+ }
1002
+ check("id", typeof renderer.id === "string" && renderer.id.length > 0, `id=${String(renderer.id)}`);
1003
+ check("method.createElement", typeof renderer.createElement === "function");
1004
+ check("method.insert", typeof renderer.insert === "function");
1005
+ check("method.remove", typeof renderer.remove === "function");
1006
+ check("method.patchProp", typeof renderer.patchProp === "function");
1007
+ check("method.setText", typeof renderer.setText === "function");
1008
+ const probe = { type: "p-box", semantic: "layout.box", props: {}, children: [] };
1009
+ const routed = renderer.routeFor(probe);
1010
+ check("route.default-non-null", routed !== void 0 && routed !== null);
1011
+ const handle = renderer.createElement(probe);
1012
+ const owned = renderer.backendOf(handle);
1013
+ check("route.backendOf-owned", owned !== void 0, "createElement \u540E backendOf \u5E94\u6709\u5F52\u5C5E");
1014
+ if (owned) check("route.consistent", owned === routed, "routeFor \u4E0E backendOf \u5E94\u4E00\u81F4");
1015
+ check("optional.registerExternalTexture", typeof renderer.registerExternalTexture === "function");
1016
+ check("optional.unregisterExternalTexture", typeof renderer.unregisterExternalTexture === "function");
1017
+ check("devtools.trace-recorded", renderer.traces().length > 0, `traces=${renderer.traces().length}`);
1018
+ return { ok: checks.every((c) => c.pass), checks };
1019
+ }
1020
+
1021
+ // ../component-ir/dist/index.js
1022
+ var TAG_SEMANTIC_MAP = {
1023
+ // G-32 ① 布局原语(12)
1024
+ "p-box": "layout.box",
1025
+ "p-inline": "layout.inline",
1026
+ "p-stack": "layout.stack",
1027
+ "p-grid": "layout.grid",
1028
+ "p-fluid": "layout.fluid",
1029
+ "p-adaptive": "layout.adaptive",
1030
+ "p-fit": "layout.fit",
1031
+ "p-spacer": "layout.spacer",
1032
+ "p-divider": "layout.divider",
1033
+ "p-scroll": "layout.scroll",
1034
+ "p-virtual-list": "layout.virtual-list",
1035
+ "p-masonry": "layout.masonry",
1036
+ // G-32 ② UI 原语(18)
1037
+ "p-text": "ui.text",
1038
+ "p-heading": "ui.heading",
1039
+ "p-rich-text": "ui.rich-text",
1040
+ "p-icon": "ui.icon",
1041
+ "p-image": "ui.image",
1042
+ "p-avatar": "ui.avatar",
1043
+ "p-media": "ui.media",
1044
+ "p-canvas": "ui.canvas",
1045
+ "p-svg": "ui.svg",
1046
+ "p-input": "ui.input",
1047
+ "p-textarea": "ui.textarea",
1048
+ "p-select": "ui.select",
1049
+ "p-checkbox": "ui.checkbox",
1050
+ "p-radio": "ui.radio",
1051
+ "p-switch": "ui.switch",
1052
+ "p-slider": "ui.slider",
1053
+ "p-picker": "ui.picker",
1054
+ "p-form": "ui.form",
1055
+ "p-button": "ui.button",
1056
+ // 既有按钮
1057
+ // G-32 ③ Shell 原语(10)
1058
+ "p-page": "shell.page",
1059
+ "p-nav": "shell.nav",
1060
+ "p-tabbar": "shell.tabbar",
1061
+ "p-segment": "shell.segment",
1062
+ "p-drawer": "shell.drawer",
1063
+ "p-modal": "shell.modal",
1064
+ "p-popover": "shell.popover",
1065
+ "p-toast": "shell.toast",
1066
+ "p-action-sheet": "shell.action-sheet",
1067
+ "p-split": "layout.split",
1068
+ // ★已落地绑定(G-32 S10 分栏语义——layout.split 承载)
1069
+ // G-32 ④ Gesture 组件形态(2)
1070
+ "p-draggable": "gesture.draggable",
1071
+ "p-scrollable": "gesture.scrollable",
1072
+ // G-32 ⑥ Engineering 组件形态(3)
1073
+ "p-router-link": "engineering.router-link",
1074
+ // ★E18 组件形态(p- 前缀产 C-IR)
1075
+ "router-link": "engineering.router-link",
1076
+ // ★兼容别名(Vue Router 风格 <router-link> 标签;非 catalog 条目——p-view 先例 G-31 B4)
1077
+ "p-transition": "engineering.transition",
1078
+ "p-animate": "engineering.animate",
1079
+ // G-31 能力入口
1080
+ "p-scan-qr": "capability.scan-qr",
1081
+ "p-pick-photo": "capability.pick-photo",
1082
+ "p-location": "capability.location",
1083
+ // ★G-31 B4 现有组件对齐(src/components 实际标签 → L1 语义)
1084
+ "p-view": "layout.box",
1085
+ // 原子容器 = p-box 角色
1086
+ "p-list-view": "ui.list",
1087
+ "p-nav-bar": "ui.nav",
1088
+ // ★G-31 B4 Fluid 体系扩展语义(有明确系统原生对应——原则 #10.8)
1089
+ "p-safe": "layout.safe",
1090
+ "p-sidebar": "layout.sidebar"
1091
+ };
1092
+ function toComponentIR(tag, props = {}, children = []) {
1093
+ if (!tag.startsWith("p-")) return null;
1094
+ const semantic = TAG_SEMANTIC_MAP[tag];
1095
+ if (!semantic) return null;
1096
+ return { tag, semantic, props: { ...props }, children };
1097
+ }
1098
+ var LAYOUT = [
1099
+ { id: "L1", kind: "layout", semantic: "layout.box", tag: "p-box", props: ["aspectRatio", "overflow"], mpEquiv: "<view>", tier: "L1", status: "implemented" },
1100
+ { id: "L2", kind: "layout", semantic: "layout.inline", tag: "p-inline", props: ["wrap"], mpEquiv: "<text> \u5185\u8054", tier: "L1", status: "implemented" },
1101
+ { id: "L3", kind: "layout", semantic: "layout.stack", tag: "p-stack", props: ["direction", "gap", "align", "wrap", "snap", "loop"], mpEquiv: "flex + scroll-view + swiper", tier: "L1", status: "implemented" },
1102
+ { id: "L4", kind: "layout", semantic: "layout.grid", tag: "p-grid", props: ["minColWidth", "maxCols", "gap", "autoFlow"], mpEquiv: "<view> + CSS Grid", tier: "L1", status: "implemented" },
1103
+ { id: "L5", kind: "layout", semantic: "layout.fluid", tag: "p-fluid", props: ["breakpoints", "minItemWidth"], mpEquiv: "\u54CD\u5E94\u5F0F CSS", tier: "L1", status: "implemented" },
1104
+ { id: "L6", kind: "layout", semantic: "layout.adaptive", tag: "p-adaptive", props: ["sheet", "dialog", "popover", "drawer"], mpEquiv: "\u65E0\uFF08\u5BB9\u5668\u5BBD\u5EA6\u8BED\u4E49\u65AD\u70B9\uFF09", tier: "L1", status: "implemented" },
1105
+ { id: "L7", kind: "layout", semantic: "layout.fit", tag: "p-fit", props: ["mode"], mpEquiv: "fit-content", tier: "L1", status: "implemented" },
1106
+ { id: "L8", kind: "layout", semantic: "layout.spacer", tag: "p-spacer", props: ["grow", "shrink"], mpEquiv: "flex:1", tier: "L1", status: "implemented" },
1107
+ { id: "L9", kind: "layout", semantic: "layout.divider", tag: "p-divider", props: ["orientation", "inset"], mpEquiv: "<view> + border", tier: "L1", status: "implemented" },
1108
+ { id: "L10", kind: "layout", semantic: "layout.scroll", tag: "p-scroll", props: ["axis", "paging", "refresh", "indicator"], mpEquiv: "<scroll-view>", tier: "L1", status: "implemented" },
1109
+ { id: "L11", kind: "layout", semantic: "layout.virtual-list", tag: "p-virtual-list", props: ["itemSize", "buffer", "direction"], mpEquiv: "<scroll-view> + \u624B\u52A8\u56DE\u6536", tier: "L1", status: "implemented" },
1110
+ { id: "L12", kind: "layout", semantic: "layout.masonry", tag: "p-masonry", props: ["colCount", "gap"], mpEquiv: "\u7B2C\u4E09\u65B9\u7011\u5E03\u6D41", tier: "L1", status: "implemented" }
1111
+ ];
1112
+ var UI = [
1113
+ { id: "U1", kind: "ui", semantic: "ui.text", tag: "p-text", props: ["content", "selectable", "truncate", "align"], mpEquiv: "<text>", tier: "L1", status: "implemented" },
1114
+ { id: "U2", kind: "ui", semantic: "ui.heading", tag: "p-heading", props: ["level"], mpEquiv: "<h1>-<h6>", tier: "L1", status: "implemented" },
1115
+ { id: "U3", kind: "ui", semantic: "ui.rich-text", tag: "p-rich-text", props: ["source", "schema"], mpEquiv: "<rich-text>", tier: "L1", status: "implemented" },
1116
+ { id: "U4", kind: "ui", semantic: "ui.icon", tag: "p-icon", props: ["name", "size", "color", "spin"], mpEquiv: "<icon>", tier: "L1", status: "implemented" },
1117
+ { id: "U5", kind: "ui", semantic: "ui.image", tag: "p-image", props: ["src", "fit", "placeholder", "lazy"], mpEquiv: "<image>", tier: "L1", status: "implemented" },
1118
+ { id: "U6", kind: "ui", semantic: "ui.avatar", tag: "p-avatar", props: ["src", "shape", "size", "fallback"], mpEquiv: "\u7EC4\u5408", tier: "L1", status: "implemented" },
1119
+ { id: "U7", kind: "ui", semantic: "ui.media", tag: "p-media", props: ["kind", "controls", "autoplay", "poster", "loop", "muted", "pictureInPicture"], mpEquiv: "<video>+<audio>", tier: "L1", status: "implemented" },
1120
+ { id: "U8", kind: "ui", semantic: "ui.canvas", tag: "p-canvas", props: ["engine", "resolution"], mpEquiv: "<canvas>", tier: "L1", status: "implemented" },
1121
+ { id: "U9", kind: "ui", semantic: "ui.svg", tag: "p-svg", props: ["path", "viewbox"], mpEquiv: "\u65E0", tier: "L1", status: "implemented" },
1122
+ { id: "U10", kind: "ui", semantic: "ui.input", tag: "p-input", props: ["type", "mask", "validation", "clearable"], mpEquiv: "<input>", tier: "L1", status: "implemented" },
1123
+ { id: "U11", kind: "ui", semantic: "ui.textarea", tag: "p-textarea", props: ["autosize", "maxLength", "count"], mpEquiv: "<textarea>", tier: "L1", status: "implemented" },
1124
+ { id: "U12", kind: "ui", semantic: "ui.select", tag: "p-select", props: ["options", "multiple", "searchable", "cascader"], mpEquiv: "<picker> \u90E8\u5206", tier: "L1", status: "implemented" },
1125
+ { id: "U13", kind: "ui", semantic: "ui.checkbox", tag: "p-checkbox", props: ["checked", "indeterminate", "group"], mpEquiv: "<checkbox>", tier: "L1", status: "implemented" },
1126
+ { id: "U14", kind: "ui", semantic: "ui.radio", tag: "p-radio", props: ["value", "group"], mpEquiv: "<radio>", tier: "L1", status: "implemented" },
1127
+ { id: "U15", kind: "ui", semantic: "ui.switch", tag: "p-switch", props: ["checked", "loading"], mpEquiv: "<switch>", tier: "L1", status: "implemented" },
1128
+ { id: "U16", kind: "ui", semantic: "ui.slider", tag: "p-slider", props: ["min", "max", "step", "range"], mpEquiv: "<slider>", tier: "L1", status: "implemented" },
1129
+ { id: "U17", kind: "ui", semantic: "ui.picker", tag: "p-picker", props: ["mode", "start", "end"], mpEquiv: "<picker>", tier: "L1", status: "implemented" },
1130
+ { id: "U18", kind: "ui", semantic: "ui.form", tag: "p-form", props: ["model", "rules", "layout"], mpEquiv: "\u7EC4\u5408", tier: "L1", status: "implemented" }
1131
+ ];
1132
+ var SHELL = [
1133
+ { id: "S1", kind: "shell", semantic: "shell.page", tag: "p-page", props: ["title", "statusBar", "pullRefresh"], mpEquiv: "<page>", tier: "L1", status: "implemented" },
1134
+ { id: "S2", kind: "shell", semantic: "shell.nav", tag: "p-nav", props: ["title", "transparent"], mpEquiv: "\u5BFC\u822A\u680F\u914D\u7F6E", tier: "L1", status: "implemented" },
1135
+ { id: "S3", kind: "shell", semantic: "shell.tabbar", tag: "p-tabbar", props: ["tabs", "active", "badge"], mpEquiv: "<tabbar>", tier: "L1", status: "implemented" },
1136
+ { id: "S4", kind: "shell", semantic: "shell.segment", tag: "p-segment", props: ["options", "active"], mpEquiv: "<segment>", tier: "L1", status: "implemented" },
1137
+ { id: "S5", kind: "shell", semantic: "shell.drawer", tag: "p-drawer", props: ["side", "width", "overlay"], mpEquiv: "\u7EC4\u5408", tier: "L1", status: "implemented" },
1138
+ { id: "S6", kind: "shell", semantic: "shell.modal", tag: "p-modal", props: ["open", "dismissible", "sheet", "dialog", "alert"], mpEquiv: "<modal>+wx.showModal", tier: "L1", status: "implemented" },
1139
+ { id: "S7", kind: "shell", semantic: "shell.popover", tag: "p-popover", props: ["trigger", "placement"], mpEquiv: "\u7EC4\u5408", tier: "L1", status: "implemented" },
1140
+ { id: "S8", kind: "shell", semantic: "shell.toast", tag: "p-toast", props: ["message", "duration", "type"], mpEquiv: "wx.showToast", tier: "L1", status: "planned" },
1141
+ { id: "S9", kind: "shell", semantic: "shell.action-sheet", tag: "p-action-sheet", props: ["actions", "cancel"], mpEquiv: "wx.showActionSheet", tier: "L1", status: "implemented" },
1142
+ { id: "S10", kind: "shell", semantic: "layout.split", tag: "p-split", props: ["breakpoint", "ratio", "collapse"], mpEquiv: "\u65E0\uFF08\u5206\u680F\u5E03\u5C40\uFF09", tier: "L1", status: "implemented" }
1143
+ // ★已落地绑定:分栏语义由 layout.split 承载(G-32 文档为 shell.split——机器事实以实现为准)
1144
+ ];
1145
+ var GESTURE = [
1146
+ { id: "G1", kind: "gesture", semantic: "gesture.tap", api: "v-gesture:tap", props: ["count"], mpEquiv: "bindtap", tier: "L1", status: "planned" },
1147
+ { id: "G2", kind: "gesture", semantic: "gesture.longpress", api: "v-gesture:longpress", props: ["duration", "onEnd"], mpEquiv: "bindlongpress", tier: "L1", status: "planned" },
1148
+ { id: "G3", kind: "gesture", semantic: "gesture.swipe", api: "v-gesture:swipe", props: ["direction", "threshold"], mpEquiv: "bindswipe", tier: "L1", status: "planned" },
1149
+ { id: "G4", kind: "gesture", semantic: "gesture.pan", api: "v-gesture:pan", props: ["axis", "bounds"], mpEquiv: "bindtouchmove", tier: "L1", status: "planned" },
1150
+ { id: "G5", kind: "gesture", semantic: "gesture.pinch", api: "v-gesture:pinch", props: ["scale", "onChange"], mpEquiv: "touchstart/move \u7EC4\u5408", tier: "L1", status: "planned" },
1151
+ { id: "G6", kind: "gesture", semantic: "gesture.rotate", api: "v-gesture:rotate", props: ["angle"], mpEquiv: "\u7EC4\u5408", tier: "L1", status: "planned" },
1152
+ { id: "G7", kind: "gesture", semantic: "gesture.press", api: "v-gesture:press", props: ["force"], mpEquiv: "3D Touch", tier: "L1", status: "planned" },
1153
+ { id: "G8", kind: "gesture", semantic: "gesture.draggable", tag: "p-draggable", props: ["ghost", "snapToGrid", "onDrop"], mpEquiv: "movable-view", tier: "L1", status: "implemented" },
1154
+ { id: "G9", kind: "gesture", semantic: "gesture.scrollable", tag: "p-scrollable", props: ["bounce", "refresh", "loadMore"], mpEquiv: "<scroll-view>", tier: "L1", status: "implemented" },
1155
+ { id: "G10", kind: "gesture", semantic: "gesture.use-gesture", api: "useGesture()", props: ["recognizers", "simultaneous"], mpEquiv: "\u65E0", tier: "L1", status: "planned" }
1156
+ ];
1157
+ function cap(id, name, api, mpEquiv, returnType) {
1158
+ return { id, kind: "capability", semantic: `capability.${name}`, api, props: [returnType], mpEquiv, tier: "L1", status: "planned" };
1159
+ }
1160
+ var CAPABILITY = [
1161
+ // 7.1 设备/硬件(15)
1162
+ cap("C1", "camera", "useCamera()", "wx.createCameraContext", "Result<Media>"),
1163
+ cap("C2", "microphone", "useMicrophone()", "RecorderManager", "Result<AudioBuffer>"),
1164
+ { id: "C3", kind: "capability", semantic: "capability.location", api: "useLocation()", props: ["Result<Coords>"], mpEquiv: "wx.getLocation", tier: "L1", status: "implemented" },
1165
+ cap("C4", "map", "useMap()", "wx.createMapContext", "MapController"),
1166
+ cap("C5", "sensor", "useSensor()", "onAccelerometer/onCompass/onGyroscope", "SensorStream"),
1167
+ cap("C6", "vibrate", "useVibrate()", "wx.vibrateShort/Long", "void"),
1168
+ cap("C7", "battery", "useBattery()", "wx.getBatteryInfo", "BatteryInfo"),
1169
+ cap("C8", "network", "useNetwork()", "wx.getNetworkType", "NetworkType"),
1170
+ cap("C9", "clipboard", "useClipboard()", "wx.set/getClipboardData", "Result<string>"),
1171
+ cap("C10", "screen", "useScreen()", "wx.getSystemInfo", "ScreenInfo"),
1172
+ cap("C11", "device", "useDevice()", "wx.getSystemInfo", "DeviceInfo"),
1173
+ cap("C12", "orientation", "useOrientation()", "wx.onDeviceOrientationChange", "Orientation"),
1174
+ cap("C13", "brightness", "useBrightness()", "wx.setScreenBrightness", "Result<void>"),
1175
+ cap("C14", "keyboard", "useKeyboard()", "wx.onKeyboardHeightChange", "KeyboardInfo"),
1176
+ cap("C15", "storage", "useStorage()", "wx.set/getStorage", "StorageAPI"),
1177
+ // 7.2 系统/OS(10)
1178
+ cap("C16", "permission", "usePermission()", "wx.authorize", "Result<PermissionStatus>"),
1179
+ cap("C17", "notification", "useNotification()", "wx.requestSubscribeMessage", "NotificationAPI"),
1180
+ cap("C18", "share", "useShare()", "wx.shareAppMessage", "Result<void>"),
1181
+ cap("C19", "contact", "useContact()", "wx.chooseContact", "Result<Contact[]>"),
1182
+ cap("C20", "calendar", "useCalendar()", "wx.addPhoneCalendar", "CalendarAPI"),
1183
+ cap("C21", "phone-call", "usePhoneCall()", "wx.makePhoneCall", "Result<void>"),
1184
+ cap("C22", "sms", "useSMS()", "wx.??\uFF08\u53D7\u9650\uFF09", "Result<void>"),
1185
+ cap("C23", "app-lifecycle", "useAppLifecycle()", "App.onLaunch/onShow", "LifecycleHooks"),
1186
+ cap("C24", "page-lifecycle", "usePageLifecycle()", "Page.onLoad/onShow", "LifecycleHooks"),
1187
+ cap("C25", "background", "useBackground()", "wx.onBackground", "BackgroundAPI"),
1188
+ // 7.3 通信/数据(10)
1189
+ cap("C26", "fetch", "useFetch()", "wx.request", "Promise<T>"),
1190
+ cap("C27", "websocket", "useWebSocket()", "wx.connectSocket", "WSConnection"),
1191
+ cap("C28", "socket-task", "useSocketTask()", "wx.SocketTask", "SocketTask"),
1192
+ cap("C29", "upload", "useUpload()", "wx.uploadFile", "Progress<Result>"),
1193
+ cap("C30", "download", "useDownload()", "wx.downloadFile", "Progress<Result>"),
1194
+ cap("C31", "data-channel", "useDataChannel()", "wx...\uFF08\u76F4\u64AD/\u5B9E\u65F6\uFF09", "Channel"),
1195
+ cap("C32", "cookie", "useCookie()", "\u65E0", "CookieJar"),
1196
+ cap("C33", "auth", "useAuth()", "\u7EC4\u5408", "AuthState"),
1197
+ cap("C34", "analytics", "useAnalytics()", "wx.reportEvent", "TrackAPI"),
1198
+ cap("C35", "log", "useLog()", "console + \u4E0A\u62A5", "Logger"),
1199
+ // 7.4 扩展能力(10)
1200
+ cap("C36", "bluetooth", "useBluetooth()", "wx.openBluetoothAdapter", "BluetoothAPI"),
1201
+ cap("C37", "nfc", "useNFC()", "wx.getHCEState", "NFCAPI"),
1202
+ cap("C38", "biometric", "useBiometric()", "wx.checkIsSupportFingerPrint", "Result<boolean>"),
1203
+ cap("C39", "face-id", "useFaceID()", "\u7EC4\u5408", "Result<boolean>"),
1204
+ cap("C40", "payment", "usePayment()", "wx.requestPayment", "Result<PayResult>"),
1205
+ cap("C41", "login", "useLogin()", "wx.login", "Result<Token>"),
1206
+ cap("C42", "qr-code", "useQRCode()", "wx.scanCode + canvas", "Result<string>"),
1207
+ cap("C43", "file-system", "useFileSystem()", "wx.getFileSystemManager", "FSAdapter"),
1208
+ cap("C44", "archive", "useArchive()", "wx.compressFile", "Result<void>"),
1209
+ cap("C45", "shortcut", "useShortcut()", "wx.addToDesktop", "Result<void>"),
1210
+ cap("C46", "in-app-purchase", "useInAppPurchase()", "wx.requestPayment \u6269\u5C55", "Result<Receipt>"),
1211
+ // 7.5 平台特有(4——C47-C50,此前 7.4 末 C46,总 50)
1212
+ { id: "C47", kind: "capability", semantic: "capability.mini-program", api: "useMiniProgram()", props: ["MPContext"], mpEquiv: "wx.navigateToMiniProgram", tier: "L1", status: "planned" },
1213
+ { id: "C48", kind: "capability", semantic: "capability.embedded", api: "useEmbedded()", props: ["HostContext"], mpEquiv: "\u65E0\uFF08\u88AB\u5BBF\u4E3B\u5D4C\u5165\uFF09", tier: "L1", status: "planned" },
1214
+ { id: "C49", kind: "capability", semantic: "capability.live", api: "useLive()", props: ["LiveRoom"], mpEquiv: "wx...\uFF08\u76F4\u64AD\u7EC4\u4EF6\uFF09", tier: "L1", status: "planned" },
1215
+ { id: "C50", kind: "capability", semantic: "capability.extension", api: "useExtension()", props: ["ExtensionAPI"], mpEquiv: "\u65E0\uFF08\u63D2\u4EF6/\u6269\u5C55\u70B9 G-21\uFF09", tier: "L1", status: "planned" }
1216
+ ];
1217
+ var ENGINEERING = [
1218
+ { id: "E1", kind: "engineering", semantic: "engineering.state", api: "useState()", mpEquiv: "data", tier: "L1", status: "planned" },
1219
+ { id: "E2", kind: "engineering", semantic: "engineering.computed", api: "useComputed()", mpEquiv: "computed", tier: "L1", status: "planned" },
1220
+ { id: "E3", kind: "engineering", semantic: "engineering.watch", api: "useWatch()", mpEquiv: "watch", tier: "L1", status: "planned" },
1221
+ { id: "E4", kind: "engineering", semantic: "engineering.store", api: "useStore()", mpEquiv: "getApp().globalData", tier: "L1", status: "planned" },
1222
+ { id: "E5", kind: "engineering", semantic: "engineering.provide-inject", api: "useProvide()/useInject()", mpEquiv: "\u65E0", tier: "L1", status: "planned" },
1223
+ { id: "E6", kind: "engineering", semantic: "engineering.lifecycle", api: "useLifecycle()", mpEquiv: "onLoad/onShow/onHide/onUnload", tier: "L1", status: "planned" },
1224
+ { id: "E7", kind: "engineering", semantic: "engineering.ready", api: "useReady()", mpEquiv: "onReady", tier: "L1", status: "planned" },
1225
+ { id: "E8", kind: "engineering", semantic: "engineering.error-boundary", api: "useErrorBoundary()", mpEquiv: "onError", tier: "L1", status: "planned" },
1226
+ { id: "E9", kind: "engineering", semantic: "engineering.page-param", api: "usePageParam()", mpEquiv: "onLoad(options)", tier: "L1", status: "planned" },
1227
+ { id: "E10", kind: "engineering", semantic: "engineering.route", api: "useRoute()", mpEquiv: "getCurrentPages()", tier: "L1", status: "planned" },
1228
+ { id: "E11", kind: "engineering", semantic: "engineering.router-push", api: "router.push()", mpEquiv: "wx.navigateTo", tier: "L1", status: "planned" },
1229
+ { id: "E12", kind: "engineering", semantic: "engineering.router-replace", api: "router.replace()", mpEquiv: "wx.redirectTo", tier: "L1", status: "planned" },
1230
+ { id: "E13", kind: "engineering", semantic: "engineering.router-back", api: "router.back()", mpEquiv: "wx.navigateBack", tier: "L1", status: "planned" },
1231
+ { id: "E14", kind: "engineering", semantic: "engineering.router-switch-tab", api: "router.switchTab()", mpEquiv: "wx.switchTab", tier: "L1", status: "planned" },
1232
+ { id: "E15", kind: "engineering", semantic: "engineering.router-relaunch", api: "router.reLaunch()", mpEquiv: "wx.reLaunch", tier: "L1", status: "planned" },
1233
+ { id: "E16", kind: "engineering", semantic: "engineering.router-before-each", api: "router.beforeEach()", mpEquiv: "onLaunch \u624B\u52A8", tier: "L1", status: "planned" },
1234
+ { id: "E17", kind: "engineering", semantic: "engineering.router-after-each", api: "router.afterEach()", mpEquiv: "\u65E0", tier: "L1", status: "planned" },
1235
+ { id: "E18", kind: "engineering", semantic: "engineering.router-link", tag: "p-router-link", props: ["to", "replace", "switchTab"], mpEquiv: "<navigator>", tier: "L1", status: "implemented" },
1236
+ { id: "E19", kind: "engineering", semantic: "engineering.transition", tag: "p-transition", props: ["name", "mode"], mpEquiv: "transition CSS", tier: "L1", status: "implemented" },
1237
+ { id: "E20", kind: "engineering", semantic: "engineering.animate", tag: "p-animate", props: ["keyframes", "duration"], mpEquiv: "animation CSS", tier: "L1", status: "implemented" },
1238
+ { id: "E21", kind: "engineering", semantic: "engineering.animation", api: "useAnimation()", mpEquiv: "wx.createAnimation", tier: "L1", status: "planned" },
1239
+ { id: "E22", kind: "engineering", semantic: "engineering.gesture-animation", api: "useGestureAnimation()", mpEquiv: "\u7EC4\u5408", tier: "L1", status: "planned" },
1240
+ { id: "E23", kind: "engineering", semantic: "engineering.scroll-animation", api: "useScrollAnimation()", mpEquiv: "\u7EC4\u5408", tier: "L1", status: "planned" },
1241
+ { id: "E24", kind: "engineering", semantic: "engineering.devtools", api: "useDevTools()", mpEquiv: "\u5C0F\u7A0B\u5E8F DevTools", tier: "L1", status: "planned" },
1242
+ { id: "E25", kind: "engineering", semantic: "engineering.inspector", api: "useInspector()", mpEquiv: "\u65E0", tier: "L1", status: "planned" },
1243
+ { id: "E26", kind: "engineering", semantic: "engineering.performance", api: "usePerformance()", mpEquiv: "wx.reportPerformance", tier: "L1", status: "planned" },
1244
+ { id: "E27", kind: "engineering", semantic: "engineering.define-component", api: "defineComponent()", mpEquiv: "Component()", tier: "L1", status: "planned" },
1245
+ { id: "E28", kind: "engineering", semantic: "engineering.define-capability", api: "defineCapability()", mpEquiv: "\u65E0", tier: "L1", status: "planned" }
1246
+ ];
1247
+ var PRIMITIVE_CATALOG = [...LAYOUT, ...UI, ...SHELL, ...GESTURE, ...CAPABILITY, ...ENGINEERING];
1248
+ var MP_COMPONENTS = [
1249
+ { mp: "<view>", proteus: "layout.box / layout.stack", status: "ok", group: "component" },
1250
+ { mp: "<text>", proteus: "ui.text / ui.heading", status: "ok", group: "component" },
1251
+ { mp: "<image>", proteus: "ui.image", status: "ok", group: "component" },
1252
+ { mp: "<scroll-view>", proteus: "layout.scroll / layout.virtual-list", status: "ok", group: "component" },
1253
+ { mp: "<swiper>", proteus: "layout.stack snap/loop\uFF08\u6D88\u706D\u4E3A\u5C5E\u6027\uFF09", status: "ok", group: "component" },
1254
+ { mp: "<swiper-item>", proteus: "layout.stack \u5B50\u9879", status: "ok", group: "component" },
1255
+ { mp: "<movable-area>", proteus: "gesture.scrollable \u5BB9\u5668", status: "ok", group: "component" },
1256
+ { mp: "<movable-view>", proteus: "gesture.draggable", status: "ok", group: "component" },
1257
+ { mp: "<cover-view>", proteus: "p-overlay (L2)", status: "compat", group: "component" },
1258
+ { mp: "<cover-image>", proteus: "p-overlay + ui.image", status: "compat", group: "component" },
1259
+ { mp: "<icon>", proteus: "ui.icon", status: "ok", group: "component" },
1260
+ { mp: "<progress>", proteus: "p-progress (L2)", status: "compat", group: "component" },
1261
+ { mp: "<rich-text>", proteus: "ui.rich-text", status: "ok", group: "component" },
1262
+ { mp: "<button>", proteus: "ui.button", status: "ok", group: "component" },
1263
+ { mp: "<form>", proteus: "ui.form", status: "ok", group: "component" },
1264
+ { mp: "<input>", proteus: "ui.input", status: "ok", group: "component" },
1265
+ { mp: "<textarea>", proteus: "ui.textarea", status: "ok", group: "component" },
1266
+ { mp: "<checkbox>", proteus: "ui.checkbox", status: "ok", group: "component" },
1267
+ { mp: "<radio>", proteus: "ui.radio", status: "ok", group: "component" },
1268
+ { mp: "<picker>", proteus: "ui.picker / ui.select", status: "ok", group: "component" },
1269
+ { mp: "<picker-view>", proteus: "ui.picker mode=wheel", status: "ok", group: "component" },
1270
+ { mp: "<slider>", proteus: "ui.slider", status: "ok", group: "component" },
1271
+ { mp: "<switch>", proteus: "ui.switch", status: "ok", group: "component" },
1272
+ { mp: "<label>", proteus: "p-label (L2)", status: "compat", group: "component" },
1273
+ { mp: "<navigator>", proteus: "engineering.router-link / router.*", status: "ok", group: "component" },
1274
+ { mp: "<audio>", proteus: "ui.media kind=audio\uFF08\u6D88\u706D\u4E3A\u5C5E\u6027\uFF09", status: "ok", group: "component" },
1275
+ { mp: "<video>", proteus: "ui.media kind=video\uFF08\u6D88\u706D\u4E3A\u5C5E\u6027\uFF09", status: "ok", group: "component" },
1276
+ { mp: "<camera>", proteus: "p-camera (L2) + capability.camera", status: "ok", group: "component" },
1277
+ { mp: "<live-player>", proteus: "ui.media kind=live\uFF08\u6D88\u706D\u4E3A\u5C5E\u6027\uFF09", status: "ok", group: "component" },
1278
+ { mp: "<live-pusher>", proteus: "ui.media kind=live mode=push", status: "ok", group: "component" },
1279
+ { mp: "<canvas>", proteus: "ui.canvas", status: "ok", group: "component" },
1280
+ { mp: "<map>", proteus: "p-map (L2) + capability.map", status: "ok", group: "component" },
1281
+ { mp: "<web-view>", proteus: "p-webview (L2)", status: "compat", group: "component" },
1282
+ { mp: "<editor>", proteus: "ui.rich-text editable", status: "ok", group: "component" },
1283
+ { mp: "<ad>", proteus: "p-ad (L2)", status: "compat", group: "component" },
1284
+ { mp: "<official-account>", proteus: "capability.mini-program\uFF08\u5FAE\u4FE1\u79C1\u6709\uFF09", status: "private", group: "component" },
1285
+ { mp: "<open-data>", proteus: "capability.mini-program\uFF08\u5FAE\u4FE1\u79C1\u6709\uFF09", status: "private", group: "component" },
1286
+ { mp: "<share-element>", proteus: "p-share-element (L2)", status: "compat", group: "component" },
1287
+ { mp: "<aria-component>", proteus: "p-aria (L2)", status: "compat", group: "component" },
1288
+ { mp: "<page-container>", proteus: "p-page-container (L2)", status: "compat", group: "component" },
1289
+ { mp: "<voip-room>", proteus: "capability.mini-program\uFF08\u5FAE\u4FE1 VOIP\uFF09", status: "private", group: "component" },
1290
+ { mp: "<guild-room>", proteus: "capability.mini-program\uFF08\u5FAE\u4FE1\u6E38\u620F\uFF09", status: "private", group: "component" }
1291
+ ];
1292
+ var MP_API_GROUPS = [
1293
+ { mp: "wx.request/upload/download/websocket\uFF08\u7F51\u7EDC\uFF09", proteus: "capability.fetch/upload/download/websocket", status: "ok", group: "api" },
1294
+ { mp: "wx.requestPayment", proteus: "capability.payment", status: "ok", group: "api" },
1295
+ { mp: "wx.chooseImage/chooseMedia/previewImage\uFF08\u5A92\u4F53\uFF09", proteus: "capability.camera + ui.image preview", status: "ok", group: "api" },
1296
+ { mp: "wx.startRecord/RecorderManager\uFF08\u5F55\u97F3\uFF09", proteus: "capability.microphone", status: "ok", group: "api" },
1297
+ { mp: "wx.createVideoContext/CameraContext", proteus: "capability.media + ui.media", status: "ok", group: "api" },
1298
+ { mp: "wx.scanCode", proteus: "capability.qr-code / capability.scan-qr", status: "ok", group: "api" },
1299
+ { mp: "wx.saveImageToPhotosAlbum", proteus: "capability.album (L2)", status: "compat", group: "api" },
1300
+ { mp: "wx.getFileSystemManager/*\uFF08\u6587\u4EF6 30+\uFF09", proteus: "capability.file-system", status: "ok", group: "api" },
1301
+ { mp: "wx.compressFile/unzip", proteus: "capability.archive", status: "ok", group: "api" },
1302
+ { mp: "wx.set/get/remove/clearStorage(+Sync)", proteus: "capability.storage", status: "ok", group: "api" },
1303
+ { mp: "wx.getLocation/chooseLocation/openLocation", proteus: "capability.location", status: "ok", group: "api" },
1304
+ { mp: "wx.createMapContext", proteus: "capability.map", status: "ok", group: "api" },
1305
+ { mp: "wx.getSystemInfo\uFF08\u8BBE\u5907/\u5C4F\u5E55/\u7F51\u7EDC/\u7535\u91CF/\u4EAE\u5EA6/\u65B9\u5411/\u9707\u52A8/\u4F20\u611F\u5668/\u526A\u8D34\u677F/\u7535\u8BDD\uFF09", proteus: "capability.device/screen/network/battery/brightness/orientation/vibrate/sensor/clipboard/phone-call", status: "ok", group: "api" },
1306
+ { mp: "wx.openBluetoothAdapter\uFF08\u84DD\u7259 20+\uFF09", proteus: "capability.bluetooth", status: "ok", group: "api" },
1307
+ { mp: "wx.getHCEState\uFF08NFC\uFF09", proteus: "capability.nfc", status: "ok", group: "api" },
1308
+ { mp: "wx.checkIsSupportFingerPrint/FaceID", proteus: "capability.biometric / face-id", status: "ok", group: "api" },
1309
+ { mp: "wx.showToast/showLoading/showModal/showActionSheet", proteus: "shell.toast + capability.toast/loading + shell.modal + shell.action-sheet", status: "ok", group: "api" },
1310
+ { mp: "wx.setNavigationBarTitle/Color", proteus: "shell.nav", status: "ok", group: "api" },
1311
+ { mp: "wx.setTabBarItem/Style/hide/show", proteus: "shell.tabbar", status: "ok", group: "api" },
1312
+ { mp: "wx.pageScrollTo", proteus: "capability.page-scroll", status: "ok", group: "api" },
1313
+ { mp: "wx.createAnimation", proteus: "engineering.animation", status: "ok", group: "api" },
1314
+ { mp: "wx.createSelectorQuery/IntersectionObserver", proteus: "capability.element / intersection", status: "ok", group: "api" },
1315
+ { mp: "wx.navigateTo/redirectTo/navigateBack/switchTab/reLaunch", proteus: "engineering.router-push/replace/back/switch-tab/relaunch", status: "ok", group: "api" },
1316
+ { mp: "wx.getCurrentPages", proteus: "engineering.route", status: "ok", group: "api" },
1317
+ { mp: "App()/Page() \u751F\u547D\u5468\u671F/getApp()", proteus: "engineering.lifecycle + capability.app-lifecycle/page-lifecycle", status: "ok", group: "api" },
1318
+ { mp: "wx.shareAppMessage/requestSubscribeMessage", proteus: "capability.share / notification", status: "ok", group: "api" },
1319
+ { mp: "wx.login/checkSession/getUserInfo/authorize", proteus: "capability.login / auth / permission", status: "ok", group: "api" },
1320
+ { mp: "wx.getUpdateManager", proteus: "capability.update (L2)", status: "compat", group: "api" },
1321
+ { mp: "wx.requestWeChatPay/navigateToMiniProgram/\u6A21\u677F\u6D88\u606F/\u5BA2\u670D\uFF08\u5FAE\u4FE1\u79C1\u6709\uFF09", proteus: "capability.mini-program", status: "private", group: "api" }
1322
+ ];
1323
+ var MP_MAPPING_MATRIX = [...MP_COMPONENTS, ...MP_API_GROUPS];
1324
+
1325
+ // src/dispatcher.ts
1326
+ var DispatcherError = class extends Error {
1327
+ constructor(code, detail) {
1328
+ super(`unknown primitive tag: ${detail.type}\uFF08\u987B\u5728 G-32 \u539F\u8BED\u8868\u5185\uFF0C\u7F16\u8BD1\u671F\u62E6\u622A\uFF1B\u8FD0\u884C\u671F\u515C\u5E95\uFF09`);
1329
+ this.code = code;
1330
+ this.detail = detail;
1331
+ this.name = "DispatcherError";
1332
+ }
1333
+ };
1334
+ function createNodeOpsDispatcher(initialBackend) {
1335
+ let current = initialBackend;
1336
+ const history = [];
1337
+ const trace = [];
1338
+ let seq = 0;
1339
+ let handleIds = /* @__PURE__ */ new WeakMap();
1340
+ let nextHandleId = 1;
1341
+ function idOf(handle) {
1342
+ if (handle && typeof handle === "object") {
1343
+ let id = handleIds.get(handle);
1344
+ if (id === void 0) {
1345
+ id = nextHandleId++;
1346
+ handleIds.set(handle, id);
1347
+ }
1348
+ return id;
1349
+ }
1350
+ return 0;
1351
+ }
1352
+ function toIRNode(type, props = {}) {
1353
+ const cir = toComponentIR(type, props);
1354
+ if (!cir) throw new DispatcherError("unknown.primitive", { type });
1355
+ return { type: cir.tag, semantic: cir.semantic, props: { ...cir.props }, children: [] };
1356
+ }
1357
+ const nodeOps = {
1358
+ createElement(type, props = {}) {
1359
+ const ir = toIRNode(type, props);
1360
+ const handle = current.createElement(ir);
1361
+ trace.push({ seq: ++seq, op: "createElement", type, semantic: ir.semantic ?? type });
1362
+ return handle;
1363
+ },
1364
+ insert(child, parent, anchor) {
1365
+ current.insert(child, parent, anchor);
1366
+ trace.push({ seq: ++seq, op: "insert", child: idOf(child), parent: parent ? idOf(parent) : null, anchor: anchor ? idOf(anchor) : null });
1367
+ },
1368
+ remove(child) {
1369
+ current.remove(child);
1370
+ trace.push({ seq: ++seq, op: "remove", child: idOf(child) });
1371
+ },
1372
+ patchProp(el, key, prev, next) {
1373
+ current.patchProp(el, key, prev, next);
1374
+ trace.push({ seq: ++seq, op: "patchProp", el: idOf(el), key, next });
1375
+ },
1376
+ setText(el, text) {
1377
+ current.setText(el, text);
1378
+ trace.push({ seq: ++seq, op: "setText", el: idOf(el), text });
1379
+ },
1380
+ createText(text) {
1381
+ const handle = current.createElement({ type: "text", props: {}, children: [], semantic: void 0 });
1382
+ trace.push({ seq: ++seq, op: "createText", text });
1383
+ return handle;
1384
+ },
1385
+ createComment(text) {
1386
+ const handle = current.createElement({ type: "comment", props: {}, children: [] });
1387
+ trace.push({ seq: ++seq, op: "createComment", text });
1388
+ return handle;
1389
+ },
1390
+ parentNode(node) {
1391
+ const fn = current.parentNode;
1392
+ const r = fn ? fn(node) : null;
1393
+ trace.push({ seq: ++seq, op: "parentNode", node: idOf(node), result: r ? idOf(r) : null });
1394
+ return r ?? null;
1395
+ },
1396
+ nextSibling(node) {
1397
+ const fn = current.nextSibling;
1398
+ const r = fn ? fn(node) : null;
1399
+ trace.push({ seq: ++seq, op: "nextSibling", node: idOf(node), result: r ? idOf(r) : null });
1400
+ return r ?? null;
1401
+ }
1402
+ };
1403
+ return {
1404
+ get currentBackend() {
1405
+ return current;
1406
+ },
1407
+ switchBackend(next, opts) {
1408
+ const strategy = opts?.strategy ?? "rebuild";
1409
+ history.push(current);
1410
+ trace.push({ seq: ++seq, op: "switchBackend", from: current.id, to: next.id, strategy });
1411
+ current = next;
1412
+ },
1413
+ get switchHistory() {
1414
+ return history;
1415
+ },
1416
+ nodeOps,
1417
+ toIRNode,
1418
+ trace,
1419
+ clearTrace() {
1420
+ trace.length = 0;
1421
+ seq = 0;
1422
+ handleIds = /* @__PURE__ */ new WeakMap();
1423
+ }
1424
+ };
1425
+ }
1426
+ function renderIRTree(backend, ir, insertInto) {
1427
+ const root = backend.createElement(ir);
1428
+ for (const child of ir.children) {
1429
+ const c = renderIRTree(backend, child);
1430
+ if (insertInto) insertInto(c, root);
1431
+ else backend.insert(c, root);
1432
+ }
1433
+ return root;
1434
+ }
1435
+ function semanticSequence(ir) {
1436
+ return [ir.semantic ?? ir.type, ...ir.children.flatMap(semanticSequence)];
1437
+ }
1438
+
1439
+ // src/host-conformance.ts
1440
+ function createHostRuntimeStub() {
1441
+ const rt = {
1442
+ id: "terminal",
1443
+ state: "created",
1444
+ threads: ["main"],
1445
+ workers: [],
1446
+ queue: [],
1447
+ bootstrap() {
1448
+ rt.state = "running";
1449
+ return rt;
1450
+ },
1451
+ suspend() {
1452
+ rt.state = "suspended";
1453
+ },
1454
+ resume() {
1455
+ rt.state = "running";
1456
+ },
1457
+ destroy() {
1458
+ rt.state = "destroyed";
1459
+ rt.queue = [];
1460
+ rt.workers = [];
1461
+ },
1462
+ createWorker() {
1463
+ const w = { id: `w${rt.workers.length + 1}`, thread: `worker${rt.workers.length + 1}` };
1464
+ rt.workers.push(w);
1465
+ rt.threads.push(w.thread);
1466
+ return w;
1467
+ },
1468
+ postMessage() {
1469
+ return true;
1470
+ },
1471
+ enqueue(task, priority = 2) {
1472
+ rt.queue.push({ task, priority });
1473
+ },
1474
+ nextTick(fn) {
1475
+ rt.queue.push({ task: fn, priority: 0 });
1476
+ },
1477
+ drain() {
1478
+ rt.queue.sort((a, b) => a.priority - b.priority);
1479
+ const out = [];
1480
+ while (rt.queue.length) rt.queue.shift();
1481
+ return out;
1482
+ }
1483
+ };
1484
+ return rt;
1485
+ }
1486
+ function createCarrierStub(kind) {
1487
+ return {
1488
+ id: kind,
1489
+ boundaries: 0,
1490
+ capabilities: kind === "jsi" ? { threadAffinity: true, trueConcurrency: false, realtime: { capable: false } } : { threadAffinity: false, trueConcurrency: true, realtime: { capable: true } },
1491
+ cross() {
1492
+ if (kind === "aot") return 0;
1493
+ this.boundaries++;
1494
+ return this.boundaries;
1495
+ }
1496
+ };
1497
+ }
1498
+ function buildProductIR(dispatch) {
1499
+ const grid = dispatch.toIRNode("p-grid", { minColWidth: 160 });
1500
+ const box1 = dispatch.toIRNode("p-box", {});
1501
+ const box2 = dispatch.toIRNode("p-box", {});
1502
+ const text = dispatch.toIRNode("p-text", { content: "\u5546\u54C1 A" });
1503
+ const button = dispatch.toIRNode("p-button", { variant: "primary" });
1504
+ return {
1505
+ ...dispatch.toIRNode("p-page", { title: "Product" }),
1506
+ children: [
1507
+ { ...grid, children: [{ ...box1, children: [text] }, { ...box2, children: [button] }] }
1508
+ ]
1509
+ };
1510
+ }
1511
+ var tests = [];
1512
+ var register = (group) => (id, fn) => tests.push({ id: `${group}-${id}`, group, fn });
1513
+ var H01 = register("H-01");
1514
+ var H02 = register("H-02");
1515
+ var H03 = register("H-03");
1516
+ var H04 = register("H-04");
1517
+ var H05 = register("H-05");
1518
+ var H06 = register("H-06");
1519
+ var H07 = register("H-07");
1520
+ var H08 = register("H-08");
1521
+ var assert = (c, m) => {
1522
+ if (!c) throw new Error(m ?? "assertion failed");
1523
+ };
1524
+ H01("01", ({ host }) => assert(host.state === "running", "Runtime \u5E94\u5DF2 bootstrap"));
1525
+ H01("02", ({ carrier }) => assert(carrier && carrier.id === "jsi", "Carrier \u5E94\u5DF2\u6CE8\u518C"));
1526
+ H01("03", ({ dispatch, backendA }) => assert(dispatch.currentBackend === backendA, "Backend \u5E94\u5DF2\u6CE8\u518C"));
1527
+ H01("04", ({ host, dispatch, backendA }) => assert(host.state === "running" && dispatch.currentBackend === backendA, "\u6CE8\u518C\u5148\u4E8E bootstrap\uFF08G-41.6\uFF09"));
1528
+ H02("01", ({ host }) => {
1529
+ host.suspend();
1530
+ assert(host.state === "suspended");
1531
+ host.resume();
1532
+ });
1533
+ H02("02", ({ host }) => assert(host.state === "running"));
1534
+ H02("03", ({ host }) => {
1535
+ const w = host.createWorker();
1536
+ assert(host.threads.includes(w.thread), "createWorker \u5E94\u4EA7\u751F\u72EC\u7ACB\u7EBF\u7A0B");
1537
+ });
1538
+ H02("04", ({}) => {
1539
+ const r2 = createHostRuntimeStub();
1540
+ r2.bootstrap();
1541
+ r2.enqueue(() => 1);
1542
+ r2.destroy();
1543
+ assert(r2.state === "destroyed" && r2.queue.length === 0, "destroy \u5E94\u6E05\u7406\u961F\u5217");
1544
+ });
1545
+ var snapA;
1546
+ var snapB;
1547
+ H03("01", ({ backendA, productIR }) => {
1548
+ const root = renderIRTree(backendA, productIR);
1549
+ snapA = JSON.stringify(toPlainTree(root));
1550
+ assert(snapA.includes("grid"), "Backend A \u6E32\u67D3\u5E94\u542B grid");
1551
+ });
1552
+ H03("02", ({ dispatch, backendB }) => {
1553
+ dispatch.switchBackend(backendB);
1554
+ assert(dispatch.currentBackend === backendB, "switchBackend \u5E94\u751F\u6548");
1555
+ });
1556
+ H03("03", ({ backendB, productIR }) => {
1557
+ const root = renderIRTree(backendB, productIR);
1558
+ snapB = JSON.stringify(toWidgetTree(root));
1559
+ assert(snapB.includes("GridView"), "Backend B \u6E32\u67D3\u5E94\u542B GridView");
1560
+ });
1561
+ H03("04", () => {
1562
+ assert(snapA && snapB, "\u53CC\u5F15\u64CE\u5FEB\u7167\u7F3A\u5931");
1563
+ assert(snapA.length > 0 && snapB.length > 0);
1564
+ });
1565
+ H04("01", ({ backendA, productIR }) => {
1566
+ const root = renderIRTree(backendA, productIR);
1567
+ const tree = JSON.stringify(toPlainTree(root));
1568
+ assert(tree.includes('"grid"'), "\u540E\u7AEF\u5E94\u57FA\u4E8E semantic \u5206\u53D1\uFF08G-37.1\uFF1Alayout.grid \u2192 grid\uFF09");
1569
+ });
1570
+ H04("02", ({ dispatch }) => {
1571
+ let threw = false;
1572
+ try {
1573
+ dispatch.toIRNode("p-unknown-thing", {});
1574
+ } catch (e) {
1575
+ threw = e instanceof DispatcherError;
1576
+ }
1577
+ assert(threw, "\u672A\u77E5\u539F\u8BED\u5E94\u88AB\u62E6\u622A\uFF08G-32.2\uFF1B\u7F16\u8BD1\u671F\u62E6\u622A\uFF0C\u8FD0\u884C\u671F\u5151\u5E95\uFF09");
1578
+ });
1579
+ H04("03", ({ host }) => {
1580
+ const before = host.threads.length;
1581
+ host.createWorker();
1582
+ assert(host.threads.length === before + 1, "\u6846\u67B6\u4E0D\u5F97\u76F4\u63A5\u5EFA\u7EBF\u7A0B\uFF0C\u987B\u59D4\u6258 runtime.createWorker\uFF08G-41.1\uFF09");
1583
+ });
1584
+ H04("04", () => {
1585
+ const src = createHeadlessBackend.toString() + createFlutterBackend.toString();
1586
+ assert(!/from ['"]vue['"]/.test(src) && !/@vue\/runtime/.test(src), "\u5F15\u64CE\u4E0D\u5F97 import vue\uFF08G-41.3\uFF09");
1587
+ });
1588
+ H04("05", ({ host }) => {
1589
+ const src = (host.constructor?.toString?.() ?? "") + createHostRuntimeStub.toString();
1590
+ assert(!/semantic\s*===/.test(src), "\u5BBF\u4E3B\u4E0D\u5F97\u89E3\u6790 IR \u5B57\u6BB5\uFF08G-41.2\uFF09");
1591
+ });
1592
+ H05("01", ({ dispatch, backendB }) => {
1593
+ dispatch.switchBackend(backendB);
1594
+ assert(dispatch.currentBackend === backendB, "\u70ED\u5207\u6362\u540E currentBackend \u5E94\u53D8\u66F4");
1595
+ });
1596
+ H05("02", ({ backendB, productIR }) => {
1597
+ const root = renderIRTree(backendB, productIR);
1598
+ assert(JSON.stringify(toWidgetTree(root)).includes("FilledButton"), "\u70ED\u5207\u6362\u540E\u53EF\u91CD\u65B0\u6E32\u67D3");
1599
+ });
1600
+ H05("03", ({ dispatch, backendA, productIR }) => {
1601
+ dispatch.switchBackend(backendA);
1602
+ const root = renderIRTree(backendA, productIR);
1603
+ assert(JSON.stringify(toPlainTree(root)).includes("button"), "\u5207\u56DE Backend A \u4ECD\u6B63\u786E");
1604
+ });
1605
+ H05("04", ({ backendA }) => {
1606
+ const cap2 = backendA.capabilities;
1607
+ assert(cap2 && typeof cap2 === "object", "\u540E\u7AEF\u5FC5\u987B\u58F0\u660E capabilities \u5BF9\u8C61");
1608
+ for (const key of ["layout", "glass", "blur", "animation", "textureSharing", "remoteRendering", "ssr", "input"]) {
1609
+ assert(key in cap2, `capabilities \u7F3A\u5931\u5B57\u6BB5: ${key}\uFF08G-37.3 \u8BDA\u5B9E\u58F0\u660E\uFF09`);
1610
+ }
1611
+ });
1612
+ H06("01", ({ backendA, backendB }) => assert(backendA !== backendB, "\u540C\u9875\u9762\u53EF\u6301\u6709\u591A\u4E2A Backend \u5B9E\u4F8B"));
1613
+ H06("02", ({}) => {
1614
+ const cir = toComponentIR("p-canvas", { engine: "skia", resolution: 2 });
1615
+ assert(cir !== null && cir.semantic === "ui.canvas" && cir.props.engine === "skia", "\u5F15\u64CE/\u5C5E\u6027\u5E94\u4F5C\u4E3A\u8BED\u4E49\u5C5E\u6027\u8FDB C-IR");
1616
+ });
1617
+ H06("03", ({}) => {
1618
+ const cir = toComponentIR("p-grid", { minColWidth: 160 });
1619
+ assert(cir !== null && cir.props.minColWidth === 160, "\u539F\u8BED\u5C5E\u6027\u5E94\u900F\u4F20\u8FDB C-IR");
1620
+ });
1621
+ H06("04", ({ dispatch, backendB }) => {
1622
+ dispatch.switchBackend(backendB);
1623
+ assert(dispatch.currentBackend === backendB, "Dispatcher \u5355\u5B9E\u4F8B\u652F\u6301\u591A\u540E\u7AEF\uFF08\u65B9\u6848 B\uFF09");
1624
+ });
1625
+ H07("01", ({ carrier }) => assert(typeof carrier.capabilities.threadAffinity === "boolean", "Carrier \u5E94\u58F0\u660E threadAffinity"));
1626
+ H07("02", ({}) => {
1627
+ const jsi = createCarrierStub("jsi");
1628
+ const aot = createCarrierStub("aot");
1629
+ assert(jsi.capabilities.trueConcurrency === false && aot.capabilities.trueConcurrency === true, "JSI \u53D7\u9650\u3001AOT \u4E0D\u53D7\u9650\uFF08G-40\uFF09");
1630
+ });
1631
+ H07("03", ({}) => {
1632
+ const aot = createCarrierStub("aot");
1633
+ aot.cross();
1634
+ aot.cross();
1635
+ assert(aot.boundaries === 0, "AOT \u8DE8\u754C\u6210\u672C\u4E3A 0");
1636
+ });
1637
+ H07("04", ({}) => {
1638
+ const jsi = createCarrierStub("jsi");
1639
+ const aot = createCarrierStub("aot");
1640
+ assert(jsi.capabilities.realtime.capable === false && aot.capabilities.realtime.capable === true, "realtime \u80FD\u529B\u4EC5\u5728 AOT \u53EF\u7528");
1641
+ });
1642
+ H08("01", ({ dispatch }) => {
1643
+ let ok = false;
1644
+ try {
1645
+ dispatch.toIRNode("div", {});
1646
+ } catch {
1647
+ ok = true;
1648
+ }
1649
+ assert(ok, "\u672A\u77E5\u539F\u8BED\u5E94\u629B\u9519\u800C\u975E\u9759\u9ED8");
1650
+ });
1651
+ H08("02", ({ dispatch }) => {
1652
+ const ir = dispatch.toIRNode("p-grid", { minColWidth: 160 });
1653
+ assert(ir.semantic === "layout.grid", "\u6846\u67B6\u8DEF\u5F84 semantic \u6052\u5B58\u5728\uFF08G-37.1\uFF1A\u540E\u7AEF\u6309 semantic \u5206\u53D1\u7684\u524D\u63D0\uFF09");
1654
+ });
1655
+ H08("03", ({}) => {
1656
+ const r = createHostRuntimeStub();
1657
+ r.bootstrap();
1658
+ r.destroy();
1659
+ assert(r.state === "destroyed", "destroy \u540E\u72B6\u6001\u5E94\u4E3A destroyed");
1660
+ });
1661
+ function runHostConformance(opts = {}) {
1662
+ const host = opts.host ?? createHostRuntimeStub();
1663
+ const carrier = opts.carrier ?? createCarrierStub("jsi");
1664
+ const backendA = opts.backendA ?? createHeadlessBackend();
1665
+ const backendB = opts.backendB ?? createFlutterBackend();
1666
+ const dispatch = createNodeOpsDispatcher(backendA);
1667
+ const productIR = buildProductIR(dispatch);
1668
+ const ctx = { host, carrier, backendA, backendB, dispatch, productIR };
1669
+ const results = [];
1670
+ host.bootstrap();
1671
+ snapA = "";
1672
+ snapB = "";
1673
+ for (const t of tests) {
1674
+ if (opts.only && !t.group.startsWith(opts.only)) continue;
1675
+ try {
1676
+ const ret = t.fn(ctx);
1677
+ if (ret === "SKIP") results.push({ id: t.id, status: "SKIP" });
1678
+ else results.push({ id: t.id, status: "PASS" });
1679
+ } catch (e) {
1680
+ results.push({ id: t.id, status: "FAIL", error: e.message });
1681
+ }
1682
+ }
1683
+ return {
1684
+ total: results.length,
1685
+ pass: results.filter((r) => r.status === "PASS").length,
1686
+ fail: results.filter((r) => r.status === "FAIL").length,
1687
+ skip: results.filter((r) => r.status === "SKIP").length,
1688
+ results
1689
+ };
1690
+ }
1691
+ function formatHostConformance(s) {
1692
+ const lines = ["[G-41 Host Conformance\uFF08H-01~H-08\uFF09]"];
1693
+ for (const r of s.results) {
1694
+ const icon = r.status === "PASS" ? "\u2705" : r.status === "SKIP" ? "\u23ED\uFE0F " : "\u274C";
1695
+ lines.push(` ${icon} ${r.id}${r.status === "FAIL" ? ` \u2014 ${r.error ?? ""}` : ""}`);
1696
+ }
1697
+ lines.push("\u2500".repeat(30));
1698
+ lines.push(`\u603B\u8BA1\uFF1APASS=${s.pass} FAIL=${s.fail} SKIP=${s.skip}\uFF08${s.total} \u9879\uFF09\u2014\u2014CMP058\uFF1Afailed=0 \u624D\u5141\u8BB8\u4E0A\u7EBF`);
1699
+ return lines.join("\n");
1700
+ }
1701
+
1702
+ // src/vue-bridge.ts
1703
+ import { createRenderer } from "@vue/runtime-core";
1704
+ function createVueRendererOptions(dispatch) {
1705
+ const d = dispatch.nodeOps;
1706
+ return {
1707
+ // Vue 真实签名:createElement(type, namespace?, isCustomizedBuiltIn?, vnodeProps?)
1708
+ createElement(type, _namespace, _isCustomizedBuiltIn, vnodeProps) {
1709
+ return d.createElement(type, vnodeProps ?? {});
1710
+ },
1711
+ createText(text) {
1712
+ return d.createText(text);
1713
+ },
1714
+ createComment(text) {
1715
+ return d.createComment(text);
1716
+ },
1717
+ setText(node, text) {
1718
+ d.setText(node, text);
1719
+ },
1720
+ // 元素直接文本内容(Vue 清空子树时调用)——语义等价 setText
1721
+ setElementText(el, text) {
1722
+ d.setText(el, text);
1723
+ },
1724
+ insert(child, parent, anchor) {
1725
+ d.insert(child, parent, anchor ?? void 0);
1726
+ },
1727
+ remove(node) {
1728
+ d.remove(node);
1729
+ },
1730
+ parentNode(node) {
1731
+ return d.parentNode(node) ?? null;
1732
+ },
1733
+ nextSibling(node) {
1734
+ return d.nextSibling(node) ?? null;
1735
+ },
1736
+ patchProp(el, key, prev, next) {
1737
+ d.patchProp(el, key, prev, next);
1738
+ },
1739
+ // —— 以下对齐 renderer-app host.ts 语义降级(非核心渲染路径) ——
1740
+ querySelector() {
1741
+ return null;
1742
+ },
1743
+ setScopeId() {
1744
+ },
1745
+ cloneNode() {
1746
+ return d.createComment("");
1747
+ },
1748
+ insertStaticContent(_content, parent, anchor) {
1749
+ const c = d.createComment("");
1750
+ if (parent) d.insert(c, parent, anchor ?? void 0);
1751
+ return [c, c];
1752
+ }
1753
+ };
1754
+ }
1755
+ function createProteusRenderer(dispatch) {
1756
+ const renderer = createRenderer(createVueRendererOptions(dispatch));
1757
+ return { dispatch, renderer };
1758
+ }
1759
+ function createProteusRendererForBackend(backend) {
1760
+ return createProteusRenderer(createNodeOpsDispatcher(backend));
1761
+ }
1762
+
1763
+ // src/web-host.ts
1764
+ function defaultWorkerFactory(scriptUrl) {
1765
+ const g = globalThis;
1766
+ if (typeof g.Worker === "function") {
1767
+ try {
1768
+ const w = new g.Worker(scriptUrl ?? "");
1769
+ return { raw: w, terminate: () => w.terminate() };
1770
+ } catch {
1771
+ }
1772
+ }
1773
+ return { raw: null, terminate() {
1774
+ } };
1775
+ }
1776
+ function createWebHostRuntime(opts = {}) {
1777
+ const createWorkerImpl = opts.createWorkerImpl ?? defaultWorkerFactory;
1778
+ const g = opts.env ?? globalThis;
1779
+ const rt = {
1780
+ id: "web",
1781
+ state: "created",
1782
+ threads: ["main"],
1783
+ workers: [],
1784
+ queue: [],
1785
+ bootstrap() {
1786
+ rt.state = "running";
1787
+ return rt;
1788
+ },
1789
+ suspend() {
1790
+ rt.state = "suspended";
1791
+ },
1792
+ resume() {
1793
+ rt.state = "running";
1794
+ },
1795
+ destroy() {
1796
+ rt.state = "destroyed";
1797
+ rt.queue = [];
1798
+ for (const w of rt.workers) w.terminate();
1799
+ rt.workers = [];
1800
+ rt.threads = ["main"];
1801
+ },
1802
+ createWorker(scriptUrl) {
1803
+ const impl = createWorkerImpl(scriptUrl);
1804
+ const w = {
1805
+ id: `w${rt.workers.length + 1}`,
1806
+ thread: `worker${rt.workers.length + 1}`,
1807
+ raw: impl.raw,
1808
+ terminate: impl.terminate
1809
+ };
1810
+ rt.workers.push(w);
1811
+ rt.threads.push(w.thread);
1812
+ return w;
1813
+ },
1814
+ postMessage() {
1815
+ return true;
1816
+ },
1817
+ enqueue(task, priority = 2) {
1818
+ rt.queue.push({ task, priority });
1819
+ },
1820
+ nextTick(fn) {
1821
+ rt.queue.push({ task: fn, priority: 0 });
1822
+ },
1823
+ drain() {
1824
+ rt.queue.sort((a, b) => a.priority - b.priority);
1825
+ const out = [];
1826
+ while (rt.queue.length) {
1827
+ const { task } = rt.queue.shift();
1828
+ out.push(task());
1829
+ }
1830
+ return out;
1831
+ },
1832
+ async flush() {
1833
+ if (!rt.queue.length) return;
1834
+ const tasks = rt.queue.splice(0).sort((a, b) => a.priority - b.priority);
1835
+ for (const { task } of tasks) {
1836
+ await Promise.resolve();
1837
+ task();
1838
+ }
1839
+ },
1840
+ bindPageVisibility() {
1841
+ const doc = g.document;
1842
+ if (!doc || typeof doc.visibilityState !== "string") {
1843
+ return () => {
1844
+ };
1845
+ }
1846
+ const onVis = () => {
1847
+ if (doc.visibilityState === "hidden") rt.suspend();
1848
+ else rt.resume();
1849
+ };
1850
+ doc.addEventListener("visibilitychange", onVis);
1851
+ return () => doc.removeEventListener("visibilitychange", onVis);
1852
+ }
1853
+ };
1854
+ return rt;
1855
+ }
1856
+
1857
+ // src/hot-switch.ts
1858
+ function createBackendSwitcher(dispatch) {
1859
+ let currentIR = null;
1860
+ let root = null;
1861
+ return {
1862
+ get dispatch() {
1863
+ return dispatch;
1864
+ },
1865
+ get currentIR() {
1866
+ return currentIR;
1867
+ },
1868
+ get root() {
1869
+ return root;
1870
+ },
1871
+ mount(ir) {
1872
+ currentIR = ir;
1873
+ root = renderIRTree(dispatch.currentBackend, ir);
1874
+ return root;
1875
+ },
1876
+ switchBackend(next, opts = {}) {
1877
+ const strategy = opts.strategy ?? "rebuild";
1878
+ const from = dispatch.currentBackend;
1879
+ opts.onBeforeSwitch?.(from, next);
1880
+ dispatch.switchBackend(next, { strategy });
1881
+ if (strategy === "rehydrate" && currentIR) {
1882
+ root = renderIRTree(next, currentIR);
1883
+ } else {
1884
+ root = null;
1885
+ }
1886
+ opts.onAfterSwitch?.(from, next, root);
1887
+ return root;
1888
+ },
1889
+ destroy() {
1890
+ currentIR = null;
1891
+ root = null;
1892
+ }
1893
+ };
1894
+ }
1895
+
1896
+ // src/container-spi.ts
1897
+ var CONTAINER_PROFILES = {
1898
+ singlepage: { pageStack: false, multiBusiness: false, crashIsolation: 0, resourceQuota: false, keepAlive: false, windowManagement: false, embedded: false },
1899
+ stack: { pageStack: true, multiBusiness: false, crashIsolation: 0, resourceQuota: true, keepAlive: true, windowManagement: false, embedded: false },
1900
+ superapp: { pageStack: true, multiBusiness: true, crashIsolation: 2, resourceQuota: true, keepAlive: true, windowManagement: false, embedded: false },
1901
+ miniprogram: { pageStack: true, multiBusiness: true, crashIsolation: 1, resourceQuota: true, keepAlive: true, windowManagement: false, embedded: false },
1902
+ window: { pageStack: true, multiBusiness: false, crashIsolation: 0, resourceQuota: true, keepAlive: true, windowManagement: true, embedded: false },
1903
+ embedded: { pageStack: false, multiBusiness: false, crashIsolation: 0, resourceQuota: false, keepAlive: false, windowManagement: false, embedded: true }
1904
+ };
1905
+ var PAGE_STATE_TRANSITIONS = {
1906
+ created: ["mounted", "destroyed"],
1907
+ mounted: ["hidden", "destroyed", "crashed"],
1908
+ hidden: ["mounted", "destroyed"],
1909
+ crashed: ["destroyed"],
1910
+ destroyed: ["recycled"],
1911
+ recycled: []
1912
+ };
1913
+ function canTransitionPageState(from, to) {
1914
+ return PAGE_STATE_TRANSITIONS[from].includes(to);
1915
+ }
1916
+ var FIVE_ATOMIC_STEPS = ["unmount", "unbindEvents", "releaseResources", "destroyIR", "releaseQuota"];
1917
+ function assertAtomicDestroy(report) {
1918
+ if (report.steps.length !== 5) {
1919
+ throw new Error(`G-42.2 \u8FDD\u53CD\uFF1A\u9875\u9762\u9500\u6BC1\u5FC5\u987B\u4E94\u539F\u5B50\uFF08\u5B9E\u9645 ${report.steps.length} \u6B65\uFF1A${report.steps.join("\u2192") || "\u65E0"}\uFF09`);
1920
+ }
1921
+ for (let i = 0; i < FIVE_ATOMIC_STEPS.length; i++) {
1922
+ if (report.steps[i] !== FIVE_ATOMIC_STEPS[i]) {
1923
+ throw new Error(`G-42.2 \u8FDD\u53CD\uFF1A\u4E94\u539F\u5B50\u6B65\u9AA4\u987A\u5E8F\u9519\u8BEF\uFF08\u7B2C ${i + 1} \u6B65\u5E94\u4E3A ${FIVE_ATOMIC_STEPS[i]}\uFF0C\u5B9E\u9645 ${report.steps[i]}\uFF09`);
1924
+ }
1925
+ }
1926
+ }
1927
+ function createDestroyReport(pageId, reclaimedBytes = 0, durationMs = 0) {
1928
+ return { pageId, steps: [...FIVE_ATOMIC_STEPS], leaked: [], reclaimedBytes, durationMs };
1929
+ }
1930
+ var DEFAULT_STACK_POLICY = {
1931
+ maxDepth: 10,
1932
+ overflowStrategy: "destroy-oldest",
1933
+ keepAlive: { maxCount: 3, memoryBudgetBytes: 64 * 1024 * 1024 }
1934
+ };
1935
+ function profileOfContainer(id) {
1936
+ return CONTAINER_PROFILES[id] ?? { pageStack: false, multiBusiness: false, crashIsolation: 0, resourceQuota: false, keepAlive: false, windowManagement: false, embedded: false };
1937
+ }
1938
+
1939
+ // src/ownership.ts
1940
+ var OwnershipError = class extends Error {
1941
+ constructor(code, message, extra) {
1942
+ super(message);
1943
+ this.name = "OwnershipError";
1944
+ this.code = code;
1945
+ this.resourceId = extra.resourceId;
1946
+ }
1947
+ };
1948
+ var OWNERSHIP_ERRORS = {
1949
+ useAfterMove: (id, movedTo) => new OwnershipError("use_after_move", `\u8D44\u6E90 ${id} \u5DF2\u8F6C\u79FB\u7ED9 ${movedTo}\uFF0C\u4E0D\u53EF\u518D\u8BBF\u95EE`, { resourceId: id }),
1950
+ useAfterDrop: (id) => new OwnershipError("use_after_drop", `\u8D44\u6E90 ${id} \u5DF2\u91CA\u653E\uFF0C\u4E0D\u53EF\u518D\u8BBF\u95EE`, { resourceId: id }),
1951
+ doubleMove: (id) => new OwnershipError("double_move", `\u8D44\u6E90 ${id} \u5DF2\u8F6C\u79FB\u8FC7\uFF0C\u4E0D\u53EF\u91CD\u590D\u8F6C\u79FB`, { resourceId: id }),
1952
+ hasActiveBorrows: (id, count) => new OwnershipError("has_active_borrows", `\u8D44\u6E90 ${id} \u6709 ${count} \u4E2A\u6D3B\u8DC3\u501F\u7528\uFF0C\u4E0D\u53EF\u91CA\u653E`, { resourceId: id }),
1953
+ alreadyDropped: (id) => new OwnershipError("already_dropped", `\u8D44\u6E90 ${id} \u5DF2\u91CA\u653E`, { resourceId: id }),
1954
+ notTransferable: (id, reason) => new OwnershipError("resource_not_transferable", `\u8D44\u6E90 ${id} \u4E0D\u53EF\u8F6C\u79FB\uFF1A${reason}`, { resourceId: id })
1955
+ };
1956
+ var OwnershipGraph = class {
1957
+ constructor(opts = {}) {
1958
+ this.nodes = /* @__PURE__ */ new Map();
1959
+ this.edges = [];
1960
+ this._nextId = 1;
1961
+ this._listeners = /* @__PURE__ */ new Set();
1962
+ if (opts.onMutate) this._listeners.add(opts.onMutate);
1963
+ }
1964
+ nextId() {
1965
+ return `res_${this._nextId++}`;
1966
+ }
1967
+ /** ★G-43 B4:动态订阅图 mutation(可任意时刻挂接;返回解绑) */
1968
+ subscribe(cb) {
1969
+ this._listeners.add(cb);
1970
+ return () => {
1971
+ this._listeners.delete(cb);
1972
+ };
1973
+ }
1974
+ _emit(m) {
1975
+ for (const fn of this._listeners) fn(m);
1976
+ }
1977
+ register(opts) {
1978
+ const node = {
1979
+ id: opts.id,
1980
+ type: opts.type,
1981
+ byteSize: opts.byteSize,
1982
+ owner: opts.owner ?? null,
1983
+ state: "alive",
1984
+ createdAt: Date.now(),
1985
+ sourceLocation: opts.sourceLocation ?? null
1986
+ };
1987
+ this.nodes.set(opts.id, node);
1988
+ if (opts.owner) {
1989
+ this.edges.push({ kind: "owns", from: opts.owner, to: opts.id });
1990
+ }
1991
+ this._emit({ kind: "register", id: opts.id, owner: node.owner, type: node.type, byteSize: node.byteSize, sourceLocation: node.sourceLocation, ts: Date.now() });
1992
+ return node;
1993
+ }
1994
+ addEdge(edge) {
1995
+ this.edges.push(edge);
1996
+ this._emit({ kind: "edge", edge, ts: Date.now() });
1997
+ }
1998
+ setState(id, state) {
1999
+ const node = this.nodes.get(id);
2000
+ if (node) node.state = state;
2001
+ this._emit({ kind: "state", id, state, ts: Date.now() });
2002
+ }
2003
+ resourcesOf(owner) {
2004
+ return [...this.nodes.values()].filter((n) => n.owner === owner && n.state === "alive");
2005
+ }
2006
+ /** 无主资源检测(G-43.1——owner === null 的存活节点数为 0) */
2007
+ findOrphans() {
2008
+ return [...this.nodes.values()].filter((n) => n.owner === null && n.state === "alive");
2009
+ }
2010
+ /** 泄漏检测:页面销毁后仍存活的资源 + 反向引用链(谁还持有它——DevTools 泄漏定位) */
2011
+ detectLeaks(destroyedScope) {
2012
+ return this.resourcesOf(destroyedScope).map((n) => ({
2013
+ resourceId: n.id,
2014
+ type: n.type,
2015
+ byteSize: n.byteSize,
2016
+ sourceLocation: n.sourceLocation,
2017
+ referenceChain: this.backTrace(n.id)
2018
+ }));
2019
+ }
2020
+ /** 反向引用链(资源 ← 谁持有 ← 谁又持有……) */
2021
+ backTrace(resourceId) {
2022
+ const chain = [];
2023
+ const visited = /* @__PURE__ */ new Set();
2024
+ const walk = (id) => {
2025
+ if (visited.has(id)) return;
2026
+ visited.add(id);
2027
+ for (const e of this.edges) {
2028
+ if (e.to === id && e.from !== id) {
2029
+ chain.push(`${e.from} --${e.kind}--> ${id}`);
2030
+ walk(e.from);
2031
+ }
2032
+ }
2033
+ };
2034
+ walk(resourceId);
2035
+ return chain;
2036
+ }
2037
+ stats() {
2038
+ const alive = [...this.nodes.values()].filter((n) => n.state === "alive");
2039
+ return {
2040
+ total: this.nodes.size,
2041
+ alive: alive.length,
2042
+ moved: [...this.nodes.values()].filter((n) => n.state === "moved").length,
2043
+ dropped: [...this.nodes.values()].filter((n) => n.state === "dropped").length,
2044
+ totalBytes: alive.reduce((s, n) => s + n.byteSize, 0),
2045
+ edges: this.edges.length
2046
+ };
2047
+ }
2048
+ };
2049
+ function createQuotaTracker() {
2050
+ const usage = /* @__PURE__ */ new Map();
2051
+ return {
2052
+ request(owner, bytes) {
2053
+ usage.set(owner, (usage.get(owner) ?? 0) + bytes);
2054
+ return usage.get(owner);
2055
+ },
2056
+ release(owner, bytes) {
2057
+ usage.set(owner, Math.max(0, (usage.get(owner) ?? 0) - bytes));
2058
+ return usage.get(owner);
2059
+ },
2060
+ usageOf(owner) {
2061
+ return usage.get(owner) ?? 0;
2062
+ }
2063
+ };
2064
+ }
2065
+ var Owned = class _Owned {
2066
+ constructor(opts) {
2067
+ this._state = "alive";
2068
+ this._movedTo = null;
2069
+ this._activeBorrows = 0;
2070
+ this._borrows = /* @__PURE__ */ new Set();
2071
+ this._listeners = /* @__PURE__ */ new Set();
2072
+ this.id = opts.id;
2073
+ this.resourceType = opts.resourceType;
2074
+ this.byteSize = opts.byteSize;
2075
+ this.owner = opts.owner;
2076
+ this._value = opts.value;
2077
+ this._graph = opts.graph;
2078
+ this._transferable = opts.transferable ?? false;
2079
+ this._releaseHook = opts.releaseHook ?? null;
2080
+ this._sourceLocation = opts.sourceLocation ?? null;
2081
+ this._graph.register({ id: opts.id, type: opts.resourceType, byteSize: opts.byteSize, owner: opts.owner, sourceLocation: this._sourceLocation });
2082
+ }
2083
+ get state() {
2084
+ return this._state;
2085
+ }
2086
+ get activeBorrows() {
2087
+ return this._activeBorrows;
2088
+ }
2089
+ /** ★G-43 B5:订阅状态变化(moved/dropped 时通知——返回解绑) */
2090
+ subscribe(cb) {
2091
+ this._listeners.add(cb);
2092
+ return () => {
2093
+ this._listeners.delete(cb);
2094
+ };
2095
+ }
2096
+ _notify() {
2097
+ for (const fn of this._listeners) fn(this._state);
2098
+ }
2099
+ /** 存活断言(G-43.2/43.6:Move/Drop 后禁访问) */
2100
+ _assertAlive(op) {
2101
+ if (this._state === "moved") {
2102
+ throw OWNERSHIP_ERRORS.useAfterMove(this.id, this._movedTo ?? "?");
2103
+ }
2104
+ if (this._state === "dropped") {
2105
+ throw OWNERSHIP_ERRORS.useAfterDrop(this.id);
2106
+ }
2107
+ void op;
2108
+ }
2109
+ /** 读取(G-43.2:Move/Drop 后禁访问——use-after-move 拦截) */
2110
+ read() {
2111
+ this._assertAlive("read");
2112
+ return this._value;
2113
+ }
2114
+ /** ★Move 语义:转移所有权后原所有者不可再访问 */
2115
+ transferTo(targetOwner) {
2116
+ this._assertAlive("transferTo");
2117
+ if (this._state === "moved") throw OWNERSHIP_ERRORS.doubleMove(this.id);
2118
+ if (this._activeBorrows > 0) throw OWNERSHIP_ERRORS.hasActiveBorrows(this.id, this._activeBorrows);
2119
+ this._state = "moved";
2120
+ this._movedTo = targetOwner;
2121
+ this._graph.setState(this.id, "moved");
2122
+ this._graph.addEdge({ kind: "moved-from", from: targetOwner, to: this.id });
2123
+ this._notify();
2124
+ return new _Owned({
2125
+ id: `${this.id}@${targetOwner}`,
2126
+ resourceType: this.resourceType,
2127
+ byteSize: this.byteSize,
2128
+ owner: targetOwner,
2129
+ value: this._value,
2130
+ graph: this._graph,
2131
+ transferable: this._transferable,
2132
+ releaseHook: this._releaseHook ?? void 0,
2133
+ sourceLocation: this._sourceLocation
2134
+ });
2135
+ }
2136
+ /** ★Borrow:临时借用(G-43.3 不逃逸作用域——drop 后失效) */
2137
+ borrow(scopeName = "anonymous") {
2138
+ this._assertAlive("borrow");
2139
+ const b = new Borrow(this, scopeName, this._graph);
2140
+ this._activeBorrows++;
2141
+ this._borrows.add(b);
2142
+ this._graph.addEdge({ kind: "borrows", from: scopeName, to: this.id, since: Date.now() });
2143
+ return b;
2144
+ }
2145
+ /** ★Weak:弱引用(打破循环) */
2146
+ weak() {
2147
+ return new Weak(this);
2148
+ }
2149
+ /** ★确定性 Drop(五阶段协议) */
2150
+ drop(opts = {}) {
2151
+ const { force = false } = opts;
2152
+ if (this._state === "dropped") return { ok: false, freedBytes: 0, freedHandles: 0, invalidatedBorrows: 0, error: OWNERSHIP_ERRORS.alreadyDropped(this.id) };
2153
+ if (this._state === "moved") return { ok: false, freedBytes: 0, freedHandles: 0, invalidatedBorrows: 0, error: OWNERSHIP_ERRORS.useAfterMove(this.id, this._movedTo ?? "?") };
2154
+ if (!force && this._activeBorrows > 0) {
2155
+ return { ok: false, freedBytes: 0, freedHandles: 0, invalidatedBorrows: 0, error: OWNERSHIP_ERRORS.hasActiveBorrows(this.id, this._activeBorrows) };
2156
+ }
2157
+ let invalidated = 0;
2158
+ for (const b of this._borrows) {
2159
+ if (b.valid) {
2160
+ b.invalidate();
2161
+ invalidated++;
2162
+ }
2163
+ }
2164
+ this._borrows.clear();
2165
+ this._activeBorrows = 0;
2166
+ let freedBytes = 0;
2167
+ if (this._releaseHook) {
2168
+ try {
2169
+ this._releaseHook(this._value);
2170
+ } catch {
2171
+ }
2172
+ }
2173
+ freedBytes = this.byteSize;
2174
+ this._state = "dropped";
2175
+ this._graph.setState(this.id, "dropped");
2176
+ this._notify();
2177
+ return { ok: true, freedBytes, freedHandles: 1, invalidatedBorrows: invalidated };
2178
+ }
2179
+ };
2180
+ var Borrow = class {
2181
+ constructor(source, scopeName, graph) {
2182
+ this.borrowedAt = Date.now();
2183
+ this._valid = true;
2184
+ this._source = source;
2185
+ this._scopeName = scopeName;
2186
+ this._graph = graph;
2187
+ }
2188
+ get valid() {
2189
+ return this._valid && this._source.state === "alive";
2190
+ }
2191
+ get durationMs() {
2192
+ return Date.now() - this.borrowedAt;
2193
+ }
2194
+ get() {
2195
+ if (!this.valid) return void 0;
2196
+ return this._source.read();
2197
+ }
2198
+ release() {
2199
+ if (!this._valid) return;
2200
+ this.invalidate();
2201
+ void this._graph;
2202
+ void this._scopeName;
2203
+ }
2204
+ invalidate() {
2205
+ this._valid = false;
2206
+ }
2207
+ };
2208
+ var Weak = class {
2209
+ constructor(source) {
2210
+ this._source = source;
2211
+ }
2212
+ get alive() {
2213
+ return this._source.state === "alive";
2214
+ }
2215
+ /** 升级为借用(弱引用仅在持有者存活时可用) */
2216
+ upgrade() {
2217
+ if (!this.alive) return void 0;
2218
+ return this._source.borrow("weak-upgrade");
2219
+ }
2220
+ };
2221
+ var Managed = class {
2222
+ constructor(opts) {
2223
+ this.disposed = false;
2224
+ this.value = opts.value;
2225
+ this.owner = opts.owner;
2226
+ this._disposeFn = opts.disposeFn ?? null;
2227
+ opts.registry?.add(this);
2228
+ }
2229
+ /** 释放(框架在页面销毁时自动调用——业务零心智负担) */
2230
+ dispose() {
2231
+ if (this.disposed) return false;
2232
+ this.disposed = true;
2233
+ try {
2234
+ this._disposeFn?.();
2235
+ } catch {
2236
+ }
2237
+ return true;
2238
+ }
2239
+ };
2240
+ var ManagedRegistry = class {
2241
+ constructor() {
2242
+ this._items = /* @__PURE__ */ new Set();
2243
+ }
2244
+ add(m) {
2245
+ this._items.add(m);
2246
+ }
2247
+ /** 全部释放(返回释放数) */
2248
+ disposeAll() {
2249
+ let n = 0;
2250
+ for (const m of this._items) {
2251
+ if (m.dispose()) n++;
2252
+ }
2253
+ this._items.clear();
2254
+ return n;
2255
+ }
2256
+ get size() {
2257
+ return this._items.size;
2258
+ }
2259
+ };
2260
+ var singletonGraph = null;
2261
+ function getProteusOwnershipGraph() {
2262
+ if (!singletonGraph) singletonGraph = new OwnershipGraph();
2263
+ return singletonGraph;
2264
+ }
2265
+
2266
+ // src/page-ownership.ts
2267
+ function createPageOwnership(owner, opts) {
2268
+ const { graph } = opts;
2269
+ const owned = /* @__PURE__ */ new Set();
2270
+ const managed = new ManagedRegistry();
2271
+ const quota = createQuotaTracker();
2272
+ function requestQuota(byteSize) {
2273
+ const used = quota.usageOf(owner);
2274
+ if (opts.quotaBytes !== void 0 && used + byteSize > opts.quotaBytes) {
2275
+ throw new Error(`G-43 \u914D\u989D\u4E0D\u8DB3\uFF1A${owner} \u5DF2\u7528 ${used}B / \u9650\u989D ${opts.quotaBytes}B\uFF08\u7533\u8BF7 ${byteSize}B\uFF09`);
2276
+ }
2277
+ quota.request(owner, byteSize);
2278
+ }
2279
+ return {
2280
+ owner,
2281
+ alloc(allocOpts) {
2282
+ const type = allocOpts.type ?? "shared-buffer";
2283
+ const value = allocOpts.value ?? new ArrayBuffer(allocOpts.byteSize);
2284
+ requestQuota(allocOpts.byteSize);
2285
+ const res = new Owned({
2286
+ id: graph.nextId(),
2287
+ resourceType: type,
2288
+ byteSize: allocOpts.byteSize,
2289
+ owner,
2290
+ value,
2291
+ graph,
2292
+ transferable: allocOpts.transferable ?? false,
2293
+ releaseHook: allocOpts.releaseHook,
2294
+ sourceLocation: allocOpts.sourceLocation ?? null
2295
+ });
2296
+ owned.add(res);
2297
+ return res;
2298
+ },
2299
+ register(res) {
2300
+ requestQuota(res.byteSize);
2301
+ owned.add(res);
2302
+ },
2303
+ managed,
2304
+ resourcesOf() {
2305
+ return graph.resourcesOf(owner);
2306
+ },
2307
+ destroy(destroyOpts = {}) {
2308
+ const force = destroyOpts.force ?? true;
2309
+ let freedCount = 0;
2310
+ let freedBytes = 0;
2311
+ let invalidatedBorrows = 0;
2312
+ const leaked = [];
2313
+ for (const res of [...owned]) {
2314
+ const r = res.drop({ force });
2315
+ if (r.ok) {
2316
+ freedCount++;
2317
+ freedBytes += r.freedBytes;
2318
+ invalidatedBorrows += r.invalidatedBorrows;
2319
+ quota.release(owner, r.freedBytes);
2320
+ owned.delete(res);
2321
+ } else if (r.error && (r.error.code === "already_dropped" || r.error.code === "use_after_move")) {
2322
+ owned.delete(res);
2323
+ } else if (r.error && r.error.code === "has_active_borrows") {
2324
+ leaked.push({ resourceId: r.error.resourceId, reason: r.error.code });
2325
+ }
2326
+ }
2327
+ const managedDisposed = managed.disposeAll();
2328
+ const remaining = quota.usageOf(owner);
2329
+ if (leaked.length === 0 && remaining > 0) quota.release(owner, remaining);
2330
+ return { freedCount, freedBytes, invalidatedBorrows, managedDisposed, quotaRemaining: quota.usageOf(owner), leaked };
2331
+ }
2332
+ };
2333
+ }
2334
+
2335
+ // src/stack-container.ts
2336
+ function createResourcePool(ownerLabel) {
2337
+ const timers = /* @__PURE__ */ new Set();
2338
+ const listeners = /* @__PURE__ */ new Set();
2339
+ const subscriptions = /* @__PURE__ */ new Set();
2340
+ const requests = /* @__PURE__ */ new Set();
2341
+ let seq = 0;
2342
+ const nextId = (kind) => `${ownerLabel}-${kind}-${++seq}`;
2343
+ return {
2344
+ timer(fn, ms, opts) {
2345
+ const h = {
2346
+ id: nextId("timer"),
2347
+ label: opts?.once ? "once-timer" : "timer",
2348
+ cleared: false,
2349
+ cancel() {
2350
+ h.cleared = true;
2351
+ timers.delete(h);
2352
+ }
2353
+ };
2354
+ timers.add(h);
2355
+ return h;
2356
+ },
2357
+ interval(fn, ms) {
2358
+ const h = { id: nextId("interval"), label: "interval", cleared: false, cancel() {
2359
+ h.cleared = true;
2360
+ timers.delete(h);
2361
+ } };
2362
+ timers.add(h);
2363
+ return h;
2364
+ },
2365
+ on(target, type, fn) {
2366
+ const b = { id: nextId("listener"), released: false };
2367
+ listeners.add(b);
2368
+ return () => {
2369
+ b.released = true;
2370
+ listeners.delete(b);
2371
+ };
2372
+ },
2373
+ bus(topic, fn) {
2374
+ const s = { id: nextId("bus"), released: false };
2375
+ subscriptions.add(s);
2376
+ return () => {
2377
+ s.released = true;
2378
+ subscriptions.delete(s);
2379
+ };
2380
+ },
2381
+ fetch() {
2382
+ const r = { id: nextId("req"), released: false };
2383
+ requests.add(r);
2384
+ return Object.assign(new Promise(() => {
2385
+ }), { cancel: () => {
2386
+ r.released = true;
2387
+ requests.delete(r);
2388
+ } });
2389
+ },
2390
+ subscribe() {
2391
+ const s = { id: nextId("subscribe"), released: false };
2392
+ subscriptions.add(s);
2393
+ return () => {
2394
+ s.released = true;
2395
+ subscriptions.delete(s);
2396
+ };
2397
+ },
2398
+ releaseAll() {
2399
+ const report = { timersCleared: timers.size, listenersUnbound: listeners.size, requestsAborted: requests.size };
2400
+ timers.forEach((t) => t.cleared = true);
2401
+ listeners.forEach((l) => l.released = true);
2402
+ requests.forEach((r) => r.released = true);
2403
+ timers.clear();
2404
+ listeners.clear();
2405
+ subscriptions.clear();
2406
+ requests.clear();
2407
+ return report;
2408
+ },
2409
+ get total() {
2410
+ return timers.size + listeners.size + subscriptions.size + requests.size;
2411
+ }
2412
+ };
2413
+ }
2414
+ function createQuotaManager(limitBytes) {
2415
+ let usedBytes = 0;
2416
+ let pageCount = 0;
2417
+ let sandboxCount = 0;
2418
+ function pressure() {
2419
+ if (usedBytes >= limitBytes) return "critical";
2420
+ if (usedBytes >= limitBytes * 0.8) return "warning";
2421
+ return "normal";
2422
+ }
2423
+ return {
2424
+ attach(scope, bytes) {
2425
+ if (usedBytes + bytes > limitBytes) return null;
2426
+ usedBytes += bytes;
2427
+ pageCount++;
2428
+ return {
2429
+ bytes,
2430
+ scope,
2431
+ release: () => {
2432
+ usedBytes = Math.max(0, usedBytes - bytes);
2433
+ pageCount = Math.max(0, pageCount - 1);
2434
+ }
2435
+ };
2436
+ },
2437
+ request(bytes) {
2438
+ if (usedBytes + bytes > limitBytes) return null;
2439
+ usedBytes += bytes;
2440
+ pageCount++;
2441
+ return { bytes, scope: "page", release: () => {
2442
+ usedBytes = Math.max(0, usedBytes - bytes);
2443
+ pageCount = Math.max(0, pageCount - 1);
2444
+ } };
2445
+ },
2446
+ release(handle) {
2447
+ handle.release();
2448
+ },
2449
+ get usage() {
2450
+ return { usedBytes, limitBytes, pageCount, sandboxCount };
2451
+ },
2452
+ get pressure() {
2453
+ return pressure();
2454
+ }
2455
+ };
2456
+ }
2457
+ function setPageState(page, to) {
2458
+ if (!canTransitionPageState(page.state, to)) {
2459
+ throw new Error(`G-42 \u72B6\u6001\u673A\u62D2\u7EDD\uFF1A${page.state} \u2192 ${to}\uFF08page ${page.pageId}\uFF09`);
2460
+ }
2461
+ ;
2462
+ page.state = to;
2463
+ }
2464
+ function createStackContainer(opts = {}) {
2465
+ const policy = opts.policy ?? DEFAULT_STACK_POLICY;
2466
+ const quota = createQuotaManager(opts.quotaLimitBytes ?? 512 * 1024 * 1024);
2467
+ const pages = /* @__PURE__ */ new Map();
2468
+ const stack = [];
2469
+ const events = {};
2470
+ const pressureHandlers = [];
2471
+ let seq = 0;
2472
+ const emit = (event, payload) => {
2473
+ for (const fn of events[event] ?? []) fn(payload);
2474
+ };
2475
+ function createPage(config) {
2476
+ const pageId = config.pageId ?? `page-${++seq}`;
2477
+ const page = {
2478
+ pageId,
2479
+ irId: config.irId,
2480
+ state: "created",
2481
+ mountPoint: null,
2482
+ resourcePool: createResourcePool(pageId),
2483
+ eventRegistry: {
2484
+ bind() {
2485
+ return () => {
2486
+ };
2487
+ },
2488
+ unbindAll() {
2489
+ },
2490
+ size: 0
2491
+ },
2492
+ ir: opts.createIR ? opts.createIR(config) : { irId: config.irId },
2493
+ budgetBytes: config.budgetBytes ?? 0,
2494
+ quotaHandle: null,
2495
+ alive: true,
2496
+ // keepAlive 标记(keep-alive 配额仅统计此类页面)
2497
+ keepAlive: config.keepAlive ?? false,
2498
+ // ★G-43 B3:ownership 选项启用 → 页面 = 所有权 scope(页面销毁时该页资源 forceDrop 回收)
2499
+ ownership: opts.ownership ? createPageOwnership(pageId, { graph: opts.ownership.graph, quotaBytes: opts.ownership.quotaBytes }) : null
2500
+ };
2501
+ pages.set(pageId, page);
2502
+ emit("page-created", { pageId });
2503
+ return page;
2504
+ }
2505
+ async function mountPage(handle) {
2506
+ const page = pageRecordOf(handle);
2507
+ const keepAlivePages = [...pages.values()].filter((p) => p.state !== "destroyed" && p.state !== "created" && p.keepAlive);
2508
+ if (keepAlivePages.length > policy.keepAlive.maxCount) {
2509
+ const oldest = stack[0];
2510
+ if (oldest) await destroyPage(oldest);
2511
+ }
2512
+ setPageState(page, "mounted");
2513
+ page.mountPoint = {};
2514
+ page.quotaHandle = page.budgetBytes > 0 ? quota.attach(page.pageId, page.budgetBytes) : null;
2515
+ }
2516
+ async function unmountPage(handle) {
2517
+ const page = pageRecordOf(handle);
2518
+ setPageState(page, "hidden");
2519
+ page.mountPoint = null;
2520
+ }
2521
+ async function destroyPage(handle) {
2522
+ const page = pageRecordOf(handle);
2523
+ const steps = [];
2524
+ page.mountPoint = null;
2525
+ steps.push(FIVE_ATOMIC_STEPS[0]);
2526
+ page.eventRegistry.unbindAll();
2527
+ steps.push(FIVE_ATOMIC_STEPS[1]);
2528
+ page.resourcePool.releaseAll();
2529
+ let reclaimedBytes = 0;
2530
+ const ownershipReport = page.ownership ? page.ownership.destroy({ force: true }) : null;
2531
+ if (ownershipReport) {
2532
+ reclaimedBytes += ownershipReport.freedBytes;
2533
+ if (ownershipReport.quotaRemaining !== 0) {
2534
+ throw new Error(`G-43 \u9875\u9762\u9500\u6BC1\u540E\u914D\u989D\u672A\u5F52\u96F6\uFF1A${page.pageId} \u5269\u4F59 ${ownershipReport.quotaRemaining}B`);
2535
+ }
2536
+ }
2537
+ steps.push(FIVE_ATOMIC_STEPS[2]);
2538
+ page.ir = null;
2539
+ steps.push(FIVE_ATOMIC_STEPS[3]);
2540
+ page.quotaHandle?.release();
2541
+ page.quotaHandle = null;
2542
+ steps.push(FIVE_ATOMIC_STEPS[4]);
2543
+ const report = { pageId: page.pageId, steps, leaked: [], reclaimedBytes, durationMs: 0 };
2544
+ assertAtomicDestroy(report);
2545
+ page.alive = false;
2546
+ page.state = "destroyed";
2547
+ pages.delete(page.pageId);
2548
+ const stackIdx = stack.indexOf(page);
2549
+ if (stackIdx >= 0) stack.splice(stackIdx, 1);
2550
+ emit("page-destroyed", { pageId: page.pageId, reclaimedBytes: report.reclaimedBytes });
2551
+ return report;
2552
+ }
2553
+ function pageRecordOf(handle) {
2554
+ const page = pages.get(handle.pageId);
2555
+ if (!page) throw new Error(`G-42: \u672A\u77E5\u9875\u9762 ${handle.pageId}`);
2556
+ return page;
2557
+ }
2558
+ const container = {
2559
+ id: "stack",
2560
+ version: "0.1.0",
2561
+ capabilities: CONTAINER_PROFILES.stack,
2562
+ async initialize() {
2563
+ },
2564
+ dispose() {
2565
+ pages.clear();
2566
+ stack.length = 0;
2567
+ },
2568
+ createPage(config) {
2569
+ return createPage(config);
2570
+ },
2571
+ mountPage,
2572
+ unmountPage,
2573
+ destroyPage,
2574
+ async push(config) {
2575
+ if (stack.length >= policy.maxDepth) {
2576
+ if (policy.overflowStrategy === "destroy-oldest") {
2577
+ const oldest = stack[0];
2578
+ if (oldest) {
2579
+ await destroyPage(oldest);
2580
+ emit("overflow", { strategy: "destroy-oldest", pageId: oldest.pageId });
2581
+ }
2582
+ } else if (policy.overflowStrategy === "reject") {
2583
+ throw new Error(`G-42 \u6808\u6EA2\u51FA\uFF1AmaxDepth=${policy.maxDepth}\uFF08reject \u7B56\u7565\uFF09`);
2584
+ }
2585
+ }
2586
+ const page = createPage(config);
2587
+ await mountPage(page);
2588
+ stack.push(page);
2589
+ return page;
2590
+ },
2591
+ async pop() {
2592
+ const page = stack.pop();
2593
+ if (!page) return null;
2594
+ await destroyPage(page);
2595
+ return page;
2596
+ },
2597
+ getCurrent() {
2598
+ return stack[stack.length - 1] ?? null;
2599
+ },
2600
+ getStackDepth() {
2601
+ return stack.length;
2602
+ },
2603
+ async createSandbox(bizId, manifest) {
2604
+ void bizId;
2605
+ void manifest;
2606
+ throw new Error("G-42: stack \u5BB9\u5668\u65E0\u6C99\u7BB1\uFF08capabilities.multiBusiness=false\uFF09\u2014\u2014SuperApp \u5BB9\u5668 B4 \u63D0\u4F9B");
2607
+ },
2608
+ async destroySandbox(bizId) {
2609
+ return createDestroyReport(bizId);
2610
+ },
2611
+ listSandboxes() {
2612
+ return [];
2613
+ },
2614
+ quota,
2615
+ onMemoryPressure(cb) {
2616
+ pressureHandlers.push(cb);
2617
+ return () => {
2618
+ const i = pressureHandlers.indexOf(cb);
2619
+ if (i >= 0) pressureHandlers.splice(i, 1);
2620
+ };
2621
+ },
2622
+ on(event, handler) {
2623
+ events[event] = events[event] ?? [];
2624
+ events[event].push(handler);
2625
+ },
2626
+ get stackView() {
2627
+ return [...stack];
2628
+ },
2629
+ get events() {
2630
+ return events;
2631
+ },
2632
+ ownershipOf(pageId) {
2633
+ const page = pages.get(pageId);
2634
+ return page ? page.ownership : null;
2635
+ }
2636
+ };
2637
+ return container;
2638
+ }
2639
+
2640
+ // src/container-conformance.ts
2641
+ var FORK_SIGNATURES = [
2642
+ /packages\/core\/src\//,
2643
+ // 框架源码副本
2644
+ /proteus-core\/internal\//,
2645
+ // 内部模块
2646
+ /from\s+['"]@proteus\/core\/internal/,
2647
+ // 直接 import 内部模块
2648
+ /__PROTEUS_FORKED__/
2649
+ // fork 标记
2650
+ ];
2651
+ function scanRepoForFork(fileContents) {
2652
+ const hits = [];
2653
+ for (const [filename, content] of Object.entries(fileContents)) {
2654
+ for (const sig of FORK_SIGNATURES) {
2655
+ if (sig.test(content)) hits.push({ filename, pattern: String(sig) });
2656
+ }
2657
+ }
2658
+ return hits;
2659
+ }
2660
+ function checkBizManifest(input, opts) {
2661
+ if (opts.requireSignature && !input.signature) {
2662
+ return { ok: false, code: "G39_SIGN", message: `business ${input.bizId} missing signature` };
2663
+ }
2664
+ const illegal = (input.capabilities ?? []).filter((c) => !opts.whitelist.includes(c));
2665
+ if (illegal.length > 0) {
2666
+ return { ok: false, code: "G39_CAP", message: `business ${input.bizId} illegal capabilities: ${illegal.join(",")}` };
2667
+ }
2668
+ return { ok: true };
2669
+ }
2670
+ var SENSITIVE_CAPABILITIES = ["location", "camera", "contacts", "phone-call", "biometric", "payment", "storage", "clipboard"];
2671
+ function checkCapabilityAuthorization(capabilities, opts) {
2672
+ const sensitive = opts.sensitive ?? SENSITIVE_CAPABILITIES;
2673
+ const denied = capabilities.filter((c) => sensitive.includes(c) && !opts.granted.includes(c));
2674
+ if (denied.length > 0) return { ok: false, code: "G39_AUTH", denied };
2675
+ return { ok: true };
2676
+ }
2677
+ var tests2 = [];
2678
+ var register2 = (group) => (id, fn) => tests2.push({ id: `${group}-${id}`, group, fn });
2679
+ var C01 = register2("C-01");
2680
+ var C02 = register2("C-02");
2681
+ var C03 = register2("C-03");
2682
+ var C04 = register2("C-04");
2683
+ var C05 = register2("C-05");
2684
+ var C06 = register2("C-06");
2685
+ var C07 = register2("C-07");
2686
+ var C08 = register2("C-08");
2687
+ var assert2 = (c, m) => {
2688
+ if (!c) throw new Error(m ?? "assertion failed");
2689
+ };
2690
+ C01("01", ({ container }) => assert2(typeof container.id === "string" && container.id.length > 0, "\u5BB9\u5668\u5E94\u58F0\u660E id"));
2691
+ C01("02", ({ container }) => assert2(typeof container.version === "string" && container.version.length > 0, "\u5BB9\u5668\u5E94\u58F0\u660E version"));
2692
+ C01("03", ({ container }) => assert2(container.capabilities && typeof container.capabilities === "object", "capabilities \u5E94\u5B58\u5728"));
2693
+ C01("04", ({ container }) => {
2694
+ const cap2 = container.capabilities;
2695
+ assert2(typeof cap2.pageStack === "boolean", "pageStack \u5E94\u58F0\u660E\u4E3A boolean");
2696
+ });
2697
+ C02("01", async ({ container }) => {
2698
+ const page = await container.push({ irId: "ir-c02-1" });
2699
+ assert2(page && page.pageId, "push \u5E94\u521B\u5EFA\u9875\u9762\u5E76\u8FD4\u56DE\u53E5\u67C4");
2700
+ });
2701
+ C02("02", async ({ container }) => {
2702
+ const page = await container.push({ irId: "ir-c02-2" });
2703
+ assert2(page.state === "mounted", "\u9875\u9762\u5E94 mounted");
2704
+ });
2705
+ C02("03", async ({ container }) => {
2706
+ const page = await container.push({ irId: "ir-c02-3" });
2707
+ assert2(page.irId === "ir-c02-3", "IR \u5B9E\u4F8B\u5DF2\u521B\u5EFA\u4E14\u5B58\u6D3B\uFF08\u552F\u4E00\u771F\u76F8 G-42.1\uFF09");
2708
+ });
2709
+ C02("04", async ({ container }) => {
2710
+ const page = await container.push({ irId: "ir-c02-4" });
2711
+ await container.unmountPage(page);
2712
+ assert2(page.state === "hidden", "unmount \u2192 hidden");
2713
+ });
2714
+ C02("05", async ({ container }) => {
2715
+ const page = await container.push({ irId: "ir-c02-5" });
2716
+ await container.unmountPage(page);
2717
+ await container.mountPage(page);
2718
+ assert2(page.state === "mounted", "\u91CD\u65B0 mount \u2192 mounted\uFF08show \u8BED\u4E49\uFF09");
2719
+ });
2720
+ async function fiveAtomicFlow(container, irId = "ir-c03") {
2721
+ const page = await container.push({ irId });
2722
+ return container.destroyPage(page);
2723
+ }
2724
+ C03("01", async ({ container }) => {
2725
+ const report = await fiveAtomicFlow(container);
2726
+ assert2(report.steps.length === 5, `\u9500\u6BC1\u5E94\u6070\u597D 5 \u6B65\uFF08\u5B9E\u9645 ${report.steps.length}\uFF09`);
2727
+ });
2728
+ C03("02", async ({ container }) => {
2729
+ const report = await fiveAtomicFlow(container);
2730
+ assert2(JSON.stringify(report.steps) === JSON.stringify(["unmount", "unbindEvents", "releaseResources", "destroyIR", "releaseQuota"]), "\u6B65\u5E8F\u5E94\u4E3A unmount\u2192unbindEvents\u2192releaseResources\u2192destroyIR\u2192releaseQuota");
2731
+ });
2732
+ C03("03", async ({ container }) => {
2733
+ const page = await container.push({ irId: "ir-c03-3" });
2734
+ await container.destroyPage(page);
2735
+ assert2(page.mountPoint == null, "\u6302\u8F7D\u70B9\u5E94\u7F6E\u7A7A\uFF08\u2460 \u2461\u5DF2\u5378\u8F7D\uFF09");
2736
+ });
2737
+ C03("04", async ({ container }) => {
2738
+ const page = await container.push({ irId: "ir-c03-4" });
2739
+ const pool = page.resourcePool;
2740
+ pool.total;
2741
+ await container.destroyPage(page);
2742
+ assert2(pool.total === 0, "\u8D44\u6E90\u6C60\u603B\u91CF\u5E94\u5F52\u96F6\uFF08\u2462 releaseResources\uFF09");
2743
+ });
2744
+ C03("05", async ({ container }) => {
2745
+ const page = await container.push({ irId: "ir-c03-5" });
2746
+ const ir = page.ir;
2747
+ await container.destroyPage(page);
2748
+ assert2(ir === null || !page.alive, "IR \u5B9E\u4F8B\u5E94\u9500\u6BC1\uFF08\u2463 destroyIR\u2014\u2014\u5BB9\u5668\u4E0D\u89E3\u6790\u5185\u5BB9 G-42.4\uFF09");
2749
+ });
2750
+ C03("06", async ({ container }) => {
2751
+ const report = await fiveAtomicFlow(container, "ir-c03-6");
2752
+ assert2(report.reclaimedBytes >= 0, "\u914D\u989D\u53E5\u67C4\u5F52\u8FD8\uFF08\u2464 releaseQuota\uFF09");
2753
+ });
2754
+ C04("01", async ({ container }) => {
2755
+ const base = container.getStackDepth();
2756
+ await container.push({ irId: "ir-c04-1a" });
2757
+ await container.push({ irId: "ir-c04-1b" });
2758
+ assert2(container.getStackDepth() === base + 2, "push \u540E\u6808\u6DF1\u5EA6\u5E94 +2");
2759
+ });
2760
+ C04("02", async ({ container }) => {
2761
+ let caught = false;
2762
+ try {
2763
+ for (let i = 0; i < 12; i++) await container.push({ irId: `ir-c04-2-${i}` });
2764
+ assert2(container.getStackDepth() >= 1, "\u6CBB\u7406\u540E\u81F3\u5C11\u4FDD\u7559\u9875\u9762");
2765
+ } catch (e) {
2766
+ caught = true;
2767
+ }
2768
+ assert2(caught || container.getStackDepth() <= 12, "\u8D85\u9650\u540E\u6DF1\u5EA6\u53D7\u63A7\uFF08destroy-oldest \u6216 reject\uFF09");
2769
+ });
2770
+ C04("03", async ({ container }) => {
2771
+ await container.push({ irId: "ir-c04-3-a" });
2772
+ await container.push({ irId: "ir-c04-3-b" });
2773
+ assert2(container.getCurrent()?.irId === "ir-c04-3-b", "\u5F53\u524D\u9875\u5E94\u4E3A\u65B0\u9875");
2774
+ });
2775
+ C04("04", async ({ container }) => {
2776
+ await container.push({ irId: "ir-c04-4" });
2777
+ const popped = await container.pop();
2778
+ assert2(popped !== null, "pop \u5E94\u8FD4\u56DE\u9875\u9762");
2779
+ assert2(popped.state === "destroyed", "pop \u540E\u9875\u9762\u5E94\u5DF2\u9500\u6BC1");
2780
+ });
2781
+ C05("01", async ({ container }) => {
2782
+ const page = await container.push({ irId: "ir-c05-1" });
2783
+ const pool = page.resourcePool;
2784
+ pool.timer(() => {
2785
+ }, 100);
2786
+ pool.timer(() => {
2787
+ }, 200);
2788
+ pool.timer(() => {
2789
+ }, 300);
2790
+ assert2(pool.total === 3, "\u9500\u6BC1\u524D\u8D44\u6E90\u5DF2\u767B\u8BB0");
2791
+ });
2792
+ C05("02", async ({ container }) => {
2793
+ const page = await container.push({ irId: "ir-c05-2" });
2794
+ const ir = page.ir;
2795
+ assert2(ir !== null, "\u9500\u6BC1\u524D IR \u5B58\u6D3B");
2796
+ await container.destroyPage(page);
2797
+ assert2(ir === null || !page.alive, "\u9500\u6BC1\u540E IR \u5B9E\u4F8B\u9500\u6BC1");
2798
+ });
2799
+ C05("03", async ({ container }) => {
2800
+ const page = await container.push({ irId: "ir-c05-3" });
2801
+ const pool = page.resourcePool;
2802
+ pool.interval(() => {
2803
+ }, 500);
2804
+ await container.destroyPage(page);
2805
+ assert2(pool.total === 0, "\u9500\u6BC1\u540E\u5B9A\u65F6\u5668\u6C60\u6E05\u96F6");
2806
+ });
2807
+ C05("04", async ({ container }) => {
2808
+ const page = await container.push({ irId: "ir-c05-4" });
2809
+ const pool = page.resourcePool;
2810
+ pool.on({ addEventListener() {
2811
+ }, removeEventListener() {
2812
+ } }, "tap", () => {
2813
+ });
2814
+ assert2(pool.total === 1, "\u76D1\u542C\u5DF2\u767B\u8BB0");
2815
+ await container.destroyPage(page);
2816
+ assert2(pool.total === 0, "\u9500\u6BC1\u540E\u76D1\u542C\u6E05\u96F6");
2817
+ });
2818
+ C05("05", async ({ container }) => {
2819
+ const page = await container.push({ irId: "ir-c05-5" });
2820
+ const pool = page.resourcePool;
2821
+ pool.timer(() => {
2822
+ }, 100);
2823
+ pool.on({ addEventListener() {
2824
+ }, removeEventListener() {
2825
+ } }, "tap", () => {
2826
+ });
2827
+ assert2(pool.total === 2, "\u9500\u6BC1\u524D total=2");
2828
+ await container.destroyPage(page);
2829
+ assert2(pool.total === 0, "\u9500\u6BC1\u540E\u8D44\u6E90\u6C60\u603B\u91CF = 0\uFF08C-05 \u6CC4\u6F0F\u68C0\u6D4B\u673A\u5668\u8BC1\u636E\uFF09");
2830
+ });
2831
+ C06("01", async ({ container }) => assert2(container.quota !== null && typeof container.quota.request === "function", "\u914D\u989D manager \u5E94\u5B58\u5728"));
2832
+ C06("02", async ({ container }) => {
2833
+ const h = container.quota.request(100);
2834
+ assert2(h !== null, "\u914D\u989D\u7533\u8BF7\u5E94\u6210\u529F");
2835
+ assert2(container.quota.usage.usedBytes >= 100, "\u7528\u91CF\u7EDF\u8BA1\u5E94\u6B63\u786E");
2836
+ });
2837
+ C06("03", async ({ container }) => {
2838
+ const limit = container.quota.usage.limitBytes;
2839
+ const big = container.quota.request(limit + 1);
2840
+ assert2(big === null, "\u8D85\u9650\u5E94\u8FD4\u56DE null\uFF08CMP061\uFF09");
2841
+ });
2842
+ C06("04", async ({ container }) => {
2843
+ const h = container.quota.request(50);
2844
+ if (h) container.quota.release(h);
2845
+ assert2(container.quota.usage.usedBytes >= 0, "\u5F52\u8FD8\u540E\u7528\u91CF\u8BB0\u5F55\u6B63\u786E");
2846
+ });
2847
+ C07("01", async ({ container }) => assert2(container.capabilities.multiBusiness === true, "\u6C99\u7BB1\u80FD\u529B\uFF1AmultiBusiness=true\uFF08Stack \u5BB9\u5668 SKIP\uFF09"));
2848
+ C07("02", async ({ container }) => assert2(container.capabilities.crashIsolation > 0, "\u5D29\u6E83\u9694\u79BB\u80FD\u529B\uFF1AcrashIsolation>0"));
2849
+ C07("03", async ({ container }) => assert2(typeof container.createSandbox === "function", "\u5E94\u53EF\u521B\u5EFA\u6C99\u7BB1"));
2850
+ C07("04", async ({ container }) => {
2851
+ const sb = await container.createSandbox("biz-a", { bizId: "biz-a", signature: "test-sig", requiredCapabilities: [] });
2852
+ assert2(sb !== null, "\u6C99\u7BB1 A \u5E94\u521B\u5EFA");
2853
+ });
2854
+ C07("05", async ({ container }) => {
2855
+ const sb = await container.createSandbox("biz-b", { bizId: "biz-b", signature: "test-sig", requiredCapabilities: [] });
2856
+ assert2(sb !== null, "\u6C99\u7BB1 B \u5E94\u521B\u5EFA");
2857
+ });
2858
+ C07("06", async ({ container }) => {
2859
+ const sbs = container.listSandboxes();
2860
+ assert2(Array.isArray(sbs), "\u6C99\u7BB1\u5217\u8868\u5E94\u53EF\u67E5\u8BE2\uFF08\u5D29\u6E83\u9694\u79BB\u771F\u9A8C\u8BC1\u7531 B4 SuperApp \u5BB9\u5668\u63D0\u4F9B\uFF09");
2861
+ });
2862
+ C08("01", async ({ container }) => {
2863
+ const r = checkBizManifest({ bizId: "evil" }, { requireSignature: false, whitelist: ["camera"] });
2864
+ assert2(r.ok === true, "\u65E0\u7B7E\u540D\u62D2\u7EDD\u7531\u58F0\u660E requireSignature \u7684\u5BB9\u5668\u9A8C\u8BC1");
2865
+ });
2866
+ C08("02", async ({ container }) => {
2867
+ const r = checkBizManifest({ bizId: "bad", signature: "s", capabilities: ["contacts"] }, { requireSignature: true, whitelist: ["camera"] });
2868
+ assert2(r.ok === false && r.code === "G39_CAP", "\u8D8A\u6743\u80FD\u529B\u5E94\u88AB\u62D2\u7EDD\uFF08G39_CAP\uFF09");
2869
+ });
2870
+ C08("03", ({}) => {
2871
+ const hits = scanRepoForFork({ "host/src/main.js": "import { createContainer } from '@proteus/container';" });
2872
+ assert2(hits.length === 0, "\u5408\u89C4\u4ED3\u5E93\u5E94\u65E0 fork \u547D\u4E2D");
2873
+ });
2874
+ C08("04", ({}) => {
2875
+ const hits = scanRepoForFork({ "host/vendor/core.js": "import x from '@proteus/core/internal/diff';" });
2876
+ assert2(hits.length > 0, "fork \u4ED3\u5E93\u5E94\u88AB\u68C0\u51FA");
2877
+ });
2878
+ async function runContainerConformance(container) {
2879
+ const ctx = { container };
2880
+ const results = [];
2881
+ for (const t of tests2) {
2882
+ const caps = container.capabilities;
2883
+ if (t.group === "C-07" && !caps.multiBusiness) {
2884
+ results.push({ id: t.id, status: "SKIP", reason: "multiBusiness=false\uFF08\u5BB9\u5668\u65E0\u6C99\u7BB1\u80FD\u529B\u2014\u2014\u8BDA\u5B9E\u58F0\u660E CMP065\uFF09" });
2885
+ continue;
2886
+ }
2887
+ if (t.group === "C-04" && !caps.pageStack) {
2888
+ results.push({ id: t.id, status: "SKIP", reason: "pageStack=false\uFF08\u5BB9\u5668\u65E0\u9875\u9762\u6808\u6CBB\u7406\u2014\u2014singlepage/embedded \u5355\u69FD\u8BED\u4E49\uFF0CCMP065\uFF09" });
2889
+ continue;
2890
+ }
2891
+ if (t.group === "C-06" && !caps.resourceQuota) {
2892
+ results.push({ id: t.id, status: "SKIP", reason: "resourceQuota=false\uFF08\u5BB9\u5668\u65E0\u914D\u989D\u6CBB\u7406\u2014\u2014CMP065\uFF09" });
2893
+ continue;
2894
+ }
2895
+ if (t.id === "C-08-01" || t.id === "C-08-02") {
2896
+ const gate = container._security;
2897
+ if (!gate || !gate.requireSignature) {
2898
+ if (t.id === "C-08-01") {
2899
+ const r = checkBizManifest({ bizId: "evil" }, { requireSignature: true, whitelist: ["camera"] });
2900
+ if (r.ok === false && r.code === "G39_SIGN") {
2901
+ results.push({ id: t.id, status: "PASS" });
2902
+ continue;
2903
+ }
2904
+ }
2905
+ results.push({ id: t.id, status: "SKIP", reason: "\u5BB9\u5668\u672A\u58F0\u660E\u5B89\u5168\u7F51\u5173\uFF08requireSignature=false\uFF09" });
2906
+ continue;
2907
+ }
2908
+ }
2909
+ try {
2910
+ const ret = await t.fn(ctx);
2911
+ if (ret === "SKIP") results.push({ id: t.id, status: "SKIP" });
2912
+ else results.push({ id: t.id, status: "PASS" });
2913
+ } catch (e) {
2914
+ results.push({ id: t.id, status: "FAIL", reason: e.message });
2915
+ }
2916
+ }
2917
+ return {
2918
+ total: results.length,
2919
+ pass: results.filter((r) => r.status === "PASS").length,
2920
+ fail: results.filter((r) => r.status === "FAIL").length,
2921
+ skip: results.filter((r) => r.status === "SKIP").length,
2922
+ results
2923
+ };
2924
+ }
2925
+ function formatContainerConformance(s) {
2926
+ const lines = ["[G-42 \u5BB9\u5668 Conformance\uFF08C-01~C-08\uFF09]"];
2927
+ for (const r of s.results) {
2928
+ const icon = r.status === "PASS" ? "\u2705" : r.status === "SKIP" ? "\u23ED\uFE0F " : "\u274C";
2929
+ lines.push(` ${icon} ${r.id}${r.status !== "PASS" ? ` \u2014 ${r.reason ?? ""}` : ""}`);
2930
+ }
2931
+ lines.push("\u2500".repeat(30));
2932
+ lines.push(`\u603B\u8BA1\uFF1APASS=${s.pass} FAIL=${s.fail} SKIP=${s.skip}\uFF08${s.total} \u9879 C-01~C-08\uFF09`);
2933
+ return lines.join("\n");
2934
+ }
2935
+
2936
+ // src/superapp-container.ts
2937
+ function createBusinessSandbox(bizId, manifest, opts) {
2938
+ const scope = { bizId, values: {} };
2939
+ const sb = {
2940
+ bizId,
2941
+ isolatedScope: scope,
2942
+ quotaHandle: null,
2943
+ state: "running",
2944
+ crashCount: 0,
2945
+ scope,
2946
+ run(fn) {
2947
+ try {
2948
+ const result = fn();
2949
+ return { ok: true, result };
2950
+ } catch (error) {
2951
+ return { ok: false, error };
2952
+ }
2953
+ }
2954
+ };
2955
+ return sb;
2956
+ }
2957
+ function validateManifest(manifest, policy) {
2958
+ const r = checkBizManifest(
2959
+ { bizId: manifest.bizId, signature: manifest.signature, capabilities: manifest.requiredCapabilities },
2960
+ { requireSignature: policy.security.requireSignature, whitelist: policy.security.capabilityWhitelist }
2961
+ );
2962
+ if (!r.ok) {
2963
+ throw new Error(`${r.code}: ${r.message}`);
2964
+ }
2965
+ }
2966
+ var DEFAULT_SUPERAPP_POLICY = {
2967
+ maxDepth: 10,
2968
+ overflowStrategy: "destroy-oldest",
2969
+ keepAlive: { maxCount: 3, memoryBudgetBytes: 64 * 1024 * 1024 },
2970
+ sandbox: { defaultMemoryBytes: 128 * 1024 * 1024, maxSandboxes: 8 },
2971
+ crash: { isolationLevel: 2, autoRestart: true, maxRestartCount: 3 },
2972
+ security: { requireSignature: true, capabilityWhitelist: ["camera", "location", "storage"] }
2973
+ };
2974
+ function createSuperAppContainer(opts = {}) {
2975
+ const policy = {
2976
+ ...DEFAULT_SUPERAPP_POLICY,
2977
+ ...opts.policy,
2978
+ keepAlive: { ...DEFAULT_SUPERAPP_POLICY.keepAlive, ...opts.policy?.keepAlive ?? {} },
2979
+ sandbox: { ...DEFAULT_SUPERAPP_POLICY.sandbox, ...opts.policy?.sandbox ?? {} },
2980
+ crash: { ...DEFAULT_SUPERAPP_POLICY.crash, ...opts.policy?.crash ?? {} },
2981
+ security: { ...DEFAULT_SUPERAPP_POLICY.security, ...opts.policy?.security ?? {} }
2982
+ };
2983
+ const stack = createStackContainer({ policy, quotaLimitBytes: opts.quotaLimitBytes, ownership: opts.ownership });
2984
+ const sandboxes = /* @__PURE__ */ new Map();
2985
+ const crashLog = [];
2986
+ const events = {};
2987
+ const emit = (event, payload) => {
2988
+ for (const fn of events[event] ?? []) fn(payload);
2989
+ void event;
2990
+ };
2991
+ const container = {
2992
+ id: "superapp",
2993
+ version: "1.0.0",
2994
+ capabilities: {
2995
+ ...CONTAINER_PROFILES.superapp,
2996
+ crashIsolation: policy.crash.isolationLevel
2997
+ },
2998
+ // —— 生命周期:委托 stack ——
2999
+ initialize: stack.initialize,
3000
+ dispose() {
3001
+ for (const bizId of [...sandboxes.keys()]) void container.destroySandbox(bizId);
3002
+ stack.dispose();
3003
+ },
3004
+ createPage: stack.createPage,
3005
+ mountPage: stack.mountPage,
3006
+ unmountPage: stack.unmountPage,
3007
+ destroyPage: stack.destroyPage,
3008
+ push: stack.push,
3009
+ pop: stack.pop,
3010
+ getCurrent: stack.getCurrent,
3011
+ getStackDepth: stack.getStackDepth,
3012
+ // ★G-43 B3:页面所有权上下文(委托内部 Stack 页面记录)
3013
+ ownershipOf: stack.ownershipOf,
3014
+ // —— 业务沙箱(SuperApp 核心) ——
3015
+ async createSandbox(bizId, manifest) {
3016
+ if (sandboxes.size >= policy.sandbox.maxSandboxes) {
3017
+ throw new Error(`G39_LIMIT: max sandboxes ${policy.sandbox.maxSandboxes}`);
3018
+ }
3019
+ validateManifest(manifest, policy);
3020
+ const sb = createBusinessSandbox(bizId, manifest, { memoryBytes: policy.sandbox.defaultMemoryBytes });
3021
+ sb.state = "running";
3022
+ sandboxes.set(bizId, sb);
3023
+ emit("sandbox-created", { bizId });
3024
+ return sb;
3025
+ },
3026
+ async destroySandbox(bizId) {
3027
+ const sb = sandboxes.get(bizId);
3028
+ if (!sb) throw new Error(`G39_NOTFOUND: sandbox ${bizId} not found`);
3029
+ sb.state = "destroyed";
3030
+ sandboxes.delete(bizId);
3031
+ emit("sandbox-destroyed", { bizId });
3032
+ return { pageId: bizId, steps: ["unmount", "unbindEvents", "releaseResources", "destroyIR", "releaseQuota"], leaked: [], reclaimedBytes: 0, durationMs: 0 };
3033
+ },
3034
+ listSandboxes() {
3035
+ return [...sandboxes.values()];
3036
+ },
3037
+ // —— 资源治理 ——
3038
+ quota: stack.quota,
3039
+ onMemoryPressure(cb) {
3040
+ events["pressure"] = events["pressure"] ?? [];
3041
+ events["pressure"].push(cb);
3042
+ return () => {
3043
+ events["pressure"] = (events["pressure"] ?? []).filter((f) => f !== cb);
3044
+ };
3045
+ },
3046
+ on(event, handler) {
3047
+ events[event] = events[event] ?? [];
3048
+ events[event].push(handler);
3049
+ },
3050
+ // —— 崩溃隔离核心 ——
3051
+ executeInSandbox(bizId, fn) {
3052
+ const sb = sandboxes.get(bizId);
3053
+ if (!sb) throw new Error(`G39_NOTFOUND: sandbox ${bizId} not found`);
3054
+ const result = sb.run(fn);
3055
+ if (!result.ok) {
3056
+ const error = String(result.error?.message ?? result.error);
3057
+ crashLog.push({ bizId, error, at: Date.now() });
3058
+ emit("sandbox-crashed", { bizId, error, hostAlive: true });
3059
+ if (policy.crash.autoRestart && sb.crashCount < policy.crash.maxRestartCount) {
3060
+ sb.crashCount++;
3061
+ sb.state = "running";
3062
+ } else {
3063
+ ;
3064
+ sb.state = "crashed";
3065
+ }
3066
+ return {
3067
+ ok: false,
3068
+ bizId,
3069
+ hostAlive: true,
3070
+ otherSandboxesAlive: [...sandboxes.values()].filter((s) => s.bizId !== bizId && s.state === "running").length,
3071
+ error
3072
+ };
3073
+ }
3074
+ return { ok: true, bizId, result: result.result };
3075
+ },
3076
+ get crashLog() {
3077
+ return crashLog;
3078
+ },
3079
+ get sandboxView() {
3080
+ return [...sandboxes.values()];
3081
+ }
3082
+ };
3083
+ return container;
3084
+ }
3085
+
3086
+ // src/basic-containers.ts
3087
+ function setSlotState(page, to) {
3088
+ if (!canTransitionPageState(page.state, to)) {
3089
+ throw new Error(`G-42 \u72B6\u6001\u673A\u62D2\u7EDD\uFF1A${page.state} \u2192 ${to}\uFF08page ${page.pageId}\uFF09`);
3090
+ }
3091
+ ;
3092
+ page.state = to;
3093
+ }
3094
+ function makeSlotPage(seq, config, ownership) {
3095
+ const pageId = config.pageId ?? `page-${++seq}`;
3096
+ return {
3097
+ pageId,
3098
+ irId: config.irId,
3099
+ state: "created",
3100
+ mountPoint: null,
3101
+ resourcePool: createResourcePool(pageId),
3102
+ eventRegistry: {
3103
+ bind() {
3104
+ return () => {
3105
+ };
3106
+ },
3107
+ unbindAll() {
3108
+ },
3109
+ size: 0
3110
+ },
3111
+ config,
3112
+ ir: { irId: config.irId },
3113
+ quotaHandle: null,
3114
+ alive: true,
3115
+ ownership
3116
+ };
3117
+ }
3118
+ async function destroySlotPage(page, pages, emit) {
3119
+ const steps = [];
3120
+ page.mountPoint = null;
3121
+ steps.push(FIVE_ATOMIC_STEPS[0]);
3122
+ page.eventRegistry.unbindAll();
3123
+ steps.push(FIVE_ATOMIC_STEPS[1]);
3124
+ page.resourcePool.releaseAll();
3125
+ let reclaimedBytes = 0;
3126
+ const ownershipReport = page.ownership ? page.ownership.destroy({ force: true }) : null;
3127
+ if (ownershipReport) {
3128
+ reclaimedBytes += ownershipReport.freedBytes;
3129
+ if (ownershipReport.quotaRemaining !== 0) {
3130
+ throw new Error(`G-43 \u9875\u9762\u9500\u6BC1\u540E\u914D\u989D\u672A\u5F52\u96F6\uFF1A${page.pageId} \u5269\u4F59 ${ownershipReport.quotaRemaining}B`);
3131
+ }
3132
+ }
3133
+ steps.push(FIVE_ATOMIC_STEPS[2]);
3134
+ page.ir = null;
3135
+ steps.push(FIVE_ATOMIC_STEPS[3]);
3136
+ page.quotaHandle = null;
3137
+ steps.push(FIVE_ATOMIC_STEPS[4]);
3138
+ const report = { pageId: page.pageId, steps, leaked: [], reclaimedBytes, durationMs: 0 };
3139
+ assertAtomicDestroy(report);
3140
+ page.alive = false;
3141
+ page.state = "destroyed";
3142
+ pages.delete(page.pageId);
3143
+ emit("page-destroyed", { pageId: page.pageId, reclaimedBytes });
3144
+ return report;
3145
+ }
3146
+ function noQuota(id) {
3147
+ const refuse = () => {
3148
+ throw new Error(`G-42: ${id} \u5BB9\u5668\u65E0\u914D\u989D\u6CBB\u7406\uFF08capabilities.resourceQuota=false\u2014\u2014CMP065 \u8BDA\u5B9E\u58F0\u660E\uFF09`);
3149
+ };
3150
+ const usage = { usedBytes: 0, limitBytes: 0, pageCount: 0, sandboxCount: 0 };
3151
+ return {
3152
+ request: () => refuse(),
3153
+ release: () => refuse(),
3154
+ usage,
3155
+ pressure: "normal"
3156
+ };
3157
+ }
3158
+ function createSinglePageContainer(opts = {}) {
3159
+ const pages = /* @__PURE__ */ new Map();
3160
+ let current = null;
3161
+ let seq = 0;
3162
+ const events = {};
3163
+ const emit = (event, payload) => {
3164
+ for (const fn of events[event] ?? []) fn(payload);
3165
+ };
3166
+ function createSlot(config) {
3167
+ const ownership = opts.ownership ? createPageOwnership(config.pageId ?? `page-${++seq}`, { graph: opts.ownership.graph, quotaBytes: opts.ownership.quotaBytes }) : null;
3168
+ const page = makeSlotPage(seq, config, ownership);
3169
+ if (!config.pageId) seq++;
3170
+ pages.set(page.pageId, page);
3171
+ emit("page-created", { pageId: page.pageId });
3172
+ return page;
3173
+ }
3174
+ function createPage(config) {
3175
+ return createSlot(config);
3176
+ }
3177
+ async function destroyPage(handle) {
3178
+ const page = pages.get(handle.pageId);
3179
+ if (!page) throw new Error(`G-42: \u672A\u77E5\u9875\u9762 ${handle.pageId}`);
3180
+ const report = await destroySlotPage(page, pages, emit);
3181
+ if (current === page) current = null;
3182
+ return report;
3183
+ }
3184
+ async function mountPage(handle) {
3185
+ const page = pages.get(handle.pageId);
3186
+ if (!page) throw new Error(`G-42: \u672A\u77E5\u9875\u9762 ${handle.pageId}`);
3187
+ if (page.state === "mounted") return;
3188
+ setSlotState(page, "mounted");
3189
+ page.mountPoint = opts.getHostMountPoint ? opts.getHostMountPoint(page.config) : {};
3190
+ }
3191
+ async function unmountPage(handle) {
3192
+ const page = pages.get(handle.pageId);
3193
+ if (!page) throw new Error(`G-42: \u672A\u77E5\u9875\u9762 ${handle.pageId}`);
3194
+ setSlotState(page, "hidden");
3195
+ page.mountPoint = null;
3196
+ }
3197
+ const container = {
3198
+ id: "singlepage",
3199
+ version: "1.0.0",
3200
+ capabilities: CONTAINER_PROFILES.singlepage,
3201
+ async initialize() {
3202
+ },
3203
+ dispose() {
3204
+ pages.clear();
3205
+ current = null;
3206
+ },
3207
+ createPage,
3208
+ mountPage,
3209
+ unmountPage,
3210
+ destroyPage,
3211
+ async push(config) {
3212
+ for (const p of [...pages.values()]) await destroyPage(p);
3213
+ const page = createSlot(config);
3214
+ await mountPage(page);
3215
+ current = page;
3216
+ return page;
3217
+ },
3218
+ async pop() {
3219
+ if (!current) return null;
3220
+ const page = current;
3221
+ await destroyPage(page);
3222
+ return page;
3223
+ },
3224
+ getCurrent() {
3225
+ return current;
3226
+ },
3227
+ getStackDepth() {
3228
+ return current ? 1 : 0;
3229
+ },
3230
+ async createSandbox() {
3231
+ throw new Error("G-42: singlepage \u5BB9\u5668\u65E0\u6C99\u7BB1\uFF08capabilities.multiBusiness=false\u2014\u2014CMP065\uFF09");
3232
+ },
3233
+ async destroySandbox(bizId) {
3234
+ void bizId;
3235
+ return createDestroyReport(bizId);
3236
+ },
3237
+ listSandboxes() {
3238
+ return [];
3239
+ },
3240
+ quota: noQuota("singlepage"),
3241
+ onMemoryPressure() {
3242
+ },
3243
+ on(event, handler) {
3244
+ events[event] = events[event] ?? [];
3245
+ events[event].push(handler);
3246
+ },
3247
+ ownershipOf(pageId) {
3248
+ const page = pages.get(pageId);
3249
+ return page ? page.ownership : null;
3250
+ }
3251
+ };
3252
+ return container;
3253
+ }
3254
+ function createEmbeddedContainer(opts = {}) {
3255
+ const inner = createSinglePageContainer(opts);
3256
+ const container = {
3257
+ id: "embedded",
3258
+ version: "1.0.0",
3259
+ capabilities: CONTAINER_PROFILES.embedded,
3260
+ async initialize() {
3261
+ },
3262
+ dispose() {
3263
+ inner.dispose();
3264
+ },
3265
+ createPage: inner.createPage,
3266
+ mountPage: inner.mountPage,
3267
+ unmountPage: inner.unmountPage,
3268
+ destroyPage: inner.destroyPage,
3269
+ push: inner.push,
3270
+ pop: inner.pop,
3271
+ getCurrent: inner.getCurrent,
3272
+ getStackDepth: inner.getStackDepth,
3273
+ async createSandbox() {
3274
+ throw new Error("G-42: embedded \u5BB9\u5668\u65E0\u6C99\u7BB1\uFF08capabilities.multiBusiness=false\u2014\u2014CMP065\uFF09");
3275
+ },
3276
+ async destroySandbox(bizId) {
3277
+ void bizId;
3278
+ return createDestroyReport(bizId);
3279
+ },
3280
+ listSandboxes() {
3281
+ return [];
3282
+ },
3283
+ quota: noQuota("embedded"),
3284
+ onMemoryPressure() {
3285
+ },
3286
+ on(event, handler) {
3287
+ inner.on(event, handler);
3288
+ },
3289
+ ownershipOf(pageId) {
3290
+ return inner.ownershipOf(pageId);
3291
+ }
3292
+ };
3293
+ return container;
3294
+ }
3295
+ function createWindowContainer(opts = {}) {
3296
+ const windows = /* @__PURE__ */ new Map();
3297
+ const windowEvents = {};
3298
+ let focused = null;
3299
+ let seq = 0;
3300
+ const emitWindow = (event, payload) => {
3301
+ for (const fn of windowEvents[event] ?? []) fn(payload);
3302
+ };
3303
+ function stackOptions() {
3304
+ return { policy: opts.policy, quotaLimitBytes: opts.quotaLimitBytes, ownership: opts.ownership };
3305
+ }
3306
+ function createWindow(windowId) {
3307
+ const id = windowId ?? `window-${++seq}`;
3308
+ if (windows.has(id)) throw new Error(`G-42: \u7A97\u53E3\u5DF2\u5B58\u5728 ${id}`);
3309
+ const stack = createStackContainer(stackOptions());
3310
+ windows.set(id, stack);
3311
+ focused = id;
3312
+ emitWindow("window-created", { windowId: id });
3313
+ emitWindow("window-focused", { windowId: id });
3314
+ return stack;
3315
+ }
3316
+ function focusedStack() {
3317
+ if (!focused || !windows.has(focused)) {
3318
+ createWindow();
3319
+ }
3320
+ return windows.get(focused);
3321
+ }
3322
+ async function destroyWindow(windowId) {
3323
+ const stack = windows.get(windowId);
3324
+ if (!stack) throw new Error(`G-42: \u672A\u77E5\u7A97\u53E3 ${windowId}`);
3325
+ for (const page of [...stack.stackView]) {
3326
+ await stack.destroyPage(page);
3327
+ }
3328
+ windows.delete(windowId);
3329
+ if (focused === windowId) {
3330
+ focused = windows.keys().next().value ?? null;
3331
+ if (focused) emitWindow("window-focused", { windowId: focused });
3332
+ }
3333
+ emitWindow("window-destroyed", { windowId });
3334
+ }
3335
+ const container = {
3336
+ id: "window",
3337
+ version: "1.0.0",
3338
+ capabilities: CONTAINER_PROFILES.window,
3339
+ async initialize() {
3340
+ if (windows.size === 0) createWindow("window-1");
3341
+ },
3342
+ dispose() {
3343
+ windows.clear();
3344
+ focused = null;
3345
+ },
3346
+ createWindow,
3347
+ destroyWindow,
3348
+ focusWindow(windowId) {
3349
+ if (!windows.has(windowId)) throw new Error(`G-42: \u672A\u77E5\u7A97\u53E3 ${windowId}`);
3350
+ focused = windowId;
3351
+ emitWindow("window-focused", { windowId });
3352
+ },
3353
+ listWindows() {
3354
+ return [...windows.keys()];
3355
+ },
3356
+ get focusedWindow() {
3357
+ return focused;
3358
+ },
3359
+ createPage(config) {
3360
+ return focusedStack().createPage(config);
3361
+ },
3362
+ mountPage(handle) {
3363
+ return focusedStack().mountPage(handle);
3364
+ },
3365
+ unmountPage(handle) {
3366
+ return focusedStack().unmountPage(handle);
3367
+ },
3368
+ destroyPage(handle) {
3369
+ return focusedStack().destroyPage(handle);
3370
+ },
3371
+ push(config) {
3372
+ return focusedStack().push(config);
3373
+ },
3374
+ pop() {
3375
+ return focusedStack().pop();
3376
+ },
3377
+ getCurrent() {
3378
+ return focusedStack().getCurrent();
3379
+ },
3380
+ getStackDepth() {
3381
+ return focusedStack().getStackDepth();
3382
+ },
3383
+ async createSandbox() {
3384
+ throw new Error("G-42: window \u5BB9\u5668\u65E0\u6C99\u7BB1\uFF08capabilities.multiBusiness=false\u2014\u2014CMP065\uFF09");
3385
+ },
3386
+ async destroySandbox(bizId) {
3387
+ void bizId;
3388
+ return createDestroyReport(bizId);
3389
+ },
3390
+ listSandboxes() {
3391
+ return [];
3392
+ },
3393
+ get quota() {
3394
+ return focusedStack().quota;
3395
+ },
3396
+ onMemoryPressure() {
3397
+ },
3398
+ on(event, handler) {
3399
+ const stack = focusedStack();
3400
+ stack.on(event, handler);
3401
+ },
3402
+ ownershipOf(pageId) {
3403
+ for (const stack of windows.values()) {
3404
+ const own = stack.ownershipOf(pageId);
3405
+ if (own) return own;
3406
+ }
3407
+ return null;
3408
+ },
3409
+ onWindow(event, cb) {
3410
+ windowEvents[event] = windowEvents[event] ?? [];
3411
+ windowEvents[event].push(cb);
3412
+ }
3413
+ };
3414
+ return container;
3415
+ }
3416
+ function createMiniProgramContainer(opts = {}) {
3417
+ const policy = {
3418
+ ...{ maxDepth: 10, overflowStrategy: "reject", keepAlive: { maxCount: 3, memoryBudgetBytes: 64 * 1024 * 1024 } },
3419
+ ...opts.policy
3420
+ };
3421
+ const inner = createStackContainer({
3422
+ policy,
3423
+ quotaLimitBytes: opts.quotaLimitBytes,
3424
+ ownership: opts.ownership
3425
+ });
3426
+ const stack = [];
3427
+ const tabs = /* @__PURE__ */ new Map();
3428
+ const tabPages = /* @__PURE__ */ new Map();
3429
+ const sandboxes = /* @__PURE__ */ new Map();
3430
+ let seq = 0;
3431
+ async function destroyAll(exceptPageId) {
3432
+ for (const p of [...stack]) {
3433
+ if (exceptPageId && p.pageId === exceptPageId) continue;
3434
+ await inner.destroyPage(p);
3435
+ const i = stack.indexOf(p);
3436
+ if (i >= 0) stack.splice(i, 1);
3437
+ }
3438
+ }
3439
+ const container = {
3440
+ id: "miniprogram",
3441
+ version: "1.0.0",
3442
+ capabilities: CONTAINER_PROFILES.miniprogram,
3443
+ async initialize() {
3444
+ },
3445
+ dispose() {
3446
+ stack.length = 0;
3447
+ tabPages.clear();
3448
+ sandboxes.clear();
3449
+ },
3450
+ createPage(config) {
3451
+ return inner.createPage(config);
3452
+ },
3453
+ mountPage(handle) {
3454
+ return inner.mountPage(handle);
3455
+ },
3456
+ unmountPage(handle) {
3457
+ return inner.unmountPage(handle);
3458
+ },
3459
+ destroyPage(handle) {
3460
+ return inner.destroyPage(handle).then((report) => {
3461
+ const i = stack.findIndex((p) => p.pageId === handle.pageId);
3462
+ if (i >= 0) stack.splice(i, 1);
3463
+ return report;
3464
+ });
3465
+ },
3466
+ async push(config) {
3467
+ return container.navigateTo(config);
3468
+ },
3469
+ async pop() {
3470
+ return container.navigateBack();
3471
+ },
3472
+ getCurrent() {
3473
+ return stack[stack.length - 1] ?? null;
3474
+ },
3475
+ getStackDepth() {
3476
+ return stack.length;
3477
+ },
3478
+ async navigateTo(config) {
3479
+ if (stack.length >= policy.maxDepth) {
3480
+ if (policy.overflowStrategy === "destroy-oldest") {
3481
+ const oldest = stack[0];
3482
+ if (!oldest || tabs.has(oldest.pageId)) {
3483
+ throw new Error(`G-42 \u5C0F\u7A0B\u5E8F\u5BFC\u822A\u8D85\u9650\uFF1A\u9875\u9762\u6808\u5DF2\u8FBE ${policy.maxDepth} \u5C42\uFF08\u6808\u5E95\u4E3A tab \u9875\u4E0D\u53EF\u56DE\u6536\uFF09`);
3484
+ }
3485
+ await container.destroyPage(oldest);
3486
+ } else {
3487
+ throw new Error(`G-42 \u5C0F\u7A0B\u5E8F\u5BFC\u822A\u8D85\u9650\uFF1A\u9875\u9762\u6808\u5DF2\u8FBE ${policy.maxDepth} \u5C42\uFF08navigateTo \u62D2\u7EDD\uFF09`);
3488
+ }
3489
+ }
3490
+ const page = inner.createPage(config);
3491
+ await inner.mountPage(page);
3492
+ stack.push(page);
3493
+ return page;
3494
+ },
3495
+ async redirectTo(config) {
3496
+ const top = stack[stack.length - 1];
3497
+ if (top) {
3498
+ await container.destroyPage(top);
3499
+ }
3500
+ return container.navigateTo(config);
3501
+ },
3502
+ async reLaunch(config) {
3503
+ await destroyAll();
3504
+ return container.navigateTo(config);
3505
+ },
3506
+ async switchTab(pageId) {
3507
+ const tab = tabs.get(pageId);
3508
+ if (!tab) throw new Error(`G-42: switchTab \u76EE\u6807\u672A\u6CE8\u518C\u4E3A tab \u9875\uFF1A${pageId}`);
3509
+ const existing = tabPages.get(pageId);
3510
+ const targetIdx = existing ? stack.indexOf(existing) : -1;
3511
+ if (targetIdx >= 0) stack.splice(targetIdx, 1);
3512
+ for (const p of [...stack]) {
3513
+ if (tabs.has(p.pageId)) {
3514
+ await inner.unmountPage(p);
3515
+ } else {
3516
+ await inner.destroyPage(p);
3517
+ }
3518
+ const i = stack.indexOf(p);
3519
+ if (i >= 0) stack.splice(i, 1);
3520
+ }
3521
+ let tabPage = existing;
3522
+ if (tabPage && tabPage.state !== "destroyed") {
3523
+ stack.push(tabPage);
3524
+ if (tabPage.state === "hidden") await inner.mountPage(tabPage);
3525
+ } else {
3526
+ tabPage = inner.createPage({ ...tab, keepAlive: true });
3527
+ tabPages.set(pageId, tabPage);
3528
+ await inner.mountPage(tabPage);
3529
+ stack.push(tabPage);
3530
+ }
3531
+ return tabPage;
3532
+ },
3533
+ async navigateBack() {
3534
+ const top = stack.pop();
3535
+ if (!top) return null;
3536
+ await inner.destroyPage(top);
3537
+ return top;
3538
+ },
3539
+ registerTab(config) {
3540
+ tabs.set(config.pageId, config);
3541
+ },
3542
+ listTabs() {
3543
+ return [...tabs.keys()];
3544
+ },
3545
+ // —— L1 沙箱(scope 隔离——插件/分包业务;崩溃网关是 SuperApp L2 职责)——
3546
+ async createSandbox(bizId, manifest) {
3547
+ void manifest;
3548
+ if (sandboxes.has(bizId)) throw new Error(`G-42: \u6C99\u7BB1\u5DF2\u5B58\u5728 ${bizId}`);
3549
+ const sb = {
3550
+ bizId,
3551
+ isolatedScope: { bizId, values: {} },
3552
+ quotaHandle: null,
3553
+ state: "running",
3554
+ scope: { bizId, values: {} }
3555
+ };
3556
+ sandboxes.set(bizId, sb);
3557
+ return sb;
3558
+ },
3559
+ async destroySandbox(bizId) {
3560
+ const sb = sandboxes.get(bizId);
3561
+ if (sb) {
3562
+ ;
3563
+ sb.state = "destroyed";
3564
+ sandboxes.delete(bizId);
3565
+ }
3566
+ return createDestroyReport(bizId);
3567
+ },
3568
+ listSandboxes() {
3569
+ return [...sandboxes.values()];
3570
+ },
3571
+ quota: inner.quota,
3572
+ onMemoryPressure(cb) {
3573
+ inner.onMemoryPressure(cb);
3574
+ },
3575
+ on(event, handler) {
3576
+ inner.on(event, handler);
3577
+ },
3578
+ ownershipOf(pageId) {
3579
+ return inner.ownershipOf(pageId);
3580
+ }
3581
+ };
3582
+ return container;
3583
+ }
3584
+
3585
+ // src/borrow-checker.ts
3586
+ function tokenizeLines(source) {
3587
+ const out = [];
3588
+ let depth = 0;
3589
+ for (const raw of source.split("\n")) {
3590
+ const text = raw.trim();
3591
+ const opens = (text.match(/\{/g) ?? []).length;
3592
+ const closes = (text.match(/\}/g) ?? []).length;
3593
+ out.push({ num: 0, text, depth });
3594
+ depth += opens - closes;
3595
+ }
3596
+ out.forEach((l, i) => l.num = i + 1);
3597
+ return out;
3598
+ }
3599
+ var DECL_RE = /(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:pageContext|ctx|cp|host|container|this)?\.?(?:alloc|allocShared|createOwned|new Owned)\(/;
3600
+ var BORROW_DECL_RE = /(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*([A-Za-z_$][\w$]*)\.borrow\(/;
3601
+ var CALL_RE = /([A-Za-z_$][\w$]*)\s*\.\s*(transferTo|borrow|drop|read|write|get|use)\s*\(/;
3602
+ var ESCAPE_ASSIGN_RE = /(globalCache|windowStore|moduleScope|eventBus)\??\.?\w*\s*=\s*([A-Za-z_$][\w$]*)/;
3603
+ function closureCaptureLine(text, varName) {
3604
+ return /(setTimeout|setInterval|addEventListener|then|subscribe)\s*\(/.test(text) && text.includes(varName);
3605
+ }
3606
+ function analyzeOwnershipSource(source, opts = {}) {
3607
+ const mode = opts.mode ?? "strict";
3608
+ const diagnostics = [];
3609
+ const vars = /* @__PURE__ */ new Map();
3610
+ const borrowVars = /* @__PURE__ */ new Map();
3611
+ const refEdges = [];
3612
+ const add = (rule, severity, line, message) => {
3613
+ if (mode === "off") return;
3614
+ let sev = severity;
3615
+ if (mode === "loose" && severity === "error" && !["B-01", "B-02", "B-04", "B-05"].includes(rule)) {
3616
+ sev = "warning";
3617
+ }
3618
+ diagnostics.push({ rule, severity: sev, line, message });
3619
+ };
3620
+ for (const line of tokenizeLines(source)) {
3621
+ const txt = line.text;
3622
+ for (const [name, borrow] of borrowVars) {
3623
+ if (closureCaptureLine(txt, name)) {
3624
+ add("B-03", "error", line.num, `G4003: borrow escapes scope\u2014\u2014${name} \u88AB\u95ED\u5305\u6355\u83B7\uFF08source ${borrow.source}\uFF09`);
3625
+ }
3626
+ }
3627
+ const decl = txt.match(DECL_RE);
3628
+ if (decl) {
3629
+ const name = decl[1];
3630
+ vars.set(name, { state: "alive", line: line.num, activeBorrows: 0 });
3631
+ continue;
3632
+ }
3633
+ const bdecl = txt.match(BORROW_DECL_RE);
3634
+ if (bdecl) {
3635
+ borrowVars.set(bdecl[1], { source: bdecl[2], line: line.num });
3636
+ const src = vars.get(bdecl[2]);
3637
+ if (src) src.activeBorrows++;
3638
+ continue;
3639
+ }
3640
+ const call = txt.match(CALL_RE);
3641
+ if (call) {
3642
+ const name = call[1];
3643
+ const op = call[2];
3644
+ const info = vars.get(name);
3645
+ if (!info) continue;
3646
+ if (op === "transferTo") {
3647
+ if (info.state === "moved") {
3648
+ add("B-01", "error", line.num, `G4001: use after move\u2014\u2014${name} \u5DF2\u8F6C\u79FB\uFF0C\u4E0D\u53EF\u518D\u64CD\u4F5C`);
3649
+ } else if (info.state !== "alive") {
3650
+ add("B-02", "error", line.num, `G4002: double move\u2014\u2014${name} \u72B6\u6001 ${info.state} \u4E0D\u53EF\u8F6C\u79FB`);
3651
+ } else if (info.activeBorrows > 0) {
3652
+ add("B-05", "error", line.num, `G4005: \u8F6C\u79FB\u65F6\u6709 ${info.activeBorrows} \u4E2A\u6D3B\u8DC3\u501F\u7528\u2014\u2014\u5148 release`);
3653
+ } else {
3654
+ info.state = "moved";
3655
+ }
3656
+ } else if (op === "borrow") {
3657
+ if (info.state !== "alive") {
3658
+ add("B-01", "error", line.num, `G4001: ${name} \u72B6\u6001 ${info.state} \u4E0D\u53EF\u501F\u7528`);
3659
+ } else {
3660
+ info.activeBorrows++;
3661
+ }
3662
+ } else if (op === "drop") {
3663
+ if (info.state === "alive" && info.activeBorrows > 0 && !/force/.test(txt)) {
3664
+ add("B-05", "error", line.num, `G4005: drop \u65F6 ${name} \u6709 ${info.activeBorrows} \u4E2A\u6D3B\u8DC3\u501F\u7528`);
3665
+ } else if (info.state === "alive") {
3666
+ info.state = "dropped";
3667
+ }
3668
+ } else if (op === "read" || op === "write" || op === "get" || op === "use") {
3669
+ if (info.state === "moved") {
3670
+ add("B-01", "error", line.num, `G4001: use after move\u2014\u2014${name} \u5DF2\u8F6C\u79FB\uFF08\u7B2C ${info.line} \u884C declared\uFF09\uFF0C\u6B64\u5904\u8BBF\u95EE\u975E\u6CD5`);
3671
+ } else if (info.state === "dropped") {
3672
+ add("B-01", "error", line.num, `G4001: use after drop\u2014\u2014${name} \u5DF2\u91CA\u653E\uFF0C\u4E0D\u53EF\u518D\u8BBF\u95EE`);
3673
+ }
3674
+ }
3675
+ continue;
3676
+ }
3677
+ const esc = txt.match(ESCAPE_ASSIGN_RE);
3678
+ if (esc) {
3679
+ const varName = esc[2];
3680
+ if (borrowVars.has(varName)) {
3681
+ add("B-03", "error", line.num, `G4003: borrow escapes scope\u2014\u2014${varName} \u88AB\u5199\u5165\u66F4\u957F\u5BFF\u5BB9\u5668`);
3682
+ } else if (vars.get(varName)?.state === "alive") {
3683
+ add("B-07", "error", line.num, `G4007: \u8DE8\u9875\u9762\u5F3A\u5F15\u7528\u2014\u2014Owned ${varName} \u88AB\u5199\u5165\u8DE8\u9875\u5BB9\u5668\uFF08\u751F\u547D\u5468\u671F\u8D8A\u754C\uFF0C\u5E94 transferTo/weak\uFF09`);
3684
+ }
3685
+ continue;
3686
+ }
3687
+ const storeSet = txt.match(/\b(?:store|stores|globalCache|windowStore|moduleScope|eventBus)\s*\.\s*(?:set|add|push|write)\s*\(\s*[^,()]+,\s*([A-Za-z_$][\w$]*)/);
3688
+ if (storeSet && vars.get(storeSet[1])?.state === "alive") {
3689
+ add("B-07", "error", line.num, `G4007: \u8DE8\u9875\u9762\u5F3A\u5F15\u7528\u2014\u2014Owned ${storeSet[1]} \u88AB\u5B58\u5165\u8DE8\u9875\u5BB9\u5668\uFF08\u751F\u547D\u5468\u671F\u8D8A\u754C\uFF0C\u5E94 transferTo/weak\uFF09`);
3690
+ continue;
3691
+ }
3692
+ for (const [name, borrow] of borrowVars) {
3693
+ if (closureCaptureLine(txt, name)) {
3694
+ add("B-03", "error", line.num, `G4003: borrow escapes scope\u2014\u2014${name} \u88AB\u95ED\u5305\u6355\u83B7\uFF08source ${borrow.source}\uFF09`);
3695
+ }
3696
+ }
3697
+ const assign = txt.match(/^([A-Za-z_$][\w$]*)\s*\.\s*[\w$]+\s*=\s*([A-Za-z_$][\w$]*);?$/);
3698
+ if (assign && assign[1] !== assign[2]) {
3699
+ refEdges.push({ from: assign[1], to: assign[2], line: line.num });
3700
+ }
3701
+ }
3702
+ const ownedVars = [...vars.keys()];
3703
+ for (const [name, info] of vars) {
3704
+ if (info.state === "alive") {
3705
+ add("B-06", mode === "strict" ? "warning" : "warning", info.line, `G4006: owned resource not disposed\u2014\u2014${name} \u65E2\u672A drop \u4E5F\u672A transferTo`);
3706
+ }
3707
+ }
3708
+ for (const e of refEdges) {
3709
+ const back = refEdges.find((r) => r.from === e.to && r.to === e.from);
3710
+ if (back || e.from === e.to) {
3711
+ add("B-08", "warning", e.line, `G4008: \u5FAA\u73AF\u5F15\u7528\u2014\u2014${e.from} \u21C4 ${e.to}\uFF08\u6253\u7834\u5FAA\u73AF\u7528 Weak\uFF09`);
3712
+ }
3713
+ }
3714
+ const blocksBuild = mode === "strict" && diagnostics.some((d) => d.severity === "error");
3715
+ return { diagnostics, blocksBuild, ownedVars };
3716
+ }
3717
+
3718
+ // src/ownership-observability.ts
3719
+ function createOwnershipHistory(graph, opts = {}) {
3720
+ const limit = opts.limit ?? 2e3;
3721
+ let records = [];
3722
+ let enabled = opts.enabled ?? true;
3723
+ let disposed = false;
3724
+ function push(rec) {
3725
+ records.push(rec);
3726
+ if (records.length > limit) records = records.slice(-limit);
3727
+ }
3728
+ const unsub = graph.subscribe((m) => {
3729
+ if (!enabled || disposed) return;
3730
+ if (m.kind === "register") {
3731
+ push({ ts: m.ts, kind: "alloc", id: m.id, owner: m.owner, type: m.type, byteSize: m.byteSize, sourceLocation: m.sourceLocation });
3732
+ } else if (m.kind === "state") {
3733
+ if (m.state === "dropped") {
3734
+ const n = graph.nodes.get(m.id);
3735
+ push({ ts: m.ts, kind: "drop", id: m.id, owner: n?.owner, type: n?.type, byteSize: n?.byteSize, sourceLocation: n?.sourceLocation ?? null });
3736
+ } else if (m.state === "moved") {
3737
+ const n = graph.nodes.get(m.id);
3738
+ push({ ts: m.ts, kind: "moved", id: m.id, owner: n?.owner, type: n?.type, byteSize: n?.byteSize, sourceLocation: n?.sourceLocation ?? null });
3739
+ }
3740
+ } else if (m.kind === "edge") {
3741
+ const k = m.edge.kind;
3742
+ if (k === "owns" || k === "moved-from") return;
3743
+ push({ ts: m.ts, kind: k === "borrows" ? "borrow" : k, from: m.edge.from, to: m.edge.to });
3744
+ }
3745
+ });
3746
+ const history = {
3747
+ get records() {
3748
+ return records;
3749
+ },
3750
+ get enabled() {
3751
+ return enabled;
3752
+ },
3753
+ set enabled(v) {
3754
+ enabled = v;
3755
+ },
3756
+ setEnabled(v) {
3757
+ enabled = v;
3758
+ },
3759
+ clear() {
3760
+ records = [];
3761
+ },
3762
+ dispose() {
3763
+ disposed = true;
3764
+ unsub();
3765
+ records = [];
3766
+ }
3767
+ };
3768
+ return history;
3769
+ }
3770
+ function createOwnershipCounters(graph) {
3771
+ let alive = 0;
3772
+ let total = 0;
3773
+ let bytesAlive = 0;
3774
+ const byType = /* @__PURE__ */ new Map();
3775
+ const bump = (type, delta) => {
3776
+ const c = byType.get(type) ?? { allocated: 0, alive: 0 };
3777
+ c.alive += delta;
3778
+ if (c.alive < 0) c.alive = 0;
3779
+ byType.set(type, c);
3780
+ };
3781
+ for (const n of graph.nodes.values()) {
3782
+ total++;
3783
+ const c = byType.get(n.type) ?? { allocated: 0, alive: 0 };
3784
+ c.allocated++;
3785
+ byType.set(n.type, c);
3786
+ if (n.state === "alive") {
3787
+ alive++;
3788
+ bytesAlive += n.byteSize;
3789
+ c.alive++;
3790
+ }
3791
+ }
3792
+ graph.subscribe((m) => {
3793
+ if (m.kind === "register") {
3794
+ alive++;
3795
+ total++;
3796
+ bytesAlive += m.byteSize;
3797
+ const c = byType.get(m.type) ?? { allocated: 0, alive: 0 };
3798
+ c.allocated++;
3799
+ c.alive++;
3800
+ byType.set(m.type, c);
3801
+ } else if (m.kind === "state") {
3802
+ if (m.state === "dropped" || m.state === "moved") {
3803
+ const n = graph.nodes.get(m.id);
3804
+ alive = Math.max(0, alive - 1);
3805
+ bytesAlive = Math.max(0, bytesAlive - (n?.byteSize ?? 0));
3806
+ bump(n?.type ?? "?", -1);
3807
+ }
3808
+ }
3809
+ });
3810
+ return {
3811
+ get alive() {
3812
+ return alive;
3813
+ },
3814
+ get total() {
3815
+ return total;
3816
+ },
3817
+ get bytesAlive() {
3818
+ return bytesAlive;
3819
+ },
3820
+ get byType() {
3821
+ return Object.fromEntries(byType);
3822
+ },
3823
+ consistent() {
3824
+ const s = graph.stats();
3825
+ return alive === s.alive && total === s.total && bytesAlive === s.totalBytes;
3826
+ }
3827
+ };
3828
+ }
3829
+ function diagnoseOwnershipIssues(graph, opts = {}) {
3830
+ const now = opts.now ? opts.now() : Date.now();
3831
+ const longBorrowMs = opts.longBorrowMs ?? 1e3;
3832
+ const destroyedScopes = opts.destroyedScopes ?? [];
3833
+ const stats = graph.stats();
3834
+ const orphans = graph.findOrphans();
3835
+ const leaks = [];
3836
+ for (const scope of destroyedScopes) {
3837
+ leaks.push(...graph.detectLeaks(scope));
3838
+ }
3839
+ const longBorrows = [];
3840
+ for (const e of graph.edges) {
3841
+ if (e.kind !== "borrows") continue;
3842
+ const sinceMs = e.since ?? 0;
3843
+ const duration = sinceMs > 0 ? now - sinceMs : 0;
3844
+ if (duration > longBorrowMs) {
3845
+ const n = graph.nodes.get(e.to);
3846
+ longBorrows.push({ resourceId: e.to, type: n?.type ?? "?", owner: n?.owner ?? null, borrowedBy: e.from, sinceMs });
3847
+ }
3848
+ }
3849
+ const crossPageRefs = [];
3850
+ for (const e of graph.edges) {
3851
+ if (e.kind !== "strong") continue;
3852
+ const n = graph.nodes.get(e.to);
3853
+ if (!n || n.state !== "alive") continue;
3854
+ if (e.from === n.owner) continue;
3855
+ crossPageRefs.push({ resourceId: e.to, type: n.type, owner: n.owner, heldBy: e.from });
3856
+ }
3857
+ return { aliveNodes: stats.alive, totalNodes: stats.total, orphans, leaks, longBorrows, crossPageRefs };
3858
+ }
3859
+ function buildOwnershipTimeline(history, graph) {
3860
+ const events = [];
3861
+ const allocById = /* @__PURE__ */ new Map();
3862
+ for (const rec of history.records) {
3863
+ if (rec.kind === "alloc" && rec.id) {
3864
+ allocById.set(rec.id, rec);
3865
+ events.push(rec);
3866
+ } else if ((rec.kind === "drop" || rec.kind === "moved") && rec.id) {
3867
+ const matched = allocById.get(rec.id);
3868
+ if (matched) allocById.delete(rec.id);
3869
+ events.push({ ...rec, matchedAlloc: matched });
3870
+ } else {
3871
+ events.push(rec);
3872
+ }
3873
+ }
3874
+ const unpairedAllocs = [...allocById.values()].filter((a) => a.id !== void 0 && graph.nodes.get(a.id)?.state === "alive");
3875
+ return { events, unpairedAllocs };
3876
+ }
3877
+ function formatOwnershipDiagnosis(d, opts = {}) {
3878
+ const src = opts.sourceLabel ?? "\u{1F4CD}";
3879
+ const lines = [];
3880
+ lines.push(`\u6240\u6709\u6743\u56FE\uFF1A${d.aliveNodes} alive / ${d.totalNodes} total`);
3881
+ if (d.orphans.length > 0) {
3882
+ lines.push(`\u{1F534} \u65E0\u4E3B\u8D44\u6E90 ${d.orphans.length} \u5904\uFF08\u5FC5\u7136\u6CC4\u6F0F\u2014\u2014\u6846\u67B6 bug \u6216\u672A\u6B63\u786E\u767B\u8BB0\uFF09\uFF1A`);
3883
+ for (const o of d.orphans) lines.push(` \xB7 ${o.id}\uFF08${o.type}\uFF0C${o.byteSize}B\uFF09${o.sourceLocation ? `${src} ${o.sourceLocation}` : ""}`);
3884
+ }
3885
+ if (d.leaks.length > 0) {
3886
+ lines.push(`\u26A0\uFE0F \u6CC4\u6F0F\u8DEF\u5F84 ${d.leaks.length} \u5904\uFF08\u9875\u9762\u5DF2\u9500\u6BC1\u8D44\u6E90\u4ECD\u5B58\u6D3B\uFF09\uFF1A`);
3887
+ for (const l of d.leaks) {
3888
+ lines.push(` \xB7 ${l.resourceId}\uFF08${l.type}\uFF0C${l.byteSize}B\uFF09${l.sourceLocation ? `${src} ${l.sourceLocation}` : ""}`);
3889
+ for (const hop of l.referenceChain) lines.push(` ${hop}`);
3890
+ }
3891
+ }
3892
+ if (d.longBorrows.length > 0) {
3893
+ lines.push(`\u{1F7E1} \u957F\u671F\u501F\u7528 ${d.longBorrows.length} \u5904\uFF08>\u9608\u503C\u2014\u2014\u501F\u7528\u7684\u8FD0\u884C\u65F6\u9003\u9038\u8868\u73B0\uFF09\uFF1A`);
3894
+ for (const b of d.longBorrows) lines.push(` \xB7 ${b.resourceId} \u2190 borrowed by ${b.borrowedBy}\uFF08owner ${b.owner ?? "\u65E0\u4E3B"}\uFF09`);
3895
+ }
3896
+ if (d.crossPageRefs.length > 0) {
3897
+ lines.push(`\u26A0\uFE0F \u8DE8\u9875\u9762\u5F3A\u5F15\u7528 ${d.crossPageRefs.length} \u5904\uFF08\u9500\u6BC1\u65F6\u65E0\u6CD5\u91CA\u653E\u2014\u2014\u5E94 transferTo/weak\uFF09\uFF1A`);
3898
+ for (const c of d.crossPageRefs) lines.push(` \xB7 ${c.resourceId}\uFF08owner ${c.owner ?? "\u65E0\u4E3B"}\uFF09\u2190 \u5F3A\u6301\u6709 by ${c.heldBy}`);
3899
+ }
3900
+ if (d.orphans.length === 0 && d.leaks.length === 0 && d.longBorrows.length === 0 && d.crossPageRefs.length === 0) {
3901
+ lines.push("\u2705 \u65E0\u5F02\u5E38\uFF08\u65E0\u65E0\u4E3B\u8D44\u6E90/\u6CC4\u6F0F\u8DEF\u5F84/\u957F\u671F\u501F\u7528/\u8DE8\u9875\u5F3A\u5F15\u7528\uFF09");
3902
+ }
3903
+ return lines.join("\n");
3904
+ }
3905
+
3906
+ // src/pss.ts
3907
+ var PSS_PRAGMA_RE = /@proteus-pss\s*[:=]?\s*(strict|loose|off)/i;
3908
+ function resolvePssMode(source, fallback = "off") {
3909
+ const head = source.split("\n", 20).join("\n");
3910
+ const m = head.match(PSS_PRAGMA_RE);
3911
+ return m?.[1]?.toLowerCase() ?? fallback;
3912
+ }
3913
+ var STATIC_PSS_RULES = [
3914
+ // P1 禁 any/unknown(类型不可推导——borrow-checker.md §2.1)
3915
+ { rule: "P1", re: /:\s*(?:any|unknown)\b|\bas\s+(?:any|unknown)\b|<(?:any|unknown)>/, loose: true, message: "G4011: PSS \u7981\u7528 any/unknown\u2014\u2014\u7C7B\u578B\u4E0D\u53EF\u63A8\u5BFC\uFF0C\u6240\u6709\u6743\u5206\u6790\u5931\u6548" },
3916
+ // P2 动态属性写入(`obj[keyVar] =`——字符串字面量键放行;布局不稳定)
3917
+ { rule: "P2", re: /\[[A-Za-z_$][\w$]*\]\s*=\s/, loose: true, message: "G4012: PSS \u7981\u7528\u52A8\u6001\u5C5E\u6027\u5199\u5165\u2014\u2014\u5BF9\u8C61\u5F62\u72B6\u4E0D\u53EF\u9759\u6001\u63A8\u5BFC" },
3918
+ // P3 delete(破坏所有权登记)
3919
+ { rule: "P3", re: /\bdelete\s+[A-Za-z_$][\w$]*/, loose: false, message: "G4013: PSS \u7981\u7528 delete \u64CD\u4F5C\u7B26\u2014\u2014\u7834\u574F\u6240\u6709\u6743\u767B\u8BB0" },
3920
+ // P4 eval / new Function(完全不可分析)
3921
+ { rule: "P4", re: /\beval\s*\(|\bnew\s+Function\s*\(/, loose: false, message: "G4014: PSS \u7981\u7528 eval/new Function\u2014\u2014\u4EE3\u7801\u5B8C\u5168\u4E0D\u53EF\u5206\u6790" },
3922
+ // P8 with(作用域动态)
3923
+ { rule: "P8", re: /\bwith\s*\(/, loose: false, message: "G4018: PSS \u7981\u7528 with \u8BED\u53E5\u2014\u2014\u4F5C\u7528\u57DF\u52A8\u6001\u4E0D\u53EF\u5206\u6790" },
3924
+ // P9 原型链动态修改(破坏类型契约)
3925
+ { rule: "P9", re: /\.\s*prototype\s*\.\s*[\w$]+\s*=\s/, loose: false, message: "G4019: PSS \u7981\u7528\u539F\u578B\u94FE\u52A8\u6001\u4FEE\u6539\u2014\u2014\u7834\u574F\u7C7B\u578B\u5951\u7EA6" }
3926
+ ];
3927
+ function closureCaptureLine2(text, varName) {
3928
+ return /(setTimeout|setInterval|addEventListener|then|subscribe|queueMicrotask)\s*\(/.test(text) && text.includes(varName);
3929
+ }
3930
+ function analyzePss(source, opts = {}) {
3931
+ const mode = opts.mode ?? resolvePssMode(source);
3932
+ const diagnostics = [];
3933
+ if (mode === "off") return { diagnostics, mode };
3934
+ const ownedVars = opts.ownedVars ?? analyzeOwnershipSource(source, { mode }).ownedVars;
3935
+ const ownedSet = new Set(ownedVars);
3936
+ const add = (rule, severity, line, message) => {
3937
+ diagnostics.push({ rule, severity, line, message });
3938
+ };
3939
+ const lines = source.split("\n");
3940
+ lines.forEach((raw, i) => {
3941
+ const txt = raw.trim();
3942
+ const line = i + 1;
3943
+ if (txt.startsWith("//") || txt.startsWith("*") || txt.startsWith("/*")) return;
3944
+ for (const r of STATIC_PSS_RULES) {
3945
+ if (r.re.test(txt) && (mode === "strict" || r.loose)) {
3946
+ add(r.rule, "error", line, r.message);
3947
+ }
3948
+ }
3949
+ const globalAssign = txt.match(/(?:globalThis|window|global|self)\s*\.\s*[\w$]+\s*=\s*([A-Za-z_$][\w$]*)/);
3950
+ if (globalAssign && ownedSet.has(globalAssign[1])) {
3951
+ add("P5", "error", line, `G4015: Owned ${globalAssign[1]} \u9003\u9038\u5230\u5168\u5C40\u2014\u2014\u6240\u6709\u6743\u8131\u79BB\u4F5C\u7528\u57DF\uFF08strict \u7981\u6B62\uFF09`);
3952
+ }
3953
+ for (const name of ownedSet) {
3954
+ if (closureCaptureLine2(txt, name)) {
3955
+ add("P7", "error", line, `G4017: \u95ED\u5305\u6355\u83B7 Owned ${name}\u2014\u2014\u4F5C\u7528\u57DF\u4E0D\u53EF\u63A7\uFF0C\u5E94\u663E\u5F0F transferTo \u6216 drop`);
3956
+ }
3957
+ }
3958
+ const refWrap = txt.match(/\b(?:ref|reactive|shallowRef|readonly)\s*\(\s*([A-Za-z_$][\w$]*)/);
3959
+ if (refWrap && ownedSet.has(refWrap[1])) {
3960
+ add("CMP071", mode === "strict" ? "error" : "warning", line, `CMP071: ${refWrap[1]}\uFF08Owned\uFF09\u88AB ref/reactive \u5305\u88C5\u2014\u2014Proxy \u7834\u574F\u6240\u6709\u6743\u8BED\u4E49\uFF0C\u6539\u7528 useOwned()\uFF08\u54CD\u5E94\u5F0F\u5143\u4FE1\u606F\uFF09`);
3961
+ }
3962
+ });
3963
+ return { diagnostics, mode };
3964
+ }
3965
+ function insertScopeDrops(source) {
3966
+ const rawLines = source.split("\n");
3967
+ const depths = [];
3968
+ let depth = 0;
3969
+ for (const raw of rawLines) {
3970
+ depths.push(depth);
3971
+ const t = raw.trim();
3972
+ depth += (t.match(/\{/g) ?? []).length - (t.match(/\}/g) ?? []).length;
3973
+ }
3974
+ const pending = /* @__PURE__ */ new Set();
3975
+ const insertionsByLine = /* @__PURE__ */ new Map();
3976
+ for (let i = 0; i < rawLines.length; i++) {
3977
+ const t = rawLines[i].trim();
3978
+ const d = depths[i];
3979
+ const line = i + 1;
3980
+ const decl = t.match(/(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:pageContext|ctx|cp|host|container|this)?\.?(?:alloc|allocShared|createOwned|new Owned)\(/);
3981
+ if (decl) {
3982
+ if (d > 0) {
3983
+ const indent = rawLines[i].match(/^\s*/)?.[0] ?? "";
3984
+ pending.add({ varName: decl[1], declDepth: d, declLine: line, indent, disposed: false });
3985
+ }
3986
+ continue;
3987
+ }
3988
+ const call = t.match(/([A-Za-z_$][\w$]*)\s*\.\s*(?:drop|transferTo)\s*\(/);
3989
+ if (call) {
3990
+ for (const p of pending) {
3991
+ if (p.varName === call[1] && !p.disposed) {
3992
+ p.disposed = true;
3993
+ break;
3994
+ }
3995
+ }
3996
+ }
3997
+ if (t.includes("}")) {
3998
+ for (const p of [...pending]) {
3999
+ if (p.declDepth === d) {
4000
+ if (!p.disposed) {
4001
+ const list = insertionsByLine.get(line) ?? [];
4002
+ list.push(p);
4003
+ insertionsByLine.set(line, list);
4004
+ }
4005
+ pending.delete(p);
4006
+ }
4007
+ }
4008
+ }
4009
+ }
4010
+ const insertions = [];
4011
+ const outLines = [...rawLines];
4012
+ const closeLines = [...insertionsByLine.keys()].sort((a, b) => b - a);
4013
+ for (const closeLine of closeLines) {
4014
+ const items = insertionsByLine.get(closeLine);
4015
+ const stmts = items.map((p) => {
4016
+ insertions.push({ varName: p.varName, line: closeLine });
4017
+ return `${p.indent} ${p.varName}.drop()`;
4018
+ });
4019
+ outLines.splice(closeLine - 1, 0, ...stmts);
4020
+ }
4021
+ return { code: outLines.join("\n"), insertions };
4022
+ }
4023
+ function runPss(source, opts = {}) {
4024
+ const mode = resolvePssMode(source, opts.mode ?? "off");
4025
+ const autoDrop = opts.autoDrop ?? true;
4026
+ if (mode === "off") {
4027
+ return { mode, diagnostics: [], blocksBuild: false, ownedVars: [], code: source, insertions: [] };
4028
+ }
4029
+ const borrow = analyzeOwnershipSource(source, { mode });
4030
+ const pss = analyzePss(source, { mode, ownedVars: borrow.ownedVars });
4031
+ const diagnostics = [...borrow.diagnostics, ...pss.diagnostics];
4032
+ let code = source;
4033
+ let insertions = [];
4034
+ if (autoDrop) {
4035
+ const r = insertScopeDrops(source);
4036
+ code = r.code;
4037
+ insertions = r.insertions;
4038
+ }
4039
+ const blocksBuild = mode === "strict" && diagnostics.some((d) => d.severity === "error");
4040
+ return { mode, diagnostics, blocksBuild, ownedVars: borrow.ownedVars, code, insertions };
4041
+ }
4042
+
4043
+ // src/host-matrix.ts
4044
+ var HOSTS = ["web", "ios", "android", "harmony", "flutter", "miniprogram"];
4045
+ var ENGINES = ["vue-dom", "headless", "native-ios", "native-android", "native-harmony", "flutter"];
4046
+ var HOST_ENGINE_MATRIX = {
4047
+ web: { "vue-dom": 1, "headless": 1, "flutter": 3, "native-ios": 0, "native-android": 0, "native-harmony": 0 },
4048
+ ios: { "vue-dom": 3, "headless": 1, "flutter": 1, "native-ios": 1, "native-android": 0, "native-harmony": 0 },
4049
+ android: { "vue-dom": 3, "headless": 1, "flutter": 1, "native-ios": 0, "native-android": 1, "native-harmony": 0 },
4050
+ harmony: { "vue-dom": 3, "headless": 1, "flutter": 0, "native-ios": 0, "native-android": 0, "native-harmony": 1 },
4051
+ flutter: { "vue-dom": 0, "headless": 1, "flutter": 1, "native-ios": 3, "native-android": 3, "native-harmony": 0 },
4052
+ miniprogram: { "vue-dom": 0, "headless": 1, "flutter": 0, "native-ios": 0, "native-android": 0, "native-harmony": 0 }
4053
+ };
4054
+ function matrixCombos(filter = {}) {
4055
+ const out = [];
4056
+ for (const host of HOSTS) {
4057
+ for (const engine of ENGINES) {
4058
+ const tier = HOST_ENGINE_MATRIX[host][engine];
4059
+ if (filter.tier === void 0 || tier === filter.tier) out.push({ host, engine, tier });
4060
+ }
4061
+ }
4062
+ return out;
4063
+ }
4064
+ function createEngine(engine, documentLike) {
4065
+ switch (engine) {
4066
+ case "vue-dom":
4067
+ return createVueDomBackend(documentLike);
4068
+ case "headless":
4069
+ return createHeadlessBackend();
4070
+ case "native-ios":
4071
+ return createNativeBackend(void 0, "ios");
4072
+ case "native-android":
4073
+ return createNativeBackend(void 0, "android");
4074
+ case "native-harmony":
4075
+ return createNativeBackend(void 0, "harmony");
4076
+ case "flutter":
4077
+ return createFlutterBackend();
4078
+ }
4079
+ }
4080
+ var GRID_CONTROL = {
4081
+ "vue-dom": { attr: "class", value: "proteus-grid" },
4082
+ "headless": { attr: "type", value: "grid" },
4083
+ "native-ios": { attr: "type", value: "UICollectionView" },
4084
+ "native-android": { attr: "type", value: "GridLayoutManager" },
4085
+ "native-harmony": { attr: "type", value: "Grid" },
4086
+ "flutter": { attr: "widget", value: "GridView" }
4087
+ };
4088
+ function secondEngineOf(engine) {
4089
+ return engine === "flutter" ? "headless" : "flutter";
4090
+ }
4091
+ function countIR(ir) {
4092
+ return 1 + ir.children.reduce((s, c) => s + countIR(c), 0);
4093
+ }
4094
+ function walkNodes(node, visit) {
4095
+ const n = node;
4096
+ visit(n);
4097
+ let count = 1;
4098
+ const children = n.children;
4099
+ if (Array.isArray(children)) {
4100
+ for (const c of children) count += walkNodes(c, visit);
4101
+ } else if (children && typeof children.length === "number") {
4102
+ const list = children;
4103
+ for (let i = 0; i < list.length; i++) count += walkNodes(list[i], visit);
4104
+ }
4105
+ return count;
4106
+ }
4107
+ function buildProductIR2(dispatch) {
4108
+ const grid = dispatch.toIRNode("p-grid", { minColWidth: 160 });
4109
+ const box1 = dispatch.toIRNode("p-box", {});
4110
+ const box2 = dispatch.toIRNode("p-box", {});
4111
+ const text = dispatch.toIRNode("p-text", { content: "\u5546\u54C1 A" });
4112
+ const button = dispatch.toIRNode("p-button", { variant: "primary" });
4113
+ return {
4114
+ ...dispatch.toIRNode("p-page", { title: "Product" }),
4115
+ children: [{ ...grid, children: [{ ...box1, children: [text] }, { ...box2, children: [button] }] }]
4116
+ };
4117
+ }
4118
+ async function runComboConformance(opts) {
4119
+ const { host, engine } = opts;
4120
+ const checks = [];
4121
+ const add = (id, ok, message) => {
4122
+ checks.push({ id, ok, message: ok ? void 0 : message });
4123
+ };
4124
+ const backend = createEngine(engine, opts.documentLike);
4125
+ const backendConf = runBackendConformance(backend);
4126
+ add("backend-conformance", backendConf.ok, `\u5F15\u64CE\u81EA\u8EAB conformance \u672A\u8FC7\uFF08${backendConf.checks.filter((c) => !c.pass).length} \u9879\uFF09`);
4127
+ const dispatch = createNodeOpsDispatcher(backend);
4128
+ const carrier = createCarrierStub("jsi");
4129
+ const hostRuntime = host === "web" ? createWebHostRuntime() : createHostRuntimeStub();
4130
+ add("registration-before-bootstrap", dispatch.currentBackend === backend && hostRuntime.state === "created", "Backend \u5E94\u5728 bootstrap \u524D\u6CE8\u518C\uFF08G-41.6\uFF09");
4131
+ hostRuntime.bootstrap();
4132
+ add("host-bootstrap", hostRuntime.state === "running", "\u5BBF\u4E3B bootstrap \u540E\u5E94 running");
4133
+ const ir = buildProductIR2(dispatch);
4134
+ const fingerprint = semanticSequence(ir);
4135
+ add("semantic-fingerprint", JSON.stringify(fingerprint) === JSON.stringify(["shell.page", "layout.grid", "layout.box", "ui.text", "layout.box", "ui.button"]), `\u8BED\u4E49\u6307\u7EB9\u4E0D\u7B26\uFF1A${fingerprint.join(",")}`);
4136
+ const root = renderIRTree(backend, ir);
4137
+ const irCount = countIR(ir);
4138
+ const engineCount = walkNodes(root, () => {
4139
+ });
4140
+ add("render-complete", engineCount === irCount, `\u5F15\u64CE\u8282\u70B9\u6570 ${engineCount} \u2260 IR \u8282\u70B9\u6570 ${irCount}`);
4141
+ const ctrl = GRID_CONTROL[engine];
4142
+ let gridFound = false;
4143
+ walkNodes(root, (n) => {
4144
+ if (ctrl.attr === "class") {
4145
+ const cls = String(n.className ?? "");
4146
+ if (cls.includes(ctrl.value)) gridFound = true;
4147
+ } else if (n[ctrl.attr] === ctrl.value) {
4148
+ gridFound = true;
4149
+ }
4150
+ });
4151
+ add("semantic-control-mapping", gridFound, `layout.grid \u672A\u6620\u5C04\u5230 ${ctrl.value}\uFF08${engine}\uFF09`);
4152
+ const second = secondEngineOf(engine);
4153
+ const backend2 = createEngine(second, opts.documentLike);
4154
+ dispatch.switchBackend(backend2);
4155
+ const root2 = renderIRTree(backend2, ir);
4156
+ const count2 = walkNodes(root2, () => {
4157
+ });
4158
+ const ctrl2 = GRID_CONTROL[second];
4159
+ let grid2 = false;
4160
+ walkNodes(root2, (n) => {
4161
+ if (ctrl2.attr === "class") {
4162
+ if (String(n.className ?? "").includes(ctrl2.value)) grid2 = true;
4163
+ } else if (n[ctrl2.attr] === ctrl2.value) {
4164
+ grid2 = true;
4165
+ }
4166
+ });
4167
+ add(
4168
+ "hot-switch-equivalence",
4169
+ dispatch.currentBackend === backend2 && count2 === irCount && grid2 && semanticSequence(ir).length === fingerprint.length,
4170
+ `\u5207\u6362\u5230 ${second} \u540E\u6E32\u67D3\u4E0D\u7B49\u4EF7\uFF08count ${count2}/${irCount}\uFF0Cgrid ${grid2}\uFF09`
4171
+ );
4172
+ const failed = checks.filter((c) => !c.ok).length;
4173
+ return { host, engine, ok: failed === 0, total: checks.length, failed, checks };
4174
+ }
4175
+ async function runHostEngineMatrix(opts = {}) {
4176
+ const rows = [];
4177
+ for (const combo of matrixCombos()) {
4178
+ if (opts.only && (opts.only.host && opts.only.host !== combo.host || opts.only.engine && opts.only.engine !== combo.engine)) {
4179
+ continue;
4180
+ }
4181
+ if (combo.tier === 1) {
4182
+ const result = await runComboConformance({ host: combo.host, engine: combo.engine, documentLike: opts.documentLike });
4183
+ rows.push({ host: combo.host, engine: combo.engine, tier: combo.tier, result });
4184
+ } else {
4185
+ rows.push({ host: combo.host, engine: combo.engine, tier: combo.tier, result: null });
4186
+ }
4187
+ }
4188
+ const tier1Rows = rows.filter((r) => r.tier === 1);
4189
+ return {
4190
+ totalCombos: rows.length,
4191
+ tier1Combos: tier1Rows.length,
4192
+ verified: tier1Rows.filter((r) => r.result?.ok).length,
4193
+ failed: tier1Rows.reduce((s, r) => s + (r.result?.failed ?? 0), 0),
4194
+ rows
4195
+ };
4196
+ }
4197
+ function formatMatrixReport(report) {
4198
+ const lines = [];
4199
+ lines.push(`\u5BBF\u4E3B\xD7\u5F15\u64CE\u77E9\u9635\uFF1A${report.totalCombos} \u7EC4\u5408 / Tier 1 ${report.tier1Combos} / \u5DF2\u9A8C\u8BC1 ${report.verified} / \u5931\u8D25 ${report.failed}`);
4200
+ for (const row of report.rows) {
4201
+ if (row.tier === 1 && row.result) {
4202
+ lines.push(` ${row.result.ok ? "\u2705" : "\u274C"} T1 ${row.host} \xD7 ${row.engine}\uFF08${row.result.total - row.result.failed}/${row.result.total}\uFF09`);
4203
+ } else if (row.tier === 3) {
4204
+ lines.push(` \u26AA T3 ${row.host} \xD7 ${row.engine}\uFF08\u6DF7\u5165\u53EF\u884C\uFF0C\u4E0D\u627F\u8BFA\uFF09`);
4205
+ } else {
4206
+ lines.push(` \u2B1B T0 ${row.host} \xD7 ${row.engine}\uFF08\u8DE8\u751F\u6001\u4E0D\u5408\u6CD5\uFF09`);
4207
+ }
4208
+ }
4209
+ return lines.join("\n");
4210
+ }
4211
+ export {
4212
+ Borrow,
4213
+ CONTAINER_PROFILES,
4214
+ DEFAULT_STACK_POLICY,
4215
+ DispatcherError,
4216
+ ENGINES,
4217
+ FIVE_ATOMIC_STEPS,
4218
+ FORK_SIGNATURES,
4219
+ HOSTS,
4220
+ HOST_ENGINE_MATRIX,
4221
+ Managed,
4222
+ ManagedRegistry,
4223
+ OWNERSHIP_ERRORS,
4224
+ Owned,
4225
+ OwnershipError,
4226
+ OwnershipGraph,
4227
+ PAGE_STATE_TRANSITIONS,
4228
+ SENSITIVE_CAPABILITIES,
4229
+ Weak,
4230
+ analyzeOwnershipSource,
4231
+ analyzePss,
4232
+ assertAtomicDestroy,
4233
+ buildOwnershipTimeline,
4234
+ canTransitionPageState,
4235
+ checkBizManifest,
4236
+ checkCapabilityAuthorization,
4237
+ createBackendSwitcher,
4238
+ createCarrierStub,
4239
+ createControlReader,
4240
+ createDestroyReport,
4241
+ createEmbeddedContainer,
4242
+ createEngine,
4243
+ createFlutterBackend,
4244
+ createHeadlessBackend,
4245
+ createHostRuntimeStub,
4246
+ createHybridRenderer,
4247
+ createMiniProgramContainer,
4248
+ createMockNativeAdapter,
4249
+ createNativeBackend,
4250
+ createNodeOpsDispatcher,
4251
+ createOwnershipCounters,
4252
+ createOwnershipHistory,
4253
+ createPageOwnership,
4254
+ createProteusRenderer,
4255
+ createProteusRendererForBackend,
4256
+ createQuotaManager,
4257
+ createQuotaTracker,
4258
+ createResourcePool,
4259
+ createSinglePageContainer,
4260
+ createStackContainer,
4261
+ createSuperAppContainer,
4262
+ createVueDomBackend,
4263
+ createVueRendererOptions,
4264
+ createWebHostRuntime,
4265
+ createWindowContainer,
4266
+ diagnoseOwnershipIssues,
4267
+ formatContainerConformance,
4268
+ formatHostConformance,
4269
+ formatMatrixReport,
4270
+ formatOwnershipDiagnosis,
4271
+ getProteusOwnershipGraph,
4272
+ insertScopeDrops,
4273
+ mapWidgetType,
4274
+ matrixCombos,
4275
+ profileOfContainer,
4276
+ renderComponentSnapshot,
4277
+ renderIRTree,
4278
+ resolvePssMode,
4279
+ runBackendConformance,
4280
+ runComboConformance,
4281
+ runContainerConformance,
4282
+ runHostConformance,
4283
+ runHostEngineMatrix,
4284
+ runHybridConformance,
4285
+ runPss,
4286
+ scanRepoForFork,
4287
+ semanticSequence,
4288
+ textureRef,
4289
+ toPlainTree,
4290
+ toWidgetTree
4291
+ };