@cricnice/online-contracts 0.0.0-stage → 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/README.md +13 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/scoring/index.d.ts +2 -0
- package/dist/scoring/index.d.ts.map +1 -0
- package/dist/scoring/index.js +1 -0
- package/dist/scoring/scoreCommand.d.ts +215 -0
- package/dist/scoring/scoreCommand.d.ts.map +1 -0
- package/dist/scoring/scoreCommand.js +97 -0
- package/package.json +28 -4
package/README.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CricNice Online Contracts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Online-only API and event contracts shared by the mobile app and backend. Offline/Realm scoring types stay in their existing packages.
|
|
4
|
+
|
|
5
|
+
## Scoring API
|
|
6
|
+
|
|
7
|
+
`src/scoring/scoreCommand.ts` is the canonical contract for:
|
|
8
|
+
|
|
9
|
+
- `ScoreCommand` and its `DELIVERY`, `EVENT`, and `CORRECTION` variants
|
|
10
|
+
- `DeliveryPayload` and `MatchEventPayload`
|
|
11
|
+
- Per-event `MatchScoringState` snapshots and command responses
|
|
12
|
+
- Stable scoring error codes and runtime command validation
|
|
13
|
+
|
|
14
|
+
Build with `npm run build`. Consumers should depend on this package by a pinned version once it is published or otherwise made available to both repositories. The package contains no mobile UI, Supabase client, Realm model, or backend implementation.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './scoring/index.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scoring/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './scoreCommand.js';
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/** Stable public contract for commands submitted to the online scoring API. */
|
|
2
|
+
export declare const SCORE_CONTRACT_VERSION: 1;
|
|
3
|
+
export type UUID = string;
|
|
4
|
+
export type MatchState = 'UPCOMING' | 'LIVE' | 'COMPLETED' | 'ABANDONED';
|
|
5
|
+
export type MatchResult = 'PENDING' | 'WON' | 'TIE' | 'NO_RESULT';
|
|
6
|
+
export type ScoreCommandBase = {
|
|
7
|
+
schemaVersion: typeof SCORE_CONTRACT_VERSION;
|
|
8
|
+
/** Idempotency key. Reuse this ID when retrying the same command. */
|
|
9
|
+
eventId: UUID;
|
|
10
|
+
matchId: UUID;
|
|
11
|
+
inningsId: UUID;
|
|
12
|
+
/** Current match-wide revision observed by the scorer. */
|
|
13
|
+
expectedRevision: number;
|
|
14
|
+
};
|
|
15
|
+
export type DeliveryType = 'NORMAL' | 'WD' | 'NB';
|
|
16
|
+
export type ExtraRunType = 'BYE' | 'LB';
|
|
17
|
+
/** Dismissals recorded as part of a delivery. Non-delivery dismissals are events. */
|
|
18
|
+
export type DeliveryWicketType = 'BOWLED' | 'CAUGHT' | 'RUN_OUT' | 'STUMPED' | 'LBW' | 'HIT_WICKET' | 'OBSTRUCTING_FIELD' | 'OVER_THE_FENCE';
|
|
19
|
+
export type RunOutDetails = {
|
|
20
|
+
type: 'DIRECT';
|
|
21
|
+
directFielderId: UUID;
|
|
22
|
+
assistFielderId?: never;
|
|
23
|
+
} | {
|
|
24
|
+
type: 'ASSISTED';
|
|
25
|
+
directFielderId: UUID;
|
|
26
|
+
assistFielderId: UUID;
|
|
27
|
+
};
|
|
28
|
+
export type DeliveryPayload = {
|
|
29
|
+
ballType: DeliveryType;
|
|
30
|
+
/** Batter runs. Must be zero for wides and BYE/LB deliveries. */
|
|
31
|
+
runs: number;
|
|
32
|
+
/** Extra runs in addition to automatic wide/no-ball penalty. */
|
|
33
|
+
extraRuns: number;
|
|
34
|
+
extraRunType: ExtraRunType | null;
|
|
35
|
+
wicketType: DeliveryWicketType | null;
|
|
36
|
+
strikerId: UUID;
|
|
37
|
+
nonStrikerId: UUID | null;
|
|
38
|
+
bowlerId: UUID;
|
|
39
|
+
outBatterId: UUID | null;
|
|
40
|
+
fielderId: UUID | null;
|
|
41
|
+
runOut: RunOutDetails | null;
|
|
42
|
+
};
|
|
43
|
+
export type SetOpenersEvent = {
|
|
44
|
+
eventType: 'SET_OPENERS';
|
|
45
|
+
eventMeta: {
|
|
46
|
+
strikerId: UUID;
|
|
47
|
+
nonStrikerId: UUID | null;
|
|
48
|
+
bowlerId: UUID;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
export type NewBatterEvent = {
|
|
52
|
+
eventType: 'NEW_BATTER';
|
|
53
|
+
eventMeta: {
|
|
54
|
+
batterId: UUID;
|
|
55
|
+
battingPosition?: number;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
export type NewBowlerEvent = {
|
|
59
|
+
eventType: 'NEW_BOWLER';
|
|
60
|
+
eventMeta: {
|
|
61
|
+
bowlerId: UUID;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
export type RetiredHurtEvent = {
|
|
65
|
+
eventType: 'RETIRED_HURT';
|
|
66
|
+
eventMeta: {
|
|
67
|
+
batterId: UUID;
|
|
68
|
+
reason?: string;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
export type RetiredOutEvent = {
|
|
72
|
+
eventType: 'RETIRED_OUT';
|
|
73
|
+
eventMeta: {
|
|
74
|
+
batterId: UUID;
|
|
75
|
+
reason?: string;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
export type TimedOutEvent = {
|
|
79
|
+
eventType: 'TIMED_OUT';
|
|
80
|
+
eventMeta: {
|
|
81
|
+
batterId: UUID;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
export type MankadEvent = {
|
|
85
|
+
eventType: 'MANKAD';
|
|
86
|
+
eventMeta: {
|
|
87
|
+
batterId: UUID;
|
|
88
|
+
bowlerId: UUID;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
export type InningsEndEvent = {
|
|
92
|
+
eventType: 'INNINGS_END';
|
|
93
|
+
eventMeta: {
|
|
94
|
+
reason?: 'DECLARED' | 'TARGET_REACHED' | 'ABANDONED' | 'OTHER';
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
export type StartSuperOverEvent = {
|
|
98
|
+
eventType: 'START_SUPER_OVER';
|
|
99
|
+
eventMeta: {
|
|
100
|
+
reason?: 'TIE' | 'OTHER';
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
export type MatchEventPayload = SetOpenersEvent | NewBatterEvent | NewBowlerEvent | RetiredHurtEvent | RetiredOutEvent | TimedOutEvent | MankadEvent | InningsEndEvent | StartSuperOverEvent;
|
|
104
|
+
export type ScoreCommand = (ScoreCommandBase & {
|
|
105
|
+
kind: 'DELIVERY';
|
|
106
|
+
payload: DeliveryPayload;
|
|
107
|
+
}) | (ScoreCommandBase & {
|
|
108
|
+
kind: 'EVENT';
|
|
109
|
+
payload: MatchEventPayload;
|
|
110
|
+
}) | (ScoreCommandBase & {
|
|
111
|
+
kind: 'CORRECTION';
|
|
112
|
+
payload: {
|
|
113
|
+
targetEventId: UUID;
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
export type ExtraSummary = {
|
|
117
|
+
wides: number;
|
|
118
|
+
noBalls: number;
|
|
119
|
+
byes: number;
|
|
120
|
+
legByes: number;
|
|
121
|
+
penalty: number;
|
|
122
|
+
total: number;
|
|
123
|
+
};
|
|
124
|
+
export type BatterScoreState = {
|
|
125
|
+
playerId: UUID;
|
|
126
|
+
runs: number;
|
|
127
|
+
balls: number;
|
|
128
|
+
fours: number;
|
|
129
|
+
sixes: number;
|
|
130
|
+
isOut: boolean;
|
|
131
|
+
};
|
|
132
|
+
export type BowlerScoreState = {
|
|
133
|
+
playerId: UUID;
|
|
134
|
+
runsConceded: number;
|
|
135
|
+
legalBalls: number;
|
|
136
|
+
wickets: number;
|
|
137
|
+
};
|
|
138
|
+
/** Compact authoritative state immediately after one event was applied. */
|
|
139
|
+
export type MatchScoringState = {
|
|
140
|
+
schemaVersion: typeof SCORE_CONTRACT_VERSION;
|
|
141
|
+
matchId: UUID;
|
|
142
|
+
matchRevision: number;
|
|
143
|
+
matchState: MatchState;
|
|
144
|
+
matchResult: MatchResult;
|
|
145
|
+
activeInningsId: UUID | null;
|
|
146
|
+
innings: {
|
|
147
|
+
inningsId: UUID;
|
|
148
|
+
inningsNumber: number;
|
|
149
|
+
superOverNumber: number;
|
|
150
|
+
battingTeamId: UUID;
|
|
151
|
+
runs: number;
|
|
152
|
+
wickets: number;
|
|
153
|
+
legalBalls: number;
|
|
154
|
+
ballsPerOver: number;
|
|
155
|
+
extras: ExtraSummary;
|
|
156
|
+
isCompleted: boolean;
|
|
157
|
+
strikerId: UUID | null;
|
|
158
|
+
nonStrikerId: UUID | null;
|
|
159
|
+
bowlerId: UUID | null;
|
|
160
|
+
batters: Record<UUID, BatterScoreState>;
|
|
161
|
+
bowlers: Record<UUID, BowlerScoreState>;
|
|
162
|
+
};
|
|
163
|
+
teamScores: Array<{
|
|
164
|
+
teamId: UUID;
|
|
165
|
+
inningsNumber: number;
|
|
166
|
+
superOverNumber: number;
|
|
167
|
+
runs: number;
|
|
168
|
+
wickets: number;
|
|
169
|
+
legalBalls: number;
|
|
170
|
+
}>;
|
|
171
|
+
};
|
|
172
|
+
type ScoringEventRecordBase = {
|
|
173
|
+
eventId: UUID;
|
|
174
|
+
matchId: UUID;
|
|
175
|
+
inningsId: UUID;
|
|
176
|
+
revision: number;
|
|
177
|
+
sequenceNumber: number;
|
|
178
|
+
reversesEventId: UUID | null;
|
|
179
|
+
actorUserId: UUID;
|
|
180
|
+
createdAt: string;
|
|
181
|
+
/** Persisted immutable state immediately after this event. */
|
|
182
|
+
stateAfter: MatchScoringState;
|
|
183
|
+
};
|
|
184
|
+
export type ScoringEventRecord = (ScoringEventRecordBase & {
|
|
185
|
+
kind: 'DELIVERY';
|
|
186
|
+
payload: DeliveryPayload;
|
|
187
|
+
}) | (ScoringEventRecordBase & {
|
|
188
|
+
kind: 'EVENT';
|
|
189
|
+
payload: MatchEventPayload;
|
|
190
|
+
}) | (ScoringEventRecordBase & {
|
|
191
|
+
kind: 'CORRECTION';
|
|
192
|
+
payload: {
|
|
193
|
+
targetEventId: UUID;
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
export type ScoreCommandResult = {
|
|
197
|
+
event: ScoringEventRecord;
|
|
198
|
+
/** Current match head; may be newer than event.revision on an idempotent retry. */
|
|
199
|
+
currentRevision: number;
|
|
200
|
+
duplicate: boolean;
|
|
201
|
+
};
|
|
202
|
+
export type ScoreCommandErrorCode = 'AUTHENTICATION_REQUIRED' | 'PROFILE_INCOMPLETE' | 'SCORER_REQUIRED' | 'MATCH_UNAVAILABLE' | 'INNINGS_UNAVAILABLE' | 'REVISION_CONFLICT' | 'INVALID_COMMAND' | 'PLAYER_NOT_IN_SQUAD' | 'INNINGS_COMPLETE' | 'EVENT_ID_REUSED' | 'UNDO_NOT_LATEST' | 'INTERNAL_ERROR';
|
|
203
|
+
export type ScoreCommandError = {
|
|
204
|
+
error: {
|
|
205
|
+
code: ScoreCommandErrorCode;
|
|
206
|
+
message: string;
|
|
207
|
+
currentRevision?: number;
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
211
|
+
export declare function isUUID(value: unknown): value is UUID;
|
|
212
|
+
/** Runtime guard for the shared HTTP command envelope and its discriminated payload. */
|
|
213
|
+
export declare function isScoreCommand(value: unknown): value is ScoreCommand;
|
|
214
|
+
export {};
|
|
215
|
+
//# sourceMappingURL=scoreCommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoreCommand.d.ts","sourceRoot":"","sources":["../../src/scoring/scoreCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/** Stable public contract for commands submitted to the online scoring API. */
|
|
2
|
+
export const SCORE_CONTRACT_VERSION = 1;
|
|
3
|
+
export function isRecord(value) {
|
|
4
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
7
|
+
export function isUUID(value) {
|
|
8
|
+
return typeof value === 'string' && UUID_PATTERN.test(value);
|
|
9
|
+
}
|
|
10
|
+
function isNonNegativeInteger(value) {
|
|
11
|
+
return Number.isSafeInteger(value) && value >= 0;
|
|
12
|
+
}
|
|
13
|
+
function isDeliveryPayload(value) {
|
|
14
|
+
if (!['NORMAL', 'WD', 'NB'].includes(String(value.ballType)))
|
|
15
|
+
return false;
|
|
16
|
+
if (!isNonNegativeInteger(value.runs) || !isNonNegativeInteger(value.extraRuns))
|
|
17
|
+
return false;
|
|
18
|
+
if (value.extraRunType !== null && value.extraRunType !== 'BYE' && value.extraRunType !== 'LB')
|
|
19
|
+
return false;
|
|
20
|
+
const wicketTypes = [
|
|
21
|
+
'BOWLED', 'CAUGHT', 'RUN_OUT', 'STUMPED', 'LBW', 'HIT_WICKET',
|
|
22
|
+
'OBSTRUCTING_FIELD', 'OVER_THE_FENCE',
|
|
23
|
+
];
|
|
24
|
+
if (value.wicketType !== null && !wicketTypes.includes(value.wicketType))
|
|
25
|
+
return false;
|
|
26
|
+
if (!isUUID(value.strikerId) || !isUUID(value.bowlerId))
|
|
27
|
+
return false;
|
|
28
|
+
if (value.nonStrikerId !== null && !isUUID(value.nonStrikerId))
|
|
29
|
+
return false;
|
|
30
|
+
if (value.outBatterId !== null && !isUUID(value.outBatterId))
|
|
31
|
+
return false;
|
|
32
|
+
if (value.fielderId !== null && !isUUID(value.fielderId))
|
|
33
|
+
return false;
|
|
34
|
+
if ((value.wicketType === null) !== (value.outBatterId === null))
|
|
35
|
+
return false;
|
|
36
|
+
if (value.ballType === 'WD' && value.runs !== 0)
|
|
37
|
+
return false;
|
|
38
|
+
if ((value.extraRunType === 'BYE' || value.extraRunType === 'LB') && value.runs !== 0)
|
|
39
|
+
return false;
|
|
40
|
+
if (value.wicketType === 'CAUGHT' && !isUUID(value.fielderId))
|
|
41
|
+
return false;
|
|
42
|
+
if (value.wicketType === 'RUN_OUT') {
|
|
43
|
+
if (!isRecord(value.runOut))
|
|
44
|
+
return false;
|
|
45
|
+
if (value.runOut.type === 'DIRECT') {
|
|
46
|
+
if (!isUUID(value.runOut.directFielderId) || value.runOut.assistFielderId != null)
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
else if (value.runOut.type === 'ASSISTED') {
|
|
50
|
+
if (!isUUID(value.runOut.directFielderId) || !isUUID(value.runOut.assistFielderId))
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
else
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
else if (value.runOut !== null)
|
|
57
|
+
return false;
|
|
58
|
+
return value.runOut === null || value.wicketType === 'RUN_OUT';
|
|
59
|
+
}
|
|
60
|
+
function isMatchEventPayload(value) {
|
|
61
|
+
if (!isRecord(value.eventMeta) || typeof value.eventType !== 'string')
|
|
62
|
+
return false;
|
|
63
|
+
const meta = value.eventMeta;
|
|
64
|
+
switch (value.eventType) {
|
|
65
|
+
case 'SET_OPENERS':
|
|
66
|
+
return isUUID(meta.strikerId) && isUUID(meta.bowlerId) &&
|
|
67
|
+
(meta.nonStrikerId === null || isUUID(meta.nonStrikerId));
|
|
68
|
+
case 'NEW_BATTER':
|
|
69
|
+
return isUUID(meta.batterId) &&
|
|
70
|
+
(meta.battingPosition === undefined || (Number.isSafeInteger(meta.battingPosition) && Number(meta.battingPosition) > 0));
|
|
71
|
+
case 'NEW_BOWLER': return isUUID(meta.bowlerId);
|
|
72
|
+
case 'RETIRED_HURT':
|
|
73
|
+
case 'RETIRED_OUT': return isUUID(meta.batterId) && (meta.reason === undefined || typeof meta.reason === 'string');
|
|
74
|
+
case 'TIMED_OUT': return isUUID(meta.batterId);
|
|
75
|
+
case 'MANKAD': return isUUID(meta.batterId) && isUUID(meta.bowlerId);
|
|
76
|
+
case 'INNINGS_END':
|
|
77
|
+
return meta.reason === undefined || ['DECLARED', 'TARGET_REACHED', 'ABANDONED', 'OTHER'].includes(String(meta.reason));
|
|
78
|
+
case 'START_SUPER_OVER':
|
|
79
|
+
return meta.reason === undefined || ['TIE', 'OTHER'].includes(String(meta.reason));
|
|
80
|
+
default: return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** Runtime guard for the shared HTTP command envelope and its discriminated payload. */
|
|
84
|
+
export function isScoreCommand(value) {
|
|
85
|
+
if (!isRecord(value) || value.schemaVersion !== SCORE_CONTRACT_VERSION)
|
|
86
|
+
return false;
|
|
87
|
+
if (!isUUID(value.eventId) || !isUUID(value.matchId) || !isUUID(value.inningsId))
|
|
88
|
+
return false;
|
|
89
|
+
if (!isNonNegativeInteger(value.expectedRevision) || !isRecord(value.payload))
|
|
90
|
+
return false;
|
|
91
|
+
switch (value.kind) {
|
|
92
|
+
case 'DELIVERY': return isDeliveryPayload(value.payload);
|
|
93
|
+
case 'EVENT': return isMatchEventPayload(value.payload);
|
|
94
|
+
case 'CORRECTION': return isUUID(value.payload.targetEventId);
|
|
95
|
+
default: return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cricnice/online-contracts",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Shared online scoring API contracts for CricNice mobile and backend",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json",
|
|
20
|
+
"prepare": "npm run build",
|
|
21
|
+
"prepack": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"typescript": "~6.0.3"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public",
|
|
28
|
+
"registry": "https://registry.npmjs.org"
|
|
29
|
+
}
|
|
30
|
+
}
|