@coralai/sps-cli 0.49.16 → 0.50.1
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/dist/commands/cardAdd.d.ts.map +1 -1
- package/dist/commands/cardAdd.js +47 -67
- package/dist/commands/cardAdd.js.map +1 -1
- package/dist/commands/cardMarkComplete.js +1 -1
- package/dist/commands/cardMarkComplete.js.map +1 -1
- package/dist/commands/cardMarkStarted.js +1 -1
- package/dist/commands/cardMarkStarted.js.map +1 -1
- package/dist/commands/consoleCommand.js +3 -3
- package/dist/commands/consoleCommand.js.map +1 -1
- package/dist/commands/hookCommand.d.ts +25 -0
- package/dist/commands/hookCommand.d.ts.map +1 -1
- package/dist/commands/hookCommand.js +2 -2
- package/dist/commands/hookCommand.js.map +1 -1
- package/dist/console/index.d.ts +15 -0
- package/dist/console/index.d.ts.map +1 -0
- package/dist/console/index.js +180 -0
- package/dist/console/index.js.map +1 -0
- package/dist/console/lib/lockFile.d.ts +17 -0
- package/dist/console/lib/lockFile.d.ts.map +1 -0
- package/dist/console/lib/lockFile.js +61 -0
- package/dist/console/lib/lockFile.js.map +1 -0
- package/dist/console/lib/portPicker.d.ts +3 -0
- package/dist/console/lib/portPicker.d.ts.map +1 -0
- package/dist/console/lib/portPicker.js +25 -0
- package/dist/console/lib/portPicker.js.map +1 -0
- package/dist/console/lib/resultToJson.d.ts +18 -0
- package/dist/console/lib/resultToJson.d.ts.map +1 -0
- package/dist/console/lib/resultToJson.js +19 -0
- package/dist/console/lib/resultToJson.js.map +1 -0
- package/dist/console/routes/cards.d.ts +26 -0
- package/dist/console/routes/cards.d.ts.map +1 -0
- package/dist/console/routes/cards.js +85 -0
- package/dist/console/routes/cards.js.map +1 -0
- package/dist/console/routes/chat.d.ts +37 -0
- package/dist/console/routes/chat.d.ts.map +1 -0
- package/dist/console/routes/chat.js +378 -0
- package/dist/console/routes/chat.js.map +1 -0
- package/dist/console/routes/logs.d.ts +17 -0
- package/dist/console/routes/logs.d.ts.map +1 -0
- package/dist/console/routes/logs.js +159 -0
- package/dist/console/routes/logs.js.map +1 -0
- package/dist/console/routes/pipeline.d.ts +10 -0
- package/dist/console/routes/pipeline.d.ts.map +1 -0
- package/dist/console/routes/pipeline.js +39 -0
- package/dist/console/routes/pipeline.js.map +1 -0
- package/dist/console/routes/projects.d.ts +15 -0
- package/dist/console/routes/projects.d.ts.map +1 -0
- package/dist/console/routes/projects.js +132 -0
- package/dist/console/routes/projects.js.map +1 -0
- package/dist/console/routes/skills.d.ts +4 -0
- package/dist/console/routes/skills.d.ts.map +1 -0
- package/dist/console/routes/skills.js +91 -0
- package/dist/console/routes/skills.js.map +1 -0
- package/dist/console/routes/system.d.ts +10 -0
- package/dist/console/routes/system.d.ts.map +1 -0
- package/dist/console/routes/system.js +84 -0
- package/dist/console/routes/system.js.map +1 -0
- package/dist/console/routes/workers.d.ts +5 -0
- package/dist/console/routes/workers.d.ts.map +1 -0
- package/dist/console/routes/workers.js +149 -0
- package/dist/console/routes/workers.js.map +1 -0
- package/dist/console/sse/eventBus.d.ts +25 -0
- package/dist/console/sse/eventBus.d.ts.map +1 -0
- package/dist/console/sse/eventBus.js +32 -0
- package/dist/console/sse/eventBus.js.map +1 -0
- package/dist/console/sse/projectStream.d.ts +13 -0
- package/dist/console/sse/projectStream.d.ts.map +1 -0
- package/dist/console/sse/projectStream.js +84 -0
- package/dist/console/sse/projectStream.js.map +1 -0
- package/dist/core/checklist.d.ts +1 -1
- package/dist/core/state.d.ts +1 -1
- package/dist/engines/MonitorEngine.d.ts +1 -1
- package/dist/engines/SchedulerEngine.d.ts +1 -1
- package/dist/engines/StageEngine.d.ts +1 -1
- package/dist/infra/chokidarWatchers.d.ts +38 -0
- package/dist/infra/chokidarWatchers.d.ts.map +1 -0
- package/dist/infra/chokidarWatchers.js +213 -0
- package/dist/infra/chokidarWatchers.js.map +1 -0
- package/dist/infra/clock.d.ts +35 -0
- package/dist/infra/clock.d.ts.map +1 -0
- package/dist/infra/clock.js +45 -0
- package/dist/infra/clock.js.map +1 -0
- package/dist/infra/filesystem.d.ts +89 -0
- package/dist/infra/filesystem.d.ts.map +1 -0
- package/dist/infra/filesystem.js +247 -0
- package/dist/infra/filesystem.js.map +1 -0
- package/dist/infra/spawn.d.ts +67 -0
- package/dist/infra/spawn.d.ts.map +1 -0
- package/dist/infra/spawn.js +116 -0
- package/dist/infra/spawn.js.map +1 -0
- package/dist/infra/sseBus.d.ts +36 -0
- package/dist/infra/sseBus.d.ts.map +1 -0
- package/dist/infra/sseBus.js +72 -0
- package/dist/infra/sseBus.js.map +1 -0
- package/dist/interfaces/ACPClient.d.ts +1 -1
- package/dist/interfaces/ACPClient.d.ts.map +1 -1
- package/dist/interfaces/RepoBackend.d.ts +1 -1
- package/dist/interfaces/TaskBackend.d.ts +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/providers/GitLabRepoBackend.d.ts +1 -1
- package/dist/providers/LocalACPClient.d.ts +1 -1
- package/dist/providers/LocalACPClient.d.ts.map +1 -1
- package/dist/providers/MarkdownTaskBackend.d.ts +1 -1
- package/dist/services/CardService.d.ts +86 -0
- package/dist/services/CardService.d.ts.map +1 -0
- package/dist/services/CardService.js +313 -0
- package/dist/services/CardService.js.map +1 -0
- package/dist/services/ChatService.d.ts +62 -0
- package/dist/services/ChatService.d.ts.map +1 -0
- package/dist/services/ChatService.js +157 -0
- package/dist/services/ChatService.js.map +1 -0
- package/dist/services/LogService.d.ts +46 -0
- package/dist/services/LogService.d.ts.map +1 -0
- package/dist/services/LogService.js +185 -0
- package/dist/services/LogService.js.map +1 -0
- package/dist/services/PipelineService.d.ts +71 -0
- package/dist/services/PipelineService.d.ts.map +1 -0
- package/dist/services/PipelineService.js +349 -0
- package/dist/services/PipelineService.js.map +1 -0
- package/dist/services/ProjectService.d.ts +105 -0
- package/dist/services/ProjectService.d.ts.map +1 -0
- package/dist/services/ProjectService.js +346 -0
- package/dist/services/ProjectService.js.map +1 -0
- package/dist/services/SkillService.d.ts +38 -0
- package/dist/services/SkillService.d.ts.map +1 -0
- package/dist/services/SkillService.js +301 -0
- package/dist/services/SkillService.js.map +1 -0
- package/dist/services/SystemService.d.ts +67 -0
- package/dist/services/SystemService.d.ts.map +1 -0
- package/dist/services/SystemService.js +278 -0
- package/dist/services/SystemService.js.map +1 -0
- package/dist/services/WorkerService.d.ts +83 -0
- package/dist/services/WorkerService.d.ts.map +1 -0
- package/dist/services/WorkerService.js +262 -0
- package/dist/services/WorkerService.js.map +1 -0
- package/dist/services/container.d.ts +44 -0
- package/dist/services/container.d.ts.map +1 -0
- package/dist/services/container.js +104 -0
- package/dist/services/container.js.map +1 -0
- package/dist/services/defaults.d.ts +15 -0
- package/dist/services/defaults.d.ts.map +1 -0
- package/dist/services/defaults.js +11 -0
- package/dist/services/defaults.js.map +1 -0
- package/dist/services/executors.d.ts +38 -0
- package/dist/services/executors.d.ts.map +1 -0
- package/dist/services/executors.js +157 -0
- package/dist/services/executors.js.map +1 -0
- package/dist/services/ports.d.ts +17 -0
- package/dist/services/ports.d.ts.map +1 -0
- package/dist/services/ports.js +2 -0
- package/dist/services/ports.js.map +1 -0
- package/dist/shared/domainEvents.d.ts +118 -0
- package/dist/shared/domainEvents.d.ts.map +1 -0
- package/dist/shared/domainEvents.js +40 -0
- package/dist/shared/domainEvents.js.map +1 -0
- package/dist/shared/errors.d.ts +59 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/errors.js +79 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/result.d.ts +51 -0
- package/dist/shared/result.d.ts.map +1 -0
- package/dist/shared/result.js +48 -0
- package/dist/shared/result.js.map +1 -0
- package/dist/shared/runtimePaths.d.ts +78 -0
- package/dist/shared/runtimePaths.d.ts.map +1 -0
- package/dist/shared/runtimePaths.js +179 -0
- package/dist/shared/runtimePaths.js.map +1 -0
- package/dist/shared/runtimeSchemas.d.ts +324 -0
- package/dist/shared/runtimeSchemas.d.ts.map +1 -0
- package/dist/shared/runtimeSchemas.js +124 -0
- package/dist/shared/runtimeSchemas.js.map +1 -0
- package/dist/shared/types.d.ts +97 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +11 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// ─── WorkerExecutor —— 直接调 StageEngine 启卡 / RuntimeStore kill ─────
|
|
2
|
+
export class DefaultWorkerExecutor {
|
|
3
|
+
async launch(project, seq) {
|
|
4
|
+
const { ProjectContext } = await import('../core/context.js');
|
|
5
|
+
const { ProjectPipelineAdapter } = await import('../core/projectPipelineAdapter.js');
|
|
6
|
+
const { RuntimeStore } = await import('../core/runtimeStore.js');
|
|
7
|
+
const { SPSEventHandler } = await import('../engines/EventHandler.js');
|
|
8
|
+
const { StageEngine } = await import('../engines/StageEngine.js');
|
|
9
|
+
const { CompletionJudge } = await import('../manager/completion-judge.js');
|
|
10
|
+
const { ProcessSupervisor } = await import('../manager/supervisor.js');
|
|
11
|
+
const { WorkerManagerImpl } = await import('../manager/worker-manager-impl.js');
|
|
12
|
+
const { createAgentRuntime, createNotifier, createRepoBackend, createTaskBackend } = await import('../providers/registry.js');
|
|
13
|
+
const ctx = ProjectContext.load(project);
|
|
14
|
+
const pipelineAdapter = new ProjectPipelineAdapter(ctx.config, ctx.paths.repoDir);
|
|
15
|
+
const allStateNames = [
|
|
16
|
+
...new Set([
|
|
17
|
+
pipelineAdapter.states.planning,
|
|
18
|
+
pipelineAdapter.states.backlog,
|
|
19
|
+
pipelineAdapter.states.ready,
|
|
20
|
+
pipelineAdapter.states.done,
|
|
21
|
+
...pipelineAdapter.stages.flatMap((s) => [s.triggerState, s.activeState, s.onCompleteState]),
|
|
22
|
+
].filter(Boolean)),
|
|
23
|
+
];
|
|
24
|
+
const taskBackend = createTaskBackend(ctx.config, allStateNames);
|
|
25
|
+
const repoBackend = createRepoBackend(ctx.config);
|
|
26
|
+
const notifier = createNotifier(ctx.config);
|
|
27
|
+
const supervisor = new ProcessSupervisor();
|
|
28
|
+
const completionJudge = new CompletionJudge();
|
|
29
|
+
const agentRuntime = createAgentRuntime(ctx);
|
|
30
|
+
const workerManager = new WorkerManagerImpl({
|
|
31
|
+
supervisor,
|
|
32
|
+
completionJudge,
|
|
33
|
+
agentRuntime: agentRuntime ?? null,
|
|
34
|
+
stateFile: ctx.paths.stateFile,
|
|
35
|
+
maxWorkers: ctx.maxWorkers,
|
|
36
|
+
});
|
|
37
|
+
const runtimeStore = new RuntimeStore({
|
|
38
|
+
paths: { stateFile: ctx.paths.stateFile },
|
|
39
|
+
maxWorkers: ctx.maxWorkers,
|
|
40
|
+
});
|
|
41
|
+
const eventHandler = new SPSEventHandler({
|
|
42
|
+
taskBackend,
|
|
43
|
+
notifier,
|
|
44
|
+
runtimeStore,
|
|
45
|
+
project,
|
|
46
|
+
pipelineAdapter,
|
|
47
|
+
});
|
|
48
|
+
workerManager.onEvent((event) => eventHandler.handle(event));
|
|
49
|
+
const firstStage = pipelineAdapter.stages[0];
|
|
50
|
+
if (!firstStage) {
|
|
51
|
+
throw new Error(`Project ${project} has no pipeline stages`);
|
|
52
|
+
}
|
|
53
|
+
const engine = new StageEngine(ctx, firstStage, 0, pipelineAdapter.stages.length, taskBackend, repoBackend, workerManager, pipelineAdapter, notifier);
|
|
54
|
+
const result = await engine.launchSingle(String(seq), { dryRun: false });
|
|
55
|
+
if (result.status !== 'ok') {
|
|
56
|
+
const detail = typeof result.details === 'object' && result.details
|
|
57
|
+
? JSON.stringify(result.details)
|
|
58
|
+
: String(result.details ?? 'unknown');
|
|
59
|
+
throw new Error(detail);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async kill(project, slot) {
|
|
63
|
+
const { ProjectContext } = await import('../core/context.js');
|
|
64
|
+
const { RuntimeStore } = await import('../core/runtimeStore.js');
|
|
65
|
+
const ctx = ProjectContext.load(project);
|
|
66
|
+
const store = new RuntimeStore({
|
|
67
|
+
paths: { stateFile: ctx.paths.stateFile },
|
|
68
|
+
maxWorkers: ctx.maxWorkers,
|
|
69
|
+
});
|
|
70
|
+
const slotName = `worker-${slot}`;
|
|
71
|
+
const state = store.readState();
|
|
72
|
+
const worker = state.workers[slotName];
|
|
73
|
+
if (worker?.pid && worker.pid > 0) {
|
|
74
|
+
try {
|
|
75
|
+
process.kill(worker.pid, 'SIGTERM');
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
/* pid 已死 */
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// 清 slot 状态 —— mutator 重置为 idle
|
|
82
|
+
store.updateState(`console-worker-kill-${slot}`, (s) => {
|
|
83
|
+
const w = s.workers[slotName];
|
|
84
|
+
if (!w)
|
|
85
|
+
return;
|
|
86
|
+
w.status = 'idle';
|
|
87
|
+
w.seq = null;
|
|
88
|
+
w.branch = null;
|
|
89
|
+
w.worktree = null;
|
|
90
|
+
w.claimedAt = null;
|
|
91
|
+
w.lastHeartbeat = null;
|
|
92
|
+
if ('pid' in w)
|
|
93
|
+
w.pid = null;
|
|
94
|
+
if ('sessionId' in w)
|
|
95
|
+
w.sessionId = null;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// ─── PipelineExecutor —— 通过 ProcessSpawner 走子进程(避开 process.exit 污染)─────
|
|
100
|
+
//
|
|
101
|
+
// 细节:executeStop / executeReset 都直接调 process.exit() 做错误退出,不能在 Console
|
|
102
|
+
// server 内联调——会把服务器也杀了。用子进程隔离最干净。这是 Delivery 层不允许 spawn
|
|
103
|
+
// 但 executor glue 层允许的典型场景。
|
|
104
|
+
export class DefaultPipelineExecutor {
|
|
105
|
+
spawner;
|
|
106
|
+
constructor(spawner) {
|
|
107
|
+
this.spawner = spawner;
|
|
108
|
+
}
|
|
109
|
+
async stop(project) {
|
|
110
|
+
const result = await this.spawner.runCliSync({
|
|
111
|
+
args: ['stop', project],
|
|
112
|
+
timeoutMs: 20_000,
|
|
113
|
+
});
|
|
114
|
+
if (result.exitCode !== 0) {
|
|
115
|
+
throw new Error(result.stderr || `sps stop exited ${result.exitCode}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async reset(project, opts) {
|
|
119
|
+
const args = ['reset', project];
|
|
120
|
+
if (opts.all)
|
|
121
|
+
args.push('--all');
|
|
122
|
+
else if (Array.isArray(opts.cards) && opts.cards.length > 0) {
|
|
123
|
+
args.push('--card', opts.cards.join(','));
|
|
124
|
+
}
|
|
125
|
+
const result = await this.spawner.runCliSync({ args, timeoutMs: 60_000 });
|
|
126
|
+
if (result.exitCode !== 0) {
|
|
127
|
+
throw new Error(result.stderr || `sps reset exited ${result.exitCode}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// ─── ChatExecutor —— 直接用 DaemonClient ─────────────────────────────
|
|
132
|
+
const CHAT_SLOT_PREFIX = 'chat-';
|
|
133
|
+
export class DefaultChatExecutor {
|
|
134
|
+
async stopSession(sessionId) {
|
|
135
|
+
const { DaemonClient } = await import('../daemon/daemonClient.js');
|
|
136
|
+
const client = new DaemonClient();
|
|
137
|
+
if (!(await client.isRunning()))
|
|
138
|
+
return;
|
|
139
|
+
await client.stopSession(`${CHAT_SLOT_PREFIX}${sessionId}`).catch(() => undefined);
|
|
140
|
+
}
|
|
141
|
+
async cancelRun(sessionId) {
|
|
142
|
+
const { DaemonClient } = await import('../daemon/daemonClient.js');
|
|
143
|
+
const client = new DaemonClient();
|
|
144
|
+
if (!(await client.isRunning())) {
|
|
145
|
+
throw new Error('daemon not running');
|
|
146
|
+
}
|
|
147
|
+
await client.cancelRun(`${CHAT_SLOT_PREFIX}${sessionId}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// ─── ProjectInitExecutor —— 直接调 executeProjectInit ─────────────────
|
|
151
|
+
export class DefaultProjectInitExecutor {
|
|
152
|
+
async init(project, opts) {
|
|
153
|
+
const { executeProjectInit } = await import('../commands/projectInit.js');
|
|
154
|
+
await executeProjectInit(project, {}, opts);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=executors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executors.js","sourceRoot":"","sources":["../../src/services/executors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module services/ports
|
|
3
|
+
* @description Service 层抽象依赖端口 —— concrete 实现在 services/defaults.ts
|
|
4
|
+
*
|
|
5
|
+
* @layer services
|
|
6
|
+
*
|
|
7
|
+
* 为什么不直接 import Domain 具体类:
|
|
8
|
+
* - 单测能用 fake 替换
|
|
9
|
+
* - delivery 层不会被迫加载整个 Domain 图
|
|
10
|
+
* - 将来想换 TaskBackend 实现(gitlab/github backend)只改 factory
|
|
11
|
+
*/
|
|
12
|
+
import type { TaskBackend } from '../interfaces/TaskBackend.js';
|
|
13
|
+
/** 按 project 名拿 TaskBackend 实例 */
|
|
14
|
+
export interface TaskBackendFactory {
|
|
15
|
+
for(project: string): Promise<TaskBackend>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../../src/services/ports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAEhE,kCAAkC;AAClC,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAC5C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.js","sourceRoot":"","sources":["../../src/services/ports.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module shared/domainEvents
|
|
3
|
+
* @description 领域事件类型 + DomainEventBus port(抽象)
|
|
4
|
+
*
|
|
5
|
+
* @layer shared
|
|
6
|
+
*
|
|
7
|
+
* Service 层发布事件,Delivery 层订阅并推给客户端:
|
|
8
|
+
* - Console 侧订阅推到 SSE /stream/projects/*
|
|
9
|
+
* - CLI 侧可注入 NoOp 实现(或 journal)
|
|
10
|
+
*
|
|
11
|
+
* 事件类型是 **领域事件**(过去时,已经发生的事实),不是 UI 事件也不是命令。
|
|
12
|
+
*/
|
|
13
|
+
import type { Card } from './types.js';
|
|
14
|
+
export interface CardCreatedEvent {
|
|
15
|
+
readonly type: 'card.created';
|
|
16
|
+
readonly project: string;
|
|
17
|
+
readonly seq: number;
|
|
18
|
+
readonly card: Card;
|
|
19
|
+
readonly ts: number;
|
|
20
|
+
}
|
|
21
|
+
export interface CardUpdatedEvent {
|
|
22
|
+
readonly type: 'card.updated';
|
|
23
|
+
readonly project: string;
|
|
24
|
+
readonly seq: number;
|
|
25
|
+
readonly patch: Readonly<Partial<Card>>;
|
|
26
|
+
readonly ts: number;
|
|
27
|
+
}
|
|
28
|
+
export interface CardMovedEvent {
|
|
29
|
+
readonly type: 'card.moved';
|
|
30
|
+
readonly project: string;
|
|
31
|
+
readonly seq: number;
|
|
32
|
+
readonly from: string;
|
|
33
|
+
readonly to: string;
|
|
34
|
+
readonly ts: number;
|
|
35
|
+
}
|
|
36
|
+
export interface CardDeletedEvent {
|
|
37
|
+
readonly type: 'card.deleted';
|
|
38
|
+
readonly project: string;
|
|
39
|
+
readonly seq: number;
|
|
40
|
+
readonly ts: number;
|
|
41
|
+
}
|
|
42
|
+
export interface WorkerDispatchedEvent {
|
|
43
|
+
readonly type: 'worker.dispatched';
|
|
44
|
+
readonly project: string;
|
|
45
|
+
readonly slot: string;
|
|
46
|
+
readonly seq: number;
|
|
47
|
+
readonly stage: string;
|
|
48
|
+
readonly ts: number;
|
|
49
|
+
}
|
|
50
|
+
export interface WorkerUpdatedEvent {
|
|
51
|
+
readonly type: 'worker.updated';
|
|
52
|
+
readonly project: string;
|
|
53
|
+
readonly slot: string;
|
|
54
|
+
readonly ts: number;
|
|
55
|
+
}
|
|
56
|
+
export interface WorkerDeletedEvent {
|
|
57
|
+
readonly type: 'worker.deleted';
|
|
58
|
+
readonly project: string;
|
|
59
|
+
readonly slot: string;
|
|
60
|
+
readonly ts: number;
|
|
61
|
+
}
|
|
62
|
+
export interface PipelineStartedEvent {
|
|
63
|
+
readonly type: 'pipeline.started';
|
|
64
|
+
readonly project: string;
|
|
65
|
+
readonly pid: number;
|
|
66
|
+
readonly ts: number;
|
|
67
|
+
}
|
|
68
|
+
export interface PipelineStoppedEvent {
|
|
69
|
+
readonly type: 'pipeline.stopped';
|
|
70
|
+
readonly project: string;
|
|
71
|
+
readonly ts: number;
|
|
72
|
+
}
|
|
73
|
+
export interface SkillLinkedEvent {
|
|
74
|
+
readonly type: 'skill.linked';
|
|
75
|
+
readonly project: string;
|
|
76
|
+
readonly skill: string;
|
|
77
|
+
readonly ts: number;
|
|
78
|
+
}
|
|
79
|
+
export interface SkillUnlinkedEvent {
|
|
80
|
+
readonly type: 'skill.unlinked';
|
|
81
|
+
readonly project: string;
|
|
82
|
+
readonly skill: string;
|
|
83
|
+
readonly ts: number;
|
|
84
|
+
}
|
|
85
|
+
/** Union of all领域 events. */
|
|
86
|
+
export type DomainEvent = CardCreatedEvent | CardUpdatedEvent | CardMovedEvent | CardDeletedEvent | WorkerDispatchedEvent | WorkerUpdatedEvent | WorkerDeletedEvent | PipelineStartedEvent | PipelineStoppedEvent | SkillLinkedEvent | SkillUnlinkedEvent;
|
|
87
|
+
export type DomainEventType = DomainEvent['type'];
|
|
88
|
+
/**
|
|
89
|
+
* DomainEventBus —— 事件总线抽象。
|
|
90
|
+
* Service 层只依赖这个接口,不知道底层是 SSE / noop / journal。
|
|
91
|
+
*/
|
|
92
|
+
export interface DomainEventBus {
|
|
93
|
+
/** 发布一个事件(同步返回) */
|
|
94
|
+
emit(event: DomainEvent): void;
|
|
95
|
+
/**
|
|
96
|
+
* 订阅所有事件。返回取消订阅函数。
|
|
97
|
+
* 订阅者里抛出的异常不会影响 publisher。
|
|
98
|
+
*/
|
|
99
|
+
subscribe(handler: (event: DomainEvent) => void): () => void;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 什么都不做的实现 —— 用于 CLI / 单元测试。
|
|
103
|
+
* 保证 Service 层即使没接 real bus 也能跑。
|
|
104
|
+
*/
|
|
105
|
+
export declare class NoopEventBus implements DomainEventBus {
|
|
106
|
+
emit(_event: DomainEvent): void;
|
|
107
|
+
subscribe(_handler: (event: DomainEvent) => void): () => void;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 进程内同步广播 —— 用于测试和 CLI 的 journal 用途。
|
|
111
|
+
* 订阅者顺序执行,handler 内抛错被吞只记 console.warn(不阻塞 publisher)。
|
|
112
|
+
*/
|
|
113
|
+
export declare class InMemoryEventBus implements DomainEventBus {
|
|
114
|
+
private handlers;
|
|
115
|
+
emit(event: DomainEvent): void;
|
|
116
|
+
subscribe(handler: (event: DomainEvent) => void): () => void;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=domainEvents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domainEvents.d.ts","sourceRoot":"","sources":["../../src/shared/domainEvents.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// ─── NoopEventBus ─────────────────────────────────────────────────
|
|
2
|
+
/**
|
|
3
|
+
* 什么都不做的实现 —— 用于 CLI / 单元测试。
|
|
4
|
+
* 保证 Service 层即使没接 real bus 也能跑。
|
|
5
|
+
*/
|
|
6
|
+
export class NoopEventBus {
|
|
7
|
+
emit(_event) {
|
|
8
|
+
/* swallow */
|
|
9
|
+
}
|
|
10
|
+
subscribe(_handler) {
|
|
11
|
+
return () => {
|
|
12
|
+
/* no-op */
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// ─── InMemoryEventBus ─────────────────────────────────────────────
|
|
17
|
+
/**
|
|
18
|
+
* 进程内同步广播 —— 用于测试和 CLI 的 journal 用途。
|
|
19
|
+
* 订阅者顺序执行,handler 内抛错被吞只记 console.warn(不阻塞 publisher)。
|
|
20
|
+
*/
|
|
21
|
+
export class InMemoryEventBus {
|
|
22
|
+
handlers = new Set();
|
|
23
|
+
emit(event) {
|
|
24
|
+
for (const h of this.handlers) {
|
|
25
|
+
try {
|
|
26
|
+
h(event);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
console.warn('[DomainEventBus] handler threw:', err);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
subscribe(handler) {
|
|
34
|
+
this.handlers.add(handler);
|
|
35
|
+
return () => {
|
|
36
|
+
this.handlers.delete(handler);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=domainEvents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domainEvents.js","sourceRoot":"","sources":["../../src/shared/domainEvents.ts"],"names":[],"mappings":"AAmIA,qEAAqE;AAErE;;;GAGG;AACH,MAAM,OAAO,YAAY;IACvB,IAAI,CAAC,MAAmB;QACtB,aAAa;IACf,CAAC;IACD,SAAS,CAAC,QAAsC;QAC9C,OAAO,GAAG,EAAE;YACV,WAAW;QACb,CAAC,CAAC;IACJ,CAAC;CACF;AAED,qEAAqE;AAErE;;;GAGG;AACH,MAAM,OAAO,gBAAgB;IACnB,QAAQ,GAAsC,IAAI,GAAG,EAAE,CAAC;IAEhE,IAAI,CAAC,KAAkB;QACrB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,CAAC,CAAC,KAAK,CAAC,CAAC;YACX,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;IACH,CAAC;IAED,SAAS,CAAC,OAAqC;QAC7C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module shared/errors
|
|
3
|
+
* @description DomainError —— 所有 Service 层错误的统一结构
|
|
4
|
+
*
|
|
5
|
+
* @layer shared
|
|
6
|
+
*
|
|
7
|
+
* Delivery 层(CLI / HTTP)负责翻译 DomainError 到各自输出格式:
|
|
8
|
+
* - HTTP:toHttpStatus + problem+json body
|
|
9
|
+
* - CLI:toExitCode + stderr 渲染
|
|
10
|
+
*
|
|
11
|
+
* Domain 层允许正常抛 Error;Service 层捕获后转 DomainError。
|
|
12
|
+
*/
|
|
13
|
+
export type DomainErrorKind =
|
|
14
|
+
/** 资源不存在 —— HTTP 404 / CLI exit 1 */
|
|
15
|
+
'not-found'
|
|
16
|
+
/** etag 冲突 / 状态冲突 / 并发竞争 —— HTTP 409 / CLI exit 1 */
|
|
17
|
+
| 'conflict'
|
|
18
|
+
/** 入参非法(格式、schema 违反)—— HTTP 422 / CLI exit 2 */
|
|
19
|
+
| 'validation'
|
|
20
|
+
/** 前置条件不满足(业务规则阻挡)—— HTTP 409 / CLI exit 2 */
|
|
21
|
+
| 'precondition'
|
|
22
|
+
/** 外部系统错误(Git 失败 / npm registry 不通)—— HTTP 502 / CLI exit 3 */
|
|
23
|
+
| 'external'
|
|
24
|
+
/** 未知内部错误 —— HTTP 500 / CLI exit 1 */
|
|
25
|
+
| 'internal';
|
|
26
|
+
export interface DomainError {
|
|
27
|
+
readonly kind: DomainErrorKind;
|
|
28
|
+
/** 机读码(全大写 + 下划线),便于 i18n / 客户端分支。 */
|
|
29
|
+
readonly code: string;
|
|
30
|
+
/** 人读消息(默认中文),展示给终端用户。 */
|
|
31
|
+
readonly message: string;
|
|
32
|
+
/** 结构化上下文 —— 不含大对象 / 不含敏感数据。 */
|
|
33
|
+
readonly details?: Readonly<Record<string, unknown>>;
|
|
34
|
+
/** 原始异常 —— 仅日志 / debug 用,不暴露给客户端。 */
|
|
35
|
+
readonly cause?: unknown;
|
|
36
|
+
}
|
|
37
|
+
/** 构造 DomainError 的 factory(避免到处写 `{ kind, code, message }` 字面量)。 */
|
|
38
|
+
export declare function domainError(kind: DomainErrorKind, code: string, message: string, extra?: {
|
|
39
|
+
details?: Record<string, unknown>;
|
|
40
|
+
cause?: unknown;
|
|
41
|
+
}): DomainError;
|
|
42
|
+
/** HTTP status code 映射 —— Console route 层用。 */
|
|
43
|
+
export declare function toHttpStatus(e: DomainError): number;
|
|
44
|
+
/** CLI exit code 映射 —— CLI command 层用。 */
|
|
45
|
+
export declare function toExitCode(e: DomainError): number;
|
|
46
|
+
/**
|
|
47
|
+
* 翻译为 application/problem+json(RFC 7807 兼容 shape)。
|
|
48
|
+
* Delivery 层的 HTTP 响应体用。
|
|
49
|
+
*/
|
|
50
|
+
export interface ProblemJson {
|
|
51
|
+
type: string;
|
|
52
|
+
title: string;
|
|
53
|
+
status: number;
|
|
54
|
+
detail: string;
|
|
55
|
+
code: string;
|
|
56
|
+
[k: string]: unknown;
|
|
57
|
+
}
|
|
58
|
+
export declare function toProblemJson(e: DomainError): ProblemJson;
|
|
59
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/shared/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,eAAe;AACzB,qCAAqC;AACnC,WAAW;AACb,qDAAqD;GACnD,UAAU;AACZ,iDAAiD;GAC/C,YAAY;AACd,8CAA8C;GAC5C,cAAc;AAChB,+DAA+D;GAC7D,UAAU;AACZ,sCAAsC;GACpC,UAAU,CAAC;AAEf,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,gCAAgC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,qCAAqC;IACrC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,qEAAqE;AACrE,wBAAgB,WAAW,CACzB,IAAI,EAAE,eAAe,EACrB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,KAAK,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GACjE,WAAW,CAQb;AAED,+CAA+C;AAC/C,wBAAgB,YAAY,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAcnD;AAED,0CAA0C;AAC1C,wBAAgB,UAAU,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAYjD;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,WAAW,GAAG,WAAW,CASzD"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module shared/errors
|
|
3
|
+
* @description DomainError —— 所有 Service 层错误的统一结构
|
|
4
|
+
*
|
|
5
|
+
* @layer shared
|
|
6
|
+
*
|
|
7
|
+
* Delivery 层(CLI / HTTP)负责翻译 DomainError 到各自输出格式:
|
|
8
|
+
* - HTTP:toHttpStatus + problem+json body
|
|
9
|
+
* - CLI:toExitCode + stderr 渲染
|
|
10
|
+
*
|
|
11
|
+
* Domain 层允许正常抛 Error;Service 层捕获后转 DomainError。
|
|
12
|
+
*/
|
|
13
|
+
/** 构造 DomainError 的 factory(避免到处写 `{ kind, code, message }` 字面量)。 */
|
|
14
|
+
export function domainError(kind, code, message, extra = {}) {
|
|
15
|
+
return {
|
|
16
|
+
kind,
|
|
17
|
+
code,
|
|
18
|
+
message,
|
|
19
|
+
...(extra.details ? { details: extra.details } : {}),
|
|
20
|
+
...(extra.cause !== undefined ? { cause: extra.cause } : {}),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** HTTP status code 映射 —— Console route 层用。 */
|
|
24
|
+
export function toHttpStatus(e) {
|
|
25
|
+
switch (e.kind) {
|
|
26
|
+
case 'not-found':
|
|
27
|
+
return 404;
|
|
28
|
+
case 'conflict':
|
|
29
|
+
case 'precondition':
|
|
30
|
+
return 409;
|
|
31
|
+
case 'validation':
|
|
32
|
+
return 422;
|
|
33
|
+
case 'external':
|
|
34
|
+
return 502;
|
|
35
|
+
case 'internal':
|
|
36
|
+
return 500;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** CLI exit code 映射 —— CLI command 层用。 */
|
|
40
|
+
export function toExitCode(e) {
|
|
41
|
+
switch (e.kind) {
|
|
42
|
+
case 'validation':
|
|
43
|
+
case 'precondition':
|
|
44
|
+
return 2;
|
|
45
|
+
case 'external':
|
|
46
|
+
return 3;
|
|
47
|
+
case 'not-found':
|
|
48
|
+
case 'conflict':
|
|
49
|
+
case 'internal':
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function toProblemJson(e) {
|
|
54
|
+
return {
|
|
55
|
+
type: e.kind,
|
|
56
|
+
title: titleFor(e.kind),
|
|
57
|
+
status: toHttpStatus(e),
|
|
58
|
+
detail: e.message,
|
|
59
|
+
code: e.code,
|
|
60
|
+
...(e.details ? { details: e.details } : {}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function titleFor(kind) {
|
|
64
|
+
switch (kind) {
|
|
65
|
+
case 'not-found':
|
|
66
|
+
return 'Not Found';
|
|
67
|
+
case 'conflict':
|
|
68
|
+
return 'Conflict';
|
|
69
|
+
case 'precondition':
|
|
70
|
+
return 'Precondition Failed';
|
|
71
|
+
case 'validation':
|
|
72
|
+
return 'Validation Error';
|
|
73
|
+
case 'external':
|
|
74
|
+
return 'External Service Error';
|
|
75
|
+
case 'internal':
|
|
76
|
+
return 'Internal Error';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/shared/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA4BH,qEAAqE;AACrE,MAAM,UAAU,WAAW,CACzB,IAAqB,EACrB,IAAY,EACZ,OAAe,EACf,QAAgE,EAAE;IAElE,OAAO;QACL,IAAI;QACJ,IAAI;QACJ,OAAO;QACP,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7D,CAAC;AACJ,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,YAAY,CAAC,CAAc;IACzC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,WAAW;YACd,OAAO,GAAG,CAAC;QACb,KAAK,UAAU,CAAC;QAChB,KAAK,cAAc;YACjB,OAAO,GAAG,CAAC;QACb,KAAK,YAAY;YACf,OAAO,GAAG,CAAC;QACb,KAAK,UAAU;YACb,OAAO,GAAG,CAAC;QACb,KAAK,UAAU;YACb,OAAO,GAAG,CAAC;IACf,CAAC;AACH,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,UAAU,CAAC,CAAc;IACvC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,YAAY,CAAC;QAClB,KAAK,cAAc;YACjB,OAAO,CAAC,CAAC;QACX,KAAK,UAAU;YACb,OAAO,CAAC,CAAC;QACX,KAAK,WAAW,CAAC;QACjB,KAAK,UAAU,CAAC;QAChB,KAAK,UAAU;YACb,OAAO,CAAC,CAAC;IACb,CAAC;AACH,CAAC;AAeD,MAAM,UAAU,aAAa,CAAC,CAAc;IAC1C,OAAO;QACL,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;QACvB,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;QACvB,MAAM,EAAE,CAAC,CAAC,OAAO;QACjB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7C,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,IAAqB;IACrC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,WAAW;YACd,OAAO,WAAW,CAAC;QACrB,KAAK,UAAU;YACb,OAAO,UAAU,CAAC;QACpB,KAAK,cAAc;YACjB,OAAO,qBAAqB,CAAC;QAC/B,KAAK,YAAY;YACf,OAAO,kBAAkB,CAAC;QAC5B,KAAK,UAAU;YACb,OAAO,wBAAwB,CAAC;QAClC,KAAK,UAAU;YACb,OAAO,gBAAgB,CAAC;IAC5B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module shared/result
|
|
3
|
+
* @description Result<T, E> —— Service 层约定返回的容器类型
|
|
4
|
+
*
|
|
5
|
+
* @layer shared
|
|
6
|
+
*
|
|
7
|
+
* Service 方法不抛业务异常 —— 用 Result 表达"成功 + 值"或"失败 + 结构化错误"。
|
|
8
|
+
* `throw` 保留给真正的系统问题(OOM / 断言违反 / 库 panic)。
|
|
9
|
+
*/
|
|
10
|
+
import type { DomainError } from './errors.js';
|
|
11
|
+
export type Result<T, E = DomainError> = {
|
|
12
|
+
readonly ok: true;
|
|
13
|
+
readonly value: T;
|
|
14
|
+
} | {
|
|
15
|
+
readonly ok: false;
|
|
16
|
+
readonly error: E;
|
|
17
|
+
};
|
|
18
|
+
/** 包装成功结果。 */
|
|
19
|
+
export declare function ok<T>(value: T): Result<T, never>;
|
|
20
|
+
/** 包装失败结果。 */
|
|
21
|
+
export declare function err<E>(error: E): Result<never, E>;
|
|
22
|
+
/** 类型守卫:Result 是 Ok? */
|
|
23
|
+
export declare function isOk<T, E>(r: Result<T, E>): r is {
|
|
24
|
+
ok: true;
|
|
25
|
+
value: T;
|
|
26
|
+
};
|
|
27
|
+
/** 类型守卫:Result 是 Err? */
|
|
28
|
+
export declare function isErr<T, E>(r: Result<T, E>): r is {
|
|
29
|
+
ok: false;
|
|
30
|
+
error: E;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* 成功时 map 值;失败时原样传递。
|
|
34
|
+
* 不改变 error 类型。
|
|
35
|
+
*/
|
|
36
|
+
export declare function mapOk<T, U, E>(r: Result<T, E>, f: (t: T) => U): Result<U, E>;
|
|
37
|
+
/**
|
|
38
|
+
* 失败时 map 错误;成功时原样传递。
|
|
39
|
+
*/
|
|
40
|
+
export declare function mapErr<T, E, F>(r: Result<T, E>, f: (e: E) => F): Result<T, F>;
|
|
41
|
+
/**
|
|
42
|
+
* 对 Ok 执行会返回 Result 的下一步(链式)。
|
|
43
|
+
* 任一步失败短路。
|
|
44
|
+
*/
|
|
45
|
+
export declare function andThen<T, U, E>(r: Result<T, E>, f: (t: T) => Promise<Result<U, E>>): Promise<Result<U, E>>;
|
|
46
|
+
/**
|
|
47
|
+
* 取 Ok 值,不是 Ok 就抛 —— 仅用于测试 / 边界场景。
|
|
48
|
+
* 生产代码应该用 isOk + 分支处理。
|
|
49
|
+
*/
|
|
50
|
+
export declare function unwrap<T, E>(r: Result<T, E>, ctx?: string): T;
|
|
51
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/shared/result.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,WAAW,IACjC;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACxC;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC;AAE9C,cAAc;AACd,wBAAgB,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAEhD;AAED,cAAc;AACd,wBAAgB,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAEjD;AAED,wBAAwB;AACxB,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAEvE;AAED,yBAAyB;AACzB,wBAAgB,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAEzE;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAE5E;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAE7E;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACnC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EACf,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GACjC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAEvB;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,CAM7D"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** 包装成功结果。 */
|
|
2
|
+
export function ok(value) {
|
|
3
|
+
return { ok: true, value };
|
|
4
|
+
}
|
|
5
|
+
/** 包装失败结果。 */
|
|
6
|
+
export function err(error) {
|
|
7
|
+
return { ok: false, error };
|
|
8
|
+
}
|
|
9
|
+
/** 类型守卫:Result 是 Ok? */
|
|
10
|
+
export function isOk(r) {
|
|
11
|
+
return r.ok;
|
|
12
|
+
}
|
|
13
|
+
/** 类型守卫:Result 是 Err? */
|
|
14
|
+
export function isErr(r) {
|
|
15
|
+
return !r.ok;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 成功时 map 值;失败时原样传递。
|
|
19
|
+
* 不改变 error 类型。
|
|
20
|
+
*/
|
|
21
|
+
export function mapOk(r, f) {
|
|
22
|
+
return r.ok ? ok(f(r.value)) : r;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 失败时 map 错误;成功时原样传递。
|
|
26
|
+
*/
|
|
27
|
+
export function mapErr(r, f) {
|
|
28
|
+
return r.ok ? r : err(f(r.error));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 对 Ok 执行会返回 Result 的下一步(链式)。
|
|
32
|
+
* 任一步失败短路。
|
|
33
|
+
*/
|
|
34
|
+
export async function andThen(r, f) {
|
|
35
|
+
return r.ok ? await f(r.value) : r;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 取 Ok 值,不是 Ok 就抛 —— 仅用于测试 / 边界场景。
|
|
39
|
+
* 生产代码应该用 isOk + 分支处理。
|
|
40
|
+
*/
|
|
41
|
+
export function unwrap(r, ctx) {
|
|
42
|
+
if (!r.ok) {
|
|
43
|
+
const prefix = ctx ? `${ctx}: ` : '';
|
|
44
|
+
throw new Error(`${prefix}unwrap on Err: ${JSON.stringify(r.error)}`);
|
|
45
|
+
}
|
|
46
|
+
return r.value;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/shared/result.ts"],"names":[],"mappings":"AAeA,cAAc;AACd,MAAM,UAAU,EAAE,CAAI,KAAQ;IAC5B,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC;AAED,cAAc;AACd,MAAM,UAAU,GAAG,CAAI,KAAQ;IAC7B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC9B,CAAC;AAED,wBAAwB;AACxB,MAAM,UAAU,IAAI,CAAO,CAAe;IACxC,OAAO,CAAC,CAAC,EAAE,CAAC;AACd,CAAC;AAED,yBAAyB;AACzB,MAAM,UAAU,KAAK,CAAO,CAAe;IACzC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAU,CAAe,EAAE,CAAc;IAC5D,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,MAAM,CAAU,CAAe,EAAE,CAAc;IAC7D,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,CAAe,EACf,CAAkC;IAElC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAO,CAAe,EAAE,GAAY;IACxD,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACV,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,kBAAkB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,CAAC,CAAC,KAAK,CAAC;AACjB,CAAC"}
|