@aalis/api-webui 0.11.1 → 0.13.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 +158 -0
- package/dist/frame/schema-i18n/catalog.js +101 -0
- package/dist/frame/schema-i18n/check.js +65 -0
- package/dist/frame/schema-i18n/define.js +105 -0
- package/dist/frame/schema-i18n/errors.js +41 -0
- package/dist/frame/schema-i18n/format.js +160 -0
- package/dist/frame/schema-i18n/index.js +17 -0
- package/dist/frame/schema-i18n/locale.js +100 -0
- package/dist/frame/schema-i18n/locales/en.js +3 -0
- package/dist/frame/schema-i18n/message.js +249 -0
- package/dist/frame/schema-i18n/packs.js +52 -0
- package/dist/frame/schema-i18n/params.js +2 -0
- package/dist/frame/schema-i18n/resolve.js +85 -0
- package/dist/frame/schema-i18n/source.js +4 -0
- package/dist/frame/schema-i18n/text-ref.js +94 -0
- package/dist/frame/schema-i18n/texts.js +29 -0
- package/dist/frame.d.ts +190 -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 +137 -206
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -32
- package/dist/index.js.map +1 -1
- package/dist/locales/en.d.ts +82 -0
- package/dist/locales/en.d.ts.map +1 -0
- package/dist/locales/en.js +82 -0
- package/dist/locales/en.js.map +1 -0
- package/dist/page.d.ts +433 -0
- package/dist/page.d.ts.map +1 -0
- package/dist/page.js +30 -0
- package/dist/page.js.map +1 -0
- package/dist/rest.d.ts +674 -0
- package/dist/rest.d.ts.map +1 -0
- package/dist/rest.js +21 -0
- package/dist/rest.js.map +1 -0
- package/dist/texts.d.ts +163 -0
- package/dist/texts.d.ts.map +1 -0
- package/dist/texts.js +85 -0
- package/dist/texts.js.map +1 -0
- package/dist/theme.d.ts +466 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +298 -0
- package/dist/theme.js.map +1 -0
- package/dist/themes/aalis-dark.json +49 -0
- package/dist/themes/aalis-light.json +49 -0
- package/dist/themes/brand-dark.json +49 -0
- package/dist/themes/brand-light.json +49 -0
- package/dist/themes/calm-dark.json +49 -0
- package/dist/themes/calm-light.json +49 -0
- package/dist/themes/neutral-dark.json +49 -0
- package/dist/themes/neutral-light.json +49 -0
- package/package.json +14 -6
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const TEXT_REF_KEYS = new Set(['i18n', 'key', 'params', 'source']);
|
|
2
|
+
/**
|
|
3
|
+
* 是不是文字对象:只按形状判(i18n、key、source 是字符串,params 缺省或为普通对象,没有别的键),
|
|
4
|
+
* 从 JSON 读回的、另一份本包产出的都认
|
|
5
|
+
*/
|
|
6
|
+
export function isTextRef(value) {
|
|
7
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
8
|
+
return false;
|
|
9
|
+
const v = value;
|
|
10
|
+
if (typeof v.i18n !== 'string' || typeof v.key !== 'string' || typeof v.source !== 'string')
|
|
11
|
+
return false;
|
|
12
|
+
if (v.params !== undefined && (typeof v.params !== 'object' || v.params === null || Array.isArray(v.params))) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
return Object.keys(v).every(key => TEXT_REF_KEYS.has(key));
|
|
16
|
+
}
|
|
17
|
+
/** 展示值(core 的插件显示名、服务 label 这类 unknown 字段)收窄成文字:字符串与文字对象原样,其余 undefined */
|
|
18
|
+
export function asText(value) {
|
|
19
|
+
return typeof value === 'string' || isTextRef(value) ? value : undefined;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 两段文字是不是同一句:有一方是字符串时按原文比;文字对象之间按所属包、键与参数认,原文的措辞
|
|
23
|
+
* 变了也还是同一句。参数按结构比:参数键的先后不算,参数里的文字对象同样按包、键与参数认,数组逐个按位置比,
|
|
24
|
+
* 别的值(落盘读回的 null 一类)按 === 比
|
|
25
|
+
*/
|
|
26
|
+
export function sameText(a, b) {
|
|
27
|
+
if (typeof a === 'string' || typeof b === 'string')
|
|
28
|
+
return toSource(a) === toSource(b);
|
|
29
|
+
if (a.i18n !== b.i18n || a.key !== b.key)
|
|
30
|
+
return false;
|
|
31
|
+
const left = a.params ?? {};
|
|
32
|
+
const right = b.params ?? {};
|
|
33
|
+
const keys = Object.keys(left);
|
|
34
|
+
return (keys.length === Object.keys(right).length &&
|
|
35
|
+
keys.every(key => Object.hasOwn(right, key) && sameParam(left[key], right[key])));
|
|
36
|
+
}
|
|
37
|
+
const isList = (value) => Array.isArray(value);
|
|
38
|
+
function sameParam(a, b) {
|
|
39
|
+
if (isList(a) || isList(b)) {
|
|
40
|
+
return isList(a) && isList(b) && a.length === b.length && a.every((item, i) => sameParam(item, b[i]));
|
|
41
|
+
}
|
|
42
|
+
return isTextRef(a) && isTextRef(b) ? sameText(a, b) : a === b;
|
|
43
|
+
}
|
|
44
|
+
function isPlainObject(value) {
|
|
45
|
+
const proto = Object.getPrototypeOf(value);
|
|
46
|
+
return proto === Object.prototype || proto === null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 把值里的文字对象换成 `render(text)`。只进普通对象与数组(类实例原样),没有文字对象的子树原样返回、不复制,
|
|
50
|
+
* 有改动时只复制通往文字对象的那一路。`skip(key, parent)` 为真的属性不进:出口用它避开原样数据
|
|
51
|
+
* (工具参数与结果、审批的逐字参数、用户消息正文),那里形如文字对象的值可能是伪造的。
|
|
52
|
+
*/
|
|
53
|
+
export function localize(value, render, skip) {
|
|
54
|
+
const walk = (v) => {
|
|
55
|
+
if (typeof v !== 'object' || v === null)
|
|
56
|
+
return v;
|
|
57
|
+
if (isTextRef(v))
|
|
58
|
+
return render(v);
|
|
59
|
+
if (Array.isArray(v)) {
|
|
60
|
+
let out;
|
|
61
|
+
v.forEach((item, i) => {
|
|
62
|
+
const next = walk(item);
|
|
63
|
+
if (Object.is(next, item))
|
|
64
|
+
return;
|
|
65
|
+
out ??= [...v];
|
|
66
|
+
out[i] = next;
|
|
67
|
+
});
|
|
68
|
+
return out ?? v;
|
|
69
|
+
}
|
|
70
|
+
if (!isPlainObject(v))
|
|
71
|
+
return v;
|
|
72
|
+
let out;
|
|
73
|
+
for (const [key, item] of Object.entries(v)) {
|
|
74
|
+
if (skip?.(key, v))
|
|
75
|
+
continue;
|
|
76
|
+
const next = walk(item);
|
|
77
|
+
if (Object.is(next, item))
|
|
78
|
+
continue;
|
|
79
|
+
out ??= { ...v };
|
|
80
|
+
out[key] = next;
|
|
81
|
+
}
|
|
82
|
+
return out ?? v;
|
|
83
|
+
};
|
|
84
|
+
return walk(value);
|
|
85
|
+
}
|
|
86
|
+
/** 值里的文字对象换成 source(生成时的整句):写日志、交给模型时用 */
|
|
87
|
+
export function toSource(value) {
|
|
88
|
+
return localize(value, text => text.source);
|
|
89
|
+
}
|
|
90
|
+
/** 给 `JSON.stringify` 的 replacer:文字对象写成 source */
|
|
91
|
+
export function toSourceReplacer(_key, value) {
|
|
92
|
+
return isTextRef(value) ? value.source : value;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=text-ref.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// 本包自己的原文字典(原文表在 source.ts)
|
|
2
|
+
import { defineTexts } from './define.js';
|
|
3
|
+
import en from './locales/en.js';
|
|
4
|
+
import { SOURCE } from './source.js';
|
|
5
|
+
const t = defineTexts({ ns: '@aalis/schema-i18n', language: 'zh-CN', source: SOURCE, locales: { en } });
|
|
6
|
+
/** 把若干段(字符串或文字对象)按行连成一个文字对象:多行报告、列表类回复用它,每段按读者语言各自渲染 */
|
|
7
|
+
export function textLines(parts) {
|
|
8
|
+
return t('lines', { items: parts });
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* textLines 的逆:是 textLines 生成的文字对象(落盘读回的同样认)就给出各段,否则 undefined。要逐行加工(缩进、加前缀)
|
|
12
|
+
* 又不能先渲染的一方用它;段本身可能是多行的字符串或别的文字对象
|
|
13
|
+
*/
|
|
14
|
+
export function linesOf(text) {
|
|
15
|
+
const items = text.i18n === t.ns && text.key === 'lines' ? text.params?.items : undefined;
|
|
16
|
+
return Array.isArray(items) ? items : undefined;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 把若干分句(字符串或文字对象)连成一句:原文以「;」相连,英文以「; 」。并列的几条原因、几个问题用它。两两二分着嵌套,
|
|
20
|
+
* 层数随句数按对数增长;n 句约占 2n 个参数里的文字对象,每句另带文字对象参数时更多(见 resolveText 的渲染上限)
|
|
21
|
+
*/
|
|
22
|
+
export function textClauses(parts) {
|
|
23
|
+
if (parts.length < 2)
|
|
24
|
+
return textLines(parts);
|
|
25
|
+
const half = Math.ceil(parts.length / 2);
|
|
26
|
+
const side = (slice) => (slice.length === 1 ? slice[0] : textClauses(slice));
|
|
27
|
+
return t('clauses', { head: side(parts.slice(0, half)), tail: side(parts.slice(half)) });
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=texts.js.map
|
package/dist/frame.d.ts
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import type { SessionMethodParams, SessionMethodResult, SessionNotificationParams } from '@aalis/api-session-protocol';
|
|
2
|
+
import type { CatalogEntry, Localized, TextParam } from '@aalis/schema-i18n';
|
|
3
|
+
import type { PackedText } from './rest.js';
|
|
4
|
+
import type { ThemeKind, ThemeToken } from './theme.js';
|
|
5
|
+
/** 消息通道的协议版本 `<主>.<次>`:握手时主版本不同,宿主卸载框架 */
|
|
6
|
+
export declare const FRAME_PROTOCOL_VERSION = "1.1";
|
|
7
|
+
/**
|
|
8
|
+
* 框架的沙箱值:宿主 iframe 的 sandbox 属性与框架文件响应头 CSP 的 sandbox 指令同一份。只给脚本,
|
|
9
|
+
* 不给 allow-same-origin(与 allow-scripts 同给时框架能自己摘掉沙箱),也不给表单、弹窗、原生对话框、顶层跳转与下载
|
|
10
|
+
*/
|
|
11
|
+
export declare const FRAME_SANDBOX = "allow-scripts";
|
|
12
|
+
/**
|
|
13
|
+
* 宿主数据能力的词表,封闭集合,按加法扩:
|
|
14
|
+
* - `sessions:read`:读会话树与状态(请求 `sessions.list`,挂载起收到 `sessions.changed`);
|
|
15
|
+
* - `turn:abort`:停止会话的回合(请求 `turn.abort`)。
|
|
16
|
+
* 申请了的框架与 owner 自己的界面看得一样多,不按会话收窄
|
|
17
|
+
*/
|
|
18
|
+
export declare const FRAME_HOST_CAPABILITIES: readonly ["sessions:read", "turn:abort"];
|
|
19
|
+
export type FrameHostCapability = (typeof FRAME_HOST_CAPABILITIES)[number];
|
|
20
|
+
/** 名字(数据源名、动作名)最多这么多个字符 */
|
|
21
|
+
export declare const FRAME_NAME_MAX_LENGTH = 128;
|
|
22
|
+
/** 标识(会话 id、回合 id、翻页游标、握手的随机数)最多这么多个字符 */
|
|
23
|
+
export declare const FRAME_ID_MAX_LENGTH = 256;
|
|
24
|
+
/** 地址最多这么多个字符 */
|
|
25
|
+
export declare const FRAME_URL_MAX_LENGTH = 2048;
|
|
26
|
+
/** 握手:nonce 是宿主放在框架地址片段里的随机数 */
|
|
27
|
+
export interface FrameHello {
|
|
28
|
+
type: 'hello';
|
|
29
|
+
protocol: string;
|
|
30
|
+
nonce: string;
|
|
31
|
+
}
|
|
32
|
+
/** 取这一页一个数据源的数据:数据源名与输入由宿主照原样交给服务端核 */
|
|
33
|
+
export interface FrameDataRequest {
|
|
34
|
+
type: 'data';
|
|
35
|
+
id: number;
|
|
36
|
+
source: string;
|
|
37
|
+
input?: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
/** 调这一页的一个动作:destructive 的先由宿主弹确认框,框里按输入字段列出这次的值 */
|
|
40
|
+
export interface FrameActionRequest {
|
|
41
|
+
type: 'action';
|
|
42
|
+
id: number;
|
|
43
|
+
action: string;
|
|
44
|
+
input?: Record<string, unknown>;
|
|
45
|
+
}
|
|
46
|
+
/** 请宿主打开:外站地址(只许 http、https,与宿主同源的不收)经宿主的对话框由用户点开;会话在应用内打开 */
|
|
47
|
+
export interface FrameOpenRequest {
|
|
48
|
+
type: 'open';
|
|
49
|
+
id: number;
|
|
50
|
+
target: {
|
|
51
|
+
url: string;
|
|
52
|
+
} | {
|
|
53
|
+
session: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** 列会话(要 sessions:read):只透传会话协议 sessions/list 的这四个参数 */
|
|
57
|
+
export type FrameSessionsListRequest = {
|
|
58
|
+
type: 'sessions.list';
|
|
59
|
+
id: number;
|
|
60
|
+
} & Pick<SessionMethodParams<'sessions/list'>, 'parentId' | 'archived' | 'cursor' | 'limit'>;
|
|
61
|
+
/** 停止一个会话的回合(要 turn:abort):参数同会话协议的 turn/abort */
|
|
62
|
+
export type FrameTurnAbortRequest = {
|
|
63
|
+
type: 'turn.abort';
|
|
64
|
+
id: number;
|
|
65
|
+
} & SessionMethodParams<'turn/abort'>;
|
|
66
|
+
/** 框架文档正在被替换(离开了宿主给它的地址):宿主立即卸载,不回 */
|
|
67
|
+
export interface FrameLeaving {
|
|
68
|
+
type: 'leaving';
|
|
69
|
+
}
|
|
70
|
+
/** 框架发的请求 */
|
|
71
|
+
export type FrameRequest = FrameDataRequest | FrameActionRequest | FrameOpenRequest | FrameSessionsListRequest | FrameTurnAbortRequest;
|
|
72
|
+
/** 框架经端口发的消息 */
|
|
73
|
+
export type FrameMessage = FrameRequest | FrameLeaving;
|
|
74
|
+
/**
|
|
75
|
+
* 请求失败的原因,封闭集合:
|
|
76
|
+
* - `invalid`:消息或输入不对(转发的 HTTP 400、414、431);
|
|
77
|
+
* - `denied`:没申请的能力;这个框架已开着一个宿主对话框时,再请求外链或要确认的动作;
|
|
78
|
+
* - `not-found`:页面、会话、数据源或动作不在(HTTP 404);
|
|
79
|
+
* - `busy`:超出在途上限;
|
|
80
|
+
* - `cancelled`:用户在宿主的确认框或外链对话框里取消;
|
|
81
|
+
* - `unavailable`:会话服务不在、连接断开(HTTP 503 与连不上);
|
|
82
|
+
* - `failed`:插件的处理函数或宿主服务出错(其余 4xx 与 5xx),message 写原因。
|
|
83
|
+
*/
|
|
84
|
+
export type FrameErrorCode = 'invalid' | 'denied' | 'not-found' | 'busy' | 'cancelled' | 'unavailable' | 'failed';
|
|
85
|
+
/** 当前主题:深浅与主题令牌的值(THEME_TOKENS 里的名字到值)。尺寸不传,框架在同一个浏览器里自己求值 */
|
|
86
|
+
export interface FrameTheme {
|
|
87
|
+
kind: ThemeKind;
|
|
88
|
+
tokens: Record<ThemeToken, string>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 插件前端的字典:这一页所属插件的原文字典(schema-i18n 的目录条目,ns 是插件名,原文与字典自带的全部译文;插件没有字典时
|
|
92
|
+
* 为 null)与语言包给它补的译文(键 → 各语言包给的,只留界面的语言链用得上的)。宿主取自
|
|
93
|
+
* GET /api/pages/:instance/:pageKey/texts,原样随 init 交给框架
|
|
94
|
+
*/
|
|
95
|
+
export interface FrameTexts {
|
|
96
|
+
texts: CatalogEntry | null;
|
|
97
|
+
packs: Record<string, PackedText[]>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 握手时随端口交出;sessionId 只给会话面板,换会话时宿主重新挂载。languages 与 timeZone 与宿主界面同一份:全局层的
|
|
101
|
+
* 语言链(空的是 i18n 不在,文字按原文)与服务进程的时区。全局语言变了宿主重新挂载
|
|
102
|
+
*/
|
|
103
|
+
export interface FrameInit extends FrameTexts {
|
|
104
|
+
type: 'init';
|
|
105
|
+
protocol: string;
|
|
106
|
+
experimental: true;
|
|
107
|
+
sessionId?: string;
|
|
108
|
+
theme: FrameTheme;
|
|
109
|
+
languages: string[];
|
|
110
|
+
timeZone: string;
|
|
111
|
+
}
|
|
112
|
+
/** 回一个请求 */
|
|
113
|
+
export type FrameResult = {
|
|
114
|
+
type: 'result';
|
|
115
|
+
id: number;
|
|
116
|
+
} & ({
|
|
117
|
+
ok: true;
|
|
118
|
+
value: unknown;
|
|
119
|
+
} | {
|
|
120
|
+
ok: false;
|
|
121
|
+
error: {
|
|
122
|
+
code: FrameErrorCode;
|
|
123
|
+
message: string;
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
/** 主题切换,或跟随系统时深浅变了 */
|
|
127
|
+
export type FrameThemeChange = {
|
|
128
|
+
type: 'theme';
|
|
129
|
+
} & FrameTheme;
|
|
130
|
+
/**
|
|
131
|
+
* 数据变了,重取:带 source 时只重取这个数据源。插件调了 invalidate 时推,带会话的只推给挂在那个会话上的框架;
|
|
132
|
+
* 会话或管理连接断线重连后,给每个框架推一次不带 source 的,意思是全部重取(含会话列表)
|
|
133
|
+
*/
|
|
134
|
+
export interface FrameInvalidate {
|
|
135
|
+
type: 'invalidate';
|
|
136
|
+
source?: string;
|
|
137
|
+
}
|
|
138
|
+
/** 会话列表的增量(申请了 sessions:read 的框架挂载起就收):转发会话协议的 sessions/changed(文字已渲染),不带 groups */
|
|
139
|
+
export type FrameSessionsChanged = {
|
|
140
|
+
type: 'sessions.changed';
|
|
141
|
+
} & Omit<Localized<SessionNotificationParams<'sessions/changed'>>, 'groups'>;
|
|
142
|
+
/** 宿主经端口发给框架的消息 */
|
|
143
|
+
export type FrameHostMessage = FrameInit | FrameResult | FrameThemeChange | FrameInvalidate | FrameSessionsChanged;
|
|
144
|
+
/** connect() 给出的连接。请求失败时抛出 Error,带 code(FrameErrorCode) */
|
|
145
|
+
export interface FrameClient {
|
|
146
|
+
/** 会话面板挂在哪个会话上;插件页面没有 */
|
|
147
|
+
readonly sessionId?: string;
|
|
148
|
+
/** 服务进程的时区(init 给的):在 t() 之外格式化时间也按它。次版本更早的宿主不给,为 undefined */
|
|
149
|
+
readonly timeZone?: string;
|
|
150
|
+
/** 助手设进 <html lang> 的语言,即这个插件的字典实际选中的语言;插件没有字典或次版本更早的宿主时为 undefined */
|
|
151
|
+
readonly language?: string;
|
|
152
|
+
/** 取这一页一个数据源的数据 */
|
|
153
|
+
data(source: string, input?: Record<string, unknown>): Promise<unknown>;
|
|
154
|
+
/** 调这一页的一个动作,回处理函数给的一句话或 null;destructive 的由宿主先弹确认框,取消时抛 cancelled */
|
|
155
|
+
action(name: string, input?: Record<string, unknown>): Promise<string | null>;
|
|
156
|
+
/** 请宿主打开外站地址(经宿主的对话框)或应用内的会话 */
|
|
157
|
+
open(target: FrameOpenRequest['target']): Promise<void>;
|
|
158
|
+
/** 数据变了:带 source 时只重取这个数据源,不带时全部重取。回退订 */
|
|
159
|
+
onInvalidate(listener: (source?: string) => void): () => void;
|
|
160
|
+
/** 列会话(要 sessions:read),文字已渲染 */
|
|
161
|
+
listSessions(params?: Omit<FrameSessionsListRequest, 'type' | 'id'>): Promise<Omit<Localized<SessionMethodResult<'sessions/list'>>, 'groups'>>;
|
|
162
|
+
/** 会话列表的增量(要 sessions:read)。回退订 */
|
|
163
|
+
onSessionsChanged(listener: (change: Omit<FrameSessionsChanged, 'type'>) => void): () => void;
|
|
164
|
+
/** 停止一个会话的回合(要 turn:abort) */
|
|
165
|
+
abortTurn(params: Omit<FrameTurnAbortRequest, 'type' | 'id'>): Promise<void>;
|
|
166
|
+
/**
|
|
167
|
+
* 这个插件字典里的一句,按界面的语言渲染:语言链与语言包的补充随 init 交来,没有这种语言的译文时用原文,时间按服务进程的
|
|
168
|
+
* 时区;字典里没有这个键时回键名
|
|
169
|
+
*/
|
|
170
|
+
t(key: string, params?: Readonly<Record<string, TextParam>>): string;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 能力表:每种请求与推送要哪种宿主数据能力,null 为不要。宿主转发请求前按页面的 `frame.host` 核,没申请的回 denied;
|
|
174
|
+
* 推送只发给申请了的框架
|
|
175
|
+
*/
|
|
176
|
+
export declare const FRAME_REQUIRED_CAPABILITY: Readonly<Record<FrameRequest['type'] | FrameSessionsChanged['type'], FrameHostCapability | null>>;
|
|
177
|
+
/** 校验的结果:不过时给出原因;消息里认得出请求 id 时一并给出,宿主据它回 invalid */
|
|
178
|
+
export type FrameParseResult<T> = {
|
|
179
|
+
ok: true;
|
|
180
|
+
message: T;
|
|
181
|
+
} | {
|
|
182
|
+
ok: false;
|
|
183
|
+
id?: number;
|
|
184
|
+
problem: string;
|
|
185
|
+
};
|
|
186
|
+
/** 校验框架发来的握手(窗口消息)。来源窗口、nonce 与主版本由宿主另核;不过的窗口消息一律忽略 */
|
|
187
|
+
export declare const parseFrameHello: (value: unknown) => FrameParseResult<FrameHello>;
|
|
188
|
+
/** 校验框架经端口发来的消息:类型标记、字段类型、多余的键与字符串长度;不过的请求回 invalid,不往下走 */
|
|
189
|
+
export declare const parseFrameMessage: (value: unknown) => FrameParseResult<FrameMessage>;
|
|
190
|
+
//# sourceMappingURL=frame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../src/frame.ts"],"names":[],"mappings":"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"}
|
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 与助手导入的 schema-i18n/,随本包发布
|
|
14
|
+
/** 消息通道的协议版本 `<主>.<次>`:握手时主版本不同,宿主卸载框架 */
|
|
15
|
+
export const FRAME_PROTOCOL_VERSION = '1.1';
|
|
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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"}
|