dsh-oh-my-terminal 0.2.2 → 0.2.3
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/lib/index.js +9 -2
- package/package.json +1 -1
- package/src/index.ts +4 -3
- package/src/settings/index.ts +22 -1
- package/tests/unit/settings-integration.test.ts +99 -0
package/lib/index.js
CHANGED
|
@@ -1014,9 +1014,16 @@ function createSettingsBridgeRoutes(settings, namespace, runtimeProfiles) {
|
|
|
1014
1014
|
}
|
|
1015
1015
|
|
|
1016
1016
|
// src/settings/index.ts
|
|
1017
|
-
function registerSettingsIntegration(ctx, runtimeProfiles) {
|
|
1017
|
+
function registerSettingsIntegration(ctx, schema, runtimeProfiles) {
|
|
1018
1018
|
ctx.inject(["settings"], (sctx) => {
|
|
1019
1019
|
sctx.effect(() => sctx.settings.configure({ auto: false }, ctx.fiber));
|
|
1020
|
+
const registrar = sctx.settings;
|
|
1021
|
+
try {
|
|
1022
|
+
registrar.register(SETTINGS_NS, schema);
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1025
|
+
if (!message.includes("already registered")) throw error;
|
|
1026
|
+
}
|
|
1020
1027
|
});
|
|
1021
1028
|
ctx.inject(["webServer", "settings"], (sctx) => {
|
|
1022
1029
|
sctx.effect(() => {
|
|
@@ -1277,7 +1284,7 @@ function apply(ctx, config) {
|
|
|
1277
1284
|
upgradeDisposers
|
|
1278
1285
|
})
|
|
1279
1286
|
});
|
|
1280
|
-
registerSettingsIntegration(ctx, cachedProfiles);
|
|
1287
|
+
registerSettingsIntegration(ctx, Config, cachedProfiles);
|
|
1281
1288
|
ctx.effect(() => {
|
|
1282
1289
|
return () => {
|
|
1283
1290
|
disposeRoute();
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -521,9 +521,10 @@ export function apply(ctx: Context, config: Config): void {
|
|
|
521
521
|
});
|
|
522
522
|
|
|
523
523
|
// —— settings 集成(可选,软探测)——
|
|
524
|
-
// 传入
|
|
525
|
-
//
|
|
526
|
-
|
|
524
|
+
// 传入 Config schema:auto=false 下命名空间需显式注册(见 settings/index.ts),
|
|
525
|
+
// 缺注册则设置卡片静默不渲染;cachedProfiles 让 describe 端点返回运行时合并
|
|
526
|
+
// 探测结果后的完整配置表,而非配置系统持久化值(可能是空串)
|
|
527
|
+
registerSettingsIntegration(ctx, Config, cachedProfiles);
|
|
527
528
|
|
|
528
529
|
// —— 插件销毁清理 ——
|
|
529
530
|
ctx.effect(() => {
|
package/src/settings/index.ts
CHANGED
|
@@ -37,14 +37,18 @@ export { createSettingsBridgeRoutes } from './bridge.js';
|
|
|
37
37
|
* 逐一调用清理。
|
|
38
38
|
*
|
|
39
39
|
* @param ctx - cordis 上下文(插件主入口的 apply 参数)
|
|
40
|
+
* @param schema - 插件 Config 的 schemastery schema,用于注册 settings 命名空间。
|
|
41
|
+
* auto=false 下服务不会自动注册,缺了这一步 describe/mutate 恒报「命名空间
|
|
42
|
+
* 不存在」,前端配置卡片静默不渲染(无报错、无入口)
|
|
40
43
|
* @param runtimeProfiles - 运行时终端配置表(探测+合并后的完整数组),传给 bridge
|
|
41
44
|
* 供 describe 端点覆盖持久化的 terminalProfiles 值
|
|
42
45
|
*/
|
|
43
46
|
export function registerSettingsIntegration(
|
|
44
47
|
ctx: Context,
|
|
48
|
+
schema: unknown,
|
|
45
49
|
runtimeProfiles?: unknown[],
|
|
46
50
|
): void {
|
|
47
|
-
// 1.
|
|
51
|
+
// 1. 注册 settings 命名空间 + 关闭自动模式——软探测,服务不存在时不激活
|
|
48
52
|
ctx.inject(['settings'], (sctx) => {
|
|
49
53
|
// settings.configure({ auto: false }, fiber) 关闭自动模式:
|
|
50
54
|
// - auto=true:settings 服务自动从 profile patch 读配置并写回插件 Config
|
|
@@ -53,6 +57,23 @@ export function registerSettingsIntegration(
|
|
|
53
57
|
//
|
|
54
58
|
// fiber 参数绑定配置生命周期到当前 effect——effect 销毁时配置自动清理
|
|
55
59
|
sctx.effect(() => sctx.settings.configure({ auto: false }, ctx.fiber));
|
|
60
|
+
|
|
61
|
+
// auto=false 同时关闭了命名空间的自动注册——必须在此显式 register,否则
|
|
62
|
+
// describe/mutate 恒报「命名空间不存在」,前端配置卡片静默不渲染。重复注册
|
|
63
|
+
// (热重载、重复 apply)抛 "already registered",作为幂等情形吞掉;其余注册
|
|
64
|
+
// 失败如实上抛,不把失败包装成成功。
|
|
65
|
+
/** settings 服务注册面的最小鸭子类型(对齐官方 SettingsProvider.register) */
|
|
66
|
+
interface SettingsRegistrar {
|
|
67
|
+
/** 注册命名空间 schema;重复注册抛 "already registered" */
|
|
68
|
+
register(ns: string, schema: unknown): unknown;
|
|
69
|
+
}
|
|
70
|
+
const registrar = sctx.settings as unknown as SettingsRegistrar;
|
|
71
|
+
try {
|
|
72
|
+
registrar.register(SETTINGS_NS, schema);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
75
|
+
if (!message.includes('already registered')) throw error;
|
|
76
|
+
}
|
|
56
77
|
});
|
|
57
78
|
|
|
58
79
|
// 2. 注册 settings bridge 路由(describe + mutate)——软探测,服务不存在时不激活
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file settings 集成注册单元测试
|
|
3
|
+
* @description 回归锁定 registerSettingsIntegration 的命名空间注册契约:
|
|
4
|
+
* auto=false 下 settings 服务不会自动注册命名空间,插件必须显式
|
|
5
|
+
* register(SETTINGS_NS, schema),否则 describe/mutate 恒报
|
|
6
|
+
* 「命名空间不存在」——前端配置卡片静默不渲染(无报错、无入口)。
|
|
7
|
+
* 本测试用假 settings 服务断言:注册确实发生且 schema 原样透传、
|
|
8
|
+
* 重复注册幂等吞掉、其余注册失败如实上抛(不把失败包装成成功)。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it } from 'node:test';
|
|
12
|
+
import assert from 'node:assert/strict';
|
|
13
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
14
|
+
import { registerSettingsIntegration, SETTINGS_NS } from '../../src/settings/index.js';
|
|
15
|
+
|
|
16
|
+
/** register 调用记录 */
|
|
17
|
+
interface RegisterCall {
|
|
18
|
+
/** 被注册的命名空间 */
|
|
19
|
+
ns: string;
|
|
20
|
+
/** 传入的 schema 引用 */
|
|
21
|
+
schema: unknown;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** 假 settings 服务:只实现集成代码用到的 configure / register / describe / mutate */
|
|
25
|
+
function makeFakeSettings(options: { failRegisterWith?: Error } = {}): {
|
|
26
|
+
settings: Record<string, unknown>;
|
|
27
|
+
registerCalls: RegisterCall[];
|
|
28
|
+
configureCalls: unknown[];
|
|
29
|
+
} {
|
|
30
|
+
const registerCalls: RegisterCall[] = [];
|
|
31
|
+
const configureCalls: unknown[] = [];
|
|
32
|
+
const settings = {
|
|
33
|
+
configure(value: unknown): void {
|
|
34
|
+
configureCalls.push(value);
|
|
35
|
+
},
|
|
36
|
+
register(ns: string, schema: unknown): unknown {
|
|
37
|
+
if (options.failRegisterWith !== undefined) throw options.failRegisterWith;
|
|
38
|
+
registerCalls.push({ ns, schema });
|
|
39
|
+
return {
|
|
40
|
+
get: () => ({}),
|
|
41
|
+
watch: () => (): void => undefined,
|
|
42
|
+
update: async (): Promise<void> => undefined,
|
|
43
|
+
replace: async (): Promise<void> => undefined,
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
describe: async (): Promise<unknown> => ({ ok: true }),
|
|
47
|
+
mutate: async (): Promise<unknown> => ({ ok: true }),
|
|
48
|
+
};
|
|
49
|
+
return { settings, registerCalls, configureCalls };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** 假 cordis 上下文:inject 直接回调,effect 立即执行并回收 cleanup */
|
|
53
|
+
function makeFakeContext(settings: unknown): Context {
|
|
54
|
+
const ctx = {
|
|
55
|
+
fiber: {},
|
|
56
|
+
inject(_deps: string[], callback: (sctx: Record<string, unknown>) => void): void {
|
|
57
|
+
callback({
|
|
58
|
+
settings,
|
|
59
|
+
webServer: { register: () => (): void => undefined },
|
|
60
|
+
effect: (fn: () => unknown): void => {
|
|
61
|
+
const returned = fn();
|
|
62
|
+
if (typeof returned === 'function') returned();
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
return ctx as unknown as Context;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
describe('settings 集成注册', () => {
|
|
71
|
+
it('settings 服务就位时显式注册命名空间并透传 schema', () => {
|
|
72
|
+
const { settings, registerCalls, configureCalls } = makeFakeSettings();
|
|
73
|
+
const schema = { marker: 'config-schema' };
|
|
74
|
+
registerSettingsIntegration(makeFakeContext(settings), schema, []);
|
|
75
|
+
assert.equal(registerCalls.length, 1);
|
|
76
|
+
assert.equal(registerCalls[0]?.ns, SETTINGS_NS);
|
|
77
|
+
assert.equal(registerCalls[0]?.schema, schema);
|
|
78
|
+
assert.deepEqual(configureCalls, [{ auto: false }]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('重复注册视为幂等情形,吞掉 already registered 不上抛', () => {
|
|
82
|
+
const { settings } = makeFakeSettings({
|
|
83
|
+
failRegisterWith: new Error('settings namespace "terminal-panel" is already registered'),
|
|
84
|
+
});
|
|
85
|
+
assert.doesNotThrow(() => {
|
|
86
|
+
registerSettingsIntegration(makeFakeContext(settings), { marker: 'x' });
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('其他注册失败如实上抛,不把失败包装成成功', () => {
|
|
91
|
+
const { settings } = makeFakeSettings({
|
|
92
|
+
failRegisterWith: new Error('settings namespace "terminal-panel" 存储段损坏'),
|
|
93
|
+
});
|
|
94
|
+
assert.throws(
|
|
95
|
+
() => registerSettingsIntegration(makeFakeContext(settings), { marker: 'x' }),
|
|
96
|
+
/存储段损坏/u,
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
});
|