@hunsu/protocol 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +157 -0
- package/dist/command-decoder.d.ts +5 -0
- package/dist/command-decoder.js +329 -0
- package/dist/constants.d.ts +15 -0
- package/dist/constants.js +15 -0
- package/dist/decoder-helpers.d.ts +58 -0
- package/dist/decoder-helpers.js +1757 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.js +28 -0
- package/dist/event-decoder.d.ts +5 -0
- package/dist/event-decoder.js +232 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/lifecycle.d.ts +67 -0
- package/dist/lifecycle.js +271 -0
- package/dist/model.d.ts +1058 -0
- package/dist/model.js +1 -0
- package/dist/primitives.d.ts +59 -0
- package/dist/primitives.js +135 -0
- package/dist/projection.d.ts +8 -0
- package/dist/projection.js +875 -0
- package/dist/prompt-template.d.ts +12 -0
- package/dist/prompt-template.js +83 -0
- package/dist/protocol-validation.d.ts +36 -0
- package/dist/protocol-validation.js +1139 -0
- package/dist/result.d.ts +20 -0
- package/dist/result.js +21 -0
- package/dist/workflow-helpers.d.ts +31 -0
- package/dist/workflow-helpers.js +193 -0
- package/dist/workflow.d.ts +10 -0
- package/dist/workflow.js +594 -0
- package/package.json +30 -0
- package/src/command-decoder.ts +292 -0
- package/src/constants.ts +27 -0
- package/src/decoder-helpers.ts +1672 -0
- package/src/errors.ts +38 -0
- package/src/event-decoder.ts +225 -0
- package/src/index.ts +43 -0
- package/src/lifecycle.ts +403 -0
- package/src/model.ts +1233 -0
- package/src/primitives.ts +196 -0
- package/src/projection.ts +1081 -0
- package/src/prompt-template.ts +97 -0
- package/src/protocol-validation.ts +1252 -0
- package/src/result.ts +35 -0
- package/src/workflow-helpers.ts +239 -0
- package/src/workflow.ts +753 -0
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Result } from "./result.ts";
|
|
2
|
+
export declare class DomainInvariantError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
export type DomainWorkflowError = {
|
|
6
|
+
type: "DomainWorkflowError";
|
|
7
|
+
message: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function workflowError(message: string): Result<never, DomainWorkflowError>;
|
|
10
|
+
export declare function toDomainWorkflowError(error: unknown): DomainWorkflowError;
|
|
11
|
+
export declare function unwrapWorkflowResult<T>(result: Result<T, DomainWorkflowError>): T;
|
|
12
|
+
export declare function unwrapDomainModelResult<T>(result: Result<T, {
|
|
13
|
+
message: string;
|
|
14
|
+
}>): T;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { err } from "./result.js";
|
|
2
|
+
export class DomainInvariantError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "DomainInvariantError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export function workflowError(message) {
|
|
9
|
+
return err({ type: "DomainWorkflowError", message });
|
|
10
|
+
}
|
|
11
|
+
export function toDomainWorkflowError(error) {
|
|
12
|
+
return {
|
|
13
|
+
type: "DomainWorkflowError",
|
|
14
|
+
message: error instanceof Error ? error.message : String(error)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function unwrapWorkflowResult(result) {
|
|
18
|
+
if (result.ok) {
|
|
19
|
+
return result.value;
|
|
20
|
+
}
|
|
21
|
+
throw new DomainInvariantError(result.error.message);
|
|
22
|
+
}
|
|
23
|
+
export function unwrapDomainModelResult(result) {
|
|
24
|
+
if (result.ok) {
|
|
25
|
+
return result.value;
|
|
26
|
+
}
|
|
27
|
+
throw new DomainInvariantError(result.error.message);
|
|
28
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type DomainWorkflowError } from "./errors.ts";
|
|
2
|
+
import type { DomainEvent } from "./model.ts";
|
|
3
|
+
import { type Result } from "./result.ts";
|
|
4
|
+
import { type HarnessDecoder } from "./decoder-helpers.ts";
|
|
5
|
+
export declare function decodeDomainEvent(value: unknown, decodeHarness: HarnessDecoder): Result<DomainEvent, DomainWorkflowError>;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { workflowError } from "./errors.js";
|
|
2
|
+
import { makeDestinationId, makeTeamName, makeFailureReason, makeHunsuId, makeLineId, makeMoveId, makeRequestId, makeSkillDraftId } from "./primitives.js";
|
|
3
|
+
import { ok } from "./result.js";
|
|
4
|
+
import { decodeArtifactRecord, decodeDestinationSeed, decodeDestinationSeedArray, decodeHubPackageLock, decodeHunsuOrigin, decodeHunsuRecord, decodeLineRecord, decodeMoveRecord, decodeNonEmptyText, decodeOptionalAt, decodeOptionalMoveId, decodeOptionalNonEmptyText, decodeRecord, decodeRequestRecord, decodeSkillBinding, decodeSkillDraftRecord, decodeNodeRecord } from "./decoder-helpers.js";
|
|
5
|
+
export function decodeDomainEvent(value, decodeHarness) {
|
|
6
|
+
const event = decodeRecord(value, "DomainEvent");
|
|
7
|
+
if (!event.ok) {
|
|
8
|
+
return event;
|
|
9
|
+
}
|
|
10
|
+
switch (event.value.type) {
|
|
11
|
+
case "HunsuOriginRegistered": {
|
|
12
|
+
const origin = decodeHunsuOrigin(event.value.origin, "event.origin");
|
|
13
|
+
if (!origin.ok)
|
|
14
|
+
return origin;
|
|
15
|
+
const at = decodeOptionalAt(event.value.at, "event.at");
|
|
16
|
+
if (!at.ok)
|
|
17
|
+
return at;
|
|
18
|
+
return ok({ type: "HunsuOriginRegistered", origin: origin.value, at: at.value });
|
|
19
|
+
}
|
|
20
|
+
case "InitialTeamCreated": {
|
|
21
|
+
const request = decodeRequestRecord(event.value.request, "event.request");
|
|
22
|
+
if (!request.ok)
|
|
23
|
+
return request;
|
|
24
|
+
const line = decodeLineRecord(event.value.line, "event.line");
|
|
25
|
+
if (!line.ok)
|
|
26
|
+
return line;
|
|
27
|
+
const destinations = decodeDestinationSeedArray(event.value.destinations, "event.destinations");
|
|
28
|
+
if (!destinations.ok)
|
|
29
|
+
return destinations;
|
|
30
|
+
const harness = event.value.harness === undefined ? ok(undefined) : decodeHarness(event.value.harness);
|
|
31
|
+
if (!harness.ok)
|
|
32
|
+
return harness;
|
|
33
|
+
const harnessLock = decodeHubPackageLock(event.value.harnessLock, "event.harnessLock", ["team"]);
|
|
34
|
+
if (!harnessLock.ok)
|
|
35
|
+
return harnessLock;
|
|
36
|
+
const at = decodeOptionalAt(event.value.at, "event.at");
|
|
37
|
+
if (!at.ok)
|
|
38
|
+
return at;
|
|
39
|
+
return ok({
|
|
40
|
+
type: "InitialTeamCreated",
|
|
41
|
+
request: request.value,
|
|
42
|
+
line: line.value,
|
|
43
|
+
destinations: destinations.value,
|
|
44
|
+
harness: harness.value,
|
|
45
|
+
harnessLock: harnessLock.value,
|
|
46
|
+
at: at.value
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
case "RequestCreated": {
|
|
50
|
+
const request = decodeRequestRecord(event.value.request, "event.request");
|
|
51
|
+
return request.ok ? ok({ type: "RequestCreated", request: request.value }) : request;
|
|
52
|
+
}
|
|
53
|
+
case "DestinationDeclared": {
|
|
54
|
+
const requestId = primitive(makeRequestId(event.value.requestId));
|
|
55
|
+
if (!requestId.ok)
|
|
56
|
+
return requestId;
|
|
57
|
+
const destination = decodeDestinationSeed(event.value.destination, "event.destination");
|
|
58
|
+
if (!destination.ok)
|
|
59
|
+
return destination;
|
|
60
|
+
const at = decodeOptionalAt(event.value.at, "event.at");
|
|
61
|
+
if (!at.ok)
|
|
62
|
+
return at;
|
|
63
|
+
return ok({ type: "DestinationDeclared", requestId: requestId.value, destination: destination.value, at: at.value });
|
|
64
|
+
}
|
|
65
|
+
case "HarnessSeeded": {
|
|
66
|
+
const requestId = primitive(makeRequestId(event.value.requestId));
|
|
67
|
+
if (!requestId.ok)
|
|
68
|
+
return requestId;
|
|
69
|
+
const harness = decodeHarness(event.value.harness);
|
|
70
|
+
if (!harness.ok)
|
|
71
|
+
return harness;
|
|
72
|
+
const at = decodeOptionalAt(event.value.at, "event.at");
|
|
73
|
+
if (!at.ok)
|
|
74
|
+
return at;
|
|
75
|
+
return ok({ type: "HarnessSeeded", requestId: requestId.value, harness: harness.value, at: at.value });
|
|
76
|
+
}
|
|
77
|
+
case "LineStarted": {
|
|
78
|
+
const line = decodeLineRecord(event.value.line, "event.line");
|
|
79
|
+
return line.ok ? ok({ type: "LineStarted", line: line.value }) : line;
|
|
80
|
+
}
|
|
81
|
+
case "SkillDraftCreated": {
|
|
82
|
+
const draft = decodeSkillDraftRecord(event.value.draft, "event.draft");
|
|
83
|
+
return draft.ok ? ok({ type: "SkillDraftCreated", draft: draft.value }) : draft;
|
|
84
|
+
}
|
|
85
|
+
case "SkillDraftAccepted": {
|
|
86
|
+
const draftId = primitive(makeSkillDraftId(event.value.draftId));
|
|
87
|
+
if (!draftId.ok)
|
|
88
|
+
return draftId;
|
|
89
|
+
const skill = decodeSkillBinding(event.value.skill, "event.skill");
|
|
90
|
+
if (!skill.ok)
|
|
91
|
+
return skill;
|
|
92
|
+
const at = decodeOptionalAt(event.value.at, "event.at");
|
|
93
|
+
if (!at.ok)
|
|
94
|
+
return at;
|
|
95
|
+
return ok({ type: "SkillDraftAccepted", draftId: draftId.value, skill: skill.value, at: at.value });
|
|
96
|
+
}
|
|
97
|
+
case "SkillDraftDiscarded": {
|
|
98
|
+
const draftId = primitive(makeSkillDraftId(event.value.draftId));
|
|
99
|
+
if (!draftId.ok)
|
|
100
|
+
return draftId;
|
|
101
|
+
const at = decodeOptionalAt(event.value.at, "event.at");
|
|
102
|
+
if (!at.ok)
|
|
103
|
+
return at;
|
|
104
|
+
return ok({ type: "SkillDraftDiscarded", draftId: draftId.value, at: at.value });
|
|
105
|
+
}
|
|
106
|
+
case "NodeCreated": {
|
|
107
|
+
const node = decodeNodeRecord(event.value.node, "event.node", decodeHarness);
|
|
108
|
+
return node.ok ? ok({ type: "NodeCreated", node: node.value }) : node;
|
|
109
|
+
}
|
|
110
|
+
case "LinePaused":
|
|
111
|
+
case "LineResumed": {
|
|
112
|
+
const lineId = primitive(makeLineId(event.value.lineId));
|
|
113
|
+
if (!lineId.ok)
|
|
114
|
+
return lineId;
|
|
115
|
+
const at = decodeOptionalAt(event.value.at, "event.at");
|
|
116
|
+
if (!at.ok)
|
|
117
|
+
return at;
|
|
118
|
+
return ok({ type: event.value.type, lineId: lineId.value, at: at.value });
|
|
119
|
+
}
|
|
120
|
+
case "LineAccepted":
|
|
121
|
+
case "LineRejected": {
|
|
122
|
+
const lineId = primitive(makeLineId(event.value.lineId));
|
|
123
|
+
if (!lineId.ok)
|
|
124
|
+
return lineId;
|
|
125
|
+
const reason = decodeOptionalNonEmptyText(event.value.reason, "event.reason");
|
|
126
|
+
if (!reason.ok)
|
|
127
|
+
return reason;
|
|
128
|
+
const at = decodeOptionalAt(event.value.at, "event.at");
|
|
129
|
+
if (!at.ok)
|
|
130
|
+
return at;
|
|
131
|
+
return ok({ type: event.value.type, lineId: lineId.value, reason: reason.value, at: at.value });
|
|
132
|
+
}
|
|
133
|
+
case "DestinationClaimed":
|
|
134
|
+
case "DestinationWorkStarted": {
|
|
135
|
+
const destinationId = primitive(makeDestinationId(event.value.destinationId));
|
|
136
|
+
if (!destinationId.ok)
|
|
137
|
+
return destinationId;
|
|
138
|
+
const actor = decodeNonEmptyText(event.value.actor, "event.actor");
|
|
139
|
+
if (!actor.ok)
|
|
140
|
+
return actor;
|
|
141
|
+
const at = decodeOptionalAt(event.value.at, "event.at");
|
|
142
|
+
if (!at.ok)
|
|
143
|
+
return at;
|
|
144
|
+
return ok({ type: event.value.type, destinationId: destinationId.value, actor: actor.value, at: at.value });
|
|
145
|
+
}
|
|
146
|
+
case "DestinationBlocked": {
|
|
147
|
+
const destinationId = primitive(makeDestinationId(event.value.destinationId));
|
|
148
|
+
if (!destinationId.ok)
|
|
149
|
+
return destinationId;
|
|
150
|
+
const reason = primitive(makeFailureReason(event.value.reason, "event.reason"));
|
|
151
|
+
if (!reason.ok)
|
|
152
|
+
return reason;
|
|
153
|
+
const actor = decodeNonEmptyText(event.value.actor, "event.actor");
|
|
154
|
+
if (!actor.ok)
|
|
155
|
+
return actor;
|
|
156
|
+
const at = decodeOptionalAt(event.value.at, "event.at");
|
|
157
|
+
if (!at.ok)
|
|
158
|
+
return at;
|
|
159
|
+
return ok({ type: "DestinationBlocked", destinationId: destinationId.value, reason: reason.value, actor: actor.value, at: at.value });
|
|
160
|
+
}
|
|
161
|
+
case "MoveRecorded": {
|
|
162
|
+
const move = decodeMoveRecord(event.value.move, "event.move", decodeHarness);
|
|
163
|
+
return move.ok ? ok({ type: "MoveRecorded", move: move.value }) : move;
|
|
164
|
+
}
|
|
165
|
+
case "DestinationReached": {
|
|
166
|
+
const destinationId = primitive(makeDestinationId(event.value.destinationId));
|
|
167
|
+
if (!destinationId.ok)
|
|
168
|
+
return destinationId;
|
|
169
|
+
const moveId = primitive(makeMoveId(event.value.moveId));
|
|
170
|
+
if (!moveId.ok)
|
|
171
|
+
return moveId;
|
|
172
|
+
const at = decodeOptionalAt(event.value.at, "event.at");
|
|
173
|
+
if (!at.ok)
|
|
174
|
+
return at;
|
|
175
|
+
return ok({ type: "DestinationReached", destinationId: destinationId.value, moveId: moveId.value, at: at.value });
|
|
176
|
+
}
|
|
177
|
+
case "HunsuRecorded": {
|
|
178
|
+
const hunsu = decodeHunsuRecord(event.value.hunsu, "event.hunsu", decodeHarness);
|
|
179
|
+
return hunsu.ok ? ok({ type: "HunsuRecorded", hunsu: hunsu.value }) : hunsu;
|
|
180
|
+
}
|
|
181
|
+
case "LineForkedByHunsu": {
|
|
182
|
+
const hunsuId = primitive(makeHunsuId(event.value.hunsuId));
|
|
183
|
+
if (!hunsuId.ok)
|
|
184
|
+
return hunsuId;
|
|
185
|
+
const fromLineId = primitive(makeLineId(event.value.fromLineId));
|
|
186
|
+
if (!fromLineId.ok)
|
|
187
|
+
return fromLineId;
|
|
188
|
+
const newLineId = primitive(makeLineId(event.value.newLineId));
|
|
189
|
+
if (!newLineId.ok)
|
|
190
|
+
return newLineId;
|
|
191
|
+
const rawNewTeamName = event.value.newTeamName ?? event.value.newTeamName;
|
|
192
|
+
const newTeamName = rawNewTeamName === undefined
|
|
193
|
+
? ok(undefined)
|
|
194
|
+
: primitive(makeTeamName(rawNewTeamName, rawNewTeamName === event.value.newTeamName ? "event.newTeamName" : "event.newTeamName"));
|
|
195
|
+
if (!newTeamName.ok)
|
|
196
|
+
return newTeamName;
|
|
197
|
+
const fromMoveId = decodeOptionalMoveId(event.value.fromMoveId);
|
|
198
|
+
if (!fromMoveId.ok)
|
|
199
|
+
return fromMoveId;
|
|
200
|
+
const requestId = primitive(makeRequestId(event.value.requestId));
|
|
201
|
+
if (!requestId.ok)
|
|
202
|
+
return requestId;
|
|
203
|
+
const at = decodeOptionalAt(event.value.at, "event.at");
|
|
204
|
+
if (!at.ok)
|
|
205
|
+
return at;
|
|
206
|
+
return ok({
|
|
207
|
+
type: "LineForkedByHunsu",
|
|
208
|
+
hunsuId: hunsuId.value,
|
|
209
|
+
fromLineId: fromLineId.value,
|
|
210
|
+
newLineId: newLineId.value,
|
|
211
|
+
newTeamName: newTeamName.value,
|
|
212
|
+
fromMoveId: fromMoveId.value,
|
|
213
|
+
requestId: requestId.value,
|
|
214
|
+
at: at.value
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
case "ArtifactRecorded": {
|
|
218
|
+
const artifact = decodeArtifactRecord(event.value.artifact, "event.artifact");
|
|
219
|
+
if (!artifact.ok)
|
|
220
|
+
return artifact;
|
|
221
|
+
const at = decodeOptionalAt(event.value.at, "event.at");
|
|
222
|
+
if (!at.ok)
|
|
223
|
+
return at;
|
|
224
|
+
return ok({ type: "ArtifactRecorded", artifact: artifact.value, at: at.value });
|
|
225
|
+
}
|
|
226
|
+
default:
|
|
227
|
+
return workflowError(`Unsupported Hunsu domain event type: ${String(event.value.type)}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function primitive(result) {
|
|
231
|
+
return result.ok ? ok(result.value) : workflowError(result.error.message);
|
|
232
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./model.ts";
|
|
2
|
+
export * from "./errors.ts";
|
|
3
|
+
export { cloneHarnessPlanner, cloneHarnessEntity, composeHarnessSnapshot, cloneHarness, createDefaultHarness, createDefaultManagerConfig, createDefaultMemberConfig, DEFAULT_MANAGER_PROMPT, DEFAULT_TEAM_PROMPT, DEFAULT_MEMBER_APPROVAL, DEFAULT_MEMBER_EXECUTION, getHarnessExecutor, getHarnessMember, getHarnessTeam, harnessPlannerFromSnapshot, harnessEntityFromSnapshot, harnessSnapshotForTeam, getHarnessMemberConfig, isExecutableHarness, memberConfigFromMemberEntity, rootHarnessSnapshot, updateHarnessTeamPromptTemplate, updateHarnessMemberConfig, validateManagerConfig, validateMemberConfig, validateHarnessEntity, validateHarnessPlanner, validateHarness, validateExecutableHarness } from "./protocol-validation.ts";
|
|
4
|
+
export * from "./workflow.ts";
|
|
5
|
+
export { nextTeamName } from "./workflow-helpers.ts";
|
|
6
|
+
export * from "./result.ts";
|
|
7
|
+
export * from "./primitives.ts";
|
|
8
|
+
export * from "./prompt-template.ts";
|
|
9
|
+
export * from "./lifecycle.ts";
|
|
10
|
+
export { decodeArtifactActionDefinition, decodeArtifactActionDefinitionArray } from "./decoder-helpers.ts";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./model.js";
|
|
2
|
+
export * from "./errors.js";
|
|
3
|
+
export { cloneHarnessPlanner, cloneHarnessEntity, composeHarnessSnapshot, cloneHarness, createDefaultHarness, createDefaultManagerConfig, createDefaultMemberConfig, DEFAULT_MANAGER_PROMPT, DEFAULT_TEAM_PROMPT, DEFAULT_MEMBER_APPROVAL, DEFAULT_MEMBER_EXECUTION, getHarnessExecutor, getHarnessMember, getHarnessTeam, harnessPlannerFromSnapshot, harnessEntityFromSnapshot, harnessSnapshotForTeam, getHarnessMemberConfig, isExecutableHarness, memberConfigFromMemberEntity, rootHarnessSnapshot, updateHarnessTeamPromptTemplate, updateHarnessMemberConfig, validateManagerConfig, validateMemberConfig, validateHarnessEntity, validateHarnessPlanner, validateHarness, validateExecutableHarness } from "./protocol-validation.js";
|
|
4
|
+
export * from "./workflow.js";
|
|
5
|
+
export { nextTeamName } from "./workflow-helpers.js";
|
|
6
|
+
export * from "./result.js";
|
|
7
|
+
export * from "./primitives.js";
|
|
8
|
+
export * from "./prompt-template.js";
|
|
9
|
+
export * from "./lifecycle.js";
|
|
10
|
+
export { decodeArtifactActionDefinition, decodeArtifactActionDefinitionArray } from "./decoder-helpers.js";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { AccidentMoveRecord, AbandonedLine, ActiveLine, ArrivedMoveRecord, BlockedDestination, CanceledDestination, ClaimedDestination, CompleteLine, ConfirmedHunsuDraft, Destination, DestinationId, DestinationPatch, DestinationSeed, DestinationSource, DiscardedHunsuDraft, DomainRole, DraftHunsuDraft, DraftSkillDraft, FailedLine, HunsuDraftBase, HunsuDraftRecord, HunsuId, InProgressDestination, LineId, LineRecord, MoveId, MoveRecord, MoveRecordBase, PausedLine, PlayableLine, PendingDestination, ReadyHunsuDraft, ReachedDestination, RequestId, SkillBinding, SkillDraftBase, SkillDraftRecord, SupersededDestination } from "./model.ts";
|
|
2
|
+
import { type Result } from "./result.ts";
|
|
3
|
+
export type DomainModelError = {
|
|
4
|
+
type: "DomainModelError";
|
|
5
|
+
message: string;
|
|
6
|
+
};
|
|
7
|
+
export type DestinationTransitionMeta = {
|
|
8
|
+
updatedBy: DomainRole;
|
|
9
|
+
updatedAt?: string;
|
|
10
|
+
};
|
|
11
|
+
export type ClaimDestinationInput = DestinationTransitionMeta & {
|
|
12
|
+
claimedBy: string;
|
|
13
|
+
};
|
|
14
|
+
export type StartDestinationWorkInput = DestinationTransitionMeta & {
|
|
15
|
+
claimedBy: string;
|
|
16
|
+
};
|
|
17
|
+
export type BlockDestinationInput = DestinationTransitionMeta & {
|
|
18
|
+
blockedReason: string;
|
|
19
|
+
};
|
|
20
|
+
export type ReachDestinationInput = DestinationTransitionMeta & {
|
|
21
|
+
reachedByMoveId: MoveId;
|
|
22
|
+
};
|
|
23
|
+
export type CancelDestinationInput = DestinationTransitionMeta & {
|
|
24
|
+
canceledReason: string;
|
|
25
|
+
};
|
|
26
|
+
export type SupersedeDestinationInput = DestinationTransitionMeta & {
|
|
27
|
+
supersededByDestinationId: DestinationId;
|
|
28
|
+
};
|
|
29
|
+
export type UpdateDestinationDetailsInput = DestinationTransitionMeta & DestinationPatch;
|
|
30
|
+
export declare function createPendingDestination(requestId: RequestId, seed: DestinationSeed, source: DestinationSource, at?: string): PendingDestination;
|
|
31
|
+
export declare function makePendingDestination(input: Omit<PendingDestination, "status">): Result<PendingDestination, DomainModelError>;
|
|
32
|
+
export declare function claimDestination(destination: Destination, input: ClaimDestinationInput): Result<ClaimedDestination, DomainModelError>;
|
|
33
|
+
export declare function startDestinationWork(destination: Destination, input: StartDestinationWorkInput): Result<InProgressDestination, DomainModelError>;
|
|
34
|
+
export declare function blockDestination(destination: Destination, input: BlockDestinationInput): Result<BlockedDestination, DomainModelError>;
|
|
35
|
+
export declare function unblockDestination(destination: Destination, input: DestinationTransitionMeta): Result<PendingDestination, DomainModelError>;
|
|
36
|
+
export declare function reachDestination(destination: Destination, input: ReachDestinationInput): Result<ReachedDestination, DomainModelError>;
|
|
37
|
+
export declare function cancelDestination(destination: Destination, input: CancelDestinationInput): Result<CanceledDestination, DomainModelError>;
|
|
38
|
+
export declare function supersedeDestination(destination: Destination, input: SupersedeDestinationInput): Result<SupersededDestination, DomainModelError>;
|
|
39
|
+
export declare function updateDestinationDetails(destination: Destination, input: UpdateDestinationDetailsInput): Result<Destination, DomainModelError>;
|
|
40
|
+
export declare function pauseLine(line: LineRecord): Result<PausedLine, DomainModelError>;
|
|
41
|
+
export declare function resumeLine(line: LineRecord): Result<ActiveLine, DomainModelError>;
|
|
42
|
+
export declare function completeLine(line: LineRecord): Result<CompleteLine, DomainModelError>;
|
|
43
|
+
export declare function abandonLine(line: LineRecord): Result<AbandonedLine, DomainModelError>;
|
|
44
|
+
export declare function failLine(line: LineRecord): Result<FailedLine, DomainModelError>;
|
|
45
|
+
export declare function makePlayableLine(line: LineRecord): Result<PlayableLine, DomainModelError>;
|
|
46
|
+
export declare function makeArrivedMoveRecord(input: MoveRecordBase & {
|
|
47
|
+
reachedDestinationIds: DestinationId[];
|
|
48
|
+
}): Result<ArrivedMoveRecord, DomainModelError>;
|
|
49
|
+
export declare function makeAccidentMoveRecord(input: MoveRecordBase & {
|
|
50
|
+
failureReason: string;
|
|
51
|
+
}): Result<AccidentMoveRecord, DomainModelError>;
|
|
52
|
+
export type MoveRecordBasePatch = Partial<Pick<MoveRecordBase, "fromNodeId" | "toNodeId" | "teamName" | "ordinal" | "snapshot" | "commit">>;
|
|
53
|
+
export declare function patchMoveRecordBase(move: MoveRecord, patch: MoveRecordBasePatch): MoveRecord;
|
|
54
|
+
export declare function normalizeMoveRecord(move: MoveRecord): Result<MoveRecord, DomainModelError>;
|
|
55
|
+
export declare function makeDraftSkillDraft(input: Omit<SkillDraftBase, "status">): DraftSkillDraft;
|
|
56
|
+
export declare function makeDraftHunsuDraft(input: Omit<HunsuDraftBase, "status">): DraftHunsuDraft;
|
|
57
|
+
export declare function makeReadyHunsuDraft(input: Omit<ReadyHunsuDraft, "status">): ReadyHunsuDraft;
|
|
58
|
+
export declare function confirmHunsuDraftRecord(draft: HunsuDraftRecord, input?: {
|
|
59
|
+
hunsuId?: HunsuId;
|
|
60
|
+
newLineId?: LineId;
|
|
61
|
+
updatedAt?: string;
|
|
62
|
+
}): Result<ConfirmedHunsuDraft, DomainModelError>;
|
|
63
|
+
export declare function discardHunsuDraftRecord(draft: HunsuDraftRecord, input?: {
|
|
64
|
+
updatedAt?: string;
|
|
65
|
+
}): Result<DiscardedHunsuDraft, DomainModelError>;
|
|
66
|
+
export declare function acceptSkillDraft(draft: SkillDraftRecord, acceptedSnapshot: SkillBinding): Result<SkillDraftRecord, DomainModelError>;
|
|
67
|
+
export declare function discardSkillDraft(draft: SkillDraftRecord): Result<SkillDraftRecord, DomainModelError>;
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { makeFailureReason, makeNonEmptyArray, makeSingleItemArray } from "./primitives.js";
|
|
2
|
+
import { err, ok } from "./result.js";
|
|
3
|
+
export function createPendingDestination(requestId, seed, source, at) {
|
|
4
|
+
const actor = source === "initial-execute-team" || source === "initial-request" ? "SYSTEM" : "DIRECTOR";
|
|
5
|
+
return {
|
|
6
|
+
...seed,
|
|
7
|
+
requestId,
|
|
8
|
+
status: "pending",
|
|
9
|
+
source,
|
|
10
|
+
createdBy: actor,
|
|
11
|
+
updatedBy: actor,
|
|
12
|
+
createdAt: at,
|
|
13
|
+
updatedAt: at
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function makePendingDestination(input) {
|
|
17
|
+
return ok({ ...input, status: "pending" });
|
|
18
|
+
}
|
|
19
|
+
export function claimDestination(destination, input) {
|
|
20
|
+
if (!hasText(input.claimedBy)) {
|
|
21
|
+
return invalid("Claim Destination requires claimedBy");
|
|
22
|
+
}
|
|
23
|
+
if (destination.status !== "pending" && destination.status !== "blocked") {
|
|
24
|
+
return invalid(`Destination ${destination.id} cannot be claimed from status ${destination.status}`);
|
|
25
|
+
}
|
|
26
|
+
return ok({
|
|
27
|
+
...baseDestinationForTransition(destination, input),
|
|
28
|
+
status: "claimed",
|
|
29
|
+
claimedBy: input.claimedBy
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export function startDestinationWork(destination, input) {
|
|
33
|
+
if (!hasText(input.claimedBy)) {
|
|
34
|
+
return invalid("Start Destination work requires claimedBy");
|
|
35
|
+
}
|
|
36
|
+
if (destination.status === "blocked") {
|
|
37
|
+
return invalid(`Destination ${destination.id} cannot start work from status blocked`);
|
|
38
|
+
}
|
|
39
|
+
if (isClosedDestination(destination)) {
|
|
40
|
+
return invalid(`Destination ${destination.id} cannot start work from status ${destination.status}`);
|
|
41
|
+
}
|
|
42
|
+
return ok({
|
|
43
|
+
...baseDestinationForTransition(destination, input),
|
|
44
|
+
status: "in_progress",
|
|
45
|
+
claimedBy: input.claimedBy
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export function blockDestination(destination, input) {
|
|
49
|
+
if (!hasText(input.blockedReason)) {
|
|
50
|
+
return invalid("Block Destination requires blockedReason");
|
|
51
|
+
}
|
|
52
|
+
if (isClosedDestination(destination)) {
|
|
53
|
+
return invalid(`Destination ${destination.id} cannot be blocked from status ${destination.status}`);
|
|
54
|
+
}
|
|
55
|
+
return ok({
|
|
56
|
+
...baseDestinationForTransition(destination, input),
|
|
57
|
+
status: "blocked",
|
|
58
|
+
claimedBy: "claimedBy" in destination ? destination.claimedBy : undefined,
|
|
59
|
+
blockedReason: input.blockedReason
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export function unblockDestination(destination, input) {
|
|
63
|
+
if (destination.status !== "blocked") {
|
|
64
|
+
return invalid(`Destination ${destination.id} cannot be unblocked from status ${destination.status}`);
|
|
65
|
+
}
|
|
66
|
+
return ok({
|
|
67
|
+
...baseDestinationForTransition(destination, input),
|
|
68
|
+
status: "pending"
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
export function reachDestination(destination, input) {
|
|
72
|
+
if (!hasText(input.reachedByMoveId)) {
|
|
73
|
+
return invalid("Reach Destination requires reachedByMoveId");
|
|
74
|
+
}
|
|
75
|
+
if (isClosedDestination(destination)) {
|
|
76
|
+
return invalid(`Destination ${destination.id} cannot be reached from status ${destination.status}`);
|
|
77
|
+
}
|
|
78
|
+
return ok({
|
|
79
|
+
...baseDestinationForTransition(destination, input),
|
|
80
|
+
status: "reached",
|
|
81
|
+
claimedBy: "claimedBy" in destination ? destination.claimedBy : undefined,
|
|
82
|
+
reachedByMoveId: input.reachedByMoveId
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
export function cancelDestination(destination, input) {
|
|
86
|
+
if (!hasText(input.canceledReason)) {
|
|
87
|
+
return invalid("Cancel Destination requires canceledReason");
|
|
88
|
+
}
|
|
89
|
+
if (isClosedDestination(destination)) {
|
|
90
|
+
return invalid(`Destination ${destination.id} cannot be canceled from status ${destination.status}`);
|
|
91
|
+
}
|
|
92
|
+
return ok({
|
|
93
|
+
...baseDestinationForTransition(destination, input),
|
|
94
|
+
status: "canceled",
|
|
95
|
+
claimedBy: "claimedBy" in destination ? destination.claimedBy : undefined,
|
|
96
|
+
canceledReason: input.canceledReason
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
export function supersedeDestination(destination, input) {
|
|
100
|
+
if (!hasText(input.supersededByDestinationId)) {
|
|
101
|
+
return invalid("Supersede Destination requires supersededByDestinationId");
|
|
102
|
+
}
|
|
103
|
+
if (isClosedDestination(destination)) {
|
|
104
|
+
return invalid(`Destination ${destination.id} cannot be superseded from status ${destination.status}`);
|
|
105
|
+
}
|
|
106
|
+
return ok({
|
|
107
|
+
...baseDestinationForTransition(destination, input),
|
|
108
|
+
status: "superseded",
|
|
109
|
+
claimedBy: "claimedBy" in destination ? destination.claimedBy : undefined,
|
|
110
|
+
supersededByDestinationId: input.supersededByDestinationId
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
export function updateDestinationDetails(destination, input) {
|
|
114
|
+
const { updatedBy, updatedAt, ...patch } = input;
|
|
115
|
+
return ok({
|
|
116
|
+
...destination,
|
|
117
|
+
...patch,
|
|
118
|
+
updatedBy,
|
|
119
|
+
updatedAt
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
export function pauseLine(line) {
|
|
123
|
+
return line.status === "active"
|
|
124
|
+
? ok({ ...line, status: "paused" })
|
|
125
|
+
: invalid(`Line ${line.id} cannot be paused from status ${line.status}`);
|
|
126
|
+
}
|
|
127
|
+
export function resumeLine(line) {
|
|
128
|
+
return line.status === "paused"
|
|
129
|
+
? ok({ ...line, status: "active" })
|
|
130
|
+
: invalid(`Line ${line.id} cannot be resumed from status ${line.status}`);
|
|
131
|
+
}
|
|
132
|
+
export function completeLine(line) {
|
|
133
|
+
return line.status === "active" || line.status === "paused"
|
|
134
|
+
? ok({ ...line, status: "complete" })
|
|
135
|
+
: invalid(`Line ${line.id} cannot be completed from status ${line.status}`);
|
|
136
|
+
}
|
|
137
|
+
export function abandonLine(line) {
|
|
138
|
+
return line.status === "active" || line.status === "paused"
|
|
139
|
+
? ok({ ...line, status: "abandoned" })
|
|
140
|
+
: invalid(`Line ${line.id} cannot be abandoned from status ${line.status}`);
|
|
141
|
+
}
|
|
142
|
+
export function failLine(line) {
|
|
143
|
+
return line.status === "active"
|
|
144
|
+
? ok({ ...line, status: "failed" })
|
|
145
|
+
: invalid(`Line ${line.id} cannot fail from status ${line.status}`);
|
|
146
|
+
}
|
|
147
|
+
export function makePlayableLine(line) {
|
|
148
|
+
return line.status === "active"
|
|
149
|
+
? ok(line)
|
|
150
|
+
: invalid(`TEAM line ${line.id} cannot continue from status ${line.status}`);
|
|
151
|
+
}
|
|
152
|
+
export function makeArrivedMoveRecord(input) {
|
|
153
|
+
const reached = makeSingleItemArray(input.reachedDestinationIds, "reachedDestinationIds");
|
|
154
|
+
if (!reached.ok) {
|
|
155
|
+
return invalid("Arrived MOVE must reach exactly one Destination");
|
|
156
|
+
}
|
|
157
|
+
const evidence = makeNonEmptyArray(input.evidence, "evidence");
|
|
158
|
+
if (!evidence.ok) {
|
|
159
|
+
return invalid("Arrived MOVE requires evidence");
|
|
160
|
+
}
|
|
161
|
+
return ok({
|
|
162
|
+
...input,
|
|
163
|
+
outcome: "arrived",
|
|
164
|
+
reachedDestinationIds: reached.value,
|
|
165
|
+
evidence: evidence.value
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
export function makeAccidentMoveRecord(input) {
|
|
169
|
+
const failureReason = makeFailureReason(input.failureReason, "failureReason");
|
|
170
|
+
if (!failureReason.ok) {
|
|
171
|
+
return invalid("Accident MOVE requires failureReason");
|
|
172
|
+
}
|
|
173
|
+
const evidence = makeNonEmptyArray(input.evidence, "evidence");
|
|
174
|
+
if (!evidence.ok) {
|
|
175
|
+
return invalid("Accident MOVE requires evidence");
|
|
176
|
+
}
|
|
177
|
+
return ok({
|
|
178
|
+
...input,
|
|
179
|
+
outcome: "accident",
|
|
180
|
+
reachedDestinationIds: [],
|
|
181
|
+
failureReason: failureReason.value,
|
|
182
|
+
evidence: evidence.value
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
export function patchMoveRecordBase(move, patch) {
|
|
186
|
+
if (move.outcome === "accident") {
|
|
187
|
+
return {
|
|
188
|
+
...move,
|
|
189
|
+
...patch,
|
|
190
|
+
outcome: "accident",
|
|
191
|
+
reachedDestinationIds: [],
|
|
192
|
+
failureReason: move.failureReason
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
...move,
|
|
197
|
+
...patch,
|
|
198
|
+
outcome: "arrived",
|
|
199
|
+
reachedDestinationIds: move.reachedDestinationIds
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
export function normalizeMoveRecord(move) {
|
|
203
|
+
if (move.outcome === "accident") {
|
|
204
|
+
return makeAccidentMoveRecord(move);
|
|
205
|
+
}
|
|
206
|
+
return makeArrivedMoveRecord({
|
|
207
|
+
...move,
|
|
208
|
+
reachedDestinationIds: move.reachedDestinationIds
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
export function makeDraftSkillDraft(input) {
|
|
212
|
+
return { ...input, status: "draft" };
|
|
213
|
+
}
|
|
214
|
+
export function makeDraftHunsuDraft(input) {
|
|
215
|
+
return { ...input, status: "draft" };
|
|
216
|
+
}
|
|
217
|
+
export function makeReadyHunsuDraft(input) {
|
|
218
|
+
return { ...input, status: "ready" };
|
|
219
|
+
}
|
|
220
|
+
export function confirmHunsuDraftRecord(draft, input = {}) {
|
|
221
|
+
if (draft.status !== "ready") {
|
|
222
|
+
return invalid(`HUNSU Draft ${draft.id} cannot be confirmed from status ${draft.status}`);
|
|
223
|
+
}
|
|
224
|
+
return ok({
|
|
225
|
+
...draft,
|
|
226
|
+
status: "confirmed",
|
|
227
|
+
hunsuId: input.hunsuId ?? draft.hunsuId,
|
|
228
|
+
newLineId: input.newLineId ?? draft.newLineId,
|
|
229
|
+
updatedAt: input.updatedAt ?? draft.updatedAt
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
export function discardHunsuDraftRecord(draft, input = {}) {
|
|
233
|
+
if (draft.status !== "draft" && draft.status !== "ready") {
|
|
234
|
+
return invalid(`HUNSU Draft ${draft.id} cannot be discarded from status ${draft.status}`);
|
|
235
|
+
}
|
|
236
|
+
const { hunsuId: _hunsuId, newLineId: _newLineId, ...base } = draft;
|
|
237
|
+
return ok({
|
|
238
|
+
...base,
|
|
239
|
+
status: "discarded",
|
|
240
|
+
updatedAt: input.updatedAt ?? draft.updatedAt
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
export function acceptSkillDraft(draft, acceptedSnapshot) {
|
|
244
|
+
if (draft.status !== "draft") {
|
|
245
|
+
return invalid(`Skill Draft ${draft.id} cannot be accepted from status ${draft.status}`);
|
|
246
|
+
}
|
|
247
|
+
return ok({ ...draft, status: "accepted", acceptedSnapshot });
|
|
248
|
+
}
|
|
249
|
+
export function discardSkillDraft(draft) {
|
|
250
|
+
if (draft.status !== "draft") {
|
|
251
|
+
return invalid(`Skill Draft ${draft.id} cannot be discarded from status ${draft.status}`);
|
|
252
|
+
}
|
|
253
|
+
return ok({ ...draft, status: "discarded" });
|
|
254
|
+
}
|
|
255
|
+
function baseDestinationForTransition(destination, input) {
|
|
256
|
+
const { claimedBy: _claimedBy, reachedByMoveId: _reachedByMoveId, blockedReason: _blockedReason, canceledReason: _canceledReason, supersededByDestinationId: _supersededByDestinationId, ...base } = destination;
|
|
257
|
+
return {
|
|
258
|
+
...base,
|
|
259
|
+
updatedBy: input.updatedBy,
|
|
260
|
+
updatedAt: input.updatedAt
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function isClosedDestination(destination) {
|
|
264
|
+
return destination.status === "reached" || destination.status === "canceled" || destination.status === "superseded";
|
|
265
|
+
}
|
|
266
|
+
function hasText(value) {
|
|
267
|
+
return typeof value === "string" && value.trim() !== "";
|
|
268
|
+
}
|
|
269
|
+
function invalid(message) {
|
|
270
|
+
return err({ type: "DomainModelError", message });
|
|
271
|
+
}
|