@ttt-productions/q-core 0.1.3 → 0.1.5

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.
Files changed (67) hide show
  1. package/dist/constants/business.d.ts +40 -9
  2. package/dist/constants/business.d.ts.map +1 -1
  3. package/dist/constants/business.js +41 -20
  4. package/dist/constants/business.js.map +1 -1
  5. package/dist/constants/media.d.ts +2 -0
  6. package/dist/constants/media.d.ts.map +1 -1
  7. package/dist/constants/media.js +8 -0
  8. package/dist/constants/media.js.map +1 -1
  9. package/dist/constants/scheduled-jobs.d.ts +1 -1
  10. package/dist/constants/scheduled-jobs.d.ts.map +1 -1
  11. package/dist/constants/scheduled-jobs.js +7 -1
  12. package/dist/constants/scheduled-jobs.js.map +1 -1
  13. package/dist/doc-schemas/admin-notifications.d.ts +1 -1
  14. package/dist/doc-schemas/award-group-grant.d.ts +53 -0
  15. package/dist/doc-schemas/award-group-grant.d.ts.map +1 -0
  16. package/dist/doc-schemas/award-group-grant.js +63 -0
  17. package/dist/doc-schemas/award-group-grant.js.map +1 -0
  18. package/dist/doc-schemas/index.d.ts +1 -0
  19. package/dist/doc-schemas/index.d.ts.map +1 -1
  20. package/dist/doc-schemas/index.js +1 -0
  21. package/dist/doc-schemas/index.js.map +1 -1
  22. package/dist/doc-schemas/migration-console.d.ts +3 -3
  23. package/dist/doc-schemas/registry.d.ts +52 -12
  24. package/dist/doc-schemas/registry.d.ts.map +1 -1
  25. package/dist/doc-schemas/registry.js +5 -0
  26. package/dist/doc-schemas/registry.js.map +1 -1
  27. package/dist/doc-schemas/sport-league.d.ts +2 -0
  28. package/dist/doc-schemas/sport-league.d.ts.map +1 -1
  29. package/dist/doc-schemas/sport-league.js +9 -0
  30. package/dist/doc-schemas/sport-league.js.map +1 -1
  31. package/dist/doc-schemas/stats-operation.d.ts +18 -18
  32. package/dist/ids/award-grant-ids.d.ts +9 -0
  33. package/dist/ids/award-grant-ids.d.ts.map +1 -0
  34. package/dist/ids/award-grant-ids.js +17 -0
  35. package/dist/ids/award-grant-ids.js.map +1 -0
  36. package/dist/ids/index.d.ts +1 -0
  37. package/dist/ids/index.d.ts.map +1 -1
  38. package/dist/ids/index.js +1 -0
  39. package/dist/ids/index.js.map +1 -1
  40. package/dist/paths/collection-groups.d.ts +1 -0
  41. package/dist/paths/collection-groups.d.ts.map +1 -1
  42. package/dist/paths/collection-groups.js +3 -1
  43. package/dist/paths/collection-groups.js.map +1 -1
  44. package/dist/paths/collection-refs.d.ts +1 -0
  45. package/dist/paths/collection-refs.d.ts.map +1 -1
  46. package/dist/paths/collection-refs.js +1 -0
  47. package/dist/paths/collection-refs.js.map +1 -1
  48. package/dist/paths/collections.d.ts +5 -0
  49. package/dist/paths/collections.d.ts.map +1 -1
  50. package/dist/paths/collections.js +8 -0
  51. package/dist/paths/collections.js.map +1 -1
  52. package/dist/paths/path-builders.d.ts +3 -0
  53. package/dist/paths/path-builders.d.ts.map +1 -1
  54. package/dist/paths/path-builders.js +13 -1
  55. package/dist/paths/path-builders.js.map +1 -1
  56. package/dist/schemas/admin-notifications.d.ts +1 -1
  57. package/dist/schemas/awards.d.ts +94 -2
  58. package/dist/schemas/awards.d.ts.map +1 -1
  59. package/dist/schemas/awards.js +49 -9
  60. package/dist/schemas/awards.js.map +1 -1
  61. package/dist/schemas/leagues.d.ts +2 -2
  62. package/dist/schemas/migration-console.d.ts +7 -7
  63. package/dist/types/unions.d.ts +8 -0
  64. package/dist/types/unions.d.ts.map +1 -1
  65. package/dist/types/unions.js +12 -2
  66. package/dist/types/unions.js.map +1 -1
  67. package/package.json +1 -1
@@ -10,7 +10,6 @@ export declare const MIN_LEAGUE_TITLE_LENGTH = 3;
10
10
  export declare const MAX_LEAGUE_TITLE_LENGTH = 100;
11
11
  export declare const MIN_LEAGUE_DESCRIPTION_LENGTH = 10;
12
12
  export declare const MAX_LEAGUE_DESCRIPTION_LENGTH = 5000;
13
- export declare const MAX_PLAYOFF_CUTOFF = 100;
14
13
  export declare const HARDWARE_CATALOG: readonly [{
15
14
  readonly id: "ipad";
16
15
  readonly name: "iPad";
@@ -22,8 +21,8 @@ export type HardwareCatalogItemId = (typeof HARDWARE_CATALOG)[number]['id'];
22
21
  export declare const HARDWARE_CATALOG_ITEM_IDS: [HardwareCatalogItemId, ...HardwareCatalogItemId[]];
23
22
  export declare const MIN_AWARD_NAME_LENGTH = 3;
24
23
  export declare const MAX_AWARD_NAME_LENGTH = 60;
25
- export declare const MAX_AWARD_GRANT_RECIPIENTS = 200;
26
24
  export declare const MAX_AWARD_GRANT_TARGETS = 20;
25
+ export declare const AWARD_GROUP_GRANT_PAGE_SIZE = 100;
27
26
  export declare const MIN_REPAIR_REASON_LENGTH = 3;
28
27
  export declare const MAX_REPAIR_REASON_LENGTH = 500;
29
28
  export declare const MAX_ADMIN_LIST_CHANGES = 20;
@@ -43,19 +42,51 @@ export declare const MAX_GAME_LOCATION_SUB_LOCATIONS = 50;
43
42
  export declare const MAX_GAME_LOCATION_SUB_LOCATION_LENGTH = 60;
44
43
  export declare const MAX_GAME_LOCATION_MEMO_LENGTH = 500;
45
44
  export declare const MAX_SCHEDULE_WEEK_TITLE_LENGTH = 100;
46
- export declare const MAX_SCHEDULE_WEEK_GAMES = 20;
47
45
  export declare const LEAGUE_TIME_ZONE = "America/New_York";
48
46
  export declare const MAX_GAME_MEMO_LENGTH = 30;
49
47
  export declare const countCapReachedMessage: (subject: string, limit: number, noun: string) => string;
50
- export declare const MAX_ACTIVE_TEAM_MEMBERS = 100;
48
+ export declare const MAX_ACTIVE_TEAM_MEMBERS = 30;
51
49
  export declare const TEAM_FULL_MESSAGE: string;
52
- export declare const MAX_ROSTER_SEED_MEMBERSHIPS_PER_TEAM = 100;
53
- export declare const MAX_DEPARTED_ROSTER_ENTRIES_PER_TEAM = 20;
50
+ export declare const MAX_ROSTER_SEED_MEMBERSHIPS_PER_TEAM = 30;
51
+ export declare const MAX_DEPARTED_ROSTER_ENTRIES_PER_TEAM = 10;
54
52
  export declare const MAX_ROSTER_ENTRIES_PER_TEAM: number;
55
- export declare const MAX_TEAMS_PER_LEAGUE = 498;
56
- export declare const MAX_GAMES_PER_LEAGUE = 1000;
53
+ export declare const MAX_TEAMS_PER_LEAGUE = 40;
54
+ export declare const MAX_GAMES_PER_LEAGUE = 250;
57
55
  export declare const LEAGUE_TEAMS_FULL_MESSAGE: string;
58
56
  export declare const LEAGUE_GAMES_FULL_MESSAGE: string;
59
- export declare const MAX_STAT_EVENTS_PER_GAME = 5000;
57
+ export declare const MAX_SCHEDULE_WEEK_GAMES = 20;
58
+ export declare const SCHEDULE_WEEK_FULL_MESSAGE: string;
59
+ export declare const MAX_PLAYOFF_CUTOFF = 40;
60
+ export declare const MAX_STAT_EVENTS_PER_GAME = 1000;
60
61
  export declare const GAME_STAT_LOG_FULL_MESSAGE: string;
62
+ export declare const CAPACITY_LIMITS: readonly [{
63
+ readonly key: "teamPlayers";
64
+ readonly label: "Players on a team, ghost players included";
65
+ readonly limit: 30;
66
+ }, {
67
+ readonly key: "gameSidePeople";
68
+ readonly label: "People on one side of one game: 30 players plus 10 who left the team after recording stats in it";
69
+ readonly limit: number;
70
+ }, {
71
+ readonly key: "leagueTeams";
72
+ readonly label: "Teams in a league";
73
+ readonly limit: 40;
74
+ }, {
75
+ readonly key: "leagueGames";
76
+ readonly label: "Games in a league, so also a team's games";
77
+ readonly limit: 250;
78
+ }, {
79
+ readonly key: "weekGames";
80
+ readonly label: "Games in one week of the schedule";
81
+ readonly limit: 20;
82
+ }, {
83
+ readonly key: "gameStats";
84
+ readonly label: "Stats recorded in one game";
85
+ readonly limit: 1000;
86
+ }, {
87
+ readonly key: "playoffCutoff";
88
+ readonly label: "Teams reaching the playoffs, at most the league's team limit";
89
+ readonly limit: 40;
90
+ }];
91
+ export type CapacityLimitKey = (typeof CAPACITY_LIMITS)[number]['key'];
61
92
  //# sourceMappingURL=business.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"business.d.ts","sourceRoot":"","sources":["../../src/constants/business.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,eAAO,MAAM,oCAAoC,KAAK,CAAC;AACvD,eAAO,MAAM,oCAAoC,MAAM,CAAC;AACxD,eAAO,MAAM,kCAAkC,MAAM,CAAC;AAEtD,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAChD,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAGlD,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAGtC,eAAO,MAAM,gBAAgB;;;;;;EAGnB,CAAC;AACX,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AAC5E,eAAO,MAAM,yBAAyB,EAA8C,CAClF,qBAAqB,EACrB,GAAG,qBAAqB,EAAE,CAC3B,CAAC;AAGF,eAAO,MAAM,qBAAqB,IAAI,CAAC;AACvC,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAIxC,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAI1C,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAG5C,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAGzC,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAG5C,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAG3C,eAAO,MAAM,sCAAsC,MAAM,CAAC;AAG1D,eAAO,MAAM,8BAA8B,OAAO,CAAC;AACnD,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAGzC,eAAO,MAAM,6BAA6B,MAAM,CAAC;AACjD,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAIlD,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,eAAO,MAAM,+BAA+B,QAAQ,CAAC;AAGrD,eAAO,MAAM,6BAA6B,MAAM,CAAC;AACjD,eAAO,MAAM,uCAAuC,MAAM,CAAC;AAC3D,eAAO,MAAM,+BAA+B,KAAK,CAAC;AAClD,eAAO,MAAM,qCAAqC,KAAK,CAAC;AACxD,eAAO,MAAM,6BAA6B,MAAM,CAAC;AAIjD,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAClD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAG1C,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAGnD,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAIvC,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,EAAE,OAAO,MAAM,EAAE,MAAM,MAAM,KAAG,MAC3B,CAAC;AAI5D,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,iBAAiB,QAA0E,CAAC;AAEzG,eAAO,MAAM,oCAAoC,MAA0B,CAAC;AAG5E,eAAO,MAAM,oCAAoC,KAAK,CAAC;AAEvD,eAAO,MAAM,2BAA2B,QAAiE,CAAC;AAK1G,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,eAAO,MAAM,oBAAoB,OAAO,CAAC;AACzC,eAAO,MAAM,yBAAyB,QAAuE,CAAC;AAC9G,eAAO,MAAM,yBAAyB,QAAuE,CAAC;AAI9G,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,0BAA0B,QAAkF,CAAC"}
1
+ {"version":3,"file":"business.d.ts","sourceRoot":"","sources":["../../src/constants/business.ts"],"names":[],"mappings":"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"}
@@ -14,9 +14,6 @@ export const MIN_LEAGUE_TITLE_LENGTH = 3;
14
14
  export const MAX_LEAGUE_TITLE_LENGTH = 100;
15
15
  export const MIN_LEAGUE_DESCRIPTION_LENGTH = 10;
16
16
  export const MAX_LEAGUE_DESCRIPTION_LENGTH = 5000;
17
- // How many teams in a pool reach the playoffs. The cap only bounds the input: no league comes near
18
- // it, and a cutoff above a pool's team count simply admits every team.
19
- export const MAX_PLAYOFF_CUTOFF = 100;
20
17
  // The equipment a referee can be loaned. The catalog is this constant: adding an item is a code
21
18
  // change, and a contract records each item's id and name as it was loaned.
22
19
  export const HARDWARE_CATALOG = [
@@ -27,11 +24,12 @@ export const HARDWARE_CATALOG_ITEM_IDS = HARDWARE_CATALOG.map((item) => item.id)
27
24
  // An award's name in the catalog.
28
25
  export const MIN_AWARD_NAME_LENGTH = 3;
29
26
  export const MAX_AWARD_NAME_LENGTH = 60;
30
- // The most people one award grant may reach once its teams and leagues are resolved to their active
31
- // members: every grant of one call commits in one transaction, beside its audit events.
32
- export const MAX_AWARD_GRANT_RECIPIENTS = 200;
33
- // The most users, teams, or leagues one award grant call may name in each list.
27
+ // The most accounts one award grant call may name in each list.
34
28
  export const MAX_AWARD_GRANT_TARGETS = 20;
29
+ // The people one page of a team's or league's award grant reaches. A group grant has no limit on
30
+ // people; each page commits on its own, and a person takes at most four writes in it — the profile,
31
+ // its public mirror, the person's record on the grant, and the audit event — beside the grant record.
32
+ export const AWARD_GROUP_GRANT_PAGE_SIZE = 100;
35
33
  // The reason an admin gives for a repair action — a backfill, a recalculation, a reopen — recorded on
36
34
  // its audit event (BACKEND-212).
37
35
  export const MIN_REPAIR_REASON_LENGTH = 3;
@@ -62,36 +60,59 @@ export const MAX_GAME_LOCATION_STREET_ADDRESS_LENGTH = 200;
62
60
  export const MAX_GAME_LOCATION_SUB_LOCATIONS = 50;
63
61
  export const MAX_GAME_LOCATION_SUB_LOCATION_LENGTH = 60;
64
62
  export const MAX_GAME_LOCATION_MEMO_LENGTH = 500;
65
- // A week of the schedule: its title, and the games one save creates with it — each seeded with its
66
- // teams' rosters in the same transaction, so a larger week is saved in parts.
63
+ // A week of the schedule's title.
67
64
  export const MAX_SCHEDULE_WEEK_TITLE_LENGTH = 100;
68
- export const MAX_SCHEDULE_WEEK_GAMES = 20;
69
65
  // The timezone a game's `dayKey` (its local calendar date) is computed in.
70
66
  export const LEAGUE_TIME_ZONE = 'America/New_York';
71
67
  // A scorer's note on one person in a game ("red shirt", "#9").
72
68
  export const MAX_GAME_MEMO_LENGTH = 30;
73
69
  // How a refusal at a count cap reads: the thing that is full, its limit, and what it holds.
74
70
  export const countCapReachedMessage = (subject, limit, noun) => `${subject} already has the maximum of ${limit} ${noun}.`;
75
- // A team's size. Every way onto a team is refused at this many active members, inside the
76
- // admission's transaction, so a game's roster can always be seeded and checked in one read.
77
- export const MAX_ACTIVE_TEAM_MEMBERS = 100;
78
- export const TEAM_FULL_MESSAGE = countCapReachedMessage('This team', MAX_ACTIVE_TEAM_MEMBERS, 'members');
71
+ // The capacity limits: how many players, teams, games and stats Q-Sports holds. Each is enforced
72
+ // where the thing is created, inside that creation's transaction, so every read of a whole set fits
73
+ // one bound and every internal bound sized from one derives from it here.
74
+ // A team's size, ghost players included. Every way onto a team is refused at this many active
75
+ // members, so a game's roster can always be seeded and checked in one read.
76
+ export const MAX_ACTIVE_TEAM_MEMBERS = 30;
77
+ export const TEAM_FULL_MESSAGE = countCapReachedMessage('This team', MAX_ACTIVE_TEAM_MEMBERS, 'players');
79
78
  // The memberships one game-roster seed reads for a team: every membership is active, so the cap.
80
79
  export const MAX_ROSTER_SEED_MEMBERSHIPS_PER_TEAM = MAX_ACTIVE_TEAM_MEMBERS;
81
80
  // Departed players a team's side of an open game may keep beside its members: a player who recorded
82
81
  // stats in the game keeps their entry after leaving the team.
83
- export const MAX_DEPARTED_ROSTER_ENTRIES_PER_TEAM = 20;
82
+ export const MAX_DEPARTED_ROSTER_ENTRIES_PER_TEAM = 10;
84
83
  // The entries one team's side of a game may hold, so a reconcile or finalize reads them in one page.
85
84
  export const MAX_ROSTER_ENTRIES_PER_TEAM = MAX_ACTIVE_TEAM_MEMBERS + MAX_DEPARTED_ROSTER_ENTRIES_PER_TEAM;
86
85
  // A league's size. Standings, schedules, and rankings need a league's complete set of teams and
87
- // games, so creation is refused at these caps rather than any read being cut short. The team cap is
88
- // also the most teams one mode switch rewrites in its one transaction.
89
- export const MAX_TEAMS_PER_LEAGUE = 498;
90
- export const MAX_GAMES_PER_LEAGUE = 1000;
86
+ // games, so creation is refused at these caps rather than any read being cut short. A team's games
87
+ // are its league's, so the game cap bounds them too.
88
+ export const MAX_TEAMS_PER_LEAGUE = 40;
89
+ export const MAX_GAMES_PER_LEAGUE = 250;
91
90
  export const LEAGUE_TEAMS_FULL_MESSAGE = countCapReachedMessage('This league', MAX_TEAMS_PER_LEAGUE, 'teams');
92
91
  export const LEAGUE_GAMES_FULL_MESSAGE = countCapReachedMessage('This league', MAX_GAMES_PER_LEAGUE, 'games');
92
+ // The most games one week of the schedule holds, counting the games it already has: creating a week
93
+ // or adding a game to one is refused past it.
94
+ export const MAX_SCHEDULE_WEEK_GAMES = 20;
95
+ export const SCHEDULE_WEEK_FULL_MESSAGE = countCapReachedMessage('This week', MAX_SCHEDULE_WEEK_GAMES, 'games');
96
+ // How many teams in a pool reach the playoffs: at most every team a league can hold. A cutoff above a
97
+ // pool's team count simply admits every team.
98
+ export const MAX_PLAYOFF_CUTOFF = MAX_TEAMS_PER_LEAGUE;
93
99
  // A game's stat log. A write that would take a game's `statEventSequence` past it is refused with
94
100
  // nothing written, which also bounds the game's live play-by-play.
95
- export const MAX_STAT_EVENTS_PER_GAME = 5000;
101
+ export const MAX_STAT_EVENTS_PER_GAME = 1000;
96
102
  export const GAME_STAT_LOG_FULL_MESSAGE = countCapReachedMessage('This game', MAX_STAT_EVENTS_PER_GAME, 'recorded stats');
103
+ // Every capacity limit as an admin reads it, in the order the Limits card lists them. Each value is
104
+ // the constant the server enforces, so the card and every refusal name the same number.
105
+ export const CAPACITY_LIMITS = [
106
+ { key: 'teamPlayers', label: 'Players on a team, ghost players included', limit: MAX_ACTIVE_TEAM_MEMBERS },
107
+ {
108
+ key: 'gameSidePeople',
109
+ label: `People on one side of one game: ${MAX_ACTIVE_TEAM_MEMBERS} players plus ${MAX_DEPARTED_ROSTER_ENTRIES_PER_TEAM} who left the team after recording stats in it`,
110
+ limit: MAX_ROSTER_ENTRIES_PER_TEAM,
111
+ },
112
+ { key: 'leagueTeams', label: 'Teams in a league', limit: MAX_TEAMS_PER_LEAGUE },
113
+ { key: 'leagueGames', label: "Games in a league, so also a team's games", limit: MAX_GAMES_PER_LEAGUE },
114
+ { key: 'weekGames', label: 'Games in one week of the schedule', limit: MAX_SCHEDULE_WEEK_GAMES },
115
+ { key: 'gameStats', label: 'Stats recorded in one game', limit: MAX_STAT_EVENTS_PER_GAME },
116
+ { key: 'playoffCutoff', label: "Teams reaching the playoffs, at most the league's team limit", limit: MAX_PLAYOFF_CUTOFF },
117
+ ];
97
118
  //# sourceMappingURL=business.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"business.js","sourceRoot":"","sources":["../../src/constants/business.ts"],"names":[],"mappings":"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","sourcesContent":["// Business limits and policy values both app roots enforce. A form reads these for its\n// `maxLength` / zod bound and the callable's input schema reads the same constant (ENG-005).\n\n// Display names, team names, and ghost player names share one length policy.\nexport const MIN_DISPLAY_NAME_LENGTH = 3;\nexport const MAX_DISPLAY_NAME_LENGTH = 50;\n// The shortest password an account takes: Firebase Auth's own floor.\nexport const PASSWORD_MIN_LENGTH = 6;\nexport const MIN_TEAM_NAME_LENGTH = 3;\nexport const MAX_TEAM_NAME_LENGTH = 50;\n\nexport const MIN_TEAM_JOIN_REQUEST_MESSAGE_LENGTH = 10;\nexport const MAX_TEAM_JOIN_REQUEST_MESSAGE_LENGTH = 500;\nexport const MAX_TEAM_INVITATION_MESSAGE_LENGTH = 500;\n\nexport const MIN_LEAGUE_TITLE_LENGTH = 3;\nexport const MAX_LEAGUE_TITLE_LENGTH = 100;\nexport const MIN_LEAGUE_DESCRIPTION_LENGTH = 10;\nexport const MAX_LEAGUE_DESCRIPTION_LENGTH = 5000;\n// How many teams in a pool reach the playoffs. The cap only bounds the input: no league comes near\n// it, and a cutoff above a pool's team count simply admits every team.\nexport const MAX_PLAYOFF_CUTOFF = 100;\n// The equipment a referee can be loaned. The catalog is this constant: adding an item is a code\n// change, and a contract records each item's id and name as it was loaned.\nexport const HARDWARE_CATALOG = [\n { id: 'ipad', name: 'iPad' },\n { id: 'ipad-charger', name: 'iPad Charger' },\n] as const;\nexport type HardwareCatalogItemId = (typeof HARDWARE_CATALOG)[number]['id'];\nexport const HARDWARE_CATALOG_ITEM_IDS = HARDWARE_CATALOG.map((item) => item.id) as [\n HardwareCatalogItemId,\n ...HardwareCatalogItemId[],\n];\n\n// An award's name in the catalog.\nexport const MIN_AWARD_NAME_LENGTH = 3;\nexport const MAX_AWARD_NAME_LENGTH = 60;\n\n// The most people one award grant may reach once its teams and leagues are resolved to their active\n// members: every grant of one call commits in one transaction, beside its audit events.\nexport const MAX_AWARD_GRANT_RECIPIENTS = 200;\n// The most users, teams, or leagues one award grant call may name in each list.\nexport const MAX_AWARD_GRANT_TARGETS = 20;\n\n// The reason an admin gives for a repair action — a backfill, a recalculation, a reopen — recorded on\n// its audit event (BACKEND-212).\nexport const MIN_REPAIR_REASON_LENGTH = 3;\nexport const MAX_REPAIR_REASON_LENGTH = 500;\n\n// The most uids one `updateAdminList` call may grant or revoke per list.\nexport const MAX_ADMIN_LIST_CHANGES = 20;\n\n// A team may be created in a league until this many days after the league's start date.\nexport const TEAM_CREATION_CUTOFF_DAYS = 14;\n\n// Contact phone numbers are stored as typed, bounded only in length.\nexport const MAX_CONTACT_PHONE_LENGTH = 30;\n\n// The failure a roster obligation records when a reconciliation run fails, cut to this length.\nexport const MAX_ROSTER_SYNC_FAILURE_MESSAGE_LENGTH = 500;\n\n// `_appConfig/app` operator messages.\nexport const MAX_MAINTENANCE_MESSAGE_LENGTH = 2000;\nexport const MAX_ANNOUNCEMENT_MESSAGE_LENGTH = 300;\n// `_appConfig/app.appVersion` — the version a live client compares itself against.\nexport const MAX_APP_VERSION_LENGTH = 64;\n\n// `_appConfig/landingPage` copy.\nexport const MAX_LANDING_HERO_TITLE_LENGTH = 200;\nexport const MAX_LANDING_ABOUT_TEXT_LENGTH = 5000;\n\n// Waiver template wording. A signed waiver carries its own copy of the title and text, so the text\n// cap also bounds every signature document.\nexport const MAX_WAIVER_TEMPLATE_TITLE_LENGTH = 120;\nexport const MAX_WAIVER_TEMPLATE_TEXT_LENGTH = 20000;\n\n// Game locations: the venue, its address, the courts or sheets inside it, and the directions memo.\nexport const MAX_GAME_LOCATION_NAME_LENGTH = 100;\nexport const MAX_GAME_LOCATION_STREET_ADDRESS_LENGTH = 200;\nexport const MAX_GAME_LOCATION_SUB_LOCATIONS = 50;\nexport const MAX_GAME_LOCATION_SUB_LOCATION_LENGTH = 60;\nexport const MAX_GAME_LOCATION_MEMO_LENGTH = 500;\n\n// A week of the schedule: its title, and the games one save creates with it — each seeded with its\n// teams' rosters in the same transaction, so a larger week is saved in parts.\nexport const MAX_SCHEDULE_WEEK_TITLE_LENGTH = 100;\nexport const MAX_SCHEDULE_WEEK_GAMES = 20;\n\n// The timezone a game's `dayKey` (its local calendar date) is computed in.\nexport const LEAGUE_TIME_ZONE = 'America/New_York';\n\n// A scorer's note on one person in a game (\"red shirt\", \"#9\").\nexport const MAX_GAME_MEMO_LENGTH = 30;\n\n\n// How a refusal at a count cap reads: the thing that is full, its limit, and what it holds.\nexport const countCapReachedMessage = (subject: string, limit: number, noun: string): string =>\n `${subject} already has the maximum of ${limit} ${noun}.`;\n\n// A team's size. Every way onto a team is refused at this many active members, inside the\n// admission's transaction, so a game's roster can always be seeded and checked in one read.\nexport const MAX_ACTIVE_TEAM_MEMBERS = 100;\nexport const TEAM_FULL_MESSAGE = countCapReachedMessage('This team', MAX_ACTIVE_TEAM_MEMBERS, 'members');\n// The memberships one game-roster seed reads for a team: every membership is active, so the cap.\nexport const MAX_ROSTER_SEED_MEMBERSHIPS_PER_TEAM = MAX_ACTIVE_TEAM_MEMBERS;\n// Departed players a team's side of an open game may keep beside its members: a player who recorded\n// stats in the game keeps their entry after leaving the team.\nexport const MAX_DEPARTED_ROSTER_ENTRIES_PER_TEAM = 20;\n// The entries one team's side of a game may hold, so a reconcile or finalize reads them in one page.\nexport const MAX_ROSTER_ENTRIES_PER_TEAM = MAX_ACTIVE_TEAM_MEMBERS + MAX_DEPARTED_ROSTER_ENTRIES_PER_TEAM;\n\n// A league's size. Standings, schedules, and rankings need a league's complete set of teams and\n// games, so creation is refused at these caps rather than any read being cut short. The team cap is\n// also the most teams one mode switch rewrites in its one transaction.\nexport const MAX_TEAMS_PER_LEAGUE = 498;\nexport const MAX_GAMES_PER_LEAGUE = 1000;\nexport const LEAGUE_TEAMS_FULL_MESSAGE = countCapReachedMessage('This league', MAX_TEAMS_PER_LEAGUE, 'teams');\nexport const LEAGUE_GAMES_FULL_MESSAGE = countCapReachedMessage('This league', MAX_GAMES_PER_LEAGUE, 'games');\n\n// A game's stat log. A write that would take a game's `statEventSequence` past it is refused with\n// nothing written, which also bounds the game's live play-by-play.\nexport const MAX_STAT_EVENTS_PER_GAME = 5000;\nexport const GAME_STAT_LOG_FULL_MESSAGE = countCapReachedMessage('This game', MAX_STAT_EVENTS_PER_GAME, 'recorded stats');\n"]}
1
+ {"version":3,"file":"business.js","sourceRoot":"","sources":["../../src/constants/business.ts"],"names":[],"mappings":"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","sourcesContent":["// Business limits and policy values both app roots enforce. A form reads these for its\n// `maxLength` / zod bound and the callable's input schema reads the same constant (ENG-005).\n\n// Display names, team names, and ghost player names share one length policy.\nexport const MIN_DISPLAY_NAME_LENGTH = 3;\nexport const MAX_DISPLAY_NAME_LENGTH = 50;\n// The shortest password an account takes: Firebase Auth's own floor.\nexport const PASSWORD_MIN_LENGTH = 6;\nexport const MIN_TEAM_NAME_LENGTH = 3;\nexport const MAX_TEAM_NAME_LENGTH = 50;\n\nexport const MIN_TEAM_JOIN_REQUEST_MESSAGE_LENGTH = 10;\nexport const MAX_TEAM_JOIN_REQUEST_MESSAGE_LENGTH = 500;\nexport const MAX_TEAM_INVITATION_MESSAGE_LENGTH = 500;\n\nexport const MIN_LEAGUE_TITLE_LENGTH = 3;\nexport const MAX_LEAGUE_TITLE_LENGTH = 100;\nexport const MIN_LEAGUE_DESCRIPTION_LENGTH = 10;\nexport const MAX_LEAGUE_DESCRIPTION_LENGTH = 5000;\n// The equipment a referee can be loaned. The catalog is this constant: adding an item is a code\n// change, and a contract records each item's id and name as it was loaned.\nexport const HARDWARE_CATALOG = [\n { id: 'ipad', name: 'iPad' },\n { id: 'ipad-charger', name: 'iPad Charger' },\n] as const;\nexport type HardwareCatalogItemId = (typeof HARDWARE_CATALOG)[number]['id'];\nexport const HARDWARE_CATALOG_ITEM_IDS = HARDWARE_CATALOG.map((item) => item.id) as [\n HardwareCatalogItemId,\n ...HardwareCatalogItemId[],\n];\n\n// An award's name in the catalog.\nexport const MIN_AWARD_NAME_LENGTH = 3;\nexport const MAX_AWARD_NAME_LENGTH = 60;\n\n// The most accounts one award grant call may name in each list.\nexport const MAX_AWARD_GRANT_TARGETS = 20;\n// The people one page of a team's or league's award grant reaches. A group grant has no limit on\n// people; each page commits on its own, and a person takes at most four writes in it — the profile,\n// its public mirror, the person's record on the grant, and the audit event — beside the grant record.\nexport const AWARD_GROUP_GRANT_PAGE_SIZE = 100;\n\n// The reason an admin gives for a repair action — a backfill, a recalculation, a reopen — recorded on\n// its audit event (BACKEND-212).\nexport const MIN_REPAIR_REASON_LENGTH = 3;\nexport const MAX_REPAIR_REASON_LENGTH = 500;\n\n// The most uids one `updateAdminList` call may grant or revoke per list.\nexport const MAX_ADMIN_LIST_CHANGES = 20;\n\n// A team may be created in a league until this many days after the league's start date.\nexport const TEAM_CREATION_CUTOFF_DAYS = 14;\n\n// Contact phone numbers are stored as typed, bounded only in length.\nexport const MAX_CONTACT_PHONE_LENGTH = 30;\n\n// The failure a roster obligation records when a reconciliation run fails, cut to this length.\nexport const MAX_ROSTER_SYNC_FAILURE_MESSAGE_LENGTH = 500;\n\n// `_appConfig/app` operator messages.\nexport const MAX_MAINTENANCE_MESSAGE_LENGTH = 2000;\nexport const MAX_ANNOUNCEMENT_MESSAGE_LENGTH = 300;\n// `_appConfig/app.appVersion` — the version a live client compares itself against.\nexport const MAX_APP_VERSION_LENGTH = 64;\n\n// `_appConfig/landingPage` copy.\nexport const MAX_LANDING_HERO_TITLE_LENGTH = 200;\nexport const MAX_LANDING_ABOUT_TEXT_LENGTH = 5000;\n\n// Waiver template wording. A signed waiver carries its own copy of the title and text, so the text\n// cap also bounds every signature document.\nexport const MAX_WAIVER_TEMPLATE_TITLE_LENGTH = 120;\nexport const MAX_WAIVER_TEMPLATE_TEXT_LENGTH = 20000;\n\n// Game locations: the venue, its address, the courts or sheets inside it, and the directions memo.\nexport const MAX_GAME_LOCATION_NAME_LENGTH = 100;\nexport const MAX_GAME_LOCATION_STREET_ADDRESS_LENGTH = 200;\nexport const MAX_GAME_LOCATION_SUB_LOCATIONS = 50;\nexport const MAX_GAME_LOCATION_SUB_LOCATION_LENGTH = 60;\nexport const MAX_GAME_LOCATION_MEMO_LENGTH = 500;\n\n// A week of the schedule's title.\nexport const MAX_SCHEDULE_WEEK_TITLE_LENGTH = 100;\n\n// The timezone a game's `dayKey` (its local calendar date) is computed in.\nexport const LEAGUE_TIME_ZONE = 'America/New_York';\n\n// A scorer's note on one person in a game (\"red shirt\", \"#9\").\nexport const MAX_GAME_MEMO_LENGTH = 30;\n\n\n// How a refusal at a count cap reads: the thing that is full, its limit, and what it holds.\nexport const countCapReachedMessage = (subject: string, limit: number, noun: string): string =>\n `${subject} already has the maximum of ${limit} ${noun}.`;\n\n// The capacity limits: how many players, teams, games and stats Q-Sports holds. Each is enforced\n// where the thing is created, inside that creation's transaction, so every read of a whole set fits\n// one bound and every internal bound sized from one derives from it here.\n\n// A team's size, ghost players included. Every way onto a team is refused at this many active\n// members, so a game's roster can always be seeded and checked in one read.\nexport const MAX_ACTIVE_TEAM_MEMBERS = 30;\nexport const TEAM_FULL_MESSAGE = countCapReachedMessage('This team', MAX_ACTIVE_TEAM_MEMBERS, 'players');\n// The memberships one game-roster seed reads for a team: every membership is active, so the cap.\nexport const MAX_ROSTER_SEED_MEMBERSHIPS_PER_TEAM = MAX_ACTIVE_TEAM_MEMBERS;\n// Departed players a team's side of an open game may keep beside its members: a player who recorded\n// stats in the game keeps their entry after leaving the team.\nexport const MAX_DEPARTED_ROSTER_ENTRIES_PER_TEAM = 10;\n// The entries one team's side of a game may hold, so a reconcile or finalize reads them in one page.\nexport const MAX_ROSTER_ENTRIES_PER_TEAM = MAX_ACTIVE_TEAM_MEMBERS + MAX_DEPARTED_ROSTER_ENTRIES_PER_TEAM;\n\n// A league's size. Standings, schedules, and rankings need a league's complete set of teams and\n// games, so creation is refused at these caps rather than any read being cut short. A team's games\n// are its league's, so the game cap bounds them too.\nexport const MAX_TEAMS_PER_LEAGUE = 40;\nexport const MAX_GAMES_PER_LEAGUE = 250;\nexport const LEAGUE_TEAMS_FULL_MESSAGE = countCapReachedMessage('This league', MAX_TEAMS_PER_LEAGUE, 'teams');\nexport const LEAGUE_GAMES_FULL_MESSAGE = countCapReachedMessage('This league', MAX_GAMES_PER_LEAGUE, 'games');\n\n// The most games one week of the schedule holds, counting the games it already has: creating a week\n// or adding a game to one is refused past it.\nexport const MAX_SCHEDULE_WEEK_GAMES = 20;\nexport const SCHEDULE_WEEK_FULL_MESSAGE = countCapReachedMessage('This week', MAX_SCHEDULE_WEEK_GAMES, 'games');\n\n// How many teams in a pool reach the playoffs: at most every team a league can hold. A cutoff above a\n// pool's team count simply admits every team.\nexport const MAX_PLAYOFF_CUTOFF = MAX_TEAMS_PER_LEAGUE;\n\n// A game's stat log. A write that would take a game's `statEventSequence` past it is refused with\n// nothing written, which also bounds the game's live play-by-play.\nexport const MAX_STAT_EVENTS_PER_GAME = 1000;\nexport const GAME_STAT_LOG_FULL_MESSAGE = countCapReachedMessage('This game', MAX_STAT_EVENTS_PER_GAME, 'recorded stats');\n\n// Every capacity limit as an admin reads it, in the order the Limits card lists them. Each value is\n// the constant the server enforces, so the card and every refusal name the same number.\nexport const CAPACITY_LIMITS = [\n { key: 'teamPlayers', label: 'Players on a team, ghost players included', limit: MAX_ACTIVE_TEAM_MEMBERS },\n {\n key: 'gameSidePeople',\n label: `People on one side of one game: ${MAX_ACTIVE_TEAM_MEMBERS} players plus ${MAX_DEPARTED_ROSTER_ENTRIES_PER_TEAM} who left the team after recording stats in it`,\n limit: MAX_ROSTER_ENTRIES_PER_TEAM,\n },\n { key: 'leagueTeams', label: 'Teams in a league', limit: MAX_TEAMS_PER_LEAGUE },\n { key: 'leagueGames', label: \"Games in a league, so also a team's games\", limit: MAX_GAMES_PER_LEAGUE },\n { key: 'weekGames', label: 'Games in one week of the schedule', limit: MAX_SCHEDULE_WEEK_GAMES },\n { key: 'gameStats', label: 'Stats recorded in one game', limit: MAX_STAT_EVENTS_PER_GAME },\n { key: 'playoffCutoff', label: \"Teams reaching the playoffs, at most the league's team limit\", limit: MAX_PLAYOFF_CUTOFF },\n] as const satisfies readonly { key: string; label: string; limit: number }[];\nexport type CapacityLimitKey = (typeof CAPACITY_LIMITS)[number]['key'];\n"]}
@@ -9,6 +9,8 @@ export declare const GALLERY_RETIREMENT_PICTURES_PER_RUN = 100;
9
9
  export declare const STAGING_LIST_PAGE_SIZE = 1000;
10
10
  export declare const MAX_STAGING_DELETES_PER_RUN = 500;
11
11
  export declare const MAX_STAGING_EXAMINED_PER_RUN: number;
12
+ export declare const MEDIA_SWEEP_BACKOFF_BASE_MS: number;
13
+ export declare const MEDIA_SWEEP_BACKOFF_MAX_MS: number;
12
14
  export declare const MAX_V1_PICTURE_CARRY_ATTEMPTS = 3;
13
15
  export declare const MAX_STAGING_UPLOAD_PATH_LENGTH = 300;
14
16
  //# sourceMappingURL=media.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../src/constants/media.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAI/C,eAAO,MAAM,yBAAyB,QAAiB,CAAC;AAIxD,eAAO,MAAM,0BAA0B,QAAiB,CAAC;AAIzD,eAAO,MAAM,wBAAwB,QAAe,CAAC;AAIrD,eAAO,MAAM,6BAA6B,MAAM,CAAC;AACjD,eAAO,MAAM,qCAAqC,IAAI,CAAC;AACvD,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAC/C,eAAO,MAAM,mCAAmC,MAAM,CAAC;AACvD,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAC3C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAK/C,eAAO,MAAM,4BAA4B,QAA6B,CAAC;AAIvE,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAG/C,eAAO,MAAM,8BAA8B,MAAM,CAAC"}
1
+ {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../src/constants/media.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAI/C,eAAO,MAAM,yBAAyB,QAAiB,CAAC;AAIxD,eAAO,MAAM,0BAA0B,QAAiB,CAAC;AAIzD,eAAO,MAAM,wBAAwB,QAAe,CAAC;AAIrD,eAAO,MAAM,6BAA6B,MAAM,CAAC;AACjD,eAAO,MAAM,qCAAqC,IAAI,CAAC;AACvD,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAC/C,eAAO,MAAM,mCAAmC,MAAM,CAAC;AACvD,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAC3C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAK/C,eAAO,MAAM,4BAA4B,QAA6B,CAAC;AAQvE,eAAO,MAAM,2BAA2B,QAAiB,CAAC;AAC1D,eAAO,MAAM,0BAA0B,QAAe,CAAC;AAIvD,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAG/C,eAAO,MAAM,8BAA8B,MAAM,CAAC"}
@@ -28,6 +28,14 @@ export const MAX_STAGING_DELETES_PER_RUN = 500;
28
28
  // this is what bounds a pass over a long kept stretch; two listing pages keep the pass well inside
29
29
  // the sweep's timeout beside its other passes, and the next run resumes after the last one examined.
30
30
  export const MAX_STAGING_EXAMINED_PER_RUN = 2 * STAGING_LIST_PAGE_SIZE;
31
+ // Deferral backoff for the sweep's picture-retirement and stale-upload passes. A row a pass keeps or
32
+ // fails on leaves its scan window as a sweep-state `deferred` entry, next due
33
+ // `min(BASE × 2^(n−1), MAX)` from now with `n` its attempt count: 15 and 30 minutes, 1, 2, 4, 8 and
34
+ // 16 hours, then a day from there on. The base is the sweep's 15-minute cadence, since any shorter
35
+ // delay is simply the next run; the cap bounds how long a released reference or a recovered failure
36
+ // waits to be noticed.
37
+ export const MEDIA_SWEEP_BACKOFF_BASE_MS = 15 * MINUTE_MS;
38
+ export const MEDIA_SWEEP_BACKOFF_MAX_MS = 24 * HOUR_MS;
31
39
  // How many times the migration console's picture carry enqueues one source before reporting it as refused by the
32
40
  // pipeline.
33
41
  export const MAX_V1_PICTURE_CARRY_ATTEMPTS = 3;
@@ -1 +1 @@
1
- {"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/constants/media.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,mGAAmG;AACnG,6CAA6C;AAE7C,MAAM,SAAS,GAAG,EAAE,GAAG,IAAI,CAAC;AAC5B,MAAM,OAAO,GAAG,EAAE,GAAG,SAAS,CAAC;AAE/B,iGAAiG;AACjG,yCAAyC;AACzC,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC;AAE/C,+FAA+F;AAC/F,wFAAwF;AACxF,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,GAAG,SAAS,CAAC;AAExD,oGAAoG;AACpG,oEAAoE;AACpE,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,GAAG,SAAS,CAAC;AAEzD,6FAA6F;AAC7F,4FAA4F;AAC5F,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,GAAG,OAAO,CAAC;AAErD,kGAAkG;AAClG,2BAA2B;AAC3B,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AACjD,MAAM,CAAC,MAAM,qCAAqC,GAAG,CAAC,CAAC;AACvD,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,CAAC;AAC/C,MAAM,CAAC,MAAM,mCAAmC,GAAG,GAAG,CAAC;AACvD,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAC3C,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAC/C,4FAA4F;AAC5F,sGAAsG;AACtG,mGAAmG;AACnG,qGAAqG;AACrG,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,GAAG,sBAAsB,CAAC;AAEvE,iHAAiH;AACjH,YAAY;AACZ,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC;AAE/C,+FAA+F;AAC/F,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC","sourcesContent":["// Upload pipeline policy: the processing claim, the maintenance sweep's windows and bounds, and the\n// retry ceiling of the migration console's picture carry. The generic claim and lease machinery is\n// media-schemas'; the numbers are Q-Sports'.\n\nconst MINUTE_MS = 60 * 1000;\nconst HOUR_MS = 60 * MINUTE_MS;\n\n// Processing attempts a picture upload gets, the first included. A second attempt exists only to\n// recover a crashed or killed first one.\nexport const MEDIA_PROCESSING_MAX_ATTEMPTS = 2;\n\n// How long a processing claim owns its row before a redelivery may reclaim it. Longer than the\n// processing trigger's timeout, so a live attempt is never reclaimed from under itself.\nexport const MEDIA_PROCESSING_LEASE_MS = 10 * MINUTE_MS;\n\n// An upload still `pending` or `processing` this long after it was accepted, past its lease, has no\n// live attempt left driving it, and the maintenance sweep fails it.\nexport const STALE_PENDING_MEDIA_AGE_MS = 30 * MINUTE_MS;\n\n// A staged object this old with no `pending` or `processing` upload row is abandoned — never\n// accepted, consumed by a signing, or left by a finished upload — and the sweep deletes it.\nexport const ABANDONED_STAGING_AGE_MS = 24 * HOUR_MS;\n\n// The maintenance sweep's per-run bounds (BACKEND-112). Each pass stops at its bound and the next\n// scheduled run continues.\nexport const STALE_PENDING_MEDIA_PAGE_SIZE = 100;\nexport const MAX_STALE_PENDING_MEDIA_PAGES_PER_RUN = 5;\nexport const PICTURE_RETIREMENT_PAGE_SIZE = 50;\nexport const GALLERY_RETIREMENT_PICTURES_PER_RUN = 100;\nexport const STAGING_LIST_PAGE_SIZE = 1000;\nexport const MAX_STAGING_DELETES_PER_RUN = 500;\n// The most staged objects one abandoned-staging pass examines before it stops and saves its\n// position. Kept objects (young, with a live row, unreadable) cost nothing against the delete cap, so\n// this is what bounds a pass over a long kept stretch; two listing pages keep the pass well inside\n// the sweep's timeout beside its other passes, and the next run resumes after the last one examined.\nexport const MAX_STAGING_EXAMINED_PER_RUN = 2 * STAGING_LIST_PAGE_SIZE;\n\n// How many times the migration console's picture carry enqueues one source before reporting it as refused by the\n// pipeline.\nexport const MAX_V1_PICTURE_CARRY_ATTEMPTS = 3;\n\n// The longest staging path a callable accepts: `uploads/{origin}/{uid}/{uuid}` is far shorter.\nexport const MAX_STAGING_UPLOAD_PATH_LENGTH = 300;\n"]}
1
+ {"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/constants/media.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,mGAAmG;AACnG,6CAA6C;AAE7C,MAAM,SAAS,GAAG,EAAE,GAAG,IAAI,CAAC;AAC5B,MAAM,OAAO,GAAG,EAAE,GAAG,SAAS,CAAC;AAE/B,iGAAiG;AACjG,yCAAyC;AACzC,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC;AAE/C,+FAA+F;AAC/F,wFAAwF;AACxF,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,GAAG,SAAS,CAAC;AAExD,oGAAoG;AACpG,oEAAoE;AACpE,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,GAAG,SAAS,CAAC;AAEzD,6FAA6F;AAC7F,4FAA4F;AAC5F,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,GAAG,OAAO,CAAC;AAErD,kGAAkG;AAClG,2BAA2B;AAC3B,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AACjD,MAAM,CAAC,MAAM,qCAAqC,GAAG,CAAC,CAAC;AACvD,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,CAAC;AAC/C,MAAM,CAAC,MAAM,mCAAmC,GAAG,GAAG,CAAC;AACvD,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAC3C,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAC/C,4FAA4F;AAC5F,sGAAsG;AACtG,mGAAmG;AACnG,qGAAqG;AACrG,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,GAAG,sBAAsB,CAAC;AAEvE,qGAAqG;AACrG,8EAA8E;AAC9E,oGAAoG;AACpG,mGAAmG;AACnG,oGAAoG;AACpG,uBAAuB;AACvB,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,GAAG,SAAS,CAAC;AAC1D,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,GAAG,OAAO,CAAC;AAEvD,iHAAiH;AACjH,YAAY;AACZ,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC;AAE/C,+FAA+F;AAC/F,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC","sourcesContent":["// Upload pipeline policy: the processing claim, the maintenance sweep's windows and bounds, and the\n// retry ceiling of the migration console's picture carry. The generic claim and lease machinery is\n// media-schemas'; the numbers are Q-Sports'.\n\nconst MINUTE_MS = 60 * 1000;\nconst HOUR_MS = 60 * MINUTE_MS;\n\n// Processing attempts a picture upload gets, the first included. A second attempt exists only to\n// recover a crashed or killed first one.\nexport const MEDIA_PROCESSING_MAX_ATTEMPTS = 2;\n\n// How long a processing claim owns its row before a redelivery may reclaim it. Longer than the\n// processing trigger's timeout, so a live attempt is never reclaimed from under itself.\nexport const MEDIA_PROCESSING_LEASE_MS = 10 * MINUTE_MS;\n\n// An upload still `pending` or `processing` this long after it was accepted, past its lease, has no\n// live attempt left driving it, and the maintenance sweep fails it.\nexport const STALE_PENDING_MEDIA_AGE_MS = 30 * MINUTE_MS;\n\n// A staged object this old with no `pending` or `processing` upload row is abandoned — never\n// accepted, consumed by a signing, or left by a finished upload — and the sweep deletes it.\nexport const ABANDONED_STAGING_AGE_MS = 24 * HOUR_MS;\n\n// The maintenance sweep's per-run bounds (BACKEND-112). Each pass stops at its bound and the next\n// scheduled run continues.\nexport const STALE_PENDING_MEDIA_PAGE_SIZE = 100;\nexport const MAX_STALE_PENDING_MEDIA_PAGES_PER_RUN = 5;\nexport const PICTURE_RETIREMENT_PAGE_SIZE = 50;\nexport const GALLERY_RETIREMENT_PICTURES_PER_RUN = 100;\nexport const STAGING_LIST_PAGE_SIZE = 1000;\nexport const MAX_STAGING_DELETES_PER_RUN = 500;\n// The most staged objects one abandoned-staging pass examines before it stops and saves its\n// position. Kept objects (young, with a live row, unreadable) cost nothing against the delete cap, so\n// this is what bounds a pass over a long kept stretch; two listing pages keep the pass well inside\n// the sweep's timeout beside its other passes, and the next run resumes after the last one examined.\nexport const MAX_STAGING_EXAMINED_PER_RUN = 2 * STAGING_LIST_PAGE_SIZE;\n\n// Deferral backoff for the sweep's picture-retirement and stale-upload passes. A row a pass keeps or\n// fails on leaves its scan window as a sweep-state `deferred` entry, next due\n// `min(BASE × 2^(n−1), MAX)` from now with `n` its attempt count: 15 and 30 minutes, 1, 2, 4, 8 and\n// 16 hours, then a day from there on. The base is the sweep's 15-minute cadence, since any shorter\n// delay is simply the next run; the cap bounds how long a released reference or a recovered failure\n// waits to be noticed.\nexport const MEDIA_SWEEP_BACKOFF_BASE_MS = 15 * MINUTE_MS;\nexport const MEDIA_SWEEP_BACKOFF_MAX_MS = 24 * HOUR_MS;\n\n// How many times the migration console's picture carry enqueues one source before reporting it as refused by the\n// pipeline.\nexport const MAX_V1_PICTURE_CARRY_ATTEMPTS = 3;\n\n// The longest staging path a callable accepts: `uploads/{origin}/{uid}/{uuid}` is far shorter.\nexport const MAX_STAGING_UPLOAD_PATH_LENGTH = 300;\n"]}
@@ -2,7 +2,7 @@
2
2
  * The `sweepState/{sweepName}` document ids: one per scheduled pass that keeps a rolling cursor
3
3
  * (`SweepStateSchema`), so each pass owns exactly one document and its cursor is never shared.
4
4
  */
5
- export declare const SWEEP_STATE_NAMES: readonly ["reconcilePublicUsers", "orphanRegistrationCleanup", "reconcileAccountStatus"];
5
+ export declare const SWEEP_STATE_NAMES: readonly ["reconcilePublicUsers", "orphanRegistrationCleanup", "reconcileAccountStatus", "pictureRetirement", "stalePendingMedia"];
6
6
  export type SweepStateName = (typeof SWEEP_STATE_NAMES)[number];
7
7
  /**
8
8
  * Most rows one pass holds in its `deferred` set — the rows its cursor moved past without clearing.
@@ -1 +1 @@
1
- {"version":3,"file":"scheduled-jobs.d.ts","sourceRoot":"","sources":["../../src/constants/scheduled-jobs.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,iBAAiB,0FAA2F,CAAC;AAC1H,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,uEAAuE;AACvE,eAAO,MAAM,gCAAgC,MAAM,CAAC;AAEpD;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,QAAiB,CAAC;AAE3D,iFAAiF;AACjF,eAAO,MAAM,oCAAoC,iBAAiB,CAAC;AAEnE;;;GAGG;AACH,eAAO,MAAM,yBAAyB,QAAsB,CAAC"}
1
+ {"version":3,"file":"scheduled-jobs.d.ts","sourceRoot":"","sources":["../../src/constants/scheduled-jobs.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,iBAAiB,oIAMpB,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,uEAAuE;AACvE,eAAO,MAAM,gCAAgC,MAAM,CAAC;AAEpD;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,QAAiB,CAAC;AAE3D,iFAAiF;AACjF,eAAO,MAAM,oCAAoC,iBAAiB,CAAC;AAEnE;;;GAGG;AACH,eAAO,MAAM,yBAAyB,QAAsB,CAAC"}
@@ -3,7 +3,13 @@
3
3
  * The `sweepState/{sweepName}` document ids: one per scheduled pass that keeps a rolling cursor
4
4
  * (`SweepStateSchema`), so each pass owns exactly one document and its cursor is never shared.
5
5
  */
6
- export const SWEEP_STATE_NAMES = ['reconcilePublicUsers', 'orphanRegistrationCleanup', 'reconcileAccountStatus'];
6
+ export const SWEEP_STATE_NAMES = [
7
+ 'reconcilePublicUsers',
8
+ 'orphanRegistrationCleanup',
9
+ 'reconcileAccountStatus',
10
+ 'pictureRetirement',
11
+ 'stalePendingMedia',
12
+ ];
7
13
  /**
8
14
  * Most rows one pass holds in its `deferred` set — the rows its cursor moved past without clearing.
9
15
  * A row a pass cannot clear is rare, so a full set means something systemic is failing: the signal
@@ -1 +1 @@
1
- {"version":3,"file":"scheduled-jobs.js","sourceRoot":"","sources":["../../src/constants/scheduled-jobs.ts"],"names":[],"mappings":"AAAA,mDAAmD;AAEnD;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,sBAAsB,EAAE,2BAA2B,EAAE,wBAAwB,CAAU,CAAC;AAG1H;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAE3C,uEAAuE;AACvE,MAAM,CAAC,MAAM,gCAAgC,GAAG,GAAG,CAAC;AAEpD;;;;GAIG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE3D,iFAAiF;AACjF,MAAM,CAAC,MAAM,oCAAoC,GAAG,cAAc,CAAC;AAEnE;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC","sourcesContent":["// Scheduled passes that keep a persisted position.\n\n/**\n * The `sweepState/{sweepName}` document ids: one per scheduled pass that keeps a rolling cursor\n * (`SweepStateSchema`), so each pass owns exactly one document and its cursor is never shared.\n */\nexport const SWEEP_STATE_NAMES = ['reconcilePublicUsers', 'orphanRegistrationCleanup', 'reconcileAccountStatus'] as const;\nexport type SweepStateName = (typeof SWEEP_STATE_NAMES)[number];\n\n/**\n * Most rows one pass holds in its `deferred` set — the rows its cursor moved past without clearing.\n * A row a pass cannot clear is rare, so a full set means something systemic is failing: the signal\n * to stop and report, not to keep growing the list. It also keeps the document a few kilobytes.\n */\nexport const SWEEP_STATE_MAX_DEFERRED = 50;\n\n/** Profiles one `reconcilePublicUsers` pass reads after its cursor. */\nexport const RECONCILE_PUBLIC_USERS_PAGE_SIZE = 200;\n\n/**\n * How long a login may hold no profile after its registration started before the orphan-login\n * cleanup deletes it. A taken name is refused before the login is created, so a registration that\n * does not finish within this window was abandoned.\n */\nexport const ORPHAN_REGISTRATION_GRACE_MS = 30 * 60 * 1000;\n\n/** How often the orphan-login cleanup runs (cron, in the leagues' time zone). */\nexport const ORPHAN_REGISTRATION_CLEANUP_SCHEDULE = '*/15 * * * *';\n\n/**\n * How long a registration-start record lives before its native-TTL policy removes it. Far longer than\n * the grace window, so every abandoned registration is still on the cleanup's work list when it is due.\n */\nexport const REGISTRATION_START_TTL_MS = 24 * 60 * 60 * 1000;\n"]}
1
+ {"version":3,"file":"scheduled-jobs.js","sourceRoot":"","sources":["../../src/constants/scheduled-jobs.ts"],"names":[],"mappings":"AAAA,mDAAmD;AAEnD;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,sBAAsB;IACtB,2BAA2B;IAC3B,wBAAwB;IACxB,mBAAmB;IACnB,mBAAmB;CACX,CAAC;AAGX;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAE3C,uEAAuE;AACvE,MAAM,CAAC,MAAM,gCAAgC,GAAG,GAAG,CAAC;AAEpD;;;;GAIG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE3D,iFAAiF;AACjF,MAAM,CAAC,MAAM,oCAAoC,GAAG,cAAc,CAAC;AAEnE;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC","sourcesContent":["// Scheduled passes that keep a persisted position.\n\n/**\n * The `sweepState/{sweepName}` document ids: one per scheduled pass that keeps a rolling cursor\n * (`SweepStateSchema`), so each pass owns exactly one document and its cursor is never shared.\n */\nexport const SWEEP_STATE_NAMES = [\n 'reconcilePublicUsers',\n 'orphanRegistrationCleanup',\n 'reconcileAccountStatus',\n 'pictureRetirement',\n 'stalePendingMedia',\n] as const;\nexport type SweepStateName = (typeof SWEEP_STATE_NAMES)[number];\n\n/**\n * Most rows one pass holds in its `deferred` set — the rows its cursor moved past without clearing.\n * A row a pass cannot clear is rare, so a full set means something systemic is failing: the signal\n * to stop and report, not to keep growing the list. It also keeps the document a few kilobytes.\n */\nexport const SWEEP_STATE_MAX_DEFERRED = 50;\n\n/** Profiles one `reconcilePublicUsers` pass reads after its cursor. */\nexport const RECONCILE_PUBLIC_USERS_PAGE_SIZE = 200;\n\n/**\n * How long a login may hold no profile after its registration started before the orphan-login\n * cleanup deletes it. A taken name is refused before the login is created, so a registration that\n * does not finish within this window was abandoned.\n */\nexport const ORPHAN_REGISTRATION_GRACE_MS = 30 * 60 * 1000;\n\n/** How often the orphan-login cleanup runs (cron, in the leagues' time zone). */\nexport const ORPHAN_REGISTRATION_CLEANUP_SCHEDULE = '*/15 * * * *';\n\n/**\n * How long a registration-start record lives before its native-TTL policy removes it. Far longer than\n * the grace window, so every abandoned registration is still on the cleanup's work list when it is due.\n */\nexport const REGISTRATION_START_TTL_MS = 24 * 60 * 60 * 1000;\n"]}
@@ -134,8 +134,8 @@ export declare const NotificationArchiveAllJobSchema: z.ZodObject<{
134
134
  userId: z.ZodString;
135
135
  category: z.ZodLiteral<"admin">;
136
136
  state: z.ZodEnum<{
137
- failed: "failed";
138
137
  complete: "complete";
138
+ failed: "failed";
139
139
  "in-progress": "in-progress";
140
140
  incomplete: "incomplete";
141
141
  }>;
@@ -0,0 +1,53 @@
1
+ import { z } from 'zod';
2
+ export declare const awardGroupTypeSchema: z.ZodEnum<{
3
+ team: "team";
4
+ league: "league";
5
+ }>;
6
+ export declare const awardGroupGrantActionSchema: z.ZodEnum<{
7
+ grant: "grant";
8
+ revoke: "revoke";
9
+ }>;
10
+ export declare const awardGroupGrantStatusSchema: z.ZodEnum<{
11
+ pending: "pending";
12
+ complete: "complete";
13
+ }>;
14
+ export declare const awardGroupGrantDecisionSchema: z.ZodNumber;
15
+ export declare const AwardGroupGrantSchema: z.ZodObject<{
16
+ grantId: z.ZodString;
17
+ action: z.ZodEnum<{
18
+ grant: "grant";
19
+ revoke: "revoke";
20
+ }>;
21
+ awardId: z.ZodString;
22
+ groupType: z.ZodEnum<{
23
+ team: "team";
24
+ league: "league";
25
+ }>;
26
+ groupId: z.ZodString;
27
+ targetKey: z.ZodString;
28
+ decision: z.ZodNumber;
29
+ actorUid: z.ZodString;
30
+ status: z.ZodEnum<{
31
+ pending: "pending";
32
+ complete: "complete";
33
+ }>;
34
+ createdAt: z.ZodNumber;
35
+ completedAt: z.ZodOptional<z.ZodNumber>;
36
+ attemptCount: z.ZodNumber;
37
+ nextAttemptAt: z.ZodNumber;
38
+ lastError: z.ZodOptional<z.ZodString>;
39
+ }, z.core.$strict>;
40
+ export type AwardGroupGrant = z.infer<typeof AwardGroupGrantSchema>;
41
+ export declare const AwardGroupGrantPersonSchema: z.ZodObject<{
42
+ userId: z.ZodString;
43
+ heldBefore: z.ZodBoolean;
44
+ recordedAt: z.ZodNumber;
45
+ revoked: z.ZodBoolean;
46
+ revokedAt: z.ZodOptional<z.ZodNumber>;
47
+ revokeSkipped: z.ZodOptional<z.ZodEnum<{
48
+ heldBefore: "heldBefore";
49
+ heldByAnotherGrant: "heldByAnotherGrant";
50
+ }>>;
51
+ }, z.core.$strict>;
52
+ export type AwardGroupGrantPerson = z.infer<typeof AwardGroupGrantPersonSchema>;
53
+ //# sourceMappingURL=award-group-grant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"award-group-grant.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/award-group-grant.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,eAAO,MAAM,oBAAoB;;;EAAgC,CAAC;AAClE,eAAO,MAAM,2BAA2B;;;EAAwC,CAAC;AACjF,eAAO,MAAM,2BAA2B;;;EAAwC,CAAC;AAGjF,eAAO,MAAM,6BAA6B,aAAiB,CAAC;AAE5D,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;kBA6B9B,CAAC;AACL,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAMpE,eAAO,MAAM,2BAA2B;;;;;;;;;;kBAOtC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC"}
@@ -0,0 +1,63 @@
1
+ // `awardGroupGrants/{grantId}` — one decision on one award for one group (a team, or a whole league):
2
+ // grant the award to the group's active members, or take back what the group's grants gave. The
3
+ // people are reached a page at a time, after the decision opens in its own small transaction. Written
4
+ // only by Cloud Functions; an admin reads a decision through the status callable.
5
+ //
6
+ // A grant records each person in `awardGroupGrantPeople` before it changes their profile, so a re-run
7
+ // after any interruption finds every recorded person and finishes them, and its revoke reverses exactly
8
+ // the people it gave the award to. Someone who already held the award is recorded as holding it before,
9
+ // and a revoke leaves them holding it. `status` stays `pending` until every page is done; the retry
10
+ // ledger lets a decision that keeps failing back off instead of holding the resume's window.
11
+ import { z } from 'zod';
12
+ import { buildAwardGroupGrantId, buildAwardGroupGrantTargetKey } from '../ids/award-grant-ids.js';
13
+ import { AWARD_GROUP_GRANT_ACTION_KEYS, AWARD_GROUP_GRANT_STATUS_KEYS, AWARD_GROUP_REVOKE_SKIP_KEYS, AWARD_GROUP_TYPE_KEYS, } from '../types/unions.js';
14
+ import { countSchema, documentIdSegmentSchema, epochMsSchema, revisionSchema, userIdSchema } from './atoms.js';
15
+ import { awardIdSchema } from './catalogs.js';
16
+ export const awardGroupTypeSchema = z.enum(AWARD_GROUP_TYPE_KEYS);
17
+ export const awardGroupGrantActionSchema = z.enum(AWARD_GROUP_GRANT_ACTION_KEYS);
18
+ export const awardGroupGrantStatusSchema = z.enum(AWARD_GROUP_GRANT_STATUS_KEYS);
19
+ // A decision's number among every decision on its award and group: the first is 1.
20
+ export const awardGroupGrantDecisionSchema = revisionSchema;
21
+ export const AwardGroupGrantSchema = z
22
+ .strictObject({
23
+ grantId: z.string().min(1),
24
+ action: awardGroupGrantActionSchema,
25
+ awardId: awardIdSchema,
26
+ groupType: awardGroupTypeSchema,
27
+ // The team id or the league id, as `groupType` says.
28
+ groupId: documentIdSegmentSchema,
29
+ targetKey: z.string().min(1),
30
+ decision: awardGroupGrantDecisionSchema,
31
+ // The admin who decided. Each person's audit event is recorded as their action.
32
+ actorUid: userIdSchema,
33
+ status: awardGroupGrantStatusSchema,
34
+ createdAt: epochMsSchema,
35
+ completedAt: epochMsSchema.optional(),
36
+ attemptCount: countSchema,
37
+ nextAttemptAt: epochMsSchema,
38
+ lastError: z.string().optional(),
39
+ })
40
+ .superRefine((grant, ctx) => {
41
+ // The key and the id are derived fields: a decision whose stored key or id disagrees with its
42
+ // award, group, and number would sit outside that pair's decision sequence.
43
+ const targetKey = buildAwardGroupGrantTargetKey(grant);
44
+ if (grant.targetKey !== targetKey) {
45
+ ctx.addIssue({ code: 'custom', path: ['targetKey'], message: 'targetKey must be the award and group target key.' });
46
+ }
47
+ if (grant.grantId !== buildAwardGroupGrantId(targetKey, grant.decision)) {
48
+ ctx.addIssue({ code: 'custom', path: ['grantId'], message: 'grantId must be the target key and the decision.' });
49
+ }
50
+ });
51
+ // `awardGroupGrants/{grantId}/awardGroupGrantPeople/{userId}` — one person a grant decision reached,
52
+ // recorded before their profile changed. `heldBefore` says they already held the award, so the grant
53
+ // gave them nothing. `revoked` turns true once a revoke has dealt with them; `revokeSkipped` says why
54
+ // that revoke left them holding the award.
55
+ export const AwardGroupGrantPersonSchema = z.strictObject({
56
+ userId: userIdSchema,
57
+ heldBefore: z.boolean(),
58
+ recordedAt: epochMsSchema,
59
+ revoked: z.boolean(),
60
+ revokedAt: epochMsSchema.optional(),
61
+ revokeSkipped: z.enum(AWARD_GROUP_REVOKE_SKIP_KEYS).optional(),
62
+ });
63
+ //# sourceMappingURL=award-group-grant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"award-group-grant.js","sourceRoot":"","sources":["../../src/doc-schemas/award-group-grant.ts"],"names":[],"mappings":"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","sourcesContent":["// `awardGroupGrants/{grantId}` — one decision on one award for one group (a team, or a whole league):\n// grant the award to the group's active members, or take back what the group's grants gave. The\n// people are reached a page at a time, after the decision opens in its own small transaction. Written\n// only by Cloud Functions; an admin reads a decision through the status callable.\n//\n// A grant records each person in `awardGroupGrantPeople` before it changes their profile, so a re-run\n// after any interruption finds every recorded person and finishes them, and its revoke reverses exactly\n// the people it gave the award to. Someone who already held the award is recorded as holding it before,\n// and a revoke leaves them holding it. `status` stays `pending` until every page is done; the retry\n// ledger lets a decision that keeps failing back off instead of holding the resume's window.\n\nimport { z } from 'zod';\nimport { buildAwardGroupGrantId, buildAwardGroupGrantTargetKey } from '../ids/award-grant-ids.js';\nimport {\n AWARD_GROUP_GRANT_ACTION_KEYS,\n AWARD_GROUP_GRANT_STATUS_KEYS,\n AWARD_GROUP_REVOKE_SKIP_KEYS,\n AWARD_GROUP_TYPE_KEYS,\n} from '../types/unions.js';\nimport { countSchema, documentIdSegmentSchema, epochMsSchema, revisionSchema, userIdSchema } from './atoms.js';\nimport { awardIdSchema } from './catalogs.js';\n\nexport const awardGroupTypeSchema = z.enum(AWARD_GROUP_TYPE_KEYS);\nexport const awardGroupGrantActionSchema = z.enum(AWARD_GROUP_GRANT_ACTION_KEYS);\nexport const awardGroupGrantStatusSchema = z.enum(AWARD_GROUP_GRANT_STATUS_KEYS);\n\n// A decision's number among every decision on its award and group: the first is 1.\nexport const awardGroupGrantDecisionSchema = revisionSchema;\n\nexport const AwardGroupGrantSchema = z\n .strictObject({\n grantId: z.string().min(1),\n action: awardGroupGrantActionSchema,\n awardId: awardIdSchema,\n groupType: awardGroupTypeSchema,\n // The team id or the league id, as `groupType` says.\n groupId: documentIdSegmentSchema,\n targetKey: z.string().min(1),\n decision: awardGroupGrantDecisionSchema,\n // The admin who decided. Each person's audit event is recorded as their action.\n actorUid: userIdSchema,\n status: awardGroupGrantStatusSchema,\n createdAt: epochMsSchema,\n completedAt: epochMsSchema.optional(),\n attemptCount: countSchema,\n nextAttemptAt: epochMsSchema,\n lastError: z.string().optional(),\n })\n .superRefine((grant, ctx) => {\n // The key and the id are derived fields: a decision whose stored key or id disagrees with its\n // award, group, and number would sit outside that pair's decision sequence.\n const targetKey = buildAwardGroupGrantTargetKey(grant);\n if (grant.targetKey !== targetKey) {\n ctx.addIssue({ code: 'custom', path: ['targetKey'], message: 'targetKey must be the award and group target key.' });\n }\n if (grant.grantId !== buildAwardGroupGrantId(targetKey, grant.decision)) {\n ctx.addIssue({ code: 'custom', path: ['grantId'], message: 'grantId must be the target key and the decision.' });\n }\n });\nexport type AwardGroupGrant = z.infer<typeof AwardGroupGrantSchema>;\n\n// `awardGroupGrants/{grantId}/awardGroupGrantPeople/{userId}` — one person a grant decision reached,\n// recorded before their profile changed. `heldBefore` says they already held the award, so the grant\n// gave them nothing. `revoked` turns true once a revoke has dealt with them; `revokeSkipped` says why\n// that revoke left them holding the award.\nexport const AwardGroupGrantPersonSchema = z.strictObject({\n userId: userIdSchema,\n heldBefore: z.boolean(),\n recordedAt: epochMsSchema,\n revoked: z.boolean(),\n revokedAt: epochMsSchema.optional(),\n revokeSkipped: z.enum(AWARD_GROUP_REVOKE_SKIP_KEYS).optional(),\n});\nexport type AwardGroupGrantPerson = z.infer<typeof AwardGroupGrantPersonSchema>;\n"]}
@@ -22,6 +22,7 @@ export * from './waiver-template.js';
22
22
  export * from './waivers-and-hardware.js';
23
23
  export * from './game-location.js';
24
24
  export * from './catalogs.js';
25
+ export * from './award-group-grant.js';
25
26
  export * from './system.js';
26
27
  export * from './admin-notifications.js';
27
28
  export * from './migration-console.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC"}
@@ -22,6 +22,7 @@ export * from './waiver-template.js';
22
22
  export * from './waivers-and-hardware.js';
23
23
  export * from './game-location.js';
24
24
  export * from './catalogs.js';
25
+ export * from './award-group-grant.js';
25
26
  export * from './system.js';
26
27
  export * from './admin-notifications.js';
27
28
  export * from './migration-console.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/doc-schemas/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC","sourcesContent":["export * from './atoms.js';\nexport * from './firestore-primitives.js';\nexport * from './user-profile.js';\nexport * from './publicUser.js';\nexport * from './reserved-display-name.js';\nexport * from './status-reconcile-queue.js';\nexport * from './registration-start.js';\nexport * from './audit-event.js';\nexport * from './gallery.js';\nexport * from './pending-media.js';\nexport * from './picture-retirement.js';\nexport * from './sport-league.js';\nexport * from './league-team.js';\nexport * from './team-membership.js';\nexport * from './membership-roster-sync.js';\nexport * from './game-stats.js';\nexport * from './sport-game.js';\nexport * from './game-stat-history.js';\nexport * from './aggregate-stats.js';\nexport * from './stats-operation.js';\nexport * from './waiver-template.js';\nexport * from './waivers-and-hardware.js';\nexport * from './game-location.js';\nexport * from './catalogs.js';\nexport * from './system.js';\nexport * from './admin-notifications.js';\nexport * from './migration-console.js';\nexport * from './sweep-state.js';\nexport * from './registry.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/doc-schemas/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC","sourcesContent":["export * from './atoms.js';\nexport * from './firestore-primitives.js';\nexport * from './user-profile.js';\nexport * from './publicUser.js';\nexport * from './reserved-display-name.js';\nexport * from './status-reconcile-queue.js';\nexport * from './registration-start.js';\nexport * from './audit-event.js';\nexport * from './gallery.js';\nexport * from './pending-media.js';\nexport * from './picture-retirement.js';\nexport * from './sport-league.js';\nexport * from './league-team.js';\nexport * from './team-membership.js';\nexport * from './membership-roster-sync.js';\nexport * from './game-stats.js';\nexport * from './sport-game.js';\nexport * from './game-stat-history.js';\nexport * from './aggregate-stats.js';\nexport * from './stats-operation.js';\nexport * from './waiver-template.js';\nexport * from './waivers-and-hardware.js';\nexport * from './game-location.js';\nexport * from './catalogs.js';\nexport * from './award-group-grant.js';\nexport * from './system.js';\nexport * from './admin-notifications.js';\nexport * from './migration-console.js';\nexport * from './sweep-state.js';\nexport * from './registry.js';\n"]}
@@ -124,8 +124,8 @@ export declare const MigrationRunRecordSchema: z.ZodObject<{
124
124
  updatedAt: z.ZodNumber;
125
125
  completedAt: z.ZodOptional<z.ZodNumber>;
126
126
  status: z.ZodEnum<{
127
- running: "running";
128
127
  complete: "complete";
128
+ running: "running";
129
129
  stopped: "stopped";
130
130
  }>;
131
131
  pages: z.ZodNumber;
@@ -255,8 +255,8 @@ export declare const MigrationActionRunSchema: z.ZodObject<{
255
255
  updatedAt: z.ZodNumber;
256
256
  completedAt: z.ZodOptional<z.ZodNumber>;
257
257
  status: z.ZodEnum<{
258
- running: "running";
259
258
  complete: "complete";
259
+ running: "running";
260
260
  stopped: "stopped";
261
261
  }>;
262
262
  pages: z.ZodNumber;
@@ -290,8 +290,8 @@ export declare const MigrationActionRunSchema: z.ZodObject<{
290
290
  updatedAt: z.ZodNumber;
291
291
  completedAt: z.ZodOptional<z.ZodNumber>;
292
292
  status: z.ZodEnum<{
293
- running: "running";
294
293
  complete: "complete";
294
+ running: "running";
295
295
  stopped: "stopped";
296
296
  }>;
297
297
  pages: z.ZodNumber;