base-link-core 0.3.0 → 0.3.4

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 ADDED
@@ -0,0 +1,93 @@
1
+ # base-link-core 联动沉底包:组件目录
2
+
3
+ 一句话:零依赖沉底包,只管三件事——信封、命令注册、执行契约。本包不画页面、不取数。
4
+
5
+ 本包不含画图组件;画图去 `base-paint`(`packages/base-render/README.md` 的组件目录)。
6
+
7
+ ## 速查
8
+
9
+ | 我要 | 调谁 |
10
+ |---|---|
11
+ | 造/验/判定数据信封 | `createEnvelope`/`parseEnvelope`/`isEnvelope`(`src/envelope.ts`) |
12
+ | 按形状验载荷 | `assertShapeData` |
13
+ | 注册并校验命令 | `createRegistry`(对不上即抛错)/`parseRegistryKey`(拆出 `skill`/`combo`/`key`) |
14
+ | 跑一次联动 | `runCombo`(注册表+请求+取数器;失败抛错,永不合成空数组) |
15
+ | 分域接错 | `EnvelopeError`/`RegistryError`/`RunnerError`(皆继承 `LinkCoreError`) |
16
+ | 一个技能读写自己的配置文件 | `loadConfig`/`saveConfig`/`resetConfig`(`src/config/`;定位见下节) |
17
+ | 想知道配置与数据落在哪 | `configPaths(stem)` → `{ configDir, configFile, dataDir }` |
18
+ | 配置件报错 | `ConfigError`(带 `code` 与 `line`) |
19
+
20
+ ## 成员
21
+
22
+ - 信封(`src/envelope.ts`):`ENVELOPE_VERSION` 版本常量;`ENVELOPE_SHAPES` 6 形状(`list`/`detail`/`stat`/`receipt`/`analysis`/`fallback`);`Envelope`/`EnvelopeShape`/`EnvelopeDataByShape` 类型;`createEnvelope` 造封、`parseEnvelope` 验封、`isEnvelope` 判定。
23
+ - 注册(`src/registry.ts`):`RegistryKey`/`ParsedKey`/`Registry` 类型;`parseRegistryKey` 拆命令三段;`createRegistry(knownKeys)` 建表。
24
+ - 执行(`src/runner.ts`):`RunRequest` 请求形(命令+形状+参数)、`Fetcher` 取数器类型、`runCombo(registry, req, fetchData)` 跑一次联动(key 先过注册表、载荷再过信封全字段)。
25
+ - 错误(`src/errors.ts`):`LinkCoreError` 基类下分 `EnvelopeError`/`RegistryError`/`RunnerError`/`ConfigError` 四域。
26
+ - 配置(`src/config/`):`dirs.ts` 决定配置文件与数据目录在哪;`yaml.ts` 是手写的受限子集解析器与生成器(不出这个目录);`store.ts` 管读、写、校验、重置为默认;`index.ts` 是这一小面的门,对外只给 `configPaths`/`loadConfig`/`saveConfig`/`resetConfig` 四件。
27
+
28
+ ## 配置文件(`src/config/`)
29
+
30
+ 一个技能自己的可配置项住在一份 YAML 文件里,**这份文件是唯一真相**(环境变量不参与配置)。
31
+ 落点默认 `~/.ilife/<技能>.yaml`,数据目录默认 `~/.ilife/data/`(首次读时自动建)。
32
+ `ILIFE_CONFIG_DIR` 设定且非空即整体接管配置目录——它同时是测试隔离的唯一口子。
33
+
34
+ ```js
35
+ import { loadConfig } from 'base-link-core';
36
+
37
+ // 默认值由技能自己传,逐项等于现有代码常量。
38
+ const DEFAULTS = { db: { dir: '~/.ilife/data', name: 'calorie_data.db' }, html: { dir: 'calorie_html' } };
39
+ const { values, path, dataDir, created } = loadConfig('calorie', DEFAULTS);
40
+ ```
41
+
42
+ - 文件不存在 → 用默认值并落一份(`created: true`);文件存在 → **先校验再取值**(不认识的键、类型不符一律抛错),文件里缺的项回落默认值。
43
+ - 写(`saveConfig(stem, defaults, values)`)落盘的是**一份完整的**:给的项写进去,没给的按默认值补齐 ⇒ 盘上永远只有默认值表那组键。
44
+ - 重置(`resetConfig(stem, defaults)`)先另存 `<技能>.yaml.bak`(已有即覆盖)再按默认值重写;备份失败即不写盘,原文件保持不动。
45
+ - 出错误一律抛 `ConfigError`:`code` 是 `CONFIG_PARSE_FAILED`/`CONFIG_UNKNOWN_KEY`/`CONFIG_TYPE_MISMATCH`/`CONFIG_SHAPE_INVALID`/`CONFIG_IO_FAILED`/`CONFIG_STEM_INVALID`/`CONFIG_TEST_ISOLATION_MISSING`,`line` 是配置件里的行号(对不上行号时为 `null`),报文里已经带了行号与文件名。
46
+
47
+ ### 支持范围(冻结)
48
+
49
+ 这份子集只认我们写得出去、也读得回来的那点语法:
50
+
51
+ | 支持 | 写法 |
52
+ |---|---|
53
+ | 键值 | `name: calorie_data.db`(键限 `[A-Za-z_][A-Za-z0-9_-]*`) |
54
+ | 一层嵌套 | 顶层键后跟缩进**恰好 2 个空格**的子项;只有一层 |
55
+ | 标量 | 字符串、十进制数字(含负数与小数)、布尔 `true`/`false` |
56
+ | 带引号的字符串 | `"双引号"`(认 `\n`/`\t`/`\"`/`\\`)、`'单引号'`(`''` 表示一个单引号) |
57
+ | 注释 | 整行注释(行首可有空白 + `#`);值后注释(`#` 前面是空白才开始注释) |
58
+ | 换行 | `\n` 与 `\r\n` 都认;文件开头的 BOM 自动剥掉 |
59
+ | 空文件与空行 | 空文件读成空配置;空行跳过 |
60
+
61
+ 解析器与生成器互为逆:`parseConfigYaml(formatConfigYaml(x))` 等于 `x`(有测试钉死)。
62
+ 写出去时分「裸值」与「加引号」两种:字符串里含空白/`#`/`:`/引号、或长得像数字与布尔时,一律加双引号。
63
+
64
+ ### 不支持(遇到就报错,不猜、不静默)
65
+
66
+ - **列表**(`- x` 开头的行)——存储时也不要数组,报 `CONFIG_SHAPE_INVALID`。
67
+ - **两层以上嵌套**(缩进超过 2 个空格)。
68
+ - **制表符缩进**。
69
+ - **多行字符串**(`|`/`>` 块)。
70
+ - **锚点、别名、标签、`null`/`~`、日期时间、科学计数法**(`1e3` 当字符串读,不转数字)。
71
+ - **重复键**(同一层里同名键出现两次即报错,带两处的行号)。
72
+ - **空组**(`key:` 底下没有子项:写出去会解析不回来,故写入侧就拒)。
73
+ - **文件里出现默认值表以外的键**(报 `CONFIG_UNKNOWN_KEY`,带行号)——不做老配置兼容,也没有「未知键先留着」。
74
+
75
+ > 遗留出口:这份子集若在实际使用中不够(多行字符串、列表嵌套等),先记进这份不支持清单,攒到本图收尾再裁要不要扩子集。
76
+
77
+ ### 测试隔离(护栏,不许绕过)
78
+
79
+ 删掉五个写库开关(`*_FORCE_PROD`)之后,「跑测试误写真实数据」靠两件事挡住:
80
+
81
+ 1. 测试一律把 `ILIFE_CONFIG_DIR` 指到临时目录;基座是 `test/helpers/config-test-base.mjs`(`setupConfigTestBase()` 设完当场自证,`requireConfigTestBase()` 缺了直接报错)。
82
+ 2. 配置件自己那道门:跑在 node 测试运行器里(`NODE_TEST_CONTEXT` 非空)却没设 `ILIFE_CONFIG_DIR` 时,`resolveConfigDir()` 抛 `CONFIG_TEST_ISOLATION_MISSING`——绝不静默落到真实家目录。
83
+
84
+ ## 消费者
85
+
86
+ 包内唯一消费者是 `base-combos`(`comboEnvelope` 先过注册表、载荷走信封);各技能经命令调用,不直调本包亦可。
87
+ 配置面(`src/config/`)的消费者是六个技能与设置面(改配置、重置为默认),见地图 #671。
88
+
89
+ ## 测试
90
+
91
+ - 单测:`node --test ../../test/scaffold.test.mjs`(包内无单测目录)。
92
+ - 信封形状断言示例见上游 `base-paint` 的 `test/assert-shape-data-79.test.mjs`。
93
+ - 配置件验收:`node --test test/config-694.test.mjs`(需先 `tsc -b packages/base-link-core`)。
@@ -0,0 +1,24 @@
1
+ /** 位置覆盖变量的名字:设定且非空即接管配置目录。 */
2
+ export declare const CONFIG_DIR_ENV = "ILIFE_CONFIG_DIR";
3
+ /** 配置文件后缀:每个技能一份 `<技能>.yaml`。 */
4
+ export declare const CONFIG_FILE_EXT = ".yaml";
5
+ /** 数据目录在配置目录下的名字:默认 `~/.ilife/data/`。 */
6
+ export declare const DATA_DIR_NAME = "data";
7
+ /** 一个技能的两处落点:配置文件与数据目录。 */
8
+ export interface ConfigPaths {
9
+ readonly configDir: string;
10
+ readonly configFile: string;
11
+ readonly dataDir: string;
12
+ }
13
+ /**
14
+ * 配置目录的绝对路径。
15
+ *
16
+ * 测试护栏(#675 解决评论:删掉五个写库开关之后的替代护栏):跑在测试运行器里却没设
17
+ * `ILIFE_CONFIG_DIR` 即抛错——把「忘了配」从静默写到真实家目录变成响亮失败。
18
+ */
19
+ export declare function resolveConfigDir(): string;
20
+ /** 只算路径、不碰盘。 */
21
+ export declare function configPaths(stem: string): ConfigPaths;
22
+ /** 算路径并把配置目录与数据目录建出来(首次运行自动建,`mkdir -p` 语义)。 */
23
+ export declare function ensureConfigDirs(stem: string): ConfigPaths;
24
+ //# sourceMappingURL=dirs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dirs.d.ts","sourceRoot":"","sources":["../../src/config/dirs.ts"],"names":[],"mappings":"AAQA,8BAA8B;AAC9B,eAAO,MAAM,cAAc,qBAAqB,CAAC;AAEjD,iCAAiC;AACjC,eAAO,MAAM,eAAe,UAAU,CAAC;AAEvC,yCAAyC;AACzC,eAAO,MAAM,aAAa,SAAS,CAAC;AAEpC,2BAA2B;AAC3B,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAWD;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CASzC;AAED,gBAAgB;AAChB,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAWrD;AAED,gDAAgD;AAChD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAU1D"}
@@ -0,0 +1,62 @@
1
+ // config/dirs:配置文件与数据目录住在哪。默认 `~/.ilife/`(由 os.homedir() 派生,平台无关);
2
+ // `ILIFE_CONFIG_DIR` 设定且非空即整体接管配置目录(它同时是测试隔离的唯一口子)。
3
+ // 只认这两个来源:不读 $DSH_HOME,也不读别家的变量——技能要能在没有 DSH 的平台上单独跑。
4
+ import { mkdirSync } from 'node:fs';
5
+ import { homedir } from 'node:os';
6
+ import { join, resolve } from 'node:path';
7
+ import { ConfigError } from '../errors.js';
8
+ /** 位置覆盖变量的名字:设定且非空即接管配置目录。 */
9
+ export const CONFIG_DIR_ENV = 'ILIFE_CONFIG_DIR';
10
+ /** 配置文件后缀:每个技能一份 `<技能>.yaml`。 */
11
+ export const CONFIG_FILE_EXT = '.yaml';
12
+ /** 数据目录在配置目录下的名字:默认 `~/.ilife/data/`。 */
13
+ export const DATA_DIR_NAME = 'data';
14
+ /** 文件名主体:技能名(ASCII),不许带路径分隔符与后缀,免得越出配置目录。 */
15
+ const STEM_RE = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
16
+ /** 跑在 node 测试运行器里(`node --test` 的每个测试进程都带这个变量)。 */
17
+ function isTestRun() {
18
+ const context = process.env.NODE_TEST_CONTEXT;
19
+ return typeof context === 'string' && context.length > 0;
20
+ }
21
+ /**
22
+ * 配置目录的绝对路径。
23
+ *
24
+ * 测试护栏(#675 解决评论:删掉五个写库开关之后的替代护栏):跑在测试运行器里却没设
25
+ * `ILIFE_CONFIG_DIR` 即抛错——把「忘了配」从静默写到真实家目录变成响亮失败。
26
+ */
27
+ export function resolveConfigDir() {
28
+ const override = process.env[CONFIG_DIR_ENV];
29
+ if (override !== undefined && override.trim() !== '')
30
+ return resolve(override);
31
+ if (isTestRun()) {
32
+ throw new ConfigError('CONFIG_TEST_ISOLATION_MISSING', '测试缺隔离:跑在 node 测试运行器里却没有设置 ' + CONFIG_DIR_ENV
33
+ + '。测试一律用 ILIFE_CONFIG_DIR 指向临时目录(缺了直接报错,不许落到真实家目录)');
34
+ }
35
+ return join(homedir(), '.ilife');
36
+ }
37
+ /** 只算路径、不碰盘。 */
38
+ export function configPaths(stem) {
39
+ if (typeof stem !== 'string' || !STEM_RE.test(stem)) {
40
+ throw new ConfigError('CONFIG_STEM_INVALID', '配置文件主体名非法:' + JSON.stringify(stem) + '(只许字母数字与 - _,不许路径分隔符与后缀)');
41
+ }
42
+ const configDir = resolveConfigDir();
43
+ return {
44
+ configDir,
45
+ configFile: join(configDir, stem + CONFIG_FILE_EXT),
46
+ dataDir: join(configDir, DATA_DIR_NAME),
47
+ };
48
+ }
49
+ /** 算路径并把配置目录与数据目录建出来(首次运行自动建,`mkdir -p` 语义)。 */
50
+ export function ensureConfigDirs(stem) {
51
+ const paths = configPaths(stem);
52
+ for (const dir of [paths.configDir, paths.dataDir]) {
53
+ try {
54
+ mkdirSync(dir, { recursive: true });
55
+ }
56
+ catch (err) {
57
+ throw new ConfigError('CONFIG_IO_FAILED', '建目录失败:' + dir, { cause: err });
58
+ }
59
+ }
60
+ return paths;
61
+ }
62
+ //# sourceMappingURL=dirs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dirs.js","sourceRoot":"","sources":["../../src/config/dirs.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,oDAAoD;AACpD,sDAAsD;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,8BAA8B;AAC9B,MAAM,CAAC,MAAM,cAAc,GAAG,kBAAkB,CAAC;AAEjD,iCAAiC;AACjC,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC;AAEvC,yCAAyC;AACzC,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC;AASpC,6CAA6C;AAC7C,MAAM,OAAO,GAAG,6BAA6B,CAAC;AAE9C,mDAAmD;AACnD,SAAS,SAAS;IAChB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC9C,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC7C,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/E,IAAI,SAAS,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,WAAW,CAAC,+BAA+B,EACnD,4BAA4B,GAAG,cAAc;cAC3C,kDAAkD,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,gBAAgB;AAChB,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,WAAW,CAAC,qBAAqB,EACzC,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,0BAA0B,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,SAAS,GAAG,gBAAgB,EAAE,CAAC;IACrC,OAAO;QACL,SAAS;QACT,UAAU,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,eAAe,CAAC;QACnD,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC;KACxC,CAAC;AACJ,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAChC,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC;YACH,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,WAAW,CAAC,kBAAkB,EAAE,QAAQ,GAAG,GAAG,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { configPaths } from './dirs.js';
2
+ export { loadConfig, saveConfig, resetConfig } from './store.js';
3
+ export { ConfigError } from '../errors.js';
4
+ export type { ConfigPaths } from './dirs.js';
5
+ export type { LoadedConfig } from './store.js';
6
+ export type { ConfigRecord, ConfigGroup, ConfigValue } from './yaml.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,6 @@
1
+ // config 能力门:一个技能(或设置页)要读写自己的配置文件,只用这四件 + 一个错误类。
2
+ // 里面怎么实现(受限子集解析器、备份、目录建立)不出这个目录。
3
+ export { configPaths } from './dirs.js';
4
+ export { loadConfig, saveConfig, resetConfig } from './store.js';
5
+ export { ConfigError } from '../errors.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,iCAAiC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,41 @@
1
+ import type { ConfigRecord } from './yaml.js';
2
+ /** 重置前的备份后缀:`<技能>.yaml.bak`。 */
3
+ export declare const BACKUP_EXT = ".bak";
4
+ /** 读回来的一份配置。 */
5
+ export interface LoadedConfig {
6
+ /** 配置文件绝对路径。 */
7
+ readonly path: string;
8
+ /** 数据目录绝对路径(首次读时已建出来)。 */
9
+ readonly dataDir: string;
10
+ /** 取值:文件里的值 ⊕ 文件里缺的项按默认值补。 */
11
+ readonly values: ConfigRecord;
12
+ /** 本次是「文件不存在、按默认值落了一份」还是「文件本来就在」。 */
13
+ readonly created: boolean;
14
+ }
15
+ /**
16
+ * 读一份配置(配置文件是唯一真相)。
17
+ *
18
+ * - 配置文件不存在 → 按默认值落一份,返回默认值(`created: true`);
19
+ * - 文件存在 → 先校验(不认识的键/类型不符即抛错)再取值,文件里缺的项回落默认值;
20
+ * - 配置目录与数据目录(`<配置目录>/data/`)首次读时自动建。
21
+ *
22
+ * `defaults` 就是这份技能的配置表,逐项等于现有代码常量,由调用方传。
23
+ */
24
+ export declare function loadConfig(stem: string, defaults: ConfigRecord): LoadedConfig;
25
+ /**
26
+ * 写一份配置(保存即生效:每次调用现读现取,没有长连接)。
27
+ *
28
+ * 写出去的是**完整一份**:给的项落盘,没给的项按默认值补齐——所以盘上的文件永远只有默认值表那一组键。
29
+ */
30
+ export declare function saveConfig(stem: string, defaults: ConfigRecord, values: ConfigRecord): {
31
+ path: string;
32
+ };
33
+ /**
34
+ * 重置为默认:现有文件先另存 `<配置目录>/<技能>.yaml.bak`(已有 `.bak` 即覆盖),再按默认值重写。
35
+ * 备份失败即不写盘,原文件保持不动。文件本来就不存在时只落一份默认值,`backupPath` 为 null。
36
+ */
37
+ export declare function resetConfig(stem: string, defaults: ConfigRecord): {
38
+ path: string;
39
+ backupPath: string | null;
40
+ };
41
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/config/store.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAe,YAAY,EAAe,MAAM,WAAW,CAAC;AAExE,gCAAgC;AAChC,eAAO,MAAM,UAAU,SAAS,CAAC;AAEjC,gBAAgB;AAChB,MAAM,WAAW,YAAY;IAC3B,gBAAgB;IAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8BAA8B;IAC9B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,qCAAqC;IACrC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAuGD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,YAAY,CAU7E;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAOvG;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAe7G"}
@@ -0,0 +1,155 @@
1
+ // config/store:一个技能的配置怎么读、怎么取默认值、怎么重置。
2
+ // 三条口径(#675 解决评论冻结):文件不存在 → 用默认值并落一份;文件存在 → 校验后取值;
3
+ // 重置为默认前先把现有文件另存 `.bak`。配置文件是唯一真相,环境变量不参与。
4
+ import { copyFileSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
5
+ import { ConfigError } from '../errors.js';
6
+ import { ensureConfigDirs } from './dirs.js';
7
+ import { assertConfigRecord, formatConfigYaml, isConfigGroup, parseConfigYaml } from './yaml.js';
8
+ /** 重置前的备份后缀:`<技能>.yaml.bak`。 */
9
+ export const BACKUP_EXT = '.bak';
10
+ function typeName(value) {
11
+ if (value === null)
12
+ return 'null';
13
+ if (Array.isArray(value))
14
+ return '数组';
15
+ if (typeof value === 'object')
16
+ return '组';
17
+ if (typeof value === 'string')
18
+ return '字符串';
19
+ if (typeof value === 'number')
20
+ return '数字';
21
+ if (typeof value === 'boolean')
22
+ return '布尔';
23
+ return typeof value;
24
+ }
25
+ /** 报错文案统一带行号与文件名;`lineOf` 缺项(写入路径)时就只报键名。 */
26
+ function site(key, lineOf, file) {
27
+ const line = lineOf.get(key);
28
+ const at = line === undefined ? '' : '(第 ' + line + ' 行)';
29
+ const where = file === null ? '' : ',文件:' + file;
30
+ return { line: line ?? null, text: at + where };
31
+ }
32
+ /** 拿配置件的键表去对默认值表:不认识的键、类型不符一律抛错(校验后才取值)。 */
33
+ function validateAgainstDefaults(got, defaults, lineOf, file) {
34
+ for (const [key, value] of Object.entries(got)) {
35
+ const def = defaults[key];
36
+ if (def === undefined) {
37
+ const at = site(key, lineOf, file);
38
+ throw new ConfigError('CONFIG_UNKNOWN_KEY', '不认识的配置项「' + key + '」:默认值表里没有它' + at.text, { line: at.line });
39
+ }
40
+ if (!isConfigGroup(def)) {
41
+ if (typeof value !== typeof def) {
42
+ const at = site(key, lineOf, file);
43
+ throw new ConfigError('CONFIG_TYPE_MISMATCH', '配置项「' + key + '」类型不对:默认值是' + typeName(def) + ',配置件里是' + typeName(value) + at.text, { line: at.line });
44
+ }
45
+ continue;
46
+ }
47
+ if (!isConfigGroup(value)) {
48
+ const at = site(key, lineOf, file);
49
+ throw new ConfigError('CONFIG_TYPE_MISMATCH', '配置项「' + key + '」类型不对:默认值是一组键(' + Object.keys(def).join('、') + '),配置件里是' + typeName(value) + at.text, { line: at.line });
50
+ }
51
+ for (const [child, inner] of Object.entries(value)) {
52
+ const childDef = def[child];
53
+ const full = key + '.' + child;
54
+ if (childDef === undefined) {
55
+ const at = site(full, lineOf, file);
56
+ throw new ConfigError('CONFIG_UNKNOWN_KEY', '不认识的配置项「' + full + '」:默认值表里没有它' + at.text, { line: at.line });
57
+ }
58
+ if (typeof inner !== typeof childDef) {
59
+ const at = site(full, lineOf, file);
60
+ throw new ConfigError('CONFIG_TYPE_MISMATCH', '配置项「' + full + '」类型不对:默认值是' + typeName(childDef) + ',配置件里是' + typeName(inner) + at.text, { line: at.line });
61
+ }
62
+ }
63
+ }
64
+ }
65
+ /** 取值的键表与默认值表逐项对齐:文件里缺的项回落到默认值(默认值本身不可变,故按默认值重排一份)。 */
66
+ function fillDefaults(got, defaults) {
67
+ const out = {};
68
+ for (const [key, def] of Object.entries(defaults)) {
69
+ if (!isConfigGroup(def)) {
70
+ const value = got[key];
71
+ out[key] = typeof value === typeof def ? value : def;
72
+ continue;
73
+ }
74
+ const group = {};
75
+ const gotGroup = got[key];
76
+ const source = isConfigGroup(gotGroup) ? gotGroup : {};
77
+ for (const [child, childDef] of Object.entries(def)) {
78
+ const inner = source[child];
79
+ group[child] = typeof inner === typeof childDef ? inner : childDef;
80
+ }
81
+ out[key] = group;
82
+ }
83
+ return out;
84
+ }
85
+ function readConfigFile(path) {
86
+ try {
87
+ return readFileSync(path, 'utf8');
88
+ }
89
+ catch (err) {
90
+ throw new ConfigError('CONFIG_IO_FAILED', '读配置文件失败:' + path, { cause: err });
91
+ }
92
+ }
93
+ function writeConfigFile(path, record) {
94
+ const text = formatConfigYaml(record);
95
+ try {
96
+ writeFileSync(path, text, 'utf8');
97
+ }
98
+ catch (err) {
99
+ throw new ConfigError('CONFIG_IO_FAILED', '写配置文件失败:' + path, { cause: err });
100
+ }
101
+ }
102
+ /**
103
+ * 读一份配置(配置文件是唯一真相)。
104
+ *
105
+ * - 配置文件不存在 → 按默认值落一份,返回默认值(`created: true`);
106
+ * - 文件存在 → 先校验(不认识的键/类型不符即抛错)再取值,文件里缺的项回落默认值;
107
+ * - 配置目录与数据目录(`<配置目录>/data/`)首次读时自动建。
108
+ *
109
+ * `defaults` 就是这份技能的配置表,逐项等于现有代码常量,由调用方传。
110
+ */
111
+ export function loadConfig(stem, defaults) {
112
+ assertConfigRecord(defaults, '默认值');
113
+ const paths = ensureConfigDirs(stem);
114
+ if (!existsSync(paths.configFile)) {
115
+ writeConfigFile(paths.configFile, defaults);
116
+ return { path: paths.configFile, dataDir: paths.dataDir, values: fillDefaults({}, defaults), created: true };
117
+ }
118
+ const parsed = parseConfigYaml(readConfigFile(paths.configFile), paths.configFile);
119
+ validateAgainstDefaults(parsed.values, defaults, parsed.lineOf, paths.configFile);
120
+ return { path: paths.configFile, dataDir: paths.dataDir, values: fillDefaults(parsed.values, defaults), created: false };
121
+ }
122
+ /**
123
+ * 写一份配置(保存即生效:每次调用现读现取,没有长连接)。
124
+ *
125
+ * 写出去的是**完整一份**:给的项落盘,没给的项按默认值补齐——所以盘上的文件永远只有默认值表那一组键。
126
+ */
127
+ export function saveConfig(stem, defaults, values) {
128
+ assertConfigRecord(defaults, '默认值');
129
+ assertConfigRecord(values, '配置取值');
130
+ validateAgainstDefaults(values, defaults, new Map(), null);
131
+ const paths = ensureConfigDirs(stem);
132
+ writeConfigFile(paths.configFile, fillDefaults(values, defaults));
133
+ return { path: paths.configFile };
134
+ }
135
+ /**
136
+ * 重置为默认:现有文件先另存 `<配置目录>/<技能>.yaml.bak`(已有 `.bak` 即覆盖),再按默认值重写。
137
+ * 备份失败即不写盘,原文件保持不动。文件本来就不存在时只落一份默认值,`backupPath` 为 null。
138
+ */
139
+ export function resetConfig(stem, defaults) {
140
+ assertConfigRecord(defaults, '默认值');
141
+ const paths = ensureConfigDirs(stem);
142
+ let backupPath = null;
143
+ if (existsSync(paths.configFile)) {
144
+ backupPath = paths.configFile + BACKUP_EXT;
145
+ try {
146
+ copyFileSync(paths.configFile, backupPath);
147
+ }
148
+ catch (err) {
149
+ throw new ConfigError('CONFIG_IO_FAILED', '重置前备份失败(本次不写盘,原文件保持不动):' + backupPath, { cause: err });
150
+ }
151
+ }
152
+ writeConfigFile(paths.configFile, defaults);
153
+ return { path: paths.configFile, backupPath };
154
+ }
155
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/config/store.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,mDAAmD;AACnD,2CAA2C;AAC3C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAGjG,gCAAgC;AAChC,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC;AAcjC,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5C,OAAO,OAAO,KAAK,CAAC;AACtB,CAAC;AAED,6CAA6C;AAC7C,SAAS,IAAI,CAAC,GAAW,EAAE,MAAmC,EAAE,IAAmB;IACjF,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,EAAE,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC;IAC1D,MAAM,KAAK,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC;IACjD,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,IAAI,EAAE,EAAE,GAAG,KAAK,EAAE,CAAC;AAClD,CAAC;AAED,4CAA4C;AAC5C,SAAS,uBAAuB,CAC9B,GAAiB,EACjB,QAAsB,EACtB,MAAmC,EACnC,IAAmB;IAEnB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;YACnC,MAAM,IAAI,WAAW,CAAC,oBAAoB,EACxC,UAAU,GAAG,GAAG,GAAG,YAAY,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,IAAI,OAAO,KAAK,KAAK,OAAO,GAAG,EAAE,CAAC;gBAChC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;gBACnC,MAAM,IAAI,WAAW,CAAC,sBAAsB,EAC1C,MAAM,GAAG,GAAG,GAAG,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;YAC3G,CAAC;YACD,SAAS;QACX,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;YACnC,MAAM,IAAI,WAAW,CAAC,sBAAsB,EAC1C,MAAM,GAAG,GAAG,GAAG,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,EACpG,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;QACvB,CAAC;QACD,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;YAC5B,MAAM,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,KAAK,CAAC;YAC/B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;gBACpC,MAAM,IAAI,WAAW,CAAC,oBAAoB,EACxC,UAAU,GAAG,IAAI,GAAG,YAAY,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;YACnE,CAAC;YACD,IAAI,OAAO,KAAK,KAAK,OAAO,QAAQ,EAAE,CAAC;gBACrC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;gBACpC,MAAM,IAAI,WAAW,CAAC,sBAAsB,EAC1C,MAAM,GAAG,IAAI,GAAG,YAAY,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;YACjH,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,uDAAuD;AACvD,SAAS,YAAY,CAAC,GAAiB,EAAE,QAAsB;IAC7D,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YACvB,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,KAAK,KAAK,OAAO,GAAG,CAAC,CAAC,CAAE,KAAqB,CAAC,CAAC,CAAC,GAAG,CAAC;YACtE,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAgB,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1B,MAAM,MAAM,GAAgB,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,KAAK,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACpD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5B,KAAK,CAAC,KAAK,CAAC,GAAG,OAAO,KAAK,KAAK,OAAO,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;QACrE,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,cAAc,CAAC,IAAY;IAClC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CAAC,kBAAkB,EAAE,UAAU,GAAG,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/E,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,IAAY,EAAE,MAAoB;IACzD,MAAM,IAAI,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,CAAC;QACH,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CAAC,kBAAkB,EAAE,UAAU,GAAG,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/E,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,QAAsB;IAC7D,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QAClC,eAAe,CAAC,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC5C,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC/G,CAAC;IACD,MAAM,MAAM,GAAG,eAAe,CAAC,cAAc,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACnF,uBAAuB,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAClF,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC3H,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,QAAsB,EAAE,MAAoB;IACnF,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACpC,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,uBAAuB,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;IAC3D,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACrC,eAAe,CAAC,KAAK,CAAC,UAAU,EAAE,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAClE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,QAAsB;IAC9D,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,UAAU,GAAkB,IAAI,CAAC;IACrC,IAAI,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QACjC,UAAU,GAAG,KAAK,CAAC,UAAU,GAAG,UAAU,CAAC;QAC3C,IAAI,CAAC;YACH,YAAY,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAC7C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,WAAW,CAAC,kBAAkB,EACtC,yBAAyB,GAAG,UAAU,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IACD,eAAe,CAAC,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC5C,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,UAAU,EAAE,CAAC;AAChD,CAAC"}
@@ -0,0 +1,31 @@
1
+ /** 子集里的标量:字符串/数字/布尔。 */
2
+ export type ConfigValue = string | number | boolean;
3
+ /** 一层嵌套:组里的值只能是标量(再深一层即拒绝)。 */
4
+ export interface ConfigGroup {
5
+ [key: string]: ConfigValue;
6
+ }
7
+ /** 一份配置:键 → 标量,或键 → 组。 */
8
+ export interface ConfigRecord {
9
+ [key: string]: ConfigValue | ConfigGroup;
10
+ }
11
+ /** 解析结果:取值 + 每个键的行号(`db`/`db.dir` 两种键形都记),行号用于人话报错。 */
12
+ export interface ParsedConfig {
13
+ readonly values: ConfigRecord;
14
+ readonly lineOf: ReadonlyMap<string, number>;
15
+ }
16
+ /** 是不是「组」(一层嵌套的对象)。数组/null 都不算。 */
17
+ export declare function isConfigGroup(value: unknown): value is ConfigGroup;
18
+ /**
19
+ * 取值必须落在子集里(默认值与待落盘的取值都过这一关):键 → 标量,或键 → 一层组。
20
+ * 组不许为空(`key:` 底下没子项写出去就解析不回来),不许再深一层。
21
+ */
22
+ export declare function assertConfigRecord(value: unknown, label: string): asserts value is ConfigRecord;
23
+ /**
24
+ * 解析受限子集。`file` 只进报错文案。
25
+ *
26
+ * 失败一律抛 `ConfigError`(code `CONFIG_PARSE_FAILED`,带 `line`),绝不返回半份取值冒充正常。
27
+ */
28
+ export declare function parseConfigYaml(text: unknown, file?: string): ParsedConfig;
29
+ /** 一份配置怎么写出去:调用的键序即落盘的键序(常用项在前由调用方排),一行一键。 */
30
+ export declare function formatConfigYaml(values: ConfigRecord): string;
31
+ //# sourceMappingURL=yaml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../../src/config/yaml.ts"],"names":[],"mappings":"AAKA,wBAAwB;AACxB,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpD,+BAA+B;AAC/B,MAAM,WAAW,WAAW;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC;CAC5B;AAED,0BAA0B;AAC1B,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC;CAC1C;AAED,uDAAuD;AACvD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9C;AAOD,mCAAmC;AACnC,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAElE;AAQD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,YAAY,CAsB/F;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,SAAS,GAAG,YAAY,CAkE1E;AAwED,8CAA8C;AAC9C,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAY7D"}
@@ -0,0 +1,222 @@
1
+ // config/yaml:手写受限子集解析器 + 生成器(照 `packages/base-combos/scripts/build-help.mjs:56-63` 读
2
+ // combos.yaml 的先例:全仓零 YAML 依赖,只认我们自己写出去的那个子集)。
3
+ // 支持范围与不支持清单写在包 README 的同名一节里,冻结在此;解析失败给人话报错 + 行号。
4
+ import { ConfigError } from '../errors.js';
5
+ const KEY_RE = /^([A-Za-z_][A-Za-z0-9_-]*):[ \t]*(.*)$/;
6
+ const NUMBER_RE = /^-?\d+(?:\.\d+)?$/;
7
+ /** 裸值(不加引号):不以空白/#/:/引号开头,且不含空白与这些字符。 */
8
+ const PLAIN_RE = /^[A-Za-z0-9_\u4e00-\u9fff][^#:'"\s]*$/;
9
+ /** 是不是「组」(一层嵌套的对象)。数组/null 都不算。 */
10
+ export function isConfigGroup(value) {
11
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
12
+ }
13
+ function describeType(value) {
14
+ if (value === null)
15
+ return 'null';
16
+ if (Array.isArray(value))
17
+ return '数组';
18
+ return typeof value === 'object' ? '对象' : typeof value;
19
+ }
20
+ /**
21
+ * 取值必须落在子集里(默认值与待落盘的取值都过这一关):键 → 标量,或键 → 一层组。
22
+ * 组不许为空(`key:` 底下没子项写出去就解析不回来),不许再深一层。
23
+ */
24
+ export function assertConfigRecord(value, label) {
25
+ if (!isConfigGroup(value)) {
26
+ throw new ConfigError('CONFIG_SHAPE_INVALID', label + '必须是一个键值记录(对象),实为 ' + describeType(value));
27
+ }
28
+ for (const [key, entry] of Object.entries(value)) {
29
+ if (isConfigGroup(entry)) {
30
+ if (Object.keys(entry).length === 0) {
31
+ throw new ConfigError('CONFIG_SHAPE_INVALID', label + '的组「' + key + '」没有任何子项:子集里空组写出去解析不回来');
32
+ }
33
+ for (const [child, inner] of Object.entries(entry)) {
34
+ if (typeof inner !== 'string' && typeof inner !== 'number' && typeof inner !== 'boolean') {
35
+ throw new ConfigError('CONFIG_SHAPE_INVALID', label + '的「' + key + '.' + child + '」只能是字符串/数字/布尔(本子集只支持一层嵌套),实为 ' + describeType(inner));
36
+ }
37
+ }
38
+ continue;
39
+ }
40
+ if (typeof entry !== 'string' && typeof entry !== 'number' && typeof entry !== 'boolean') {
41
+ throw new ConfigError('CONFIG_SHAPE_INVALID', label + '的「' + key + '」只能是字符串/数字/布尔,实为 ' + describeType(entry));
42
+ }
43
+ }
44
+ }
45
+ /**
46
+ * 解析受限子集。`file` 只进报错文案。
47
+ *
48
+ * 失败一律抛 `ConfigError`(code `CONFIG_PARSE_FAILED`,带 `line`),绝不返回半份取值冒充正常。
49
+ */
50
+ export function parseConfigYaml(text, file = '<内存>') {
51
+ if (typeof text !== 'string') {
52
+ throw new ConfigError('CONFIG_PARSE_FAILED', '配置内容须为字符串,实为 ' + describeType(text) + '(文件:' + file + ')');
53
+ }
54
+ const fail = (line, why) => new ConfigError('CONFIG_PARSE_FAILED', '配置件第 ' + line + ' 行:' + why + '(文件:' + file + ')', { line });
55
+ const values = {};
56
+ const lineOf = new Map();
57
+ let openGroup = null;
58
+ let openGroupLine = 0;
59
+ const closeGroup = () => {
60
+ if (openGroup !== null && Object.keys(values[openGroup]).length === 0) {
61
+ throw fail(openGroupLine, '键「' + openGroup + '」冒号后没有值,也没有子项');
62
+ }
63
+ openGroup = null;
64
+ };
65
+ // 编辑器可能给文件开头加 BOM:先剥掉,否则第一行会报「不是「键: 值」形状」。
66
+ const source = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
67
+ const lines = source.replace(/\r\n?/g, '\n').split('\n');
68
+ for (let i = 0; i < lines.length; i += 1) {
69
+ const lineNo = i + 1;
70
+ const raw = lines[i];
71
+ if (raw.trim() === '')
72
+ continue;
73
+ if (/^\s*#/.test(raw))
74
+ continue; // 整行注释
75
+ const indentMatch = /^[ \t]*/.exec(raw);
76
+ const indent = indentMatch === null ? 0 : indentMatch[0].length;
77
+ const head = indentMatch === null ? '' : indentMatch[0];
78
+ const body = raw.slice(indent);
79
+ if (head.includes('\t'))
80
+ throw fail(lineNo, '缩进里有制表符:本子集只许空格,且嵌套缩进恰好 2 个空格');
81
+ if (body.startsWith('-'))
82
+ throw fail(lineNo, '本子集不支持列表(「-」开头的行)');
83
+ const matched = KEY_RE.exec(body);
84
+ if (matched === null)
85
+ throw fail(lineNo, '不是「键: 值」形状:' + body.trim());
86
+ const key = matched[1];
87
+ const rest = matched[2].trim();
88
+ if (indent > 0) {
89
+ if (indent !== 2)
90
+ throw fail(lineNo, '缩进必须恰好 2 个空格(本子集只支持一层嵌套),实为 ' + indent + ' 个空格');
91
+ if (openGroup === null)
92
+ throw fail(lineNo, '缩进的子项「' + key + '」上面没有开着子的顶层键');
93
+ const group = values[openGroup];
94
+ if (Object.prototype.hasOwnProperty.call(group, key)) {
95
+ throw fail(lineNo, '键「' + openGroup + '.' + key + '」重复定义(第 ' + String(lineOf.get(openGroup + '.' + key)) + ' 行已定义)');
96
+ }
97
+ if (rest === '')
98
+ throw fail(lineNo, '键「' + openGroup + '.' + key + '」冒号后没有值');
99
+ group[key] = parseScalar(rest, lineNo, fail);
100
+ lineOf.set(openGroup + '.' + key, lineNo);
101
+ continue;
102
+ }
103
+ closeGroup();
104
+ if (Object.prototype.hasOwnProperty.call(values, key)) {
105
+ throw fail(lineNo, '键「' + key + '」重复定义(第 ' + String(lineOf.get(key)) + ' 行已定义)');
106
+ }
107
+ lineOf.set(key, lineNo);
108
+ if (rest === '') {
109
+ values[key] = {};
110
+ openGroup = key;
111
+ openGroupLine = lineNo;
112
+ continue;
113
+ }
114
+ values[key] = parseScalar(rest, lineNo, fail);
115
+ }
116
+ closeGroup();
117
+ return { values, lineOf };
118
+ }
119
+ /** 标量:带引号/数字/布尔/裸字符串。 */
120
+ function parseScalar(text, lineNo, fail) {
121
+ const first = text[0];
122
+ if (first === '"' || first === "'")
123
+ return parseQuoted(text, lineNo, fail);
124
+ let cut = text.length;
125
+ for (let i = 0; i < text.length; i += 1) {
126
+ // 值后的注释:`#` 前面是空白(或它就在收尾处的行首)才开始注释——值里要写 `#` 就加引号。
127
+ if (text[i] === '#' && (i === 0 || /\s/.test(text[i - 1]))) {
128
+ cut = i;
129
+ break;
130
+ }
131
+ }
132
+ const body = text.slice(0, cut).trim();
133
+ if (body === '')
134
+ throw fail(lineNo, '冒号后没有值(只有注释)');
135
+ if (body === 'true')
136
+ return true;
137
+ if (body === 'false')
138
+ return false;
139
+ if (NUMBER_RE.test(body))
140
+ return Number(body);
141
+ return body;
142
+ }
143
+ /** 单双引号字符串:双引号认 `\n`/`\t`/`\"`/`\\`,单引号内 `''` 表示一个单引号。 */
144
+ function parseQuoted(text, lineNo, fail) {
145
+ const quote = text[0];
146
+ let out = '';
147
+ let i = 1;
148
+ let closed = false;
149
+ while (i < text.length) {
150
+ const ch = text[i];
151
+ if (quote === '"' && ch === '\\') {
152
+ const next = text[i + 1];
153
+ if (next === undefined)
154
+ break;
155
+ if (next === 'n')
156
+ out += '\n';
157
+ else if (next === 't')
158
+ out += '\t';
159
+ else if (next === '"')
160
+ out += '"';
161
+ else if (next === '\\')
162
+ out += '\\';
163
+ else
164
+ out += next; // 认不得的转义:原样留那个字符,不吞
165
+ i += 2;
166
+ continue;
167
+ }
168
+ if (ch === quote) {
169
+ if (quote === "'" && text[i + 1] === "'") {
170
+ out += "'";
171
+ i += 2;
172
+ continue;
173
+ }
174
+ closed = true;
175
+ i += 1;
176
+ break;
177
+ }
178
+ out += ch;
179
+ i += 1;
180
+ }
181
+ if (!closed)
182
+ throw fail(lineNo, '引号没有闭合');
183
+ const tail = text.slice(i).trim();
184
+ if (tail !== '' && !tail.startsWith('#'))
185
+ throw fail(lineNo, '引号闭合后还有多余内容:' + tail);
186
+ return out;
187
+ }
188
+ /** 一个标量怎么写出去:数字与布尔裸写,字符串只在必要时加双引号。 */
189
+ function formatScalar(value, where) {
190
+ if (typeof value === 'boolean')
191
+ return value ? 'true' : 'false';
192
+ if (typeof value === 'number') {
193
+ if (!Number.isFinite(value)) {
194
+ throw new ConfigError('CONFIG_SHAPE_INVALID', '「' + where + '」不是有限数字,写不出去:' + String(value));
195
+ }
196
+ const text = String(value);
197
+ if (!NUMBER_RE.test(text)) {
198
+ throw new ConfigError('CONFIG_SHAPE_INVALID', '「' + where + '」写不成子集里的十进制数字:' + text);
199
+ }
200
+ return text;
201
+ }
202
+ if (value === '' || NUMBER_RE.test(value) || value === 'true' || value === 'false' || !PLAIN_RE.test(value)) {
203
+ return JSON.stringify(value);
204
+ }
205
+ return value;
206
+ }
207
+ /** 一份配置怎么写出去:调用的键序即落盘的键序(常用项在前由调用方排),一行一键。 */
208
+ export function formatConfigYaml(values) {
209
+ assertConfigRecord(values, '配置取值');
210
+ const out = [];
211
+ for (const [key, entry] of Object.entries(values)) {
212
+ if (isConfigGroup(entry)) {
213
+ out.push(key + ':');
214
+ for (const [child, inner] of Object.entries(entry))
215
+ out.push(' ' + child + ': ' + formatScalar(inner, key + '.' + child));
216
+ continue;
217
+ }
218
+ out.push(key + ': ' + formatScalar(entry, key));
219
+ }
220
+ return out.length === 0 ? '' : out.join('\n') + '\n';
221
+ }
222
+ //# sourceMappingURL=yaml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml.js","sourceRoot":"","sources":["../../src/config/yaml.ts"],"names":[],"mappings":"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"}
package/dist/errors.d.ts CHANGED
@@ -20,4 +20,16 @@ export declare class RunnerError extends LinkCoreError {
20
20
  cause?: unknown;
21
21
  });
22
22
  }
23
+ /**
24
+ * 配置文件出错:解析不了、键不认识、类型不对、读写失败、测试缺隔离。
25
+ * `line` 是配置件里的行号(1 起,对不上行号时为 null);人话报错里已经带上它,
26
+ * 另给这个字段是为了让设置页能直接高亮那一行。
27
+ */
28
+ export declare class ConfigError extends LinkCoreError {
29
+ readonly line: number | null;
30
+ constructor(code: string, message: string, options?: {
31
+ cause?: unknown;
32
+ line?: number | null;
33
+ });
34
+ }
23
35
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAKzE;AAED,qBAAa,aAAc,SAAQ,aAAa;gBAClC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED,qBAAa,aAAc,SAAQ,aAAa;gBAClC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED,qBAAa,WAAY,SAAQ,aAAa;gBAChC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAKzE;AAED,qBAAa,aAAc,SAAQ,aAAa;gBAClC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED,qBAAa,aAAc,SAAQ,aAAa;gBAClC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED,qBAAa,WAAY,SAAQ,aAAa;gBAChC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED;;;;GAIG;AACH,qBAAa,WAAY,SAAQ,aAAa;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;gBACjB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAK/F"}
package/dist/errors.js CHANGED
@@ -25,4 +25,17 @@ export class RunnerError extends LinkCoreError {
25
25
  this.name = 'RunnerError';
26
26
  }
27
27
  }
28
+ /**
29
+ * 配置文件出错:解析不了、键不认识、类型不对、读写失败、测试缺隔离。
30
+ * `line` 是配置件里的行号(1 起,对不上行号时为 null);人话报错里已经带上它,
31
+ * 另给这个字段是为了让设置页能直接高亮那一行。
32
+ */
33
+ export class ConfigError extends LinkCoreError {
34
+ line;
35
+ constructor(code, message, options) {
36
+ super(code, message, options);
37
+ this.name = 'ConfigError';
38
+ this.line = options?.line ?? null;
39
+ }
40
+ }
28
41
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,IAAI,CAAS;IACtB,YAAY,IAAY,EAAE,OAAe,EAAE,OAA6B;QACtE,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,aAAa;IAC9C,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,kBAAkB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,aAAa;IAC9C,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,sBAAsB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,WAAY,SAAQ,aAAa;IAC5C,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,eAAe,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,IAAI,CAAS;IACtB,YAAY,IAAY,EAAE,OAAe,EAAE,OAA6B;QACtE,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,aAAa;IAC9C,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,kBAAkB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,aAAa;IAC9C,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,sBAAsB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,WAAY,SAAQ,aAAa;IAC5C,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,eAAe,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,WAAY,SAAQ,aAAa;IACnC,IAAI,CAAgB;IAC7B,YAAY,IAAY,EAAE,OAAe,EAAE,OAAmD;QAC5F,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,IAAI,CAAC;IACpC,CAAC;CACF"}
package/dist/index.d.ts CHANGED
@@ -4,5 +4,7 @@ export { createRegistry, parseRegistryKey } from './registry.js';
4
4
  export type { Registry, RegistryKey, ParsedKey } from './registry.js';
5
5
  export { runCombo } from './runner.js';
6
6
  export type { RunRequest, Fetcher } from './runner.js';
7
- export { LinkCoreError, EnvelopeError, RegistryError, RunnerError } from './errors.js';
7
+ export { LinkCoreError, EnvelopeError, RegistryError, RunnerError, ConfigError } from './errors.js';
8
+ export { configPaths, loadConfig, saveConfig, resetConfig } from './config/index.js';
9
+ export type { ConfigPaths, LoadedConfig, ConfigRecord, ConfigGroup, ConfigValue } from './config/index.js';
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC9H,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC9H,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEpG,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrF,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
- // link-core:沉底零依赖。envelope 全字段 + registry key 类型 + runner 契约。
1
+ // link-core:沉底零依赖。envelope 全字段 + registry key 类型 + runner 契约 + config 配置文件读写。
2
2
  export { ENVELOPE_VERSION, ENVELOPE_SHAPES, createEnvelope, parseEnvelope, isEnvelope, assertShapeData } from './envelope.js';
3
3
  export { createRegistry, parseRegistryKey } from './registry.js';
4
4
  export { runCombo } from './runner.js';
5
- export { LinkCoreError, EnvelopeError, RegistryError, RunnerError } from './errors.js';
5
+ export { LinkCoreError, EnvelopeError, RegistryError, RunnerError, ConfigError } from './errors.js';
6
+ // 配置件(#694):定位/读写校验/重置为默认,见 src/config/。
7
+ export { configPaths, loadConfig, saveConfig, resetConfig } from './config/index.js';
6
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAE9H,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAE9H,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACpG,yCAAyC;AACzC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "base-link-core",
3
- "version": "0.3.0",
3
+ "version": "0.3.4",
4
4
  "description": "联动沉底包:envelope+registry+runner+校验,零依赖",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",