@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/result.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type Result<T, E> = {
|
|
2
|
+
ok: true;
|
|
3
|
+
value: T;
|
|
4
|
+
} | {
|
|
5
|
+
ok: false;
|
|
6
|
+
error: E;
|
|
7
|
+
};
|
|
8
|
+
export type Option<T> = {
|
|
9
|
+
type: "some";
|
|
10
|
+
value: T;
|
|
11
|
+
} | {
|
|
12
|
+
type: "none";
|
|
13
|
+
};
|
|
14
|
+
export declare function ok<T>(value: T): Result<T, never>;
|
|
15
|
+
export declare function err<E>(error: E): Result<never, E>;
|
|
16
|
+
export declare function some<T>(value: T): Option<T>;
|
|
17
|
+
export declare function none<T = never>(): Option<T>;
|
|
18
|
+
export declare function map<T, U, E>(result: Result<T, E>, transform: (value: T) => U): Result<U, E>;
|
|
19
|
+
export declare function flatMap<T, U, E>(result: Result<T, E>, transform: (value: T) => Result<U, E>): Result<U, E>;
|
|
20
|
+
export declare function fromNullable<T, E>(value: T | null | undefined, error: E): Result<T, E>;
|
package/dist/result.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function ok(value) {
|
|
2
|
+
return { ok: true, value };
|
|
3
|
+
}
|
|
4
|
+
export function err(error) {
|
|
5
|
+
return { ok: false, error };
|
|
6
|
+
}
|
|
7
|
+
export function some(value) {
|
|
8
|
+
return { type: "some", value };
|
|
9
|
+
}
|
|
10
|
+
export function none() {
|
|
11
|
+
return { type: "none" };
|
|
12
|
+
}
|
|
13
|
+
export function map(result, transform) {
|
|
14
|
+
return result.ok ? ok(transform(result.value)) : result;
|
|
15
|
+
}
|
|
16
|
+
export function flatMap(result, transform) {
|
|
17
|
+
return result.ok ? transform(result.value) : result;
|
|
18
|
+
}
|
|
19
|
+
export function fromNullable(value, error) {
|
|
20
|
+
return value === null || value === undefined ? err(error) : ok(value);
|
|
21
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { BoardProjection, Destination, DestinationId, TeamName, HunsuId, LineId, LineRecord, MoveRecord, NodeId, NodeRecord, RequestId, SkillDraftRecord } from "./model.ts";
|
|
2
|
+
export declare const TEAM_NAME_POOL: string[];
|
|
3
|
+
export declare function upsertById<T extends {
|
|
4
|
+
id: string;
|
|
5
|
+
}>(items: T[], item: T): T[];
|
|
6
|
+
export declare function unique<T>(items: T[]): T[];
|
|
7
|
+
export declare function requestIdForLine(state: BoardProjection, lineId: LineId): RequestId;
|
|
8
|
+
export declare function rootNodeId(requestId: RequestId): NodeId;
|
|
9
|
+
export declare function nextNodeId(state: BoardProjection): NodeId;
|
|
10
|
+
export declare function nextTeamName(state: BoardProjection): TeamName;
|
|
11
|
+
export declare function nextForkLineId(lineId: LineId, hunsuId: HunsuId): LineId;
|
|
12
|
+
export declare function requireRequest(state: BoardProjection, requestId: string): void;
|
|
13
|
+
export declare function requireLine(state: BoardProjection, lineId: string): LineRecord;
|
|
14
|
+
export declare function requirePlayableLine(line: LineRecord): void;
|
|
15
|
+
export declare function requireNode(state: BoardProjection, nodeId: NodeId): NodeRecord;
|
|
16
|
+
export declare function requireRootNode(state: BoardProjection, requestId: RequestId): NodeRecord;
|
|
17
|
+
export declare function requireCurrentNode(state: BoardProjection, line: LineRecord): NodeRecord;
|
|
18
|
+
export declare function requireMove(state: BoardProjection, moveId: string): MoveRecord;
|
|
19
|
+
export declare function requireSkillDraft(state: BoardProjection, draftId: string): SkillDraftRecord;
|
|
20
|
+
export declare function nodeForMove(state: BoardProjection, move: MoveRecord): NodeRecord | undefined;
|
|
21
|
+
export declare function requireExistingDestination(state: BoardProjection, destinationId: DestinationId): Destination;
|
|
22
|
+
export declare function requireDestinationInNode(node: NodeRecord, destinationId: DestinationId): Destination;
|
|
23
|
+
export declare function requireClaimableDestination(state: BoardProjection, destinationId: DestinationId): Destination;
|
|
24
|
+
export declare function requireBlockableDestination(state: BoardProjection, destinationId: DestinationId): Destination;
|
|
25
|
+
export declare function requireBlockableDestinationInNode(node: NodeRecord, destinationId: DestinationId): Destination;
|
|
26
|
+
export declare function requireBlockedDestinationInNode(node: NodeRecord, destinationId: DestinationId): Destination;
|
|
27
|
+
export declare function requireReachibleDestinationInNode(node: NodeRecord, destinationId: DestinationId): Destination;
|
|
28
|
+
export declare function isClosedDestination(destination: Destination): boolean;
|
|
29
|
+
export declare function assertNonEmpty<T>(items: T[], message: string): void;
|
|
30
|
+
export declare function assertSingleDestination<T>(items: T[], message: string): void;
|
|
31
|
+
export declare function assertText(value: string, message: string): void;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { DomainInvariantError, unwrapDomainModelResult } from "./errors.js";
|
|
2
|
+
import { makeLineId, makeTeamName, makeNodeId, } from "./primitives.js";
|
|
3
|
+
export const TEAM_NAME_POOL = [
|
|
4
|
+
"T1",
|
|
5
|
+
"Gen.G",
|
|
6
|
+
"Hanwha Life Esports",
|
|
7
|
+
"KT Rolster",
|
|
8
|
+
"Dplus KIA",
|
|
9
|
+
"DRX",
|
|
10
|
+
"Nongshim RedForce",
|
|
11
|
+
"BNK FearX",
|
|
12
|
+
"DN Freecs",
|
|
13
|
+
"OKSavingsBank BRION",
|
|
14
|
+
"Bilibili Gaming",
|
|
15
|
+
"JD Gaming",
|
|
16
|
+
"Top Esports",
|
|
17
|
+
"Anyone's Legend",
|
|
18
|
+
"Ninjas in Pyjamas",
|
|
19
|
+
"Weibo Gaming",
|
|
20
|
+
"Invictus Gaming",
|
|
21
|
+
"Team WE",
|
|
22
|
+
"LNG Esports",
|
|
23
|
+
"TT Gaming",
|
|
24
|
+
"LGD Gaming",
|
|
25
|
+
"EDward Gaming",
|
|
26
|
+
"Ultra Prime",
|
|
27
|
+
"Oh My God",
|
|
28
|
+
"Cloud9",
|
|
29
|
+
"Team Liquid",
|
|
30
|
+
"FlyQuest",
|
|
31
|
+
"Shopify Rebellion",
|
|
32
|
+
"100 Thieves",
|
|
33
|
+
"Dignitas",
|
|
34
|
+
"LYON",
|
|
35
|
+
"Sentinels",
|
|
36
|
+
"G2 Esports",
|
|
37
|
+
"Fnatic",
|
|
38
|
+
"Karmine Corp",
|
|
39
|
+
"Team Vitality",
|
|
40
|
+
"Team Heretics",
|
|
41
|
+
"GIANTX",
|
|
42
|
+
"Movistar KOI",
|
|
43
|
+
"SK Gaming",
|
|
44
|
+
"Natus Vincere",
|
|
45
|
+
"Rogue"
|
|
46
|
+
];
|
|
47
|
+
export function upsertById(items, item) {
|
|
48
|
+
if (items.some(existing => existing.id === item.id)) {
|
|
49
|
+
return items.map(existing => (existing.id === item.id ? item : existing));
|
|
50
|
+
}
|
|
51
|
+
return [...items, item];
|
|
52
|
+
}
|
|
53
|
+
export function unique(items) {
|
|
54
|
+
return Array.from(new Set(items));
|
|
55
|
+
}
|
|
56
|
+
export function requestIdForLine(state, lineId) {
|
|
57
|
+
return requireLine(state, lineId).requestId;
|
|
58
|
+
}
|
|
59
|
+
export function rootNodeId(requestId) {
|
|
60
|
+
return unwrapDomainModelResult(makeNodeId(`${requestId}:root`));
|
|
61
|
+
}
|
|
62
|
+
export function nextNodeId(state) {
|
|
63
|
+
const highest = state.nodes.reduce((max, node) => {
|
|
64
|
+
const match = node.id.match(/^N(\d+)$/i);
|
|
65
|
+
return match ? Math.max(max, Number(match[1])) : max;
|
|
66
|
+
}, 0);
|
|
67
|
+
return unwrapDomainModelResult(makeNodeId(`N${String(highest + 1).padStart(4, "0")}`));
|
|
68
|
+
}
|
|
69
|
+
export function nextTeamName(state) {
|
|
70
|
+
const used = new Set(state.lines.map(line => line.teamName).filter((name) => Boolean(name)));
|
|
71
|
+
const available = TEAM_NAME_POOL.find(name => !used.has(name));
|
|
72
|
+
if (available) {
|
|
73
|
+
return unwrapDomainModelResult(makeTeamName(available));
|
|
74
|
+
}
|
|
75
|
+
return unwrapDomainModelResult(makeTeamName(`${TEAM_NAME_POOL[state.lines.length % TEAM_NAME_POOL.length]}-${state.lines.length + 1}`));
|
|
76
|
+
}
|
|
77
|
+
export function nextForkLineId(lineId, hunsuId) {
|
|
78
|
+
return unwrapDomainModelResult(makeLineId(`${lineId}/fork-${hunsuId}`));
|
|
79
|
+
}
|
|
80
|
+
export function requireRequest(state, requestId) {
|
|
81
|
+
if (!state.requests.some(request => request.id === requestId)) {
|
|
82
|
+
throw new DomainInvariantError(`Unknown request: ${requestId}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export function requireLine(state, lineId) {
|
|
86
|
+
const line = state.lines.find(candidate => candidate.id === lineId);
|
|
87
|
+
if (!line) {
|
|
88
|
+
throw new DomainInvariantError(`Unknown line: ${lineId}`);
|
|
89
|
+
}
|
|
90
|
+
return line;
|
|
91
|
+
}
|
|
92
|
+
export function requirePlayableLine(line) {
|
|
93
|
+
if (line.status !== "active") {
|
|
94
|
+
throw new DomainInvariantError(`TEAM line ${line.id} cannot continue from status ${line.status}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export function requireNode(state, nodeId) {
|
|
98
|
+
const node = state.nodes.find(candidate => candidate.id === nodeId);
|
|
99
|
+
if (!node) {
|
|
100
|
+
throw new DomainInvariantError(`Unknown node: ${nodeId}`);
|
|
101
|
+
}
|
|
102
|
+
return node;
|
|
103
|
+
}
|
|
104
|
+
export function requireRootNode(state, requestId) {
|
|
105
|
+
return requireNode(state, rootNodeId(requestId));
|
|
106
|
+
}
|
|
107
|
+
export function requireCurrentNode(state, line) {
|
|
108
|
+
return requireNode(state, line.currentNodeId);
|
|
109
|
+
}
|
|
110
|
+
export function requireMove(state, moveId) {
|
|
111
|
+
const move = state.moves.find(candidate => candidate.id === moveId);
|
|
112
|
+
if (!move) {
|
|
113
|
+
throw new DomainInvariantError(`Unknown MOVE: ${moveId}`);
|
|
114
|
+
}
|
|
115
|
+
return move;
|
|
116
|
+
}
|
|
117
|
+
export function requireSkillDraft(state, draftId) {
|
|
118
|
+
const draft = state.skillDrafts.find(candidate => candidate.id === draftId);
|
|
119
|
+
if (!draft) {
|
|
120
|
+
throw new DomainInvariantError(`Unknown Skill Draft: ${draftId}`);
|
|
121
|
+
}
|
|
122
|
+
return draft;
|
|
123
|
+
}
|
|
124
|
+
export function nodeForMove(state, move) {
|
|
125
|
+
return move.toNodeId ? state.nodes.find(node => node.id === move.toNodeId) : undefined;
|
|
126
|
+
}
|
|
127
|
+
export function requireExistingDestination(state, destinationId) {
|
|
128
|
+
const destination = state.destinations.find(candidate => candidate.id === destinationId);
|
|
129
|
+
if (!destination) {
|
|
130
|
+
throw new DomainInvariantError(`Unknown Destination: ${destinationId}`);
|
|
131
|
+
}
|
|
132
|
+
return destination;
|
|
133
|
+
}
|
|
134
|
+
export function requireDestinationInNode(node, destinationId) {
|
|
135
|
+
const destination = node.destinations.find(candidate => candidate.id === destinationId);
|
|
136
|
+
if (!destination) {
|
|
137
|
+
throw new DomainInvariantError(`Unknown Destination in node ${node.id}: ${destinationId}`);
|
|
138
|
+
}
|
|
139
|
+
return destination;
|
|
140
|
+
}
|
|
141
|
+
export function requireClaimableDestination(state, destinationId) {
|
|
142
|
+
const destination = requireExistingDestination(state, destinationId);
|
|
143
|
+
if (destination.status !== "pending" && destination.status !== "blocked") {
|
|
144
|
+
throw new DomainInvariantError(`Destination ${destinationId} cannot be claimed from status ${destination.status}`);
|
|
145
|
+
}
|
|
146
|
+
return destination;
|
|
147
|
+
}
|
|
148
|
+
export function requireBlockableDestination(state, destinationId) {
|
|
149
|
+
const destination = requireExistingDestination(state, destinationId);
|
|
150
|
+
if (isClosedDestination(destination)) {
|
|
151
|
+
throw new DomainInvariantError(`Destination ${destinationId} cannot be blocked from status ${destination.status}`);
|
|
152
|
+
}
|
|
153
|
+
return destination;
|
|
154
|
+
}
|
|
155
|
+
export function requireBlockableDestinationInNode(node, destinationId) {
|
|
156
|
+
const destination = requireDestinationInNode(node, destinationId);
|
|
157
|
+
if (isClosedDestination(destination)) {
|
|
158
|
+
throw new DomainInvariantError(`Destination ${destinationId} cannot be blocked from status ${destination.status}`);
|
|
159
|
+
}
|
|
160
|
+
return destination;
|
|
161
|
+
}
|
|
162
|
+
export function requireBlockedDestinationInNode(node, destinationId) {
|
|
163
|
+
const destination = requireDestinationInNode(node, destinationId);
|
|
164
|
+
if (destination.status !== "blocked") {
|
|
165
|
+
throw new DomainInvariantError(`Destination ${destinationId} cannot be unblocked from status ${destination.status}`);
|
|
166
|
+
}
|
|
167
|
+
return destination;
|
|
168
|
+
}
|
|
169
|
+
export function requireReachibleDestinationInNode(node, destinationId) {
|
|
170
|
+
const destination = requireDestinationInNode(node, destinationId);
|
|
171
|
+
if (isClosedDestination(destination)) {
|
|
172
|
+
throw new DomainInvariantError(`Destination ${destinationId} cannot be reached from status ${destination.status}`);
|
|
173
|
+
}
|
|
174
|
+
return destination;
|
|
175
|
+
}
|
|
176
|
+
export function isClosedDestination(destination) {
|
|
177
|
+
return destination.status === "reached" || destination.status === "canceled" || destination.status === "superseded";
|
|
178
|
+
}
|
|
179
|
+
export function assertNonEmpty(items, message) {
|
|
180
|
+
if (items.length === 0) {
|
|
181
|
+
throw new DomainInvariantError(message);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
export function assertSingleDestination(items, message) {
|
|
185
|
+
if (items.length !== 1) {
|
|
186
|
+
throw new DomainInvariantError(message);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
export function assertText(value, message) {
|
|
190
|
+
if (value.trim() === "") {
|
|
191
|
+
throw new DomainInvariantError(message);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BoardProjection, Command, DomainEvent, ValidatedCommand } from "./model.ts";
|
|
2
|
+
import { type DomainWorkflowError } from "./errors.ts";
|
|
3
|
+
import { type Result } from "./result.ts";
|
|
4
|
+
export { DomainInvariantError, type DomainWorkflowError } from "./errors.ts";
|
|
5
|
+
export { cloneHarness, createDefaultHarness, createDefaultMemberConfig, getHarnessMemberConfig, isExecutableHarness, validateHarness, validateExecutableHarness } from "./protocol-validation.ts";
|
|
6
|
+
export { emptyBoardProjection, projectBoard, projectEvent, tryProjectBoard, validateEventStream } from "./projection.ts";
|
|
7
|
+
export declare function tryHandleCommand(command: Command, state?: BoardProjection): Result<DomainEvent[], DomainWorkflowError>;
|
|
8
|
+
export declare function tryApplyCommand(events: DomainEvent[], command: Command): Result<DomainEvent[], DomainWorkflowError>;
|
|
9
|
+
export declare function validateCommand(input: unknown): Result<ValidatedCommand, DomainWorkflowError>;
|
|
10
|
+
export declare function validateDomainEvent(value: unknown): Result<DomainEvent, DomainWorkflowError>;
|