@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/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
- /** 持久化值,保留原始标量类型 / Persisted value with its original scalar type. */
12
- key: T;
13
- /** 纯文本选项名称 / Plain-text option label. */
14
- label: string;
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
- /** 不含存储根的点分路径,例如 Module.Settings.key / Dotted path without the storage root, such as Module.Settings.key. */
22
- key: string;
23
- /** 纯文本标题 / Plain-text title. */
24
- name: string;
25
- /** 纯文本说明 / Plain-text description. */
26
- description?: string;
27
- /** 原始 BoxJS 控件类型,与持久化值类型区分 / Original BoxJS control kind, separate from the stored value type. */
28
- control?: "boolean" | "checkboxes" | "selects" | "text" | "textarea" | "number";
29
- /** 输入占位文字 / Input placeholder. */
30
- placeholder?: string;
31
- /** 多行控件的基础行数,必须为正整数 / Positive baseline row count for a textarea. */
32
- rows?: number;
33
- /** 是否随多行内容自动调整高度 / Whether textarea height follows its contents. */
34
- autoGrow?: boolean;
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
- | { type: "boolean"; defaultValue?: boolean; options?: SettingsOption<boolean>[] }
43
- | { type: "number"; defaultValue?: number; options?: SettingsOption<number>[] }
44
- | { type: "string"; defaultValue?: string; options?: SettingsOption<string>[] }
45
- | { type: "array"; defaultValue?: SettingsScalar[]; options?: SettingsOption[] }
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
- /** 请求的完整 URL / Absolute request URL. */
53
- url: string;
54
- /** 区分大小写的 HTTP 方法 / Case-sensitive HTTP method. */
55
- method: string;
56
- /** 头名称在处理器中统一转为小写 / Header names are normalized to lowercase by the handler. */
57
- headers?: Record<string, string | undefined>;
58
- /** POST 的正文为 JSON 值本身;DELETE 无正文 / POST contains the JSON value itself; DELETE has no body. */
59
- body?: string;
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
- /** 数字状态码 / Numeric status code. */
67
- status: number;
68
- /** 响应头 / Response headers. */
69
- headers: Record<string, string>;
70
- /** JSON 文本;HEAD 始终为空字符串 / JSON text; always an empty string for HEAD. */
71
- body: string;
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
- * Fixed storage mapping provided by plugin installation, never a browser-selected root.
122
+ * 属于单个模块的字段、存储根及可选展示元数据。
123
+ * Fields, storage root and optional display metadata belonging to one module.
76
124
  */
77
- export interface SettingsHandlerOptions {
78
- /** 接管 /api/ 路径的 HTTPS 来源 / HTTPS origin serving /api/ paths. */
79
- origin: string;
80
- /** 顶层持久化键,不能使用 @ 路径语法 / Literal top-level storage key, without @ path syntax. */
81
- storageKey: string;
82
- /** /api/ 后允许访问的模块;独立通用模块可声明多个 / Allowed module segments following /api/; a standalone installation can declare several. */
83
- module: string | string[];
84
- /** 默认 X-Settings-Client,值必须为 1;不是认证凭据 / Defaults to X-Settings-Client with value 1; not an authentication credential. */
85
- requestHeader?: string;
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
- * 原生 Mock 的等价资源映射,支持不具备该语法的代理。
89
- * Equivalent resource mapping for proxies lacking native Mock syntax.
204
+ * 可序列化的 JSON 值。
205
+ * Serializable JSON value.
90
206
  */
91
- export interface PreferencesHandlerOptions extends SettingsHandlerOptions {
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
- * 通用安装入口;API 保持无网络读写,静态请求才下载资源。
104
- * Generic installation entry; APIs remain network-free and only static requests download resources.
209
+ * 标准 BoxJS 展示元数据。
210
+ * Standard BoxJS presentation metadata.
105
211
  */
106
- export class PreferencesHandler extends SettingsHandler {
107
- /** @param options 安装 JSON 配置 / Installation JSON configuration. */
108
- constructor(options: PreferencesHandlerOptions);
109
- }
212
+ export type BoxJSMetadata = NonNullable<ModuleDefinition["metadata"]>;
110
213
  /**
111
- * 属于单个模块的字段、存储根及可选展示元数据。
112
- * Fields, storage root and optional display metadata belonging to one module.
214
+ * 标准 BoxJS 设置项,由浏览器解析控件语义。
215
+ * Standard BoxJS setting interpreted by the browser.
113
216
  */
114
- export interface ModuleDefinition {
115
- /** 字段 ID 的模块段,不能由 app 名称推断 / Module segment from field IDs, never inferred from app names. */
116
- module: string;
117
- /** @root.module.path 中的 root 提取 / Root extracted from @root.module.path. */
118
- storageKey: string;
119
- /** 保留配置文件中的字段顺序 / Fields in configuration order. */
120
- fields: SettingsField[];
121
- /** 含模块名的公共父路径片段 / Common parent path segments including the module name. */
122
- settingsPath: string[];
123
- /** 只有字段来自唯一 app 时提供 / Present only when all fields belong to one app. */
124
- metadata?: {
125
- /** 应用标识,仅用于展示或溯源 / App identifier for display or provenance only. */
126
- id?: string;
127
- /** 显示名称 / Display name. */
128
- name?: string;
129
- /** 作者纯文本 / Plain-text author. */
130
- author?: string;
131
- /** 项目地址;页面仅接受 HTTP(S) 或相对链接 / Project address; the page accepts only HTTP(S) or relative URLs. */
132
- repo?: string;
133
- /** 仅保留来源信息,不执行脚本 / Source metadata only; never executed. */
134
- script?: string;
135
- /** 优先使用的显式图标地址 / Explicit preferred icon URL. */
136
- icon?: string;
137
- /** 原版透明/彩色顺序,不是亮暗顺序 / Original transparent/color order, not light/dark order. */
138
- icons?: string[];
139
- /** 多段纯文本说明 / Multiple plain-text paragraphs. */
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
- * BoxJS 字段数组、app 或订阅解析为模块定义,不执行脚本或 HTML。
149
- * Parse a BoxJS field array, app or subscription into a module definition without executing scripts or HTML.
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 function normalizeBoxJs(config: unknown, module: string): ModuleDefinition;
248
+ export interface BoxJSApp extends BoxJSMetadata {
249
+ /**
250
+ * 应用设置
251
+ * Application settings.
252
+ */
253
+ settings: BoxJSSetting[];
254
+ }
156
255
  /**
157
- * 使用 util 桥接指定模块的持久化存储,不下载或校验 BoxJS。
158
- * Bridge module persistence through util without downloading or validating BoxJS.
256
+ * BoxJS 订阅。
257
+ * A BoxJS subscription.
159
258
  */
160
- export class SettingsHandler {
161
- /**
162
- * 创建实例,不发送请求或读取存储。
163
- * Construct an instance without network requests or storage reads.
164
- * @param options 来源、存储根与模块 / Origin, storage root and module.
165
- * @throws {TypeError} 来源、存储根、模块或头名称无效 / Invalid origin, storage root, module or header name.
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
- * 从完整 URL 解析 /api/ 后的 database 路径。
179
- * Parse database path segments following /api/ from an absolute URL.
180
- * @param url 完整请求地址 / Absolute request URL.
181
- * @returns 已解码的路径片段;非 API 路径返回 undefined / Decoded segments, or undefined for non-API paths.
182
- * @throws {TypeError} URL、编码或路径片段非法 / Invalid URL, encoding or path segment.
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 parseSettingsPath(url: string): string[] | undefined;
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
- * Public entry for the proxy handler and configuration parsing.
2
+ * 仅接收 BoxJS JSON 与可选 CSS 的构建入口。
3
+ * Build entry accepting only BoxJS JSON and optional CSS.
7
4
  * @module @nsnanocat/preference-panes
8
5
  */
9
- export { SettingsHandler } from "./SettingsHandler.mjs";
6
+ export { build } from "./build.mjs";