@nsnanocat/preference-panes 0.6.0 → 0.7.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/README.md +31 -127
- package/dist/module/app.mjs +1030 -0
- package/dist/module/index.html +14 -0
- package/dist/preference-panes.config.js +839 -813
- package/dist/preference-panes.mjs +935 -825
- package/dist/preference-panes.proxy.js +1726 -2123
- package/package.json +62 -67
- package/src/BoxJS.mjs +86 -0
- package/src/Store.mjs +127 -0
- package/src/browser/app.mjs +24 -148
- package/src/browser/client.d.mts +150 -0
- package/src/browser/client.mjs +191 -212
- package/src/browser/components.mjs +59 -0
- package/src/browser/index.d.ts +19 -152
- package/src/browser/index.mjs +60 -5
- package/src/browser/module.html +14 -0
- package/src/browser/panel.css +221 -221
- package/src/browser/panel.mjs +455 -514
- package/src/build.mjs +31 -0
- package/src/index.d.ts +227 -133
- package/src/index.mjs +3 -6
- package/src/lib/boxjs.mjs +77 -99
- package/src/lib/response.mjs +16 -0
- package/src/lib/settings-path.mjs +10 -23
- package/src/proxy/config.mjs +6 -11
- package/src/proxy/handler.mjs +40 -27
- package/src/proxy/response.mjs +16 -0
- package/dist/preference-panes.request.js +0 -2126
- package/dist/settings/app.mjs +0 -1044
- package/dist/settings/home.css +0 -134
- package/dist/settings/index.html +0 -15
- package/dist/settings/panel.css +0 -325
- package/src/PreferencesHandler.mjs +0 -50
- package/src/SettingsHandler.mjs +0 -144
- package/src/browser/home.css +0 -134
- package/src/browser/site.html +0 -15
- package/src/proxy/request.mjs +0 -5
package/src/build.mjs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { BoxJS } from "./BoxJS.mjs";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 从两个配置输入生成一个模块页及其读写与配置 Mock,不生成项目主页。
|
|
6
|
+
* Build one module page, its storage script and config Mock without a project landing page.
|
|
7
|
+
* @param {unknown} boxjs 仅包含一个模块的 BoxJS JSON / BoxJS JSON containing exactly one module.
|
|
8
|
+
* @param {string} [css] 可选自定义 CSS 正文 / Optional custom CSS text.
|
|
9
|
+
* @returns {Promise<Record<string, string>>} 相对路径到文件正文的映射 / Relative file paths mapped to file contents.
|
|
10
|
+
*/
|
|
11
|
+
export async function build(boxjs, css = "") {
|
|
12
|
+
if (typeof css !== "string") throw new TypeError("CSS must be a string");
|
|
13
|
+
const catalog = new BoxJS(boxjs);
|
|
14
|
+
const module = catalog.module.module;
|
|
15
|
+
const [html, app, proxy, mock] = await Promise.all([
|
|
16
|
+
readFile(new URL("../dist/module/index.html", import.meta.url), "utf8"),
|
|
17
|
+
readFile(new URL("../dist/module/app.mjs", import.meta.url), "utf8"),
|
|
18
|
+
readFile(new URL("../dist/preference-panes.proxy.js", import.meta.url), "utf8"),
|
|
19
|
+
readFile(new URL("../dist/preference-panes.config.js", import.meta.url), "utf8"),
|
|
20
|
+
]);
|
|
21
|
+
const config = JSON.stringify(catalog.select(module));
|
|
22
|
+
return {
|
|
23
|
+
[`settings/${module}/index.html`]: html,
|
|
24
|
+
[`settings/assets/${module}.html`]: html,
|
|
25
|
+
"settings/assets/app.mjs": app,
|
|
26
|
+
[`settings/assets/${module}.boxjs.json`]: config,
|
|
27
|
+
[`settings/assets/${module}.css`]: css,
|
|
28
|
+
[`settings/assets/${module}.request.js`]: `${proxy}\nPreferencePanes.run(${config},${JSON.stringify(css)});\n`,
|
|
29
|
+
[`settings/assets/${module}.config.js`]: `${mock}\nPreferencePanes.mock(${config});\n`,
|
|
30
|
+
};
|
|
31
|
+
}
|
package/src/index.d.ts
CHANGED
|
@@ -8,177 +8,271 @@ export type SettingsScalar = string | number | boolean;
|
|
|
8
8
|
* Stored value and display label of a choice.
|
|
9
9
|
*/
|
|
10
10
|
export interface SettingsOption<T extends SettingsScalar = SettingsScalar> {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
/**
|
|
12
|
+
* 持久化值,保留原始标量类型
|
|
13
|
+
* Persisted value with its original scalar type.
|
|
14
|
+
*/
|
|
15
|
+
key: T;
|
|
16
|
+
/**
|
|
17
|
+
* 纯文本选项名称
|
|
18
|
+
* Plain-text option label.
|
|
19
|
+
*/
|
|
20
|
+
label: string;
|
|
15
21
|
}
|
|
16
22
|
/**
|
|
17
23
|
* 控件共同的路径与展示属性。
|
|
18
24
|
* Shared path and presentation attributes of a control.
|
|
19
25
|
*/
|
|
20
26
|
interface FieldBase {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
/**
|
|
28
|
+
* 不含存储根的点分路径,例如 Module.Settings.key
|
|
29
|
+
* Dotted path without the storage root, such as Module.Settings.key.
|
|
30
|
+
*/
|
|
31
|
+
key: string;
|
|
32
|
+
/**
|
|
33
|
+
* 纯文本标题
|
|
34
|
+
* Plain-text title.
|
|
35
|
+
*/
|
|
36
|
+
name: string;
|
|
37
|
+
/**
|
|
38
|
+
* 纯文本说明
|
|
39
|
+
* Plain-text description.
|
|
40
|
+
*/
|
|
41
|
+
description?: string;
|
|
42
|
+
/**
|
|
43
|
+
* 原始 BoxJS 控件类型,与持久化值类型区分
|
|
44
|
+
* Original BoxJS control kind, separate from the stored value type.
|
|
45
|
+
*/
|
|
46
|
+
control?: "boolean" | "checkboxes" | "selects" | "text" | "textarea" | "number";
|
|
47
|
+
/**
|
|
48
|
+
* 输入占位文字
|
|
49
|
+
* Input placeholder.
|
|
50
|
+
*/
|
|
51
|
+
placeholder?: string;
|
|
52
|
+
/**
|
|
53
|
+
* 多行控件的基础行数,必须为正整数
|
|
54
|
+
* Positive baseline row count for a textarea.
|
|
55
|
+
*/
|
|
56
|
+
rows?: number;
|
|
57
|
+
/**
|
|
58
|
+
* 是否随多行内容自动调整高度
|
|
59
|
+
* Whether textarea height follows its contents.
|
|
60
|
+
*/
|
|
61
|
+
autoGrow?: boolean;
|
|
35
62
|
}
|
|
36
63
|
/**
|
|
37
64
|
* 归一化字段;默认值和选项必须符合对应 type,缺失默认值保持缺失。
|
|
38
65
|
* Normalized field; defaults and choices match type, and absent defaults stay absent.
|
|
39
66
|
*/
|
|
40
67
|
export type SettingsField = FieldBase &
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
68
|
+
(
|
|
69
|
+
| { type: "boolean"; defaultValue?: boolean; options?: SettingsOption<boolean>[] }
|
|
70
|
+
| { type: "number"; defaultValue?: number; options?: SettingsOption<number>[] }
|
|
71
|
+
| { type: "string"; defaultValue?: string; options?: SettingsOption<string>[] }
|
|
72
|
+
| { type: "array"; defaultValue?: SettingsScalar[]; options?: SettingsOption[] }
|
|
73
|
+
);
|
|
47
74
|
/**
|
|
48
75
|
* 代理宿主提供的请求;保留字符串方法以便拒绝不支持的方法。
|
|
49
76
|
* Request provided by the proxy host; string methods allow unsupported methods to be rejected.
|
|
50
77
|
*/
|
|
51
78
|
export interface SettingsRequest {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
79
|
+
/**
|
|
80
|
+
* 请求的完整 URL
|
|
81
|
+
* Absolute request URL.
|
|
82
|
+
*/
|
|
83
|
+
url: string;
|
|
84
|
+
/**
|
|
85
|
+
* 区分大小写的 HTTP 方法
|
|
86
|
+
* Case-sensitive HTTP method.
|
|
87
|
+
*/
|
|
88
|
+
method: string;
|
|
89
|
+
/**
|
|
90
|
+
* 头名称在处理器中统一转为小写
|
|
91
|
+
* Header names are normalized to lowercase by the handler.
|
|
92
|
+
*/
|
|
93
|
+
headers?: Record<string, string | undefined>;
|
|
94
|
+
/**
|
|
95
|
+
* POST 的正文为 JSON 值本身;DELETE 无正文
|
|
96
|
+
* POST contains the JSON value itself; DELETE has no body.
|
|
97
|
+
*/
|
|
98
|
+
body?: string;
|
|
60
99
|
}
|
|
61
100
|
/**
|
|
62
101
|
* 通用 HTTP 响应,调用方负责转换为代理宿主的 done 格式。
|
|
63
102
|
* Generic HTTP response; the caller adapts it to the host's done format.
|
|
64
103
|
*/
|
|
65
104
|
export interface SettingsResponse {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
105
|
+
/**
|
|
106
|
+
* 数字状态码
|
|
107
|
+
* Numeric status code.
|
|
108
|
+
*/
|
|
109
|
+
status: number;
|
|
110
|
+
/**
|
|
111
|
+
* 响应头
|
|
112
|
+
* Response headers.
|
|
113
|
+
*/
|
|
114
|
+
headers: Record<string, string>;
|
|
115
|
+
/**
|
|
116
|
+
* JSON 文本;HEAD 始终为空字符串
|
|
117
|
+
* JSON text; always an empty string for HEAD.
|
|
118
|
+
*/
|
|
119
|
+
body: string;
|
|
72
120
|
}
|
|
73
121
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
122
|
+
* 属于单个模块的字段、存储根及可选展示元数据。
|
|
123
|
+
* Fields, storage root and optional display metadata belonging to one module.
|
|
76
124
|
*/
|
|
77
|
-
export interface
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
125
|
+
export interface ModuleDefinition {
|
|
126
|
+
/**
|
|
127
|
+
* 字段 ID 的模块段,不能由 app 名称推断
|
|
128
|
+
* Module segment from field IDs, never inferred from app names.
|
|
129
|
+
*/
|
|
130
|
+
module: string;
|
|
131
|
+
/**
|
|
132
|
+
* 由 @root.module.path 中的 root 提取
|
|
133
|
+
* Root extracted from @root.module.path.
|
|
134
|
+
*/
|
|
135
|
+
storageKey: string;
|
|
136
|
+
/**
|
|
137
|
+
* 保留配置文件中的字段顺序
|
|
138
|
+
* Fields in configuration order.
|
|
139
|
+
*/
|
|
140
|
+
fields: SettingsField[];
|
|
141
|
+
/**
|
|
142
|
+
* 含模块名的公共父路径片段
|
|
143
|
+
* Common parent path segments including the module name.
|
|
144
|
+
*/
|
|
145
|
+
settingsPath: string[];
|
|
146
|
+
/**
|
|
147
|
+
* 只有字段来自唯一 app 时提供
|
|
148
|
+
* Present only when all fields belong to one app.
|
|
149
|
+
*/
|
|
150
|
+
metadata?: {
|
|
151
|
+
/**
|
|
152
|
+
* 应用标识,仅用于展示或溯源
|
|
153
|
+
* App identifier for display or provenance only.
|
|
154
|
+
*/
|
|
155
|
+
id?: string;
|
|
156
|
+
/**
|
|
157
|
+
* 显示名称
|
|
158
|
+
* Display name.
|
|
159
|
+
*/
|
|
160
|
+
name?: string;
|
|
161
|
+
/**
|
|
162
|
+
* 作者纯文本
|
|
163
|
+
* Plain-text author.
|
|
164
|
+
*/
|
|
165
|
+
author?: string;
|
|
166
|
+
/**
|
|
167
|
+
* 项目地址;页面仅接受 HTTP(S) 或相对链接
|
|
168
|
+
* Project address; the page accepts only HTTP(S) or relative URLs.
|
|
169
|
+
*/
|
|
170
|
+
repo?: string;
|
|
171
|
+
/**
|
|
172
|
+
* 仅保留来源信息,不执行脚本
|
|
173
|
+
* Source metadata only; never executed.
|
|
174
|
+
*/
|
|
175
|
+
script?: string;
|
|
176
|
+
/**
|
|
177
|
+
* 优先使用的显式图标地址
|
|
178
|
+
* Explicit preferred icon URL.
|
|
179
|
+
*/
|
|
180
|
+
icon?: string;
|
|
181
|
+
/**
|
|
182
|
+
* 原版透明/彩色顺序,不是亮暗顺序
|
|
183
|
+
* Original transparent/color order, not light/dark order.
|
|
184
|
+
*/
|
|
185
|
+
icons?: string[];
|
|
186
|
+
/**
|
|
187
|
+
* 多段纯文本说明
|
|
188
|
+
* Multiple plain-text paragraphs.
|
|
189
|
+
*/
|
|
190
|
+
descs?: string[];
|
|
191
|
+
/**
|
|
192
|
+
* desc 未提供时的说明
|
|
193
|
+
* Description used when desc is absent.
|
|
194
|
+
*/
|
|
195
|
+
description?: string;
|
|
196
|
+
/**
|
|
197
|
+
* 优先使用的纯文本说明
|
|
198
|
+
* Preferred plain-text description.
|
|
199
|
+
*/
|
|
200
|
+
desc?: string;
|
|
201
|
+
};
|
|
86
202
|
}
|
|
87
203
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
204
|
+
* 可序列化的 JSON 值。
|
|
205
|
+
* Serializable JSON value.
|
|
90
206
|
*/
|
|
91
|
-
export
|
|
92
|
-
/** 按 pathname 匹配,不匹配下载源自身 / Match pathnames without intercepting the download source itself. */
|
|
93
|
-
resources: Array<{
|
|
94
|
-
/** 锚定的 pathname 正则 / Anchored pathname regular expression. */
|
|
95
|
-
pattern: string;
|
|
96
|
-
/** HTTPS 下载源 / HTTPS resource source. */
|
|
97
|
-
source: string;
|
|
98
|
-
/** 响应媒体类型 / Response media type. */
|
|
99
|
-
contentType: string;
|
|
100
|
-
}>;
|
|
101
|
-
}
|
|
207
|
+
export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
|
|
102
208
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
209
|
+
* 标准 BoxJS 展示元数据。
|
|
210
|
+
* Standard BoxJS presentation metadata.
|
|
105
211
|
*/
|
|
106
|
-
export
|
|
107
|
-
/** @param options 安装 JSON 配置 / Installation JSON configuration. */
|
|
108
|
-
constructor(options: PreferencesHandlerOptions);
|
|
109
|
-
}
|
|
212
|
+
export type BoxJSMetadata = NonNullable<ModuleDefinition["metadata"]>;
|
|
110
213
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
214
|
+
* 标准 BoxJS 设置项,由浏览器解析控件语义。
|
|
215
|
+
* Standard BoxJS setting interpreted by the browser.
|
|
113
216
|
*/
|
|
114
|
-
export interface
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
descs?: string[];
|
|
141
|
-
/** desc 未提供时的说明 / Description used when desc is absent. */
|
|
142
|
-
description?: string;
|
|
143
|
-
/** 优先使用的纯文本说明 / Preferred plain-text description. */
|
|
144
|
-
desc?: string;
|
|
145
|
-
};
|
|
217
|
+
export interface BoxJSSetting {
|
|
218
|
+
/**
|
|
219
|
+
* 字段存储 ID
|
|
220
|
+
* Field storage ID.
|
|
221
|
+
*/
|
|
222
|
+
id: string;
|
|
223
|
+
/**
|
|
224
|
+
* 显示名称
|
|
225
|
+
* Display name.
|
|
226
|
+
*/
|
|
227
|
+
name: string;
|
|
228
|
+
/**
|
|
229
|
+
* BoxJS 控件类型
|
|
230
|
+
* BoxJS control type.
|
|
231
|
+
*/
|
|
232
|
+
type: string;
|
|
233
|
+
/**
|
|
234
|
+
* 默认 JSON 值
|
|
235
|
+
* Default JSON value.
|
|
236
|
+
*/
|
|
237
|
+
val?: JsonValue;
|
|
238
|
+
/**
|
|
239
|
+
* 标准扩展属性
|
|
240
|
+
* Standard extension properties.
|
|
241
|
+
*/
|
|
242
|
+
[key: string]: unknown;
|
|
146
243
|
}
|
|
147
244
|
/**
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
* @param config 外部 JSON 数据,在运行时校验 / External JSON validated at runtime.
|
|
151
|
-
* @param module 请求路径中的模块标识 / Module identifier from the request path.
|
|
152
|
-
* @returns 字段、公共路径与元数据 / Fields, common path and metadata.
|
|
153
|
-
* @throws {TypeError} 配置格式、类型、路径或选项无效 / Invalid configuration shape, types, paths or options.
|
|
245
|
+
* 单个 BoxJS 应用。
|
|
246
|
+
* A BoxJS application.
|
|
154
247
|
*/
|
|
155
|
-
export
|
|
248
|
+
export interface BoxJSApp extends BoxJSMetadata {
|
|
249
|
+
/**
|
|
250
|
+
* 应用设置
|
|
251
|
+
* Application settings.
|
|
252
|
+
*/
|
|
253
|
+
settings: BoxJSSetting[];
|
|
254
|
+
}
|
|
156
255
|
/**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
256
|
+
* BoxJS 订阅。
|
|
257
|
+
* A BoxJS subscription.
|
|
159
258
|
*/
|
|
160
|
-
export
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
*/
|
|
167
|
-
constructor(options: SettingsHandlerOptions);
|
|
168
|
-
/**
|
|
169
|
-
* HEAD 不读存储;GET 返回任意指定值,POST/DELETE 对键或子树读改写一次。
|
|
170
|
-
* HEAD avoids storage; GET returns any requested value, and POST/DELETE mutate a key or subtree in one read-modify-write.
|
|
171
|
-
* @param request 代理请求 / Proxy request.
|
|
172
|
-
* @returns HTTP 响应;非目标来源或非 API 路径返回 undefined / HTTP response, or undefined outside the configured API origin and path.
|
|
173
|
-
* @throws {Error} 请求 URL 无效;存储失败以 HTTP 500 返回 / Invalid request URL; storage failures return HTTP 500.
|
|
174
|
-
*/
|
|
175
|
-
handle(request: SettingsRequest): Promise<SettingsResponse | undefined>;
|
|
259
|
+
export interface BoxJSSubscription extends BoxJSMetadata {
|
|
260
|
+
/**
|
|
261
|
+
* 应用列表
|
|
262
|
+
* Application list.
|
|
263
|
+
*/
|
|
264
|
+
apps: BoxJSApp[];
|
|
176
265
|
}
|
|
177
266
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
267
|
+
* 唯一的数据配置输入。
|
|
268
|
+
* The sole data configuration input.
|
|
269
|
+
*/
|
|
270
|
+
export type BoxJSInput = BoxJSSetting[] | BoxJSApp | BoxJSSubscription;
|
|
271
|
+
/**
|
|
272
|
+
* 生成具体模块的页面、代理和配置 Mock,不生成项目入口页。
|
|
273
|
+
* Build a concrete module's page, proxy and config Mock without a project landing page.
|
|
274
|
+
* @param boxjs 恰好包含一个模块的 BoxJS JSON / BoxJS JSON describing exactly one module.
|
|
275
|
+
* @param css 可选 CSS 正文 / Optional CSS text.
|
|
276
|
+
* @returns 相对路径到文件内容的映射 / Relative paths mapped to file contents.
|
|
183
277
|
*/
|
|
184
|
-
export function
|
|
278
|
+
export function build(boxjs: BoxJSInput, css?: string): Promise<Record<string, string>>;
|
package/src/index.mjs
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
export { normalizeBoxJs } from "./lib/boxjs.mjs";
|
|
2
|
-
export { parseSettingsPath } from "./lib/settings-path.mjs";
|
|
3
|
-
export { PreferencesHandler } from "./PreferencesHandler.mjs";
|
|
4
1
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* 仅接收 BoxJS JSON 与可选 CSS 的构建入口。
|
|
3
|
+
* Build entry accepting only BoxJS JSON and optional CSS.
|
|
7
4
|
* @module @nsnanocat/preference-panes
|
|
8
5
|
*/
|
|
9
|
-
export {
|
|
6
|
+
export { build } from "./build.mjs";
|