@ttt-productions/q-core 0.1.51 → 0.1.53
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/constants/business.d.ts +1 -1
- package/dist/constants/business.d.ts.map +1 -1
- package/dist/constants/business.js +1 -1
- package/dist/constants/business.js.map +1 -1
- package/dist/constants/sports.d.ts +1 -1
- package/dist/constants/sports.d.ts.map +1 -1
- package/dist/constants/sports.js +3 -3
- package/dist/constants/sports.js.map +1 -1
- package/dist/constants/text-fields.d.ts +1 -1
- package/dist/constants/text-fields.d.ts.map +1 -1
- package/dist/constants/text-fields.js +2 -2
- package/dist/constants/text-fields.js.map +1 -1
- package/dist/doc-schemas/atoms.d.ts +1 -0
- package/dist/doc-schemas/atoms.d.ts.map +1 -1
- package/dist/doc-schemas/atoms.js +3 -2
- package/dist/doc-schemas/atoms.js.map +1 -1
- package/dist/doc-schemas/game-stat-history.js +2 -2
- package/dist/doc-schemas/game-stat-history.js.map +1 -1
- package/dist/doc-schemas/registry.d.ts +1 -1
- package/dist/doc-schemas/sport-game.d.ts.map +1 -1
- package/dist/doc-schemas/sport-game.js +2 -1
- package/dist/doc-schemas/sport-game.js.map +1 -1
- package/dist/doc-schemas/stats-job.d.ts +1 -1
- package/dist/schemas/admin-notifications.d.ts +1 -4
- package/dist/schemas/admin-notifications.d.ts.map +1 -1
- package/dist/schemas/admin-notifications.js +4 -6
- package/dist/schemas/admin-notifications.js.map +1 -1
- package/dist/schemas/scoring.d.ts +0 -1
- package/dist/schemas/scoring.d.ts.map +1 -1
- package/dist/schemas/scoring.js +1 -1
- package/dist/schemas/scoring.js.map +1 -1
- package/dist/schemas/stats.d.ts +2 -4
- package/dist/schemas/stats.d.ts.map +1 -1
- package/dist/schemas/stats.js +3 -5
- package/dist/schemas/stats.js.map +1 -1
- package/dist/schemas/teams.d.ts +0 -1
- package/dist/schemas/teams.d.ts.map +1 -1
- package/dist/schemas/teams.js +1 -1
- package/dist/schemas/teams.js.map +1 -1
- package/dist/sports/basketball-scoring.d.ts.map +1 -1
- package/dist/sports/basketball-scoring.js +2 -9
- package/dist/sports/basketball-scoring.js.map +1 -1
- package/dist/sports/bulk-entry.d.ts +0 -1
- package/dist/sports/bulk-entry.d.ts.map +1 -1
- package/dist/sports/bulk-entry.js +0 -1
- package/dist/sports/bulk-entry.js.map +1 -1
- package/dist/sports/curling-scoring.d.ts.map +1 -1
- package/dist/sports/curling-scoring.js +2 -9
- package/dist/sports/curling-scoring.js.map +1 -1
- package/dist/sports/football-scoring.d.ts.map +1 -1
- package/dist/sports/football-scoring.js +2 -9
- package/dist/sports/football-scoring.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { CURLING_ROUND_COUNT, MAX_CURLING_ROUND_POINTS
|
|
2
|
+
import { CURLING_ROUND_COUNT, MAX_CURLING_ROUND_POINTS } from '../constants/sports.js';
|
|
3
3
|
import { CURLING_ROUND_BLANK_EVENT_TYPE, CURLING_ROUND_SCORE_EVENT_TYPE, CURLING_STAT_EVENT_TYPE_KEYS, } from '../types/sports.js';
|
|
4
4
|
import { STAT_VOID_EVENT_TYPE } from '../types/stats.js';
|
|
5
|
-
import {
|
|
5
|
+
import { bulkEntryCountField, } from './bulk-entry.js';
|
|
6
6
|
import { buildGameContribution, decideParticipation, } from './contribution.js';
|
|
7
7
|
import { FINALIZATION_READY, STAT_EVENT_ACCEPTED, isSportStatEventIn, isStatCorrection, refuseStatEvent, } from './scoring.js';
|
|
8
8
|
import { describeStoreEvent, } from './scoring-display.js';
|
|
@@ -220,13 +220,6 @@ export const CURLING_BULK_ENTRY = {
|
|
|
220
220
|
});
|
|
221
221
|
}
|
|
222
222
|
});
|
|
223
|
-
if (roundDrafts(values, context).length > MAX_STAT_EVENTS_PER_WRITE) {
|
|
224
|
-
issues.addIssue({
|
|
225
|
-
code: 'custom',
|
|
226
|
-
path: ['rounds'],
|
|
227
|
-
message: BULK_ENTRY_TOO_LARGE_MESSAGE,
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
223
|
});
|
|
231
224
|
},
|
|
232
225
|
toEvents: roundDrafts,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"curling-scoring.js","sourceRoot":"","sources":["../../src/sports/curling-scoring.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACL,8BAA8B,EAC9B,8BAA8B,EAC9B,4BAA4B,GAC7B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EACL,4BAA4B,EAC5B,mBAAmB,GAKpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,qBAAqB,EACrB,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,GAOhB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,kBAAkB,GAOnB,MAAM,sBAAsB,CAAC;AAI9B,SAAS,cAAc,CAAC,KAAgC;IACtD,OAAO,kBAAkB,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,QAAQ,CAAC,KAAgB;IAChC,OAAO,KAAyB,CAAC;AACnC,CAAC;AAED,SAAS,WAAW,CAAC,IAAkB;IACrC,OAAO,IAAwB,CAAC;AAClC,CAAC;AAID,SAAS,WAAW,CAAC,WAAuB;IAC1C,OAAO,WAAW,CAAC,MAAM,CAAS,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,MAAM,WAAW,GAAe,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,mBAAmB,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACxF,OAAO,EAAE,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;AAC1D,CAAC;AAED,SAAS,SAAS,CAChB,IAAsB,EACtB,WAAmB,EACnB,IAAmB;IAEnB,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC;IAC1C,WAAW,CAAC,WAAW,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC;IACpC,OAAO,EAAE,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;AAC1D,CAAC;AAED,SAAS,QAAQ,CAAC,OAAkB,EAAE,KAAmB,EAAE,QAAiB;IAC1E,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAChC,MAAM,SAAS,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;IACzC,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,KAAK,8BAA8B,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/F,IAAI,aAAa,KAAK,SAAS,IAAI,SAAS,CAAC,aAAa,CAAC,KAAK,SAAS,EAAE,CAAC;QAC1E,SAAS,CAAC,aAAa,CAAC,GAAG,oBAAoB,EAAE,CAAC;IACpD,CAAC;IACD,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACvD,MAAM,MAAM,GACV,KAAK,CAAC,IAAI,KAAK,8BAA8B,IAAI,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/F,SAAS,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;AACvD,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAkB;IAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC;IACzD,KAAK,IAAI,WAAW,GAAG,CAAC,EAAE,WAAW,IAAI,mBAAmB,EAAE,WAAW,IAAI,CAAC,EAAE,CAAC;QAC/E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC;YACxF,OAAO,WAAW,CAAC;IACvB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,kBAAkB,GAAsB,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAEvE,SAAS,WAAW,CAAC,KAAoB;IACvC,OAAO,cAAc,IAAI,KAAK,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC;AACrE,CAAC;AAED,MAAM,QAAQ,GAA6B,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;IAClE,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;QAAE,OAAO,eAAe,CAAC,qBAAqB,CAAC,CAAC;IAC1E,IAAI,WAAW,CAAC,KAAK,CAAC;QAAE,OAAO,eAAe,CAAC,kBAAkB,CAAC,CAAC;IACnE,IAAI,KAAK,CAAC,IAAI,KAAK,8BAA8B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5F,OAAO,eAAe,CAAC,eAAe,CAAC,CAAC;IAC1C,CAAC;IACD,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,eAAe,CAAC,gBAAgB,CAAC,CAAC;IAC5D,OAAO,KAAK,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;AAC5F,CAAC,CAAC;AAEF,SAAS,qBAAqB,CAAC,KAAgB;IAC7C,OAAO,gBAAgB,CAAC,KAAK,CAAC,KAAK,IAAI;QACrC,CAAC,CAAC,kBAAkB;QACpB,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;AACnD,CAAC;AAED,MAAM,oBAAoB,GAAqB;IAC7C,cAAc,EAAE,EAAE;IAClB,oBAAoB,EAAE,EAAE;IACxB,oBAAoB,EAAE,KAAK;IAC3B,UAAU,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;CAClC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAiB;IAC3C,QAAQ;IACR,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACtB,IAAI,gBAAgB,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACjF,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,sCAAsC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;QACvE,OAAO,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;QACtB,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,qCAAqC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;QACvE,OAAO,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IACxC,CAAC;IACD,UAAU,EAAE,oBAAoB,CAAC,UAAU;IAC3C,iBAAiB,EAAE,kBAAkB;IACrC,SAAS,EAAE,gBAAgB;IAC3B,qBAAqB;IACrB,aAAa,EAAE,CAAC,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,EAAE,CAC/D,mBAAmB,CAAC,oBAAoB,EAAE,KAAK,EAAE,eAAe,EAAE,qBAAqB,CAAC;IAC1F,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,qBAAqB,CAAC,oBAAoB,EAAE,IAAI,CAAC;CAC1E,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAkB,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAE5F,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,CAAC,8BAA8B,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE;IAC1E,CAAC,8BAA8B,CAAC,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE;CACd,CAAC;AAElE,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;IACxB,MAAM,EAAE,EAAE;CACmD,CAAC;AAEhE,MAAM,UAAU,oBAAoB,CAAC,KAAoB,EAAE,OAA8B;IACvF,MAAM,UAAU,GAAG,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,oBAAoB,CAAC,CAAC;IAC5E,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,UAAU,CAAC;IAC3C,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC;IAC9C,MAAM,KAAK,GAAG,GAAG,cAAc,CAAC,KAAK,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;IAC7D,IAAI,KAAK,CAAC,IAAI,KAAK,8BAA8B;QAC/C,OAAO,GAAG,KAAK,KAAK,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;IAC5E,OAAO,GAAG,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,KAAK,CAAC,MAAM,EAAE,CAAC;AAC1E,CAAC;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAe;IAC5C,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACjF,QAAQ,EAAE,KAAK;IACf,UAAU,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,WAAW;CAC5D,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAuB;IACvD,IAAI,EAAE,QAAQ;IACd,cAAc,EAAE,8BAA8B;IAC9C,cAAc,EAAE,8BAA8B;IAC9C,SAAS,EAAE,wBAAwB;CACpC,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAG,yBAAyB,wBAAwB,UAAU,CAAC;AACxG,MAAM,CAAC,MAAM,gCAAgC,GAC3C,gGAAgG,CAAC;AACnG,MAAM,CAAC,MAAM,gCAAgC,GAC3C,kEAAkE,CAAC;AAQrE,SAAS,YAAY,CACnB,GAAwD,EACxD,OAA0B;IAE1B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;IACjF,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;IAC/E,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACvE,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAChF,MAAM,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC;IACzB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QAC3E,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IAC1E,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAC7B,CAAC;AAED,SAAS,WAAW,CAAC,IAAkB,EAAE,KAAmB;IAC1D,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;QACnD,OAAO,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC;IACtE,OAAO,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC;AAClC,CAAC;AAED,SAAS,WAAW,CAClB,OAAyB,EACzB,WAAmB;IAEnB,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtC,OAAO,MAAM,CAAC,WAAW,CACvB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;QACnC,MAAM;QACN,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,WAAW,GAAG,CAAC,CAAC,IAAI,IAAI;KAC9D,CAAC,CACH,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,MAAoB,EAAE,OAAyB;IAClE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IACjC,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAC9B,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAC3E,CAAC;IACF,MAAM,KAAK,GAAqB,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAqB,EAAE,CAAC;IACtC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;QACnC,MAAM,WAAW,GAAG,KAAK,GAAG,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC3C,IAAI,WAAW,CAAC,OAAO,EAAE,YAAY,CAAC,WAAW,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;YAAE,OAAO;QAC3F,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACtD,IAAI,WAAW,KAAK,SAAS;YAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC,CAAC;QACzE,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9B,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,8BAA8B;gBACpC,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,WAAW;gBACX,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,8BAA8B,EAAE,WAAW,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC,CAAC,CAAC;IACH,gGAAgG;IAChG,8FAA8F;IAC9F,OAAO,CAAC,GAAG,KAAK,EAAE,GAAG,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,SAAS,GAAG,mBAAmB;KAClC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,wBAAwB,EAAE,4BAA4B,CAAC,CAAC;KAC5E,QAAQ,EAAE,CAAC;AAEd,MAAM,CAAC,MAAM,kBAAkB,GAAoB;IACjD,IAAI,EAAE,QAAQ;IACd,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC1B,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAC/D,WAAW,CAAC,OAAO,EAAE,KAAK,GAAG,CAAC,CAAC,CAChC;KACF,CAAC;IACF,UAAU,EAAE,CAAC,OAAO,EAAE,EAAE;QACtB,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;QACjC,MAAM,GAAG,GAAG,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7F,OAAO,CAAC;aACL,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,EAAE,CAAC;aAC5D,WAAW,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE;YAC9B,IAAI,gBAAgB,GAAG,KAAK,CAAC;YAC7B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;gBACrC,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;gBAC7C,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC/B,MAAM,CAAC,QAAQ,CAAC;wBACd,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC;wBACvB,OAAO,EAAE,gCAAgC;qBAC1C,CAAC,CAAC;gBACL,CAAC;qBAAM,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;oBACzC,gBAAgB,GAAG,IAAI,CAAC;gBAC1B,CAAC;qBAAM,IAAI,gBAAgB,EAAE,CAAC;oBAC5B,MAAM,CAAC,QAAQ,CAAC;wBACd,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC;wBACvB,OAAO,EAAE,gCAAgC;qBAC1C,CAAC,CAAC;gBACL,CAAC;YACH,CAAC,CAAC,CAAC;YACH,IAAI,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,GAAG,yBAAyB,EAAE,CAAC;gBACpE,MAAM,CAAC,QAAQ,CAAC;oBACd,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,CAAC,QAAQ,CAAC;oBAChB,OAAO,EAAE,4BAA4B;iBACtC,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;IACP,CAAC;IACD,QAAQ,EAAE,WAAW;CACtB,CAAC","sourcesContent":["import { z } from 'zod';\nimport {\n CURLING_ROUND_COUNT,\n MAX_CURLING_ROUND_POINTS,\n MAX_STAT_EVENTS_PER_WRITE,\n} from '../constants/sports.js';\nimport type { CurlingTeamStats, GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type { CurlingGameStats, GameStats } from '../doc-schemas/sport-game.js';\nimport {\n CURLING_ROUND_BLANK_EVENT_TYPE,\n CURLING_ROUND_SCORE_EVENT_TYPE,\n CURLING_STAT_EVENT_TYPE_KEYS,\n} from '../types/sports.js';\nimport type { CurlingStatEventType, CurlingTeamStatKey } from '../types/sports.js';\nimport { STAT_VOID_EVENT_TYPE } from '../types/stats.js';\nimport {\n BULK_ENTRY_TOO_LARGE_MESSAGE,\n bulkEntryCountField,\n type BulkEntryContext,\n type RoundsBulkEntry,\n type RoundsValues,\n type StatEventDraft,\n} from './bulk-entry.js';\nimport {\n buildGameContribution,\n decideParticipation,\n type ContributionSpec,\n} from './contribution.js';\nimport {\n FINALIZATION_READY,\n STAT_EVENT_ACCEPTED,\n isSportStatEventIn,\n isStatCorrection,\n refuseStatEvent,\n type CorrectableFields,\n type FinalizationReadiness,\n type GameTeamLine,\n type SportScoring,\n type SportStatEventBody,\n type StatEventBody,\n} from './scoring.js';\nimport {\n describeStoreEvent,\n type RoundsDisplay,\n type RoundsScoringPanel,\n type Scoreboard,\n type StatEventLabel,\n type StatEventNameResolver,\n type StatFieldLabels,\n} from './scoring-display.js';\n\ntype CurlingEvent = Extract<SportStatEventBody, { type: CurlingStatEventType }>;\n\nfunction isCurlingEvent(event: StatEventBody | undefined): event is CurlingEvent {\n return isSportStatEventIn(CURLING_STAT_EVENT_TYPE_KEYS, event);\n}\n\nfunction ownStats(state: GameStats): CurlingGameStats {\n return state as CurlingGameStats;\n}\n\nfunction ownTeamLine(line: GameTeamLine): CurlingTeamStats {\n return line as CurlingTeamStats;\n}\n\ntype RoundSlots = CurlingTeamStats['roundScores'];\n\nfunction sumOfRounds(roundScores: RoundSlots): number {\n return roundScores.reduce<number>((total, slot) => total + (slot ?? 0), 0);\n}\n\nexport function emptyCurlingTeamLine(): CurlingTeamStats {\n const roundScores: RoundSlots = Array.from({ length: CURLING_ROUND_COUNT }, () => null);\n return { score: sumOfRounds(roundScores), roundScores };\n}\n\nfunction withRound(\n line: CurlingTeamStats,\n roundNumber: number,\n slot: number | null,\n): CurlingTeamStats {\n const roundScores = [...line.roundScores];\n roundScores[roundNumber - 1] = slot;\n return { score: sumOfRounds(roundScores), roundScores };\n}\n\nfunction setRound(stateIn: GameStats, event: CurlingEvent, recorded: boolean): CurlingGameStats {\n const state = ownStats(stateIn);\n const teamStats = { ...state.teamStats };\n const scoringTeamId = event.type === CURLING_ROUND_SCORE_EVENT_TYPE ? event.teamId : undefined;\n if (scoringTeamId !== undefined && teamStats[scoringTeamId] === undefined) {\n teamStats[scoringTeamId] = emptyCurlingTeamLine();\n }\n for (const [teamId, line] of Object.entries(teamStats)) {\n const points =\n event.type === CURLING_ROUND_SCORE_EVENT_TYPE && teamId === scoringTeamId ? event.points : 0;\n teamStats[teamId] = withRound(line, event.roundNumber, recorded ? points : null);\n }\n return { teamStats, playerStats: state.playerStats };\n}\n\nfunction nextCurlingRound(stateIn: GameStats): number | null {\n const lines = Object.values(ownStats(stateIn).teamStats);\n for (let roundNumber = 1; roundNumber <= CURLING_ROUND_COUNT; roundNumber += 1) {\n if (lines.length === 0 || lines.some((line) => line.roundScores[roundNumber - 1] === null))\n return roundNumber;\n }\n return null;\n}\n\nconst CORRECTABLE_FIELDS: CorrectableFields = { team: [], player: [] };\n\nfunction namesPlayer(event: StatEventBody): boolean {\n return 'playerUserId' in event && event.playerUserId !== undefined;\n}\n\nconst validate: SportScoring['validate'] = (state, event, roster) => {\n if (!isCurlingEvent(event)) return refuseStatEvent('eventTypeNotInSport');\n if (namesPlayer(event)) return refuseStatEvent('playerNotAllowed');\n if (event.type === CURLING_ROUND_SCORE_EVENT_TYPE && !roster.teamIds.includes(event.teamId)) {\n return refuseStatEvent('teamNotInGame');\n }\n const next = nextCurlingRound(state);\n if (next === null) return refuseStatEvent('roundsComplete');\n return event.roundNumber === next ? STAT_EVENT_ACCEPTED : refuseStatEvent('roundNotNext');\n};\n\nfunction finalizationReadiness(state: GameStats): FinalizationReadiness {\n return nextCurlingRound(state) === null\n ? FINALIZATION_READY\n : { ready: false, reason: 'roundsIncomplete' };\n}\n\nconst CURLING_CONTRIBUTION: ContributionSpec = {\n playerStatKeys: [],\n teamSeasonPlayerSums: [],\n zeroLineIsNonScoring: false,\n deriveLine: (_kind, line) => line,\n};\n\nexport const CURLING_SCORING: SportScoring = {\n validate,\n apply: (state, event) => {\n if (isStatCorrection(event)) throw new Error('No curling field is correctable.');\n if (!isCurlingEvent(event))\n throw new Error(`The curling adapter does not apply ${event.type}.`);\n return setRound(state, event, true);\n },\n undo: (state, target) => {\n if (!isCurlingEvent(target))\n throw new Error(`The curling adapter does not undo ${target.type}.`);\n return setRound(state, target, false);\n },\n deriveLine: CURLING_CONTRIBUTION.deriveLine,\n correctableFields: CORRECTABLE_FIELDS,\n nextRound: nextCurlingRound,\n finalizationReadiness,\n participation: (state, rosterPlayerIds, absentRosterPlayerIds) =>\n decideParticipation(CURLING_CONTRIBUTION, state, rosterPlayerIds, absentRosterPlayerIds),\n contribution: (game) => buildGameContribution(CURLING_CONTRIBUTION, game),\n};\n\nexport const CURLING_ROUNDS: RoundsDisplay = { count: CURLING_ROUND_COUNT, label: 'Round' };\n\nexport const CURLING_EVENT_LABELS = {\n [CURLING_ROUND_SCORE_EVENT_TYPE]: { button: 'Score', play: 'Round score' },\n [CURLING_ROUND_BLANK_EVENT_TYPE]: { button: '+0 (Blank)', play: 'Blank end' },\n} as const satisfies Record<CurlingStatEventType, StatEventLabel>;\n\nexport const CURLING_FIELD_LABELS = {\n team: { score: 'Score' },\n player: {},\n} as const satisfies StatFieldLabels<CurlingTeamStatKey, never>;\n\nexport function describeCurlingEvent(event: GameStatEvent, resolve: StatEventNameResolver): string {\n const storeEvent = describeStoreEvent(event, resolve, CURLING_FIELD_LABELS);\n if (storeEvent !== null) return storeEvent;\n if (!isCurlingEvent(event)) return event.type;\n const round = `${CURLING_ROUNDS.label} ${event.roundNumber}`;\n if (event.type === CURLING_ROUND_BLANK_EVENT_TYPE)\n return `${round}: ${CURLING_EVENT_LABELS[event.type].play.toLowerCase()}`;\n return `${round}: ${resolve.team(event.teamId)} scored ${event.points}`;\n}\n\nexport const CURLING_SCOREBOARD: Scoreboard = {\n figures: [{ id: 'points', label: 'Points', value: (teamLine) => teamLine.score }],\n jumpBall: false,\n roundSlots: (teamLine) => ownTeamLine(teamLine).roundScores,\n};\n\nexport const CURLING_SCORING_PANEL: RoundsScoringPanel = {\n kind: 'rounds',\n scoreEventType: CURLING_ROUND_SCORE_EVENT_TYPE,\n blankEventType: CURLING_ROUND_BLANK_EVENT_TYPE,\n maxPoints: MAX_CURLING_ROUND_POINTS,\n};\n\nexport const CURLING_ROUND_POINTS_MESSAGE = `A round is worth 0 to ${MAX_CURLING_ROUND_POINTS} points.`;\nexport const CURLING_ONE_SCORING_TEAM_MESSAGE =\n 'A round is scored by one team (the other 0 or empty), blank (0 for every team), or left empty.';\nexport const CURLING_NO_SKIPPED_ROUND_MESSAGE =\n 'Rounds are recorded in order: fill in every earlier round first.';\n\ntype RoundOutcome =\n | { readonly kind: 'unrecorded' }\n | { readonly kind: 'blank' }\n | { readonly kind: 'scored'; readonly teamId: string; readonly points: number }\n | { readonly kind: 'invalid' };\n\nfunction roundOutcome(\n row: Readonly<Record<string, number | null | undefined>>,\n teamIds: readonly string[],\n): RoundOutcome {\n const cells = teamIds.map((teamId) => ({ teamId, points: row[teamId] ?? null }));\n if (cells.every((cell) => cell.points === null)) return { kind: 'unrecorded' };\n if (cells.every((cell) => cell.points === 0)) return { kind: 'blank' };\n const scorers = cells.filter((cell) => cell.points !== null && cell.points > 0);\n const [scorer] = scorers;\n if (scorers.length === 1 && scorer !== undefined && scorer.points !== null) {\n return { kind: 'scored', teamId: scorer.teamId, points: scorer.points };\n }\n return { kind: 'invalid' };\n}\n\nfunction sameOutcome(left: RoundOutcome, right: RoundOutcome): boolean {\n if (left.kind === 'scored' && right.kind === 'scored')\n return left.teamId === right.teamId && left.points === right.points;\n return left.kind === right.kind;\n}\n\nfunction storedRound(\n context: BulkEntryContext,\n roundNumber: number,\n): Record<string, number | null> {\n const state = ownStats(context.state);\n return Object.fromEntries(\n context.game.teamIds.map((teamId) => [\n teamId,\n state.teamStats[teamId]?.roundScores[roundNumber - 1] ?? null,\n ]),\n );\n}\n\nfunction roundDrafts(values: RoundsValues, context: BulkEntryContext): StatEventDraft[] {\n const { teamIds } = context.game;\n const liveEventByRound = new Map(\n context.liveRoundEvents.map((event) => [event.roundNumber, event.eventId]),\n );\n const voids: StatEventDraft[] = [];\n const recorded: StatEventDraft[] = [];\n values.rounds.forEach((row, index) => {\n const roundNumber = index + 1;\n const entered = roundOutcome(row, teamIds);\n if (sameOutcome(entered, roundOutcome(storedRound(context, roundNumber), teamIds))) return;\n const liveEventId = liveEventByRound.get(roundNumber);\n if (liveEventId !== undefined)\n voids.push({ type: STAT_VOID_EVENT_TYPE, targetEventId: liveEventId });\n if (entered.kind === 'scored') {\n recorded.push({\n type: CURLING_ROUND_SCORE_EVENT_TYPE,\n teamId: entered.teamId,\n roundNumber,\n points: entered.points,\n });\n } else if (entered.kind === 'blank') {\n recorded.push({ type: CURLING_ROUND_BLANK_EVENT_TYPE, roundNumber });\n }\n });\n // Every void first, then the new rounds in order: each changed round is unrecorded again before\n // any new event is validated, so each new event is the next unrecorded round when it arrives.\n return [...voids, ...recorded];\n}\n\nconst roundCell = bulkEntryCountField\n .pipe(z.number().max(MAX_CURLING_ROUND_POINTS, CURLING_ROUND_POINTS_MESSAGE))\n .nullable();\n\nexport const CURLING_BULK_ENTRY: RoundsBulkEntry = {\n kind: 'rounds',\n formDefaults: (context) => ({\n rounds: Array.from({ length: CURLING_ROUND_COUNT }, (_, index) =>\n storedRound(context, index + 1),\n ),\n }),\n formSchema: (context) => {\n const { teamIds } = context.game;\n const row = z.strictObject(Object.fromEntries(teamIds.map((teamId) => [teamId, roundCell])));\n return z\n .object({ rounds: z.array(row).length(CURLING_ROUND_COUNT) })\n .superRefine((values, issues) => {\n let unrecordedBefore = false;\n values.rounds.forEach((cells, index) => {\n const outcome = roundOutcome(cells, teamIds);\n if (outcome.kind === 'invalid') {\n issues.addIssue({\n code: 'custom',\n path: ['rounds', index],\n message: CURLING_ONE_SCORING_TEAM_MESSAGE,\n });\n } else if (outcome.kind === 'unrecorded') {\n unrecordedBefore = true;\n } else if (unrecordedBefore) {\n issues.addIssue({\n code: 'custom',\n path: ['rounds', index],\n message: CURLING_NO_SKIPPED_ROUND_MESSAGE,\n });\n }\n });\n if (roundDrafts(values, context).length > MAX_STAT_EVENTS_PER_WRITE) {\n issues.addIssue({\n code: 'custom',\n path: ['rounds'],\n message: BULK_ENTRY_TOO_LARGE_MESSAGE,\n });\n }\n });\n },\n toEvents: roundDrafts,\n};\n"]}
|
|
1
|
+
{"version":3,"file":"curling-scoring.js","sourceRoot":"","sources":["../../src/sports/curling-scoring.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport { CURLING_ROUND_COUNT, MAX_CURLING_ROUND_POINTS } from '../constants/sports.js';\nimport type { CurlingTeamStats, GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type { CurlingGameStats, GameStats } from '../doc-schemas/sport-game.js';\nimport {\n CURLING_ROUND_BLANK_EVENT_TYPE,\n CURLING_ROUND_SCORE_EVENT_TYPE,\n CURLING_STAT_EVENT_TYPE_KEYS,\n} from '../types/sports.js';\nimport type { CurlingStatEventType, CurlingTeamStatKey } from '../types/sports.js';\nimport { STAT_VOID_EVENT_TYPE } from '../types/stats.js';\nimport {\n bulkEntryCountField,\n type BulkEntryContext,\n type RoundsBulkEntry,\n type RoundsValues,\n type StatEventDraft,\n} from './bulk-entry.js';\nimport {\n buildGameContribution,\n decideParticipation,\n type ContributionSpec,\n} from './contribution.js';\nimport {\n FINALIZATION_READY,\n STAT_EVENT_ACCEPTED,\n isSportStatEventIn,\n isStatCorrection,\n refuseStatEvent,\n type CorrectableFields,\n type FinalizationReadiness,\n type GameTeamLine,\n type SportScoring,\n type SportStatEventBody,\n type StatEventBody,\n} from './scoring.js';\nimport {\n describeStoreEvent,\n type RoundsDisplay,\n type RoundsScoringPanel,\n type Scoreboard,\n type StatEventLabel,\n type StatEventNameResolver,\n type StatFieldLabels,\n} from './scoring-display.js';\n\ntype CurlingEvent = Extract<SportStatEventBody, { type: CurlingStatEventType }>;\n\nfunction isCurlingEvent(event: StatEventBody | undefined): event is CurlingEvent {\n return isSportStatEventIn(CURLING_STAT_EVENT_TYPE_KEYS, event);\n}\n\nfunction ownStats(state: GameStats): CurlingGameStats {\n return state as CurlingGameStats;\n}\n\nfunction ownTeamLine(line: GameTeamLine): CurlingTeamStats {\n return line as CurlingTeamStats;\n}\n\ntype RoundSlots = CurlingTeamStats['roundScores'];\n\nfunction sumOfRounds(roundScores: RoundSlots): number {\n return roundScores.reduce<number>((total, slot) => total + (slot ?? 0), 0);\n}\n\nexport function emptyCurlingTeamLine(): CurlingTeamStats {\n const roundScores: RoundSlots = Array.from({ length: CURLING_ROUND_COUNT }, () => null);\n return { score: sumOfRounds(roundScores), roundScores };\n}\n\nfunction withRound(\n line: CurlingTeamStats,\n roundNumber: number,\n slot: number | null,\n): CurlingTeamStats {\n const roundScores = [...line.roundScores];\n roundScores[roundNumber - 1] = slot;\n return { score: sumOfRounds(roundScores), roundScores };\n}\n\nfunction setRound(stateIn: GameStats, event: CurlingEvent, recorded: boolean): CurlingGameStats {\n const state = ownStats(stateIn);\n const teamStats = { ...state.teamStats };\n const scoringTeamId = event.type === CURLING_ROUND_SCORE_EVENT_TYPE ? event.teamId : undefined;\n if (scoringTeamId !== undefined && teamStats[scoringTeamId] === undefined) {\n teamStats[scoringTeamId] = emptyCurlingTeamLine();\n }\n for (const [teamId, line] of Object.entries(teamStats)) {\n const points =\n event.type === CURLING_ROUND_SCORE_EVENT_TYPE && teamId === scoringTeamId ? event.points : 0;\n teamStats[teamId] = withRound(line, event.roundNumber, recorded ? points : null);\n }\n return { teamStats, playerStats: state.playerStats };\n}\n\nfunction nextCurlingRound(stateIn: GameStats): number | null {\n const lines = Object.values(ownStats(stateIn).teamStats);\n for (let roundNumber = 1; roundNumber <= CURLING_ROUND_COUNT; roundNumber += 1) {\n if (lines.length === 0 || lines.some((line) => line.roundScores[roundNumber - 1] === null))\n return roundNumber;\n }\n return null;\n}\n\nconst CORRECTABLE_FIELDS: CorrectableFields = { team: [], player: [] };\n\nfunction namesPlayer(event: StatEventBody): boolean {\n return 'playerUserId' in event && event.playerUserId !== undefined;\n}\n\nconst validate: SportScoring['validate'] = (state, event, roster) => {\n if (!isCurlingEvent(event)) return refuseStatEvent('eventTypeNotInSport');\n if (namesPlayer(event)) return refuseStatEvent('playerNotAllowed');\n if (event.type === CURLING_ROUND_SCORE_EVENT_TYPE && !roster.teamIds.includes(event.teamId)) {\n return refuseStatEvent('teamNotInGame');\n }\n const next = nextCurlingRound(state);\n if (next === null) return refuseStatEvent('roundsComplete');\n return event.roundNumber === next ? STAT_EVENT_ACCEPTED : refuseStatEvent('roundNotNext');\n};\n\nfunction finalizationReadiness(state: GameStats): FinalizationReadiness {\n return nextCurlingRound(state) === null\n ? FINALIZATION_READY\n : { ready: false, reason: 'roundsIncomplete' };\n}\n\nconst CURLING_CONTRIBUTION: ContributionSpec = {\n playerStatKeys: [],\n teamSeasonPlayerSums: [],\n zeroLineIsNonScoring: false,\n deriveLine: (_kind, line) => line,\n};\n\nexport const CURLING_SCORING: SportScoring = {\n validate,\n apply: (state, event) => {\n if (isStatCorrection(event)) throw new Error('No curling field is correctable.');\n if (!isCurlingEvent(event))\n throw new Error(`The curling adapter does not apply ${event.type}.`);\n return setRound(state, event, true);\n },\n undo: (state, target) => {\n if (!isCurlingEvent(target))\n throw new Error(`The curling adapter does not undo ${target.type}.`);\n return setRound(state, target, false);\n },\n deriveLine: CURLING_CONTRIBUTION.deriveLine,\n correctableFields: CORRECTABLE_FIELDS,\n nextRound: nextCurlingRound,\n finalizationReadiness,\n participation: (state, rosterPlayerIds, absentRosterPlayerIds) =>\n decideParticipation(CURLING_CONTRIBUTION, state, rosterPlayerIds, absentRosterPlayerIds),\n contribution: (game) => buildGameContribution(CURLING_CONTRIBUTION, game),\n};\n\nexport const CURLING_ROUNDS: RoundsDisplay = { count: CURLING_ROUND_COUNT, label: 'Round' };\n\nexport const CURLING_EVENT_LABELS = {\n [CURLING_ROUND_SCORE_EVENT_TYPE]: { button: 'Score', play: 'Round score' },\n [CURLING_ROUND_BLANK_EVENT_TYPE]: { button: '+0 (Blank)', play: 'Blank end' },\n} as const satisfies Record<CurlingStatEventType, StatEventLabel>;\n\nexport const CURLING_FIELD_LABELS = {\n team: { score: 'Score' },\n player: {},\n} as const satisfies StatFieldLabels<CurlingTeamStatKey, never>;\n\nexport function describeCurlingEvent(event: GameStatEvent, resolve: StatEventNameResolver): string {\n const storeEvent = describeStoreEvent(event, resolve, CURLING_FIELD_LABELS);\n if (storeEvent !== null) return storeEvent;\n if (!isCurlingEvent(event)) return event.type;\n const round = `${CURLING_ROUNDS.label} ${event.roundNumber}`;\n if (event.type === CURLING_ROUND_BLANK_EVENT_TYPE)\n return `${round}: ${CURLING_EVENT_LABELS[event.type].play.toLowerCase()}`;\n return `${round}: ${resolve.team(event.teamId)} scored ${event.points}`;\n}\n\nexport const CURLING_SCOREBOARD: Scoreboard = {\n figures: [{ id: 'points', label: 'Points', value: (teamLine) => teamLine.score }],\n jumpBall: false,\n roundSlots: (teamLine) => ownTeamLine(teamLine).roundScores,\n};\n\nexport const CURLING_SCORING_PANEL: RoundsScoringPanel = {\n kind: 'rounds',\n scoreEventType: CURLING_ROUND_SCORE_EVENT_TYPE,\n blankEventType: CURLING_ROUND_BLANK_EVENT_TYPE,\n maxPoints: MAX_CURLING_ROUND_POINTS,\n};\n\nexport const CURLING_ROUND_POINTS_MESSAGE = `A round is worth 0 to ${MAX_CURLING_ROUND_POINTS} points.`;\nexport const CURLING_ONE_SCORING_TEAM_MESSAGE =\n 'A round is scored by one team (the other 0 or empty), blank (0 for every team), or left empty.';\nexport const CURLING_NO_SKIPPED_ROUND_MESSAGE =\n 'Rounds are recorded in order: fill in every earlier round first.';\n\ntype RoundOutcome =\n | { readonly kind: 'unrecorded' }\n | { readonly kind: 'blank' }\n | { readonly kind: 'scored'; readonly teamId: string; readonly points: number }\n | { readonly kind: 'invalid' };\n\nfunction roundOutcome(\n row: Readonly<Record<string, number | null | undefined>>,\n teamIds: readonly string[],\n): RoundOutcome {\n const cells = teamIds.map((teamId) => ({ teamId, points: row[teamId] ?? null }));\n if (cells.every((cell) => cell.points === null)) return { kind: 'unrecorded' };\n if (cells.every((cell) => cell.points === 0)) return { kind: 'blank' };\n const scorers = cells.filter((cell) => cell.points !== null && cell.points > 0);\n const [scorer] = scorers;\n if (scorers.length === 1 && scorer !== undefined && scorer.points !== null) {\n return { kind: 'scored', teamId: scorer.teamId, points: scorer.points };\n }\n return { kind: 'invalid' };\n}\n\nfunction sameOutcome(left: RoundOutcome, right: RoundOutcome): boolean {\n if (left.kind === 'scored' && right.kind === 'scored')\n return left.teamId === right.teamId && left.points === right.points;\n return left.kind === right.kind;\n}\n\nfunction storedRound(\n context: BulkEntryContext,\n roundNumber: number,\n): Record<string, number | null> {\n const state = ownStats(context.state);\n return Object.fromEntries(\n context.game.teamIds.map((teamId) => [\n teamId,\n state.teamStats[teamId]?.roundScores[roundNumber - 1] ?? null,\n ]),\n );\n}\n\nfunction roundDrafts(values: RoundsValues, context: BulkEntryContext): StatEventDraft[] {\n const { teamIds } = context.game;\n const liveEventByRound = new Map(\n context.liveRoundEvents.map((event) => [event.roundNumber, event.eventId]),\n );\n const voids: StatEventDraft[] = [];\n const recorded: StatEventDraft[] = [];\n values.rounds.forEach((row, index) => {\n const roundNumber = index + 1;\n const entered = roundOutcome(row, teamIds);\n if (sameOutcome(entered, roundOutcome(storedRound(context, roundNumber), teamIds))) return;\n const liveEventId = liveEventByRound.get(roundNumber);\n if (liveEventId !== undefined)\n voids.push({ type: STAT_VOID_EVENT_TYPE, targetEventId: liveEventId });\n if (entered.kind === 'scored') {\n recorded.push({\n type: CURLING_ROUND_SCORE_EVENT_TYPE,\n teamId: entered.teamId,\n roundNumber,\n points: entered.points,\n });\n } else if (entered.kind === 'blank') {\n recorded.push({ type: CURLING_ROUND_BLANK_EVENT_TYPE, roundNumber });\n }\n });\n // Every void first, then the new rounds in order: each changed round is unrecorded again before\n // any new event is validated, so each new event is the next unrecorded round when it arrives.\n return [...voids, ...recorded];\n}\n\nconst roundCell = bulkEntryCountField\n .pipe(z.number().max(MAX_CURLING_ROUND_POINTS, CURLING_ROUND_POINTS_MESSAGE))\n .nullable();\n\nexport const CURLING_BULK_ENTRY: RoundsBulkEntry = {\n kind: 'rounds',\n formDefaults: (context) => ({\n rounds: Array.from({ length: CURLING_ROUND_COUNT }, (_, index) =>\n storedRound(context, index + 1),\n ),\n }),\n formSchema: (context) => {\n const { teamIds } = context.game;\n const row = z.strictObject(Object.fromEntries(teamIds.map((teamId) => [teamId, roundCell])));\n return z\n .object({ rounds: z.array(row).length(CURLING_ROUND_COUNT) })\n .superRefine((values, issues) => {\n let unrecordedBefore = false;\n values.rounds.forEach((cells, index) => {\n const outcome = roundOutcome(cells, teamIds);\n if (outcome.kind === 'invalid') {\n issues.addIssue({\n code: 'custom',\n path: ['rounds', index],\n message: CURLING_ONE_SCORING_TEAM_MESSAGE,\n });\n } else if (outcome.kind === 'unrecorded') {\n unrecordedBefore = true;\n } else if (unrecordedBefore) {\n issues.addIssue({\n code: 'custom',\n path: ['rounds', index],\n message: CURLING_NO_SKIPPED_ROUND_MESSAGE,\n });\n }\n });\n });\n },\n toEvents: roundDrafts,\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"football-scoring.d.ts","sourceRoot":"","sources":["../../src/sports/football-scoring.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"football-scoring.d.ts","sourceRoot":"","sources":["../../src/sports/football-scoring.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAYrF,OAAO,EAIL,KAAK,mBAAmB,EAEzB,MAAM,iBAAiB,CAAC;AAMzB,OAAO,EAOL,KAAK,YAAY,EAIlB,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,KAAK,UAAU,EAEf,KAAK,qBAAqB,EAE1B,KAAK,sBAAsB,EAC5B,MAAM,sBAAsB,CAAC;AAY9B,wBAAgB,qBAAqB,IAAI,iBAAiB,CAEzD;AAoED,eAAO,MAAM,gBAAgB,EAAE,YAqB9B,CAAC;AAEF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;CAKgC,CAAC;AAEnE,eAAO,MAAM,qBAAqB;;;;;CAG8B,CAAC;AAEjE,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,qBAAqB,GAC7B,MAAM,CASR;AAED,eAAO,MAAM,mBAAmB,EAAE,UAGjC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,sBAGpC,CAAC;AAaF,eAAO,MAAM,mBAAmB,EAAE,mBAuBjC,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { FOOTBALL_POINTS_BY_EVENT_TYPE,
|
|
2
|
+
import { FOOTBALL_POINTS_BY_EVENT_TYPE, TEAM_NOT_IN_GAME_MESSAGE } from '../constants/sports.js';
|
|
3
3
|
import { FOOTBALL_BULK_EDIT_EVENT_TYPE, FOOTBALL_ONE_POINT_EVENT_TYPE, FOOTBALL_SCORE_EVENT_TYPE_KEYS, FOOTBALL_SIX_POINT_EVENT_TYPE, FOOTBALL_STAT_EVENT_TYPE_KEYS, FOOTBALL_TEAM_STAT_KEYS, FOOTBALL_TWO_POINT_EVENT_TYPE, } from '../types/sports.js';
|
|
4
|
-
import {
|
|
4
|
+
import { bulkEntryCountField, } from './bulk-entry.js';
|
|
5
5
|
import { buildGameContribution, decideParticipation, } from './contribution.js';
|
|
6
6
|
import { FINALIZATION_READY, STAT_EVENT_ACCEPTED, isSportStatEventIn, isStatCorrection, refuseStatEvent, } from './scoring.js';
|
|
7
7
|
import { describeStoreEvent, formatSignedValue, } from './scoring-display.js';
|
|
@@ -154,13 +154,6 @@ export const FOOTBALL_BULK_ENTRY = {
|
|
|
154
154
|
message: TEAM_NOT_IN_GAME_MESSAGE,
|
|
155
155
|
});
|
|
156
156
|
}
|
|
157
|
-
if (scoreDrafts(values, context).length > MAX_STAT_EVENTS_PER_WRITE) {
|
|
158
|
-
issues.addIssue({
|
|
159
|
-
code: 'custom',
|
|
160
|
-
path: ['scores'],
|
|
161
|
-
message: BULK_ENTRY_TOO_LARGE_MESSAGE,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
157
|
});
|
|
165
158
|
},
|
|
166
159
|
toEvents: scoreDrafts,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"football-scoring.js","sourceRoot":"","sources":["../../src/sports/football-scoring.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport {\n FOOTBALL_POINTS_BY_EVENT_TYPE,\n MAX_STAT_EVENTS_PER_WRITE,\n TEAM_NOT_IN_GAME_MESSAGE,\n} from '../constants/sports.js';\nimport type { FootballTeamStats, GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type { FootballGameStats, GameStats } from '../doc-schemas/sport-game.js';\nimport {\n FOOTBALL_BULK_EDIT_EVENT_TYPE,\n FOOTBALL_ONE_POINT_EVENT_TYPE,\n FOOTBALL_SCORE_EVENT_TYPE_KEYS,\n FOOTBALL_SIX_POINT_EVENT_TYPE,\n FOOTBALL_STAT_EVENT_TYPE_KEYS,\n FOOTBALL_TEAM_STAT_KEYS,\n FOOTBALL_TWO_POINT_EVENT_TYPE,\n} from '../types/sports.js';\nimport type { FootballStatEventType, FootballTeamStatKey } from '../types/sports.js';\nimport {\n BULK_ENTRY_TOO_LARGE_MESSAGE,\n bulkEntryCountField,\n type BulkEntryContext,\n type StatEventDraft,\n type TeamScoresBulkEntry,\n type TeamScoresValues,\n} from './bulk-entry.js';\nimport {\n buildGameContribution,\n decideParticipation,\n type ContributionSpec,\n} from './contribution.js';\nimport {\n FINALIZATION_READY,\n STAT_EVENT_ACCEPTED,\n isSportStatEventIn,\n isStatCorrection,\n refuseStatEvent,\n type CorrectableFields,\n type SportScoring,\n type SportStatEventBody,\n type StatCorrectionBody,\n type StatEventBody,\n} from './scoring.js';\nimport {\n describeStoreEvent,\n formatSignedValue,\n type Scoreboard,\n type StatEventLabel,\n type StatEventNameResolver,\n type StatFieldLabels,\n type TeamPointsScoringPanel,\n} from './scoring-display.js';\n\ntype FootballEvent = Extract<SportStatEventBody, { type: FootballStatEventType }>;\n\nfunction isFootballEvent(event: StatEventBody | undefined): event is FootballEvent {\n return isSportStatEventIn(FOOTBALL_STAT_EVENT_TYPE_KEYS, event);\n}\n\nfunction ownStats(state: GameStats): FootballGameStats {\n return state as FootballGameStats;\n}\n\nexport function emptyFootballTeamLine(): FootballTeamStats {\n return Object.fromEntries(FOOTBALL_TEAM_STAT_KEYS.map((key) => [key, 0])) as FootballTeamStats;\n}\n\nfunction teamScoreOf(state: FootballGameStats, teamId: string): number {\n return (state.teamStats[teamId] ?? emptyFootballTeamLine()).score;\n}\n\nfunction pointsOf(event: FootballEvent): number {\n return event.type === FOOTBALL_BULK_EDIT_EVENT_TYPE\n ? event.value\n : FOOTBALL_POINTS_BY_EVENT_TYPE[event.type];\n}\n\nfunction addToScore(stateIn: GameStats, teamId: string, amount: number): FootballGameStats {\n const state = ownStats(stateIn);\n const line = state.teamStats[teamId] ?? emptyFootballTeamLine();\n return {\n teamStats: { ...state.teamStats, [teamId]: { ...line, score: line.score + amount } },\n playerStats: state.playerStats,\n };\n}\n\nconst CORRECTABLE_FIELDS: CorrectableFields = { team: FOOTBALL_TEAM_STAT_KEYS, player: [] };\n\nfunction correctionAmount(event: StatCorrectionBody): number {\n const fields =\n event.playerUserId === undefined ? CORRECTABLE_FIELDS.team : CORRECTABLE_FIELDS.player;\n if (!fields.includes(event.stat)) {\n throw new Error(\n `A football correction cannot change ${event.stat} on a ${event.playerUserId === undefined ? 'team' : 'player'} line.`,\n );\n }\n return event.value;\n}\n\nfunction namesPlayer(event: StatEventBody): boolean {\n return 'playerUserId' in event && event.playerUserId !== undefined;\n}\n\nconst validate: SportScoring['validate'] = (stateIn, event, roster) => {\n if (!isFootballEvent(event)) return refuseStatEvent('eventTypeNotInSport');\n if (!roster.teamIds.includes(event.teamId)) return refuseStatEvent('teamNotInGame');\n if (namesPlayer(event)) return refuseStatEvent('playerNotAllowed');\n if (event.type === FOOTBALL_BULK_EDIT_EVENT_TYPE) {\n if (!Number.isInteger(event.value) || event.value === 0)\n return refuseStatEvent('bulkValueInvalid');\n const score = teamScoreOf(ownStats(stateIn), event.teamId);\n if (score + event.value < 0) return refuseStatEvent('bulkValueInvalid');\n }\n return STAT_EVENT_ACCEPTED;\n};\n\nconst FOOTBALL_CONTRIBUTION: ContributionSpec = {\n playerStatKeys: [],\n teamSeasonPlayerSums: [],\n zeroLineIsNonScoring: false,\n deriveLine: (_kind, line) => line,\n};\n\nconst resolveBulkEntry: NonNullable<SportScoring['resolveBulkEntry']> = (state, submission) => {\n if (submission.type !== FOOTBALL_BULK_EDIT_EVENT_TYPE) return null;\n const { target, ...event } = submission;\n const previousValue = teamScoreOf(ownStats(state), event.teamId);\n const value = target - previousValue;\n return value === 0\n ? { kind: 'unchanged' }\n : { kind: 'change', event: { ...event, value, previousValue } };\n};\n\nexport const FOOTBALL_SCORING: SportScoring = {\n validate,\n resolveBulkEntry,\n apply: (state, event) => {\n if (isStatCorrection(event)) return addToScore(state, event.teamId, correctionAmount(event));\n if (!isFootballEvent(event))\n throw new Error(`The football adapter does not apply ${event.type}.`);\n return addToScore(state, event.teamId, pointsOf(event));\n },\n undo: (state, target) => {\n if (!isFootballEvent(target))\n throw new Error(`The football adapter does not undo ${target.type}.`);\n return addToScore(state, target.teamId, -pointsOf(target));\n },\n deriveLine: FOOTBALL_CONTRIBUTION.deriveLine,\n correctableFields: CORRECTABLE_FIELDS,\n nextRound: () => null,\n finalizationReadiness: () => FINALIZATION_READY,\n participation: (state, rosterPlayerIds, absentRosterPlayerIds) =>\n decideParticipation(FOOTBALL_CONTRIBUTION, state, rosterPlayerIds, absentRosterPlayerIds),\n contribution: (game) => buildGameContribution(FOOTBALL_CONTRIBUTION, game),\n};\n\nexport const FOOTBALL_EVENT_LABELS = {\n [FOOTBALL_SIX_POINT_EVENT_TYPE]: { button: '+6', play: '6 points' },\n [FOOTBALL_TWO_POINT_EVENT_TYPE]: { button: '+2', play: '2 points' },\n [FOOTBALL_ONE_POINT_EVENT_TYPE]: { button: '+1', play: '1 point' },\n [FOOTBALL_BULK_EDIT_EVENT_TYPE]: { button: 'Final scores', play: 'Final score entry' },\n} as const satisfies Record<FootballStatEventType, StatEventLabel>;\n\nexport const FOOTBALL_FIELD_LABELS = {\n team: { score: 'Score' },\n player: {},\n} as const satisfies StatFieldLabels<FootballTeamStatKey, never>;\n\nexport function describeFootballEvent(\n event: GameStatEvent,\n resolve: StatEventNameResolver,\n): string {\n const storeEvent = describeStoreEvent(event, resolve, FOOTBALL_FIELD_LABELS);\n if (storeEvent !== null) return storeEvent;\n if (!isFootballEvent(event)) return event.type;\n const team = resolve.team(event.teamId);\n if (event.type === FOOTBALL_BULK_EDIT_EVENT_TYPE) {\n return `${team}: ${formatSignedValue(event.value)} points (${FOOTBALL_EVENT_LABELS[event.type].play.toLowerCase()})`;\n }\n return `${team}: ${FOOTBALL_EVENT_LABELS[event.type].play}`;\n}\n\nexport const FOOTBALL_SCOREBOARD: Scoreboard = {\n figures: [{ id: 'points', label: 'Points', value: (teamLine) => teamLine.score }],\n jumpBall: false,\n};\n\nexport const FOOTBALL_SCORING_PANEL: TeamPointsScoringPanel = {\n kind: 'teamPoints',\n eventTypes: FOOTBALL_SCORE_EVENT_TYPE_KEYS,\n};\n\nfunction scoreDrafts(values: TeamScoresValues, context: BulkEntryContext): StatEventDraft[] {\n const state = ownStats(context.state);\n const drafts: StatEventDraft[] = [];\n for (const teamId of context.game.teamIds) {\n const entered = values.scores[teamId];\n if (entered === undefined || entered === teamScoreOf(state, teamId)) continue;\n drafts.push({ type: FOOTBALL_BULK_EDIT_EVENT_TYPE, teamId, target: entered });\n }\n return drafts;\n}\n\nexport const FOOTBALL_BULK_ENTRY: TeamScoresBulkEntry = {\n kind: 'teamScores',\n formDefaults: (context) => ({\n scores: Object.fromEntries(\n context.game.teamIds.map((teamId) => [teamId, teamScoreOf(ownStats(context.state), teamId)]),\n ),\n }),\n formSchema: (context) => {\n const teams = new Set(context.game.teamIds);\n return z\n .object({ scores: z.record(z.string(), bulkEntryCountField) })\n .superRefine((values, issues) => {\n for (const teamId of Object.keys(values.scores)) {\n if (!teams.has(teamId))\n issues.addIssue({\n code: 'custom',\n path: ['scores', teamId],\n message: TEAM_NOT_IN_GAME_MESSAGE,\n });\n }\n if (scoreDrafts(values, context).length > MAX_STAT_EVENTS_PER_WRITE) {\n issues.addIssue({\n code: 'custom',\n path: ['scores'],\n message: BULK_ENTRY_TOO_LARGE_MESSAGE,\n });\n }\n });\n },\n toEvents: scoreDrafts,\n};\n"]}
|
|
1
|
+
{"version":3,"file":"football-scoring.js","sourceRoot":"","sources":["../../src/sports/football-scoring.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,6BAA6B,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAGjG,OAAO,EACL,6BAA6B,EAC7B,6BAA6B,EAC7B,8BAA8B,EAC9B,6BAA6B,EAC7B,6BAA6B,EAC7B,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,mBAAmB,GAKpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,qBAAqB,EACrB,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,GAMhB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,GAMlB,MAAM,sBAAsB,CAAC;AAI9B,SAAS,eAAe,CAAC,KAAgC;IACvD,OAAO,kBAAkB,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,QAAQ,CAAC,KAAgB;IAChC,OAAO,KAA0B,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,OAAO,MAAM,CAAC,WAAW,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAsB,CAAC;AACjG,CAAC;AAED,SAAS,WAAW,CAAC,KAAwB,EAAE,MAAc;IAC3D,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,qBAAqB,EAAE,CAAC,CAAC,KAAK,CAAC;AACpE,CAAC;AAED,SAAS,QAAQ,CAAC,KAAoB;IACpC,OAAO,KAAK,CAAC,IAAI,KAAK,6BAA6B;QACjD,CAAC,CAAC,KAAK,CAAC,KAAK;QACb,CAAC,CAAC,6BAA6B,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,UAAU,CAAC,OAAkB,EAAE,MAAc,EAAE,MAAc;IACpE,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,qBAAqB,EAAE,CAAC;IAChE,OAAO;QACL,SAAS,EAAE,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,MAAM,EAAE,EAAE;QACpF,WAAW,EAAE,KAAK,CAAC,WAAW;KAC/B,CAAC;AACJ,CAAC;AAED,MAAM,kBAAkB,GAAsB,EAAE,IAAI,EAAE,uBAAuB,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAE5F,SAAS,gBAAgB,CAAC,KAAyB;IACjD,MAAM,MAAM,GACV,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC;IACzF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,uCAAuC,KAAK,CAAC,IAAI,SAAS,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,QAAQ,CACvH,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC;AACrB,CAAC;AAED,SAAS,WAAW,CAAC,KAAoB;IACvC,OAAO,cAAc,IAAI,KAAK,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC;AACrE,CAAC;AAED,MAAM,QAAQ,GAA6B,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;IACpE,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC;QAAE,OAAO,eAAe,CAAC,qBAAqB,CAAC,CAAC;IAC3E,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;QAAE,OAAO,eAAe,CAAC,eAAe,CAAC,CAAC;IACpF,IAAI,WAAW,CAAC,KAAK,CAAC;QAAE,OAAO,eAAe,CAAC,kBAAkB,CAAC,CAAC;IACnE,IAAI,KAAK,CAAC,IAAI,KAAK,6BAA6B,EAAE,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC;YACrD,OAAO,eAAe,CAAC,kBAAkB,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAC3D,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC;YAAE,OAAO,eAAe,CAAC,kBAAkB,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,mBAAmB,CAAC;AAC7B,CAAC,CAAC;AAEF,MAAM,qBAAqB,GAAqB;IAC9C,cAAc,EAAE,EAAE;IAClB,oBAAoB,EAAE,EAAE;IACxB,oBAAoB,EAAE,KAAK;IAC3B,UAAU,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;CAClC,CAAC;AAEF,MAAM,gBAAgB,GAAkD,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE;IAC5F,IAAI,UAAU,CAAC,IAAI,KAAK,6BAA6B;QAAE,OAAO,IAAI,CAAC;IACnE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,GAAG,UAAU,CAAC;IACxC,MAAM,aAAa,GAAG,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,MAAM,GAAG,aAAa,CAAC;IACrC,OAAO,KAAK,KAAK,CAAC;QAChB,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE;QACvB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,CAAC;AACpE,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAiB;IAC5C,QAAQ;IACR,gBAAgB;IAChB,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACtB,IAAI,gBAAgB,CAAC,KAAK,CAAC;YAAE,OAAO,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7F,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,uCAAuC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;QACxE,OAAO,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;QACtB,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,sCAAsC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;QACxE,OAAO,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7D,CAAC;IACD,UAAU,EAAE,qBAAqB,CAAC,UAAU;IAC5C,iBAAiB,EAAE,kBAAkB;IACrC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI;IACrB,qBAAqB,EAAE,GAAG,EAAE,CAAC,kBAAkB;IAC/C,aAAa,EAAE,CAAC,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,EAAE,CAC/D,mBAAmB,CAAC,qBAAqB,EAAE,KAAK,EAAE,eAAe,EAAE,qBAAqB,CAAC;IAC3F,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,qBAAqB,CAAC,qBAAqB,EAAE,IAAI,CAAC;CAC3E,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,CAAC,6BAA6B,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE;IACnE,CAAC,6BAA6B,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE;IACnE,CAAC,6BAA6B,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;IAClE,CAAC,6BAA6B,CAAC,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,mBAAmB,EAAE;CACtB,CAAC;AAEnE,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;IACxB,MAAM,EAAE,EAAE;CACoD,CAAC;AAEjE,MAAM,UAAU,qBAAqB,CACnC,KAAoB,EACpB,OAA8B;IAE9B,MAAM,UAAU,GAAG,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,qBAAqB,CAAC,CAAC;IAC7E,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,UAAU,CAAC;IAC3C,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC;IAC/C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,KAAK,CAAC,IAAI,KAAK,6BAA6B,EAAE,CAAC;QACjD,OAAO,GAAG,IAAI,KAAK,iBAAiB,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,qBAAqB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC;IACvH,CAAC;IACD,OAAO,GAAG,IAAI,KAAK,qBAAqB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAe;IAC7C,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACjF,QAAQ,EAAE,KAAK;CAChB,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAA2B;IAC5D,IAAI,EAAE,YAAY;IAClB,UAAU,EAAE,8BAA8B;CAC3C,CAAC;AAEF,SAAS,WAAW,CAAC,MAAwB,EAAE,OAAyB;IACtE,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,MAAM,GAAqB,EAAE,CAAC;IACpC,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC;YAAE,SAAS;QAC9E,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,6BAA6B,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAwB;IACtD,IAAI,EAAE,YAAY;IAClB,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC1B,MAAM,EAAE,MAAM,CAAC,WAAW,CACxB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAC7F;KACF,CAAC;IACF,UAAU,EAAE,CAAC,OAAO,EAAE,EAAE;QACtB,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5C,OAAO,CAAC;aACL,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,EAAE,CAAC;aAC7D,WAAW,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE;YAC9B,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;gBAChD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC;oBACpB,MAAM,CAAC,QAAQ,CAAC;wBACd,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC;wBACxB,OAAO,EAAE,wBAAwB;qBAClC,CAAC,CAAC;YACP,CAAC;QACH,CAAC,CAAC,CAAC;IACP,CAAC;IACD,QAAQ,EAAE,WAAW;CACtB,CAAC","sourcesContent":["import { z } from 'zod';\nimport { FOOTBALL_POINTS_BY_EVENT_TYPE, TEAM_NOT_IN_GAME_MESSAGE } from '../constants/sports.js';\nimport type { FootballTeamStats, GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type { FootballGameStats, GameStats } from '../doc-schemas/sport-game.js';\nimport {\n FOOTBALL_BULK_EDIT_EVENT_TYPE,\n FOOTBALL_ONE_POINT_EVENT_TYPE,\n FOOTBALL_SCORE_EVENT_TYPE_KEYS,\n FOOTBALL_SIX_POINT_EVENT_TYPE,\n FOOTBALL_STAT_EVENT_TYPE_KEYS,\n FOOTBALL_TEAM_STAT_KEYS,\n FOOTBALL_TWO_POINT_EVENT_TYPE,\n} from '../types/sports.js';\nimport type { FootballStatEventType, FootballTeamStatKey } from '../types/sports.js';\nimport {\n bulkEntryCountField,\n type BulkEntryContext,\n type StatEventDraft,\n type TeamScoresBulkEntry,\n type TeamScoresValues,\n} from './bulk-entry.js';\nimport {\n buildGameContribution,\n decideParticipation,\n type ContributionSpec,\n} from './contribution.js';\nimport {\n FINALIZATION_READY,\n STAT_EVENT_ACCEPTED,\n isSportStatEventIn,\n isStatCorrection,\n refuseStatEvent,\n type CorrectableFields,\n type SportScoring,\n type SportStatEventBody,\n type StatCorrectionBody,\n type StatEventBody,\n} from './scoring.js';\nimport {\n describeStoreEvent,\n formatSignedValue,\n type Scoreboard,\n type StatEventLabel,\n type StatEventNameResolver,\n type StatFieldLabels,\n type TeamPointsScoringPanel,\n} from './scoring-display.js';\n\ntype FootballEvent = Extract<SportStatEventBody, { type: FootballStatEventType }>;\n\nfunction isFootballEvent(event: StatEventBody | undefined): event is FootballEvent {\n return isSportStatEventIn(FOOTBALL_STAT_EVENT_TYPE_KEYS, event);\n}\n\nfunction ownStats(state: GameStats): FootballGameStats {\n return state as FootballGameStats;\n}\n\nexport function emptyFootballTeamLine(): FootballTeamStats {\n return Object.fromEntries(FOOTBALL_TEAM_STAT_KEYS.map((key) => [key, 0])) as FootballTeamStats;\n}\n\nfunction teamScoreOf(state: FootballGameStats, teamId: string): number {\n return (state.teamStats[teamId] ?? emptyFootballTeamLine()).score;\n}\n\nfunction pointsOf(event: FootballEvent): number {\n return event.type === FOOTBALL_BULK_EDIT_EVENT_TYPE\n ? event.value\n : FOOTBALL_POINTS_BY_EVENT_TYPE[event.type];\n}\n\nfunction addToScore(stateIn: GameStats, teamId: string, amount: number): FootballGameStats {\n const state = ownStats(stateIn);\n const line = state.teamStats[teamId] ?? emptyFootballTeamLine();\n return {\n teamStats: { ...state.teamStats, [teamId]: { ...line, score: line.score + amount } },\n playerStats: state.playerStats,\n };\n}\n\nconst CORRECTABLE_FIELDS: CorrectableFields = { team: FOOTBALL_TEAM_STAT_KEYS, player: [] };\n\nfunction correctionAmount(event: StatCorrectionBody): number {\n const fields =\n event.playerUserId === undefined ? CORRECTABLE_FIELDS.team : CORRECTABLE_FIELDS.player;\n if (!fields.includes(event.stat)) {\n throw new Error(\n `A football correction cannot change ${event.stat} on a ${event.playerUserId === undefined ? 'team' : 'player'} line.`,\n );\n }\n return event.value;\n}\n\nfunction namesPlayer(event: StatEventBody): boolean {\n return 'playerUserId' in event && event.playerUserId !== undefined;\n}\n\nconst validate: SportScoring['validate'] = (stateIn, event, roster) => {\n if (!isFootballEvent(event)) return refuseStatEvent('eventTypeNotInSport');\n if (!roster.teamIds.includes(event.teamId)) return refuseStatEvent('teamNotInGame');\n if (namesPlayer(event)) return refuseStatEvent('playerNotAllowed');\n if (event.type === FOOTBALL_BULK_EDIT_EVENT_TYPE) {\n if (!Number.isInteger(event.value) || event.value === 0)\n return refuseStatEvent('bulkValueInvalid');\n const score = teamScoreOf(ownStats(stateIn), event.teamId);\n if (score + event.value < 0) return refuseStatEvent('bulkValueInvalid');\n }\n return STAT_EVENT_ACCEPTED;\n};\n\nconst FOOTBALL_CONTRIBUTION: ContributionSpec = {\n playerStatKeys: [],\n teamSeasonPlayerSums: [],\n zeroLineIsNonScoring: false,\n deriveLine: (_kind, line) => line,\n};\n\nconst resolveBulkEntry: NonNullable<SportScoring['resolveBulkEntry']> = (state, submission) => {\n if (submission.type !== FOOTBALL_BULK_EDIT_EVENT_TYPE) return null;\n const { target, ...event } = submission;\n const previousValue = teamScoreOf(ownStats(state), event.teamId);\n const value = target - previousValue;\n return value === 0\n ? { kind: 'unchanged' }\n : { kind: 'change', event: { ...event, value, previousValue } };\n};\n\nexport const FOOTBALL_SCORING: SportScoring = {\n validate,\n resolveBulkEntry,\n apply: (state, event) => {\n if (isStatCorrection(event)) return addToScore(state, event.teamId, correctionAmount(event));\n if (!isFootballEvent(event))\n throw new Error(`The football adapter does not apply ${event.type}.`);\n return addToScore(state, event.teamId, pointsOf(event));\n },\n undo: (state, target) => {\n if (!isFootballEvent(target))\n throw new Error(`The football adapter does not undo ${target.type}.`);\n return addToScore(state, target.teamId, -pointsOf(target));\n },\n deriveLine: FOOTBALL_CONTRIBUTION.deriveLine,\n correctableFields: CORRECTABLE_FIELDS,\n nextRound: () => null,\n finalizationReadiness: () => FINALIZATION_READY,\n participation: (state, rosterPlayerIds, absentRosterPlayerIds) =>\n decideParticipation(FOOTBALL_CONTRIBUTION, state, rosterPlayerIds, absentRosterPlayerIds),\n contribution: (game) => buildGameContribution(FOOTBALL_CONTRIBUTION, game),\n};\n\nexport const FOOTBALL_EVENT_LABELS = {\n [FOOTBALL_SIX_POINT_EVENT_TYPE]: { button: '+6', play: '6 points' },\n [FOOTBALL_TWO_POINT_EVENT_TYPE]: { button: '+2', play: '2 points' },\n [FOOTBALL_ONE_POINT_EVENT_TYPE]: { button: '+1', play: '1 point' },\n [FOOTBALL_BULK_EDIT_EVENT_TYPE]: { button: 'Final scores', play: 'Final score entry' },\n} as const satisfies Record<FootballStatEventType, StatEventLabel>;\n\nexport const FOOTBALL_FIELD_LABELS = {\n team: { score: 'Score' },\n player: {},\n} as const satisfies StatFieldLabels<FootballTeamStatKey, never>;\n\nexport function describeFootballEvent(\n event: GameStatEvent,\n resolve: StatEventNameResolver,\n): string {\n const storeEvent = describeStoreEvent(event, resolve, FOOTBALL_FIELD_LABELS);\n if (storeEvent !== null) return storeEvent;\n if (!isFootballEvent(event)) return event.type;\n const team = resolve.team(event.teamId);\n if (event.type === FOOTBALL_BULK_EDIT_EVENT_TYPE) {\n return `${team}: ${formatSignedValue(event.value)} points (${FOOTBALL_EVENT_LABELS[event.type].play.toLowerCase()})`;\n }\n return `${team}: ${FOOTBALL_EVENT_LABELS[event.type].play}`;\n}\n\nexport const FOOTBALL_SCOREBOARD: Scoreboard = {\n figures: [{ id: 'points', label: 'Points', value: (teamLine) => teamLine.score }],\n jumpBall: false,\n};\n\nexport const FOOTBALL_SCORING_PANEL: TeamPointsScoringPanel = {\n kind: 'teamPoints',\n eventTypes: FOOTBALL_SCORE_EVENT_TYPE_KEYS,\n};\n\nfunction scoreDrafts(values: TeamScoresValues, context: BulkEntryContext): StatEventDraft[] {\n const state = ownStats(context.state);\n const drafts: StatEventDraft[] = [];\n for (const teamId of context.game.teamIds) {\n const entered = values.scores[teamId];\n if (entered === undefined || entered === teamScoreOf(state, teamId)) continue;\n drafts.push({ type: FOOTBALL_BULK_EDIT_EVENT_TYPE, teamId, target: entered });\n }\n return drafts;\n}\n\nexport const FOOTBALL_BULK_ENTRY: TeamScoresBulkEntry = {\n kind: 'teamScores',\n formDefaults: (context) => ({\n scores: Object.fromEntries(\n context.game.teamIds.map((teamId) => [teamId, teamScoreOf(ownStats(context.state), teamId)]),\n ),\n }),\n formSchema: (context) => {\n const teams = new Set(context.game.teamIds);\n return z\n .object({ scores: z.record(z.string(), bulkEntryCountField) })\n .superRefine((values, issues) => {\n for (const teamId of Object.keys(values.scores)) {\n if (!teams.has(teamId))\n issues.addIssue({\n code: 'custom',\n path: ['scores', teamId],\n message: TEAM_NOT_IN_GAME_MESSAGE,\n });\n }\n });\n },\n toEvents: scoreDrafts,\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttt-productions/q-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.53",
|
|
4
4
|
"description": "Core types, Firestore path constants, zod schemas, and shared constants for Q-Sports",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"@ttt-productions/edge-protocol-core": "^0.1.15",
|
|
81
81
|
"@ttt-productions/input-format-core": "^0.1.4",
|
|
82
82
|
"@ttt-productions/media-schemas": "^0.14.16",
|
|
83
|
-
"@ttt-productions/notification-core": "^0.15.
|
|
83
|
+
"@ttt-productions/notification-core": "^0.15.24",
|
|
84
84
|
"@ttt-productions/theme-core": "^0.12.16",
|
|
85
85
|
"zod": "^4.4.3"
|
|
86
86
|
},
|