@aalis/api-webui 0.11.0 → 0.12.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/frame/frame.css +218 -0
- package/dist/frame/frame.js +100 -0
- package/dist/frame.d.ts +165 -0
- package/dist/frame.d.ts.map +1 -0
- package/dist/frame.js +121 -0
- package/dist/frame.js.map +1 -0
- package/dist/index.d.ts +127 -208
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -15
- package/dist/index.js.map +1 -1
- package/dist/page.d.ts +423 -0
- package/dist/page.d.ts.map +1 -0
- package/dist/page.js +28 -0
- package/dist/page.js.map +1 -0
- package/dist/rest.d.ts +585 -0
- package/dist/rest.d.ts.map +1 -0
- package/dist/rest.js +12 -0
- package/dist/rest.js.map +1 -0
- package/dist/theme.d.ts +469 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +280 -0
- package/dist/theme.js.map +1 -0
- package/dist/themes/aalis-dark.json +50 -0
- package/dist/themes/aalis-light.json +50 -0
- package/dist/themes/brand-dark.json +50 -0
- package/dist/themes/brand-light.json +50 -0
- package/dist/themes/calm-dark.json +50 -0
- package/dist/themes/calm-light.json +50 -0
- package/dist/themes/neutral-dark.json +50 -0
- package/dist/themes/neutral-light.json +50 -0
- package/package.json +13 -6
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 插件前端的样式基线,托管为 /frames/_base/frame.css,插件前端可选引用:
|
|
3
|
+
* <link rel="stylesheet" href="/frames/_base/frame.css">
|
|
4
|
+
* 只给元素级的样式,不设按类名选的变体。颜色只引用主题令牌(助手收到 init 时写在根元素上,主题变了跟着更新);
|
|
5
|
+
* 尺寸写在本文件里,取值与宿主界面相同,触屏与减少动效由本文件自己的媒体查询求值。圆角用 --radius-sm、--radius-md
|
|
6
|
+
* 两个名字写默认值,宿主以后推入的值写在根元素的内联样式上,盖过这里的默认值。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
*,
|
|
10
|
+
*::before,
|
|
11
|
+
*::after {
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
:root {
|
|
16
|
+
--radius-sm: 4px;
|
|
17
|
+
--radius-md: 6px;
|
|
18
|
+
font-family:
|
|
19
|
+
system-ui, -apple-system, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC",
|
|
20
|
+
sans-serif;
|
|
21
|
+
font-size: 100%;
|
|
22
|
+
line-height: 1.375rem;
|
|
23
|
+
color: var(--color-text);
|
|
24
|
+
-webkit-text-size-adjust: 100%;
|
|
25
|
+
text-size-adjust: 100%;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
body {
|
|
29
|
+
margin: 0;
|
|
30
|
+
padding: 1rem;
|
|
31
|
+
font-size: 0.875rem;
|
|
32
|
+
background: var(--color-bg);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
h1,
|
|
36
|
+
h2,
|
|
37
|
+
h3 {
|
|
38
|
+
margin: 0 0 0.5rem;
|
|
39
|
+
font-size: 1rem;
|
|
40
|
+
font-weight: 600;
|
|
41
|
+
line-height: 1.5rem;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
p,
|
|
45
|
+
ul,
|
|
46
|
+
ol,
|
|
47
|
+
table,
|
|
48
|
+
pre {
|
|
49
|
+
margin: 0 0 0.75rem;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
ul,
|
|
53
|
+
ol {
|
|
54
|
+
padding-left: 1.5rem;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
li + li {
|
|
58
|
+
margin-top: 0.25rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
a {
|
|
62
|
+
color: var(--color-accent-text);
|
|
63
|
+
text-decoration: underline 1px;
|
|
64
|
+
text-underline-offset: 2px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* 焦点环用 outline:Windows 高对比度模式会去掉阴影 */
|
|
68
|
+
:focus-visible {
|
|
69
|
+
outline: 2px solid var(--color-focus);
|
|
70
|
+
outline-offset: 2px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
button,
|
|
74
|
+
input,
|
|
75
|
+
select,
|
|
76
|
+
textarea {
|
|
77
|
+
font: inherit;
|
|
78
|
+
color: inherit;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* 按钮只有一种,与宿主的次要按钮一致 */
|
|
82
|
+
button {
|
|
83
|
+
min-width: 1.75rem;
|
|
84
|
+
min-height: 1.75rem;
|
|
85
|
+
padding: 0 0.5rem;
|
|
86
|
+
border: 0;
|
|
87
|
+
border-radius: var(--radius-sm);
|
|
88
|
+
font-size: 0.8125rem;
|
|
89
|
+
font-weight: 400;
|
|
90
|
+
line-height: 1.25rem;
|
|
91
|
+
background: var(--color-fill-2);
|
|
92
|
+
cursor: pointer;
|
|
93
|
+
transition: background-color 100ms cubic-bezier(0.2, 0, 0, 1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* 只写底色:简写 background 会复位 background-clip,触屏上底色就铺进透明边框(见文件末尾) */
|
|
97
|
+
button:hover:not(:disabled) {
|
|
98
|
+
background-color: var(--color-fill-3);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
button:disabled {
|
|
102
|
+
color: var(--color-text-disabled);
|
|
103
|
+
cursor: default;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
input:not([type="checkbox"]):not([type="radio"]),
|
|
107
|
+
select,
|
|
108
|
+
textarea {
|
|
109
|
+
min-height: 1.75rem;
|
|
110
|
+
padding: 0 0.75rem;
|
|
111
|
+
font-size: 0.875rem;
|
|
112
|
+
background: transparent;
|
|
113
|
+
border: 1px solid var(--color-border-control);
|
|
114
|
+
border-radius: var(--radius-sm);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
textarea {
|
|
118
|
+
padding: 0.5rem 0.75rem;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
input:focus-visible,
|
|
122
|
+
select:focus-visible,
|
|
123
|
+
textarea:focus-visible {
|
|
124
|
+
outline-offset: 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
::placeholder {
|
|
128
|
+
color: var(--color-text-3);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
input[type="checkbox"],
|
|
132
|
+
input[type="radio"] {
|
|
133
|
+
accent-color: var(--color-accent);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
table {
|
|
137
|
+
width: 100%;
|
|
138
|
+
border-collapse: collapse;
|
|
139
|
+
font-size: 0.8125rem;
|
|
140
|
+
line-height: 1.25rem;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
th,
|
|
144
|
+
td {
|
|
145
|
+
height: 2.25rem;
|
|
146
|
+
padding: 0 0.75rem;
|
|
147
|
+
border-bottom: 1px solid var(--color-border);
|
|
148
|
+
text-align: left;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
th {
|
|
152
|
+
height: 2rem;
|
|
153
|
+
font-size: 0.75rem;
|
|
154
|
+
font-weight: 500;
|
|
155
|
+
color: var(--color-text-2);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
code,
|
|
159
|
+
kbd,
|
|
160
|
+
pre {
|
|
161
|
+
font-family: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Mono", Menlo, Consolas, monospace;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
:not(pre) > code {
|
|
165
|
+
padding: 0 0.25rem;
|
|
166
|
+
border-radius: var(--radius-sm);
|
|
167
|
+
font-size: 0.9em;
|
|
168
|
+
background: var(--color-fill-2);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
pre {
|
|
172
|
+
padding: 0.75rem;
|
|
173
|
+
overflow-x: auto;
|
|
174
|
+
border-radius: var(--radius-md);
|
|
175
|
+
font-size: 0.8125rem;
|
|
176
|
+
line-height: 1.25rem;
|
|
177
|
+
background: var(--color-fill-1);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/*
|
|
181
|
+
* 触屏:点按范围 44 像素。按钮外面一圈 0.25rem 的透明边框占排版位置,底色只画到边框以内,看得见的是 36 像素;
|
|
182
|
+
* 圆角加上边框宽,里面的角仍是 --radius-sm;焦点环往回收到看得见的框外 2 像素。
|
|
183
|
+
* 输入框 44 像素高,字号不小于 16 像素,免得 iOS 聚焦时放大整页
|
|
184
|
+
*/
|
|
185
|
+
@media (pointer: coarse) {
|
|
186
|
+
button {
|
|
187
|
+
min-width: 2.75rem;
|
|
188
|
+
min-height: 2.75rem;
|
|
189
|
+
border: 0.25rem solid transparent;
|
|
190
|
+
border-radius: calc(var(--radius-sm) + 0.25rem);
|
|
191
|
+
background-clip: padding-box;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
button:focus-visible {
|
|
195
|
+
outline-offset: -2px;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
input:not([type="checkbox"]):not([type="radio"]),
|
|
199
|
+
select {
|
|
200
|
+
min-height: 2.75rem;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
input:not([type="checkbox"]):not([type="radio"]),
|
|
204
|
+
select,
|
|
205
|
+
textarea {
|
|
206
|
+
font-size: 1rem;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
td {
|
|
210
|
+
height: 3rem;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
@media (prefers-reduced-motion: reduce) {
|
|
215
|
+
button {
|
|
216
|
+
transition: none;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// 插件前端的助手,托管为 /frames/_base/frame.js。片段里的 nonce 原样读出、不清:清片段会往会话历史里加条目,
|
|
2
|
+
// nonce 对框架自己本来也不保密。同一页上的框架都是不透明来源、能互相发窗口消息,但只有父文档发得出来源是
|
|
3
|
+
// window.parent 的消息,所以只认来自它、带端口的第一个 init,之后只用这个端口。主题令牌在收到 init 的同一拍写上,
|
|
4
|
+
// 框架显示出来时已是宿主的配色
|
|
5
|
+
/** 与 api-webui 的 FRAME_PROTOCOL_VERSION 同值:助手随 api-webui 同版本发布 */
|
|
6
|
+
const PROTOCOL = '1.0';
|
|
7
|
+
/** 发出 hello 之后等 init 的时长 */
|
|
8
|
+
const INIT_TIMEOUT_MS = 10_000;
|
|
9
|
+
function applyTheme({ kind, tokens }) {
|
|
10
|
+
const root = document.documentElement;
|
|
11
|
+
for (const [name, value] of Object.entries(tokens))
|
|
12
|
+
root.style.setProperty(name, value);
|
|
13
|
+
root.dataset.theme = kind;
|
|
14
|
+
root.style.colorScheme = kind;
|
|
15
|
+
}
|
|
16
|
+
function connected(port, sessionId) {
|
|
17
|
+
const pending = new Map();
|
|
18
|
+
const invalidated = new Set();
|
|
19
|
+
const changed = new Set();
|
|
20
|
+
let lastId = 0;
|
|
21
|
+
const send = (message) => port.postMessage(message);
|
|
22
|
+
function request(message) {
|
|
23
|
+
lastId += 1;
|
|
24
|
+
const id = lastId;
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
pending.set(id, { resolve, reject });
|
|
27
|
+
send({ ...message, id });
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function listen(listeners, listener) {
|
|
31
|
+
listeners.add(listener);
|
|
32
|
+
return () => listeners.delete(listener);
|
|
33
|
+
}
|
|
34
|
+
port.onmessage = ({ data }) => {
|
|
35
|
+
if (data.type === 'result') {
|
|
36
|
+
const waiting = pending.get(data.id);
|
|
37
|
+
pending.delete(data.id);
|
|
38
|
+
if (data.ok)
|
|
39
|
+
waiting?.resolve(data.value);
|
|
40
|
+
else
|
|
41
|
+
waiting?.reject(Object.assign(new Error(data.error.message), { code: data.error.code }));
|
|
42
|
+
}
|
|
43
|
+
else if (data.type === 'theme') {
|
|
44
|
+
applyTheme(data);
|
|
45
|
+
}
|
|
46
|
+
else if (data.type === 'invalidate') {
|
|
47
|
+
for (const listener of invalidated)
|
|
48
|
+
listener(data.source);
|
|
49
|
+
}
|
|
50
|
+
else if (data.type === 'sessions.changed') {
|
|
51
|
+
for (const listener of changed)
|
|
52
|
+
listener({ upserts: data.upserts, removes: data.removes });
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
addEventListener('pagehide', () => send({ type: 'leaving' }));
|
|
56
|
+
return {
|
|
57
|
+
sessionId,
|
|
58
|
+
data: (source, input) => request({ type: 'data', source, ...(input && { input }) }),
|
|
59
|
+
action: (action, input) => request({ type: 'action', action, ...(input && { input }) }),
|
|
60
|
+
open: async (target) => {
|
|
61
|
+
await request({ type: 'open', target });
|
|
62
|
+
},
|
|
63
|
+
onInvalidate: listener => listen(invalidated, listener),
|
|
64
|
+
listSessions: params => request({ type: 'sessions.list', ...params }),
|
|
65
|
+
onSessionsChanged: listener => listen(changed, listener),
|
|
66
|
+
abortTurn: async (params) => {
|
|
67
|
+
await request({ type: 'turn.abort', ...params });
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function handshake() {
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
const timer = setTimeout(() => {
|
|
74
|
+
removeEventListener('message', onInit);
|
|
75
|
+
reject(new Error('宿主没有回应握手'));
|
|
76
|
+
}, INIT_TIMEOUT_MS);
|
|
77
|
+
function onInit(event) {
|
|
78
|
+
if (event.source !== parent || event.ports.length !== 1 || event.data?.type !== 'init')
|
|
79
|
+
return;
|
|
80
|
+
removeEventListener('message', onInit);
|
|
81
|
+
clearTimeout(timer);
|
|
82
|
+
const { theme, sessionId } = event.data;
|
|
83
|
+
if (typeof theme?.tokens !== 'object' || theme.tokens === null) {
|
|
84
|
+
reject(new Error('宿主的 init 不合格:theme 要是带 tokens 的对象'));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
applyTheme(theme);
|
|
88
|
+
resolve(connected(event.ports[0], sessionId));
|
|
89
|
+
}
|
|
90
|
+
addEventListener('message', onInit);
|
|
91
|
+
const hello = { type: 'hello', protocol: PROTOCOL, nonce: location.hash.slice(1) };
|
|
92
|
+
parent.postMessage(hello, '*');
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
let connection;
|
|
96
|
+
/** 与宿主握手,拿到这一页的连接;调多次是同一个连接 */
|
|
97
|
+
export function connect() {
|
|
98
|
+
connection ??= handshake();
|
|
99
|
+
return connection;
|
|
100
|
+
}
|
package/dist/frame.d.ts
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { SessionMethodParams, SessionMethodResult, SessionNotificationParams } from '@aalis/api-session-protocol';
|
|
2
|
+
import type { ThemeKind, ThemeToken } from './theme.js';
|
|
3
|
+
/** 消息通道的协议版本 `<主>.<次>`:握手时主版本不同,宿主卸载框架 */
|
|
4
|
+
export declare const FRAME_PROTOCOL_VERSION = "1.0";
|
|
5
|
+
/**
|
|
6
|
+
* 框架的沙箱值:宿主 iframe 的 sandbox 属性与框架文件响应头 CSP 的 sandbox 指令同一份。只给脚本,
|
|
7
|
+
* 不给 allow-same-origin(与 allow-scripts 同给时框架能自己摘掉沙箱),也不给表单、弹窗、原生对话框、顶层跳转与下载
|
|
8
|
+
*/
|
|
9
|
+
export declare const FRAME_SANDBOX = "allow-scripts";
|
|
10
|
+
/**
|
|
11
|
+
* 宿主数据能力的词表,封闭集合,按加法扩:
|
|
12
|
+
* - `sessions:read`:读会话树与状态(请求 `sessions.list`,挂载起收到 `sessions.changed`);
|
|
13
|
+
* - `turn:abort`:停止会话的回合(请求 `turn.abort`)。
|
|
14
|
+
* 申请了的框架与 owner 自己的界面看得一样多,不按会话收窄
|
|
15
|
+
*/
|
|
16
|
+
export declare const FRAME_HOST_CAPABILITIES: readonly ["sessions:read", "turn:abort"];
|
|
17
|
+
export type FrameHostCapability = (typeof FRAME_HOST_CAPABILITIES)[number];
|
|
18
|
+
/** 名字(数据源名、动作名)最多这么多个字符 */
|
|
19
|
+
export declare const FRAME_NAME_MAX_LENGTH = 128;
|
|
20
|
+
/** 标识(会话 id、回合 id、翻页游标、握手的随机数)最多这么多个字符 */
|
|
21
|
+
export declare const FRAME_ID_MAX_LENGTH = 256;
|
|
22
|
+
/** 地址最多这么多个字符 */
|
|
23
|
+
export declare const FRAME_URL_MAX_LENGTH = 2048;
|
|
24
|
+
/** 握手:nonce 是宿主放在框架地址片段里的随机数 */
|
|
25
|
+
export interface FrameHello {
|
|
26
|
+
type: 'hello';
|
|
27
|
+
protocol: string;
|
|
28
|
+
nonce: string;
|
|
29
|
+
}
|
|
30
|
+
/** 取这一页一个数据源的数据:数据源名与输入由宿主照原样交给服务端核 */
|
|
31
|
+
export interface FrameDataRequest {
|
|
32
|
+
type: 'data';
|
|
33
|
+
id: number;
|
|
34
|
+
source: string;
|
|
35
|
+
input?: Record<string, unknown>;
|
|
36
|
+
}
|
|
37
|
+
/** 调这一页的一个动作:destructive 的先由宿主弹确认框,框里按输入字段列出这次的值 */
|
|
38
|
+
export interface FrameActionRequest {
|
|
39
|
+
type: 'action';
|
|
40
|
+
id: number;
|
|
41
|
+
action: string;
|
|
42
|
+
input?: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
/** 请宿主打开:外站地址(只许 http、https,与宿主同源的不收)经宿主的对话框由用户点开;会话在应用内打开 */
|
|
45
|
+
export interface FrameOpenRequest {
|
|
46
|
+
type: 'open';
|
|
47
|
+
id: number;
|
|
48
|
+
target: {
|
|
49
|
+
url: string;
|
|
50
|
+
} | {
|
|
51
|
+
session: string;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** 列会话(要 sessions:read):只透传会话协议 sessions/list 的这四个参数 */
|
|
55
|
+
export type FrameSessionsListRequest = {
|
|
56
|
+
type: 'sessions.list';
|
|
57
|
+
id: number;
|
|
58
|
+
} & Pick<SessionMethodParams<'sessions/list'>, 'parentId' | 'archived' | 'cursor' | 'limit'>;
|
|
59
|
+
/** 停止一个会话的回合(要 turn:abort):参数同会话协议的 turn/abort */
|
|
60
|
+
export type FrameTurnAbortRequest = {
|
|
61
|
+
type: 'turn.abort';
|
|
62
|
+
id: number;
|
|
63
|
+
} & SessionMethodParams<'turn/abort'>;
|
|
64
|
+
/** 框架文档正在被替换(离开了宿主给它的地址):宿主立即卸载,不回 */
|
|
65
|
+
export interface FrameLeaving {
|
|
66
|
+
type: 'leaving';
|
|
67
|
+
}
|
|
68
|
+
/** 框架发的请求 */
|
|
69
|
+
export type FrameRequest = FrameDataRequest | FrameActionRequest | FrameOpenRequest | FrameSessionsListRequest | FrameTurnAbortRequest;
|
|
70
|
+
/** 框架经端口发的消息 */
|
|
71
|
+
export type FrameMessage = FrameRequest | FrameLeaving;
|
|
72
|
+
/**
|
|
73
|
+
* 请求失败的原因,封闭集合:
|
|
74
|
+
* - `invalid`:消息或输入不对(转发的 HTTP 400、414、431);
|
|
75
|
+
* - `denied`:没申请的能力;这个框架已开着一个宿主对话框时,再请求外链或要确认的动作;
|
|
76
|
+
* - `not-found`:页面、会话、数据源或动作不在(HTTP 404);
|
|
77
|
+
* - `busy`:超出在途上限;
|
|
78
|
+
* - `cancelled`:用户在宿主的确认框或外链对话框里取消;
|
|
79
|
+
* - `unavailable`:会话服务不在、连接断开(HTTP 503 与连不上);
|
|
80
|
+
* - `failed`:插件的处理函数或宿主服务出错(其余 4xx 与 5xx),message 写原因。
|
|
81
|
+
*/
|
|
82
|
+
export type FrameErrorCode = 'invalid' | 'denied' | 'not-found' | 'busy' | 'cancelled' | 'unavailable' | 'failed';
|
|
83
|
+
/** 当前主题:深浅与主题令牌的值(THEME_TOKENS 里的名字到值)。尺寸不传,框架在同一个浏览器里自己求值 */
|
|
84
|
+
export interface FrameTheme {
|
|
85
|
+
kind: ThemeKind;
|
|
86
|
+
tokens: Record<ThemeToken, string>;
|
|
87
|
+
}
|
|
88
|
+
/** 握手时随端口交出;sessionId 只给会话面板,换会话时宿主重新挂载 */
|
|
89
|
+
export interface FrameInit {
|
|
90
|
+
type: 'init';
|
|
91
|
+
protocol: string;
|
|
92
|
+
experimental: true;
|
|
93
|
+
sessionId?: string;
|
|
94
|
+
theme: FrameTheme;
|
|
95
|
+
}
|
|
96
|
+
/** 回一个请求 */
|
|
97
|
+
export type FrameResult = {
|
|
98
|
+
type: 'result';
|
|
99
|
+
id: number;
|
|
100
|
+
} & ({
|
|
101
|
+
ok: true;
|
|
102
|
+
value: unknown;
|
|
103
|
+
} | {
|
|
104
|
+
ok: false;
|
|
105
|
+
error: {
|
|
106
|
+
code: FrameErrorCode;
|
|
107
|
+
message: string;
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
/** 主题切换,或跟随系统时深浅变了 */
|
|
111
|
+
export type FrameThemeChange = {
|
|
112
|
+
type: 'theme';
|
|
113
|
+
} & FrameTheme;
|
|
114
|
+
/**
|
|
115
|
+
* 数据变了,重取:带 source 时只重取这个数据源。插件调了 invalidate 时推,带会话的只推给挂在那个会话上的框架;
|
|
116
|
+
* 会话或管理连接断线重连后,给每个框架推一次不带 source 的,意思是全部重取(含会话列表)
|
|
117
|
+
*/
|
|
118
|
+
export interface FrameInvalidate {
|
|
119
|
+
type: 'invalidate';
|
|
120
|
+
source?: string;
|
|
121
|
+
}
|
|
122
|
+
/** 会话列表的增量(申请了 sessions:read 的框架挂载起就收):转发会话协议的 sessions/changed,不带 groups */
|
|
123
|
+
export type FrameSessionsChanged = {
|
|
124
|
+
type: 'sessions.changed';
|
|
125
|
+
} & Omit<SessionNotificationParams<'sessions/changed'>, 'groups'>;
|
|
126
|
+
/** 宿主经端口发给框架的消息 */
|
|
127
|
+
export type FrameHostMessage = FrameInit | FrameResult | FrameThemeChange | FrameInvalidate | FrameSessionsChanged;
|
|
128
|
+
/** connect() 给出的连接。请求失败时抛出 Error,带 code(FrameErrorCode) */
|
|
129
|
+
export interface FrameClient {
|
|
130
|
+
/** 会话面板挂在哪个会话上;插件页面没有 */
|
|
131
|
+
readonly sessionId?: string;
|
|
132
|
+
/** 取这一页一个数据源的数据 */
|
|
133
|
+
data(source: string, input?: Record<string, unknown>): Promise<unknown>;
|
|
134
|
+
/** 调这一页的一个动作,回处理函数给的一句话或 null;destructive 的由宿主先弹确认框,取消时抛 cancelled */
|
|
135
|
+
action(name: string, input?: Record<string, unknown>): Promise<string | null>;
|
|
136
|
+
/** 请宿主打开外站地址(经宿主的对话框)或应用内的会话 */
|
|
137
|
+
open(target: FrameOpenRequest['target']): Promise<void>;
|
|
138
|
+
/** 数据变了:带 source 时只重取这个数据源,不带时全部重取。回退订 */
|
|
139
|
+
onInvalidate(listener: (source?: string) => void): () => void;
|
|
140
|
+
/** 列会话(要 sessions:read) */
|
|
141
|
+
listSessions(params?: Omit<FrameSessionsListRequest, 'type' | 'id'>): Promise<Omit<SessionMethodResult<'sessions/list'>, 'groups'>>;
|
|
142
|
+
/** 会话列表的增量(要 sessions:read)。回退订 */
|
|
143
|
+
onSessionsChanged(listener: (change: Omit<FrameSessionsChanged, 'type'>) => void): () => void;
|
|
144
|
+
/** 停止一个会话的回合(要 turn:abort) */
|
|
145
|
+
abortTurn(params: Omit<FrameTurnAbortRequest, 'type' | 'id'>): Promise<void>;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* 能力表:每种请求与推送要哪种宿主数据能力,null 为不要。宿主转发请求前按页面的 `frame.host` 核,没申请的回 denied;
|
|
149
|
+
* 推送只发给申请了的框架
|
|
150
|
+
*/
|
|
151
|
+
export declare const FRAME_REQUIRED_CAPABILITY: Readonly<Record<FrameRequest['type'] | FrameSessionsChanged['type'], FrameHostCapability | null>>;
|
|
152
|
+
/** 校验的结果:不过时给出原因;消息里认得出请求 id 时一并给出,宿主据它回 invalid */
|
|
153
|
+
export type FrameParseResult<T> = {
|
|
154
|
+
ok: true;
|
|
155
|
+
message: T;
|
|
156
|
+
} | {
|
|
157
|
+
ok: false;
|
|
158
|
+
id?: number;
|
|
159
|
+
problem: string;
|
|
160
|
+
};
|
|
161
|
+
/** 校验框架发来的握手(窗口消息)。来源窗口、nonce 与主版本由宿主另核;不过的窗口消息一律忽略 */
|
|
162
|
+
export declare const parseFrameHello: (value: unknown) => FrameParseResult<FrameHello>;
|
|
163
|
+
/** 校验框架经端口发来的消息:类型标记、字段类型、多余的键与字符串长度;不过的请求回 invalid,不往下走 */
|
|
164
|
+
export declare const parseFrameMessage: (value: unknown) => FrameParseResult<FrameMessage>;
|
|
165
|
+
//# sourceMappingURL=frame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../src/frame.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACvH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExD,0CAA0C;AAC1C,eAAO,MAAM,sBAAsB,QAAQ,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,0CAA2C,CAAC;AAEhF,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3E,2BAA2B;AAC3B,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,0CAA0C;AAC1C,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,iBAAiB;AACjB,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAOzC,gCAAgC;AAChC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,uCAAuC;AACvC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,8DAA8D;AAC9D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAED,wDAAwD;AACxD,MAAM,MAAM,wBAAwB,GAAG;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CACjF,mBAAmB,CAAC,eAAe,CAAC,EACpC,UAAU,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,CAC7C,CAAC;AAEF,kDAAkD;AAClD,MAAM,MAAM,qBAAqB,GAAG;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,mBAAmB,CAAC,YAAY,CAAC,CAAC;AAE3G,sCAAsC;AACtC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,aAAa;AACb,MAAM,MAAM,YAAY,GACpB,gBAAgB,GAChB,kBAAkB,GAClB,gBAAgB,GAChB,wBAAwB,GACxB,qBAAqB,CAAC;AAE1B,gBAAgB;AAChB,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,YAAY,CAAC;AAIvD;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,MAAM,GAAG,WAAW,GAAG,aAAa,GAAG,QAAQ,CAAC;AAElH,8DAA8D;AAC9D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,2CAA2C;AAC3C,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,YAAY;AACZ,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,CACvD;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC5B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE;QAAE,IAAI,EAAE,cAAc,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAClE,CAAC;AAEF,sBAAsB;AACtB,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,UAAU,CAAC;AAE9D;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,6EAA6E;AAC7E,MAAM,MAAM,oBAAoB,GAAG;IAAE,IAAI,EAAE,kBAAkB,CAAA;CAAE,GAAG,IAAI,CACpE,yBAAyB,CAAC,kBAAkB,CAAC,EAC7C,QAAQ,CACT,CAAC;AAEF,mBAAmB;AACnB,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,WAAW,GAAG,gBAAgB,GAAG,eAAe,GAAG,oBAAoB,CAAC;AAUnH,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,yBAAyB;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB;IACnB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACxE,sEAAsE;IACtE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9E,gCAAgC;IAChC,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,0CAA0C;IAC1C,YAAY,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC9D,2BAA2B;IAC3B,YAAY,CACV,MAAM,CAAC,EAAE,IAAI,CAAC,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAC,GACrD,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;IACjE,mCAAmC;IACnC,iBAAiB,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC9F,8BAA8B;IAC9B,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9E;AAED;;;GAGG;AACH,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAC9C,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,oBAAoB,CAAC,MAAM,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC,CAQxF,CAAC;AAIF,oDAAoD;AACpD,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAiFzG,uDAAuD;AACvD,eAAO,MAAM,eAAe,GAAI,OAAO,OAAO,KAAG,gBAAgB,CAAC,UAAU,CAA+B,CAAC;AAE5G,4DAA4D;AAC5D,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,gBAAgB,CAAC,YAAY,CAAiC,CAAC"}
|
package/dist/frame.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// ----- 插件前端 -----
|
|
2
|
+
//
|
|
3
|
+
// 页面默认声明式;声明式表达不了时,插件可以自带一份静态网页(插件前端),只放在插件页面与会话面板两个位置。
|
|
4
|
+
// webui-server 托管它的文件,宿主把它画进 iframe,与宿主页面不同源:拿不到登录状态;可以联网,脚本只认插件自己打包的文件。
|
|
5
|
+
// 要宿主的数据与动作只有一条路:经消息通道请宿主代办,调这一页声明过的数据源与动作,或用申请过的宿主数据能力。
|
|
6
|
+
//
|
|
7
|
+
// 本文件是这条通道的公开契约:协议版本、沙箱值、宿主数据能力的词表与能力表、消息类型与消息的校验函数。
|
|
8
|
+
// 校验函数是不碰 DOM 的纯函数,宿主前端与第三方宿主前端共用,换前端时规则不漂;在途与频率上限、确认框与外链对话框
|
|
9
|
+
// 涉及界面与计时,由各宿主前端照文档实现。第一版标 experimental:只做加法,加字段、加消息升次版本。
|
|
10
|
+
//
|
|
11
|
+
// 托管(webui-server,不要登录):
|
|
12
|
+
// GET /frames/:token/<路径> 插件包 webui/ 里的文件;token 是页面登记时随机生成的,页面撤回或重新登记后旧的回 404
|
|
13
|
+
// GET /frames/_base/<路径> 宿主给框架的公用文件:助手 frame.js 与样式基线 frame.css,随本包发布
|
|
14
|
+
/** 消息通道的协议版本 `<主>.<次>`:握手时主版本不同,宿主卸载框架 */
|
|
15
|
+
export const FRAME_PROTOCOL_VERSION = '1.0';
|
|
16
|
+
/**
|
|
17
|
+
* 框架的沙箱值:宿主 iframe 的 sandbox 属性与框架文件响应头 CSP 的 sandbox 指令同一份。只给脚本,
|
|
18
|
+
* 不给 allow-same-origin(与 allow-scripts 同给时框架能自己摘掉沙箱),也不给表单、弹窗、原生对话框、顶层跳转与下载
|
|
19
|
+
*/
|
|
20
|
+
export const FRAME_SANDBOX = 'allow-scripts';
|
|
21
|
+
/**
|
|
22
|
+
* 宿主数据能力的词表,封闭集合,按加法扩:
|
|
23
|
+
* - `sessions:read`:读会话树与状态(请求 `sessions.list`,挂载起收到 `sessions.changed`);
|
|
24
|
+
* - `turn:abort`:停止会话的回合(请求 `turn.abort`)。
|
|
25
|
+
* 申请了的框架与 owner 自己的界面看得一样多,不按会话收窄
|
|
26
|
+
*/
|
|
27
|
+
export const FRAME_HOST_CAPABILITIES = ['sessions:read', 'turn:abort'];
|
|
28
|
+
/** 名字(数据源名、动作名)最多这么多个字符 */
|
|
29
|
+
export const FRAME_NAME_MAX_LENGTH = 128;
|
|
30
|
+
/** 标识(会话 id、回合 id、翻页游标、握手的随机数)最多这么多个字符 */
|
|
31
|
+
export const FRAME_ID_MAX_LENGTH = 256;
|
|
32
|
+
/** 地址最多这么多个字符 */
|
|
33
|
+
export const FRAME_URL_MAX_LENGTH = 2048;
|
|
34
|
+
/**
|
|
35
|
+
* 能力表:每种请求与推送要哪种宿主数据能力,null 为不要。宿主转发请求前按页面的 `frame.host` 核,没申请的回 denied;
|
|
36
|
+
* 推送只发给申请了的框架
|
|
37
|
+
*/
|
|
38
|
+
export const FRAME_REQUIRED_CAPABILITY = {
|
|
39
|
+
data: null,
|
|
40
|
+
action: null,
|
|
41
|
+
open: null,
|
|
42
|
+
'sessions.list': 'sessions:read',
|
|
43
|
+
'turn.abort': 'turn:abort',
|
|
44
|
+
'sessions.changed': 'sessions:read',
|
|
45
|
+
};
|
|
46
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
47
|
+
const text = (max) => value => typeof value === 'string' && value !== '' && value.length <= max ? undefined : `要是 1 到 ${max} 个字符的字符串`;
|
|
48
|
+
const name = text(FRAME_NAME_MAX_LENGTH);
|
|
49
|
+
const ident = text(FRAME_ID_MAX_LENGTH);
|
|
50
|
+
const integer = value => (Number.isSafeInteger(value) ? undefined : '要是整数');
|
|
51
|
+
const flag = value => (typeof value === 'boolean' ? undefined : '要是布尔值');
|
|
52
|
+
const object = value => (isRecord(value) ? undefined : '要是对象');
|
|
53
|
+
const version = value => typeof value === 'string' && /^\d{1,4}\.\d{1,4}$/.test(value) ? undefined : '要是「主.次」形式的版本';
|
|
54
|
+
/** 外站地址:http 或 https 的绝对地址。与宿主同源的由宿主按自己的来源再拒 */
|
|
55
|
+
function webUrl(value) {
|
|
56
|
+
const problem = text(FRAME_URL_MAX_LENGTH)(value);
|
|
57
|
+
if (problem)
|
|
58
|
+
return problem;
|
|
59
|
+
try {
|
|
60
|
+
const { protocol } = new URL(value);
|
|
61
|
+
return protocol === 'http:' || protocol === 'https:' ? undefined : '只许 http、https 地址';
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return '不是绝对地址';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const target = value => {
|
|
68
|
+
if (!isRecord(value) || Object.keys(value).length !== 1)
|
|
69
|
+
return '要是 { url } 或 { session }';
|
|
70
|
+
if ('url' in value)
|
|
71
|
+
return webUrl(value.url);
|
|
72
|
+
if ('session' in value)
|
|
73
|
+
return ident(value.session);
|
|
74
|
+
return '要是 { url } 或 { session }';
|
|
75
|
+
};
|
|
76
|
+
const HELLO_FIELDS = {
|
|
77
|
+
hello: { protocol: [version, true], nonce: [ident, true] },
|
|
78
|
+
};
|
|
79
|
+
const MESSAGE_FIELDS = {
|
|
80
|
+
data: { id: [integer, true], source: [name, true], input: [object, false] },
|
|
81
|
+
action: { id: [integer, true], action: [name, true], input: [object, false] },
|
|
82
|
+
open: { id: [integer, true], target: [target, true] },
|
|
83
|
+
'sessions.list': {
|
|
84
|
+
id: [integer, true],
|
|
85
|
+
parentId: [ident, false],
|
|
86
|
+
archived: [flag, false],
|
|
87
|
+
cursor: [ident, false],
|
|
88
|
+
limit: [integer, false],
|
|
89
|
+
},
|
|
90
|
+
'turn.abort': { id: [integer, true], sessionId: [ident, true], turnId: [ident, false] },
|
|
91
|
+
leaving: {},
|
|
92
|
+
};
|
|
93
|
+
function parse(value, table) {
|
|
94
|
+
if (!isRecord(value))
|
|
95
|
+
return { ok: false, problem: '消息要是对象' };
|
|
96
|
+
const id = Number.isSafeInteger(value.id) ? value.id : undefined;
|
|
97
|
+
const fail = (problem) => ({ ok: false, ...(id === undefined ? {} : { id }), problem });
|
|
98
|
+
const fields = typeof value.type === 'string' && Object.hasOwn(table, value.type) ? table[value.type] : undefined;
|
|
99
|
+
if (!fields)
|
|
100
|
+
return fail(`不认识的消息 ${String(value.type)}`);
|
|
101
|
+
for (const key of Object.keys(value)) {
|
|
102
|
+
if (key !== 'type' && !Object.hasOwn(fields, key))
|
|
103
|
+
return fail(`多余的键:${key}`);
|
|
104
|
+
}
|
|
105
|
+
for (const [key, [rule, required]] of Object.entries(fields)) {
|
|
106
|
+
if (value[key] === undefined) {
|
|
107
|
+
if (required)
|
|
108
|
+
return fail(`缺少 ${key}`);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const problem = rule(value[key]);
|
|
112
|
+
if (problem)
|
|
113
|
+
return fail(`${key} ${problem}`);
|
|
114
|
+
}
|
|
115
|
+
return { ok: true, message: value };
|
|
116
|
+
}
|
|
117
|
+
/** 校验框架发来的握手(窗口消息)。来源窗口、nonce 与主版本由宿主另核;不过的窗口消息一律忽略 */
|
|
118
|
+
export const parseFrameHello = (value) => parse(value, HELLO_FIELDS);
|
|
119
|
+
/** 校验框架经端口发来的消息:类型标记、字段类型、多余的键与字符串长度;不过的请求回 invalid,不往下走 */
|
|
120
|
+
export const parseFrameMessage = (value) => parse(value, MESSAGE_FIELDS);
|
|
121
|
+
//# sourceMappingURL=frame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.js","sourceRoot":"","sources":["../src/frame.ts"],"names":[],"mappings":"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"}
|