@cfp-studio/iframe 18.4.1 → 18.4.2
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.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.
|
|
1
|
+
//#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.1_13c54df9746541450cb8a36e10ae9cdd/node_modules/vite/types/hmrPayload.d.ts
|
|
2
2
|
interface UpdatePayload {
|
|
3
3
|
type: 'update';
|
|
4
4
|
updates: Update[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cfp-studio/iframe",
|
|
3
|
-
"version": "18.4.
|
|
3
|
+
"version": "18.4.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"assert-never": "1.4.0",
|
|
17
17
|
"immer": "10.1.1",
|
|
18
18
|
"typescript": "5.9.3",
|
|
19
|
-
"vite": "6.4.5"
|
|
19
|
+
"vite": "6.4.5",
|
|
20
|
+
"vitest": "5.0.5"
|
|
20
21
|
},
|
|
21
22
|
"engines": {
|
|
22
23
|
"node": ">=20"
|
|
@@ -28,6 +29,8 @@
|
|
|
28
29
|
"scripts": {
|
|
29
30
|
"build": "tsdown",
|
|
30
31
|
"typecheck": "tsc --noEmit",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest watch",
|
|
31
34
|
"release": "tsdown --minify",
|
|
32
35
|
"lint": "oxlint",
|
|
33
36
|
"format": "oxfmt --write ."
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { postToParent, type PostParentTarget } from "./runtime-bridge";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `postToParent` 的单播契约 —— 「点一次生成两个 packet」的回归(`docs/todo/32`)。
|
|
7
|
+
*
|
|
8
|
+
* ## 曾经的 bug
|
|
9
|
+
*
|
|
10
|
+
* iframe 用「向上遍历 5 层祖先、每层发两种载荷(对象 + JSON 字符串)」送消息。
|
|
11
|
+
* 后来 webview shell 增加了转发(`usePreviewSession` 的 `onWindowMessage`
|
|
12
|
+
* 把 `kind: "host"` 的消息转给宿主),于是:
|
|
13
|
+
*
|
|
14
|
+
* ```
|
|
15
|
+
* iframe → 逐层广播
|
|
16
|
+
* ├→ shell(收到 → 转发给宿主)
|
|
17
|
+
* └→ 宿主 window(也直接收到)
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* 宿主把同一条消息处理两次 → 点一次 📋 写两份 packet(`selectionId` 只差末位)。
|
|
21
|
+
*
|
|
22
|
+
* ## 现在的契约
|
|
23
|
+
*
|
|
24
|
+
* 只发给**直接父窗口**,只发**一次**,且**只发对象**(不发 JSON 字符串 ——
|
|
25
|
+
* 字符串会被路由成 `{ kind: "iframe" }` 再投回 iframe,是无意义的回声)。
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/** 记录所有到某个「窗口」的投递。 */
|
|
29
|
+
function fakeWindow(): { posts: unknown[]; postMessage: (m: unknown, o: string) => void } {
|
|
30
|
+
const posts: unknown[] = [];
|
|
31
|
+
return {
|
|
32
|
+
posts,
|
|
33
|
+
postMessage(message: unknown) {
|
|
34
|
+
posts.push(message);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe("postToParent", () => {
|
|
40
|
+
test("只发一次,且只发给直接父窗口", () => {
|
|
41
|
+
const parent = fakeWindow();
|
|
42
|
+
// 顶部再加一层祖父:旧实现会连它也发。
|
|
43
|
+
const grandparent = fakeWindow();
|
|
44
|
+
(parent as unknown as { parent: unknown }).parent = grandparent;
|
|
45
|
+
|
|
46
|
+
const source = { parent } as unknown as PostParentTarget;
|
|
47
|
+
const message = { kind: "cfp-selection-action", action: "copy-context", runtimeNodeId: "dom:root/0" };
|
|
48
|
+
|
|
49
|
+
expect(postToParent(message, source)).toBe(1);
|
|
50
|
+
expect(parent.posts).toEqual([message]);
|
|
51
|
+
// 关键断言:不能广播到祖父层,否则宿主会重复处理。
|
|
52
|
+
expect(grandparent.posts).toEqual([]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("只发对象,不发 JSON 字符串", () => {
|
|
56
|
+
const parent = fakeWindow();
|
|
57
|
+
const message = { kind: "cfp-runtime-snapshot", snapshot: {} };
|
|
58
|
+
postToParent(message, { parent } as unknown as PostParentTarget);
|
|
59
|
+
|
|
60
|
+
expect(parent.posts).toHaveLength(1);
|
|
61
|
+
expect(typeof parent.posts[0]).toBe("object");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("没有父窗口时返回 0 且不抛", () => {
|
|
65
|
+
expect(() => postToParent({ kind: "x" }, { parent: undefined as never })).not.toThrow();
|
|
66
|
+
expect(postToParent({ kind: "x" }, { parent: undefined as never })).toBe(0);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("父窗口 postMessage 抛错时返回 0", () => {
|
|
70
|
+
const hostile = {
|
|
71
|
+
parent: {
|
|
72
|
+
postMessage() {
|
|
73
|
+
throw new Error("detached");
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
} as unknown as PostParentTarget;
|
|
77
|
+
expect(postToParent({ kind: "x" }, hostile)).toBe(0);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -386,37 +386,35 @@ function publishToHost(message: unknown): void {
|
|
|
386
386
|
if (!window.parent || window.parent === window) {
|
|
387
387
|
return;
|
|
388
388
|
}
|
|
389
|
-
|
|
389
|
+
postToParent(message);
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
392
|
+
/**
|
|
393
|
+
* 只发给**直接父窗口**(webview shell),由 shell 负责转发给宿主。
|
|
394
|
+
*
|
|
395
|
+
* 为什么不再逐层广播:early 版本确实向上遍历了 5 层,当时 shell 不转发消息;
|
|
396
|
+
* 现在 shell(`usePreviewSession` 的 `onWindowMessage`)会把 `kind: "host"` 的消息
|
|
397
|
+
* 转给宿主 —— 于是「逐层广播 + shell 转发」让宿主**收到同一条消息两次**
|
|
398
|
+
* (多点一次 = 多写一份 packet,`selectionId` 只差最后一位)。
|
|
399
|
+
*
|
|
400
|
+
* 只发对象、不发 JSON 字符串:shell 与宿主都按对象处理;字符串分支会被路由成
|
|
401
|
+
* `{ kind: "iframe" }` 再投回 iframe,属于无意义的回声。
|
|
402
|
+
*/
|
|
403
|
+
export interface PostParentTarget {
|
|
404
|
+
parent: { postMessage(message: unknown, targetOrigin: string): void };
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export function postToParent(message: unknown, source?: PostParentTarget): number {
|
|
408
|
+
const from = source ?? (typeof window === "undefined" ? undefined : (window as unknown as PostParentTarget));
|
|
409
|
+
if (!from?.parent) {
|
|
410
|
+
return 0;
|
|
411
|
+
}
|
|
412
|
+
try {
|
|
413
|
+
from.parent.postMessage(message, "*");
|
|
414
|
+
return 1;
|
|
415
|
+
} catch {
|
|
416
|
+
return 0;
|
|
418
417
|
}
|
|
419
|
-
return posted;
|
|
420
418
|
}
|
|
421
419
|
|
|
422
420
|
function collectRuntimeSnapshot(): RuntimeSnapshot {
|