126 lines
4.2 KiB
TypeScript
126 lines
4.2 KiB
TypeScript
import crypto from 'crypto';
|
|
|
|
export const KNOWLEDGE_MODULE_ID = 'HL-MOD-KNOWLEDGE-001';
|
|
|
|
export interface ModuleManifest {
|
|
id: string;
|
|
name: string;
|
|
version: string;
|
|
kind: 'native-application';
|
|
state: 'LIVE';
|
|
description: string;
|
|
capabilities: string[];
|
|
dataPolicy: string;
|
|
}
|
|
|
|
export interface ChannelModuleState {
|
|
id: string;
|
|
installed: boolean;
|
|
mounted: boolean;
|
|
order: number;
|
|
}
|
|
|
|
export interface ChannelState {
|
|
schema: 'hololake.personal-channel-state/v1';
|
|
channelId: string;
|
|
revision: number;
|
|
updatedAt: string;
|
|
modules: ChannelModuleState[];
|
|
}
|
|
|
|
export interface ChannelPatch {
|
|
operation: 'set_module_state';
|
|
moduleId: string;
|
|
installed?: boolean;
|
|
mounted?: boolean;
|
|
}
|
|
|
|
export interface ChannelReceipt {
|
|
schema: 'hololake.channel-receipt/v1';
|
|
id: string;
|
|
operation: ChannelPatch;
|
|
before: ChannelState;
|
|
after: ChannelState;
|
|
createdAt: string;
|
|
reversible: true;
|
|
}
|
|
|
|
export const MODULE_REGISTRY: ModuleManifest[] = [{
|
|
id: KNOWLEDGE_MODULE_ID,
|
|
name: '知识库',
|
|
version: '0.8.0',
|
|
kind: 'native-application',
|
|
state: 'LIVE',
|
|
description: 'Git 驱动的个人知识空间,支持导入、编辑、检索、历史与 HoloLake 语言操作。',
|
|
capabilities: [
|
|
'knowledge.import',
|
|
'knowledge.read',
|
|
'knowledge.search',
|
|
'knowledge.edit',
|
|
'knowledge.history',
|
|
],
|
|
dataPolicy: '移除模块只卸载频道入口,不删除用户文档、历史或服务器绑定。',
|
|
}];
|
|
|
|
export function defaultChannelState(now = new Date().toISOString()): ChannelState {
|
|
return {
|
|
schema: 'hololake.personal-channel-state/v1',
|
|
channelId: 'CHANNEL-PERSONAL-DEFAULT',
|
|
revision: 0,
|
|
updatedAt: now,
|
|
modules: [{ id: KNOWLEDGE_MODULE_ID, installed: true, mounted: true, order: 0 }],
|
|
};
|
|
}
|
|
|
|
export function normalizeChannelState(input: unknown): ChannelState {
|
|
const fallback = defaultChannelState();
|
|
if (!input || typeof input !== 'object') return fallback;
|
|
const candidate = input as Partial<ChannelState>;
|
|
const known = new Set(MODULE_REGISTRY.map(module => module.id));
|
|
const modules = Array.isArray(candidate.modules)
|
|
? candidate.modules
|
|
.filter(module => module && known.has(module.id))
|
|
.map((module, index) => ({
|
|
id: module.id,
|
|
installed: module.installed !== false,
|
|
mounted: module.installed !== false && module.mounted !== false,
|
|
order: Number.isFinite(module.order) ? Number(module.order) : index,
|
|
}))
|
|
: fallback.modules;
|
|
for (const manifest of MODULE_REGISTRY) {
|
|
if (!modules.some(module => module.id === manifest.id)) {
|
|
modules.push({ id: manifest.id, installed: false, mounted: false, order: modules.length });
|
|
}
|
|
}
|
|
return {
|
|
schema: 'hololake.personal-channel-state/v1',
|
|
channelId: typeof candidate.channelId === 'string' && candidate.channelId ? candidate.channelId : fallback.channelId,
|
|
revision: Number.isInteger(candidate.revision) && Number(candidate.revision) >= 0 ? Number(candidate.revision) : 0,
|
|
updatedAt: typeof candidate.updatedAt === 'string' ? candidate.updatedAt : fallback.updatedAt,
|
|
modules: modules.sort((a, b) => a.order - b.order),
|
|
};
|
|
}
|
|
|
|
export function applyChannelPatch(currentInput: ChannelState, patch: ChannelPatch, now = new Date().toISOString()): ChannelReceipt {
|
|
const before = normalizeChannelState(currentInput);
|
|
if (patch.operation !== 'set_module_state') throw new Error('不支持的频道动作');
|
|
if (!MODULE_REGISTRY.some(module => module.id === patch.moduleId)) throw new Error('模块未在光湖注册表登记');
|
|
if (typeof patch.installed !== 'boolean' && typeof patch.mounted !== 'boolean') throw new Error('频道动作没有状态变化');
|
|
|
|
const modules = before.modules.map(module => {
|
|
if (module.id !== patch.moduleId) return module;
|
|
const installed = typeof patch.installed === 'boolean' ? patch.installed : module.installed;
|
|
const mounted = installed && (typeof patch.mounted === 'boolean' ? patch.mounted : module.mounted);
|
|
return { ...module, installed, mounted };
|
|
});
|
|
const after = { ...before, revision: before.revision + 1, updatedAt: now, modules };
|
|
return {
|
|
schema: 'hololake.channel-receipt/v1',
|
|
id: `HL-CHANNEL-RCPT-${crypto.randomUUID()}`,
|
|
operation: patch,
|
|
before,
|
|
after,
|
|
createdAt: now,
|
|
reversible: true,
|
|
};
|
|
}
|