@ttt-productions/q-core 0.1.20 → 0.1.22

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 (204) hide show
  1. package/dist/constants/business.js +2 -2
  2. package/dist/constants/business.js.map +1 -1
  3. package/dist/constants/deployment.d.ts +2 -0
  4. package/dist/constants/deployment.d.ts.map +1 -0
  5. package/dist/constants/deployment.js +2 -0
  6. package/dist/constants/deployment.js.map +1 -0
  7. package/dist/constants/index.d.ts +1 -0
  8. package/dist/constants/index.d.ts.map +1 -1
  9. package/dist/constants/index.js +1 -0
  10. package/dist/constants/index.js.map +1 -1
  11. package/dist/constants/media.js +1 -1
  12. package/dist/constants/media.js.map +1 -1
  13. package/dist/constants/sports.js +1 -1
  14. package/dist/constants/sports.js.map +1 -1
  15. package/dist/constants/telemetry.js +3 -3
  16. package/dist/constants/telemetry.js.map +1 -1
  17. package/dist/doc-schemas/admin-notifications.js +3 -3
  18. package/dist/doc-schemas/admin-notifications.js.map +1 -1
  19. package/dist/doc-schemas/aggregate-stats.js +3 -3
  20. package/dist/doc-schemas/aggregate-stats.js.map +1 -1
  21. package/dist/doc-schemas/atoms.d.ts +1 -1
  22. package/dist/doc-schemas/atoms.js +3 -3
  23. package/dist/doc-schemas/atoms.js.map +1 -1
  24. package/dist/doc-schemas/audit-event.js +2 -2
  25. package/dist/doc-schemas/audit-event.js.map +1 -1
  26. package/dist/doc-schemas/firestore-primitives.js +1 -1
  27. package/dist/doc-schemas/firestore-primitives.js.map +1 -1
  28. package/dist/doc-schemas/game-location.js +2 -2
  29. package/dist/doc-schemas/game-location.js.map +1 -1
  30. package/dist/doc-schemas/game-stat-history.js +1 -1
  31. package/dist/doc-schemas/game-stat-history.js.map +1 -1
  32. package/dist/doc-schemas/game-stats.js +6 -6
  33. package/dist/doc-schemas/game-stats.js.map +1 -1
  34. package/dist/doc-schemas/league-team.js +4 -4
  35. package/dist/doc-schemas/league-team.js.map +1 -1
  36. package/dist/doc-schemas/membership-roster-sync.js +2 -2
  37. package/dist/doc-schemas/membership-roster-sync.js.map +1 -1
  38. package/dist/doc-schemas/pending-media.d.ts +6 -6
  39. package/dist/doc-schemas/publicUser.js +1 -1
  40. package/dist/doc-schemas/publicUser.js.map +1 -1
  41. package/dist/doc-schemas/registration-start.js +1 -1
  42. package/dist/doc-schemas/registration-start.js.map +1 -1
  43. package/dist/doc-schemas/registry.d.ts +3 -3
  44. package/dist/doc-schemas/sport-game.js +4 -4
  45. package/dist/doc-schemas/sport-game.js.map +1 -1
  46. package/dist/doc-schemas/sport-league.js +1 -1
  47. package/dist/doc-schemas/sport-league.js.map +1 -1
  48. package/dist/doc-schemas/stats-operation.js +2 -2
  49. package/dist/doc-schemas/stats-operation.js.map +1 -1
  50. package/dist/doc-schemas/status-reconcile-queue.js +1 -1
  51. package/dist/doc-schemas/status-reconcile-queue.js.map +1 -1
  52. package/dist/doc-schemas/sweep-state.js +2 -2
  53. package/dist/doc-schemas/sweep-state.js.map +1 -1
  54. package/dist/doc-schemas/team-membership.js +1 -1
  55. package/dist/doc-schemas/team-membership.js.map +1 -1
  56. package/dist/doc-schemas/user-profile.js +1 -1
  57. package/dist/doc-schemas/user-profile.js.map +1 -1
  58. package/dist/doc-schemas/waiver-template.js +1 -1
  59. package/dist/doc-schemas/waiver-template.js.map +1 -1
  60. package/dist/doc-schemas/waivers-and-hardware.js +3 -3
  61. package/dist/doc-schemas/waivers-and-hardware.js.map +1 -1
  62. package/dist/domain-events/domain-events.js +2 -2
  63. package/dist/domain-events/domain-events.js.map +1 -1
  64. package/dist/ids/award-grant-ids.js +1 -1
  65. package/dist/ids/award-grant-ids.js.map +1 -1
  66. package/dist/ids/compound-ids.js +1 -1
  67. package/dist/ids/compound-ids.js.map +1 -1
  68. package/dist/ids/minted-ids.js +1 -1
  69. package/dist/ids/minted-ids.js.map +1 -1
  70. package/dist/ids/notification-ids.js +1 -1
  71. package/dist/ids/notification-ids.js.map +1 -1
  72. package/dist/ids/stat-event-ids.d.ts +1 -1
  73. package/dist/ids/stat-event-ids.js +2 -2
  74. package/dist/ids/stat-event-ids.js.map +1 -1
  75. package/dist/ids/stats-operation-ids.js +1 -1
  76. package/dist/ids/stats-operation-ids.js.map +1 -1
  77. package/dist/index.js +1 -1
  78. package/dist/index.js.map +1 -1
  79. package/dist/input-formats/input-formats.js +1 -1
  80. package/dist/input-formats/input-formats.js.map +1 -1
  81. package/dist/media/file-origins.js +1 -1
  82. package/dist/media/file-origins.js.map +1 -1
  83. package/dist/media/picture-url.js +1 -1
  84. package/dist/media/picture-url.js.map +1 -1
  85. package/dist/migration-console/action-ids.js +1 -1
  86. package/dist/migration-console/action-ids.js.map +1 -1
  87. package/dist/migration-console/manifest.js +1 -1
  88. package/dist/migration-console/manifest.js.map +1 -1
  89. package/dist/notifications/admin-notification-config.js +1 -1
  90. package/dist/notifications/admin-notification-config.js.map +1 -1
  91. package/dist/notifications/occurrences.js +1 -1
  92. package/dist/notifications/occurrences.js.map +1 -1
  93. package/dist/paths/collections.js +3 -3
  94. package/dist/paths/collections.js.map +1 -1
  95. package/dist/paths/picture-owner-paths.js +1 -1
  96. package/dist/paths/picture-owner-paths.js.map +1 -1
  97. package/dist/paths/storage-paths.d.ts +1 -1
  98. package/dist/paths/storage-paths.js +1 -1
  99. package/dist/paths/storage-paths.js.map +1 -1
  100. package/dist/paths/utils.d.ts +1 -1
  101. package/dist/paths/utils.js +1 -1
  102. package/dist/paths/utils.js.map +1 -1
  103. package/dist/schemas/admin-notifications.js +1 -1
  104. package/dist/schemas/admin-notifications.js.map +1 -1
  105. package/dist/schemas/app-config.js +1 -1
  106. package/dist/schemas/app-config.js.map +1 -1
  107. package/dist/schemas/awards.js +1 -1
  108. package/dist/schemas/awards.js.map +1 -1
  109. package/dist/schemas/dead-letters.js +2 -2
  110. package/dist/schemas/dead-letters.js.map +1 -1
  111. package/dist/schemas/leagues.js +1 -1
  112. package/dist/schemas/leagues.js.map +1 -1
  113. package/dist/schemas/migration-console.js +1 -1
  114. package/dist/schemas/migration-console.js.map +1 -1
  115. package/dist/schemas/pictures.js +1 -1
  116. package/dist/schemas/pictures.js.map +1 -1
  117. package/dist/schemas/schedules.js +2 -2
  118. package/dist/schemas/schedules.js.map +1 -1
  119. package/dist/schemas/scoring.js +1 -1
  120. package/dist/schemas/scoring.js.map +1 -1
  121. package/dist/schemas/signed-waivers.js +1 -1
  122. package/dist/schemas/signed-waivers.js.map +1 -1
  123. package/dist/schemas/stats.js +6 -6
  124. package/dist/schemas/stats.js.map +1 -1
  125. package/dist/schemas/team-joining.js +3 -3
  126. package/dist/schemas/team-joining.js.map +1 -1
  127. package/dist/schemas/teams.js +7 -7
  128. package/dist/schemas/teams.js.map +1 -1
  129. package/dist/schemas/users.js +3 -3
  130. package/dist/schemas/users.js.map +1 -1
  131. package/dist/sports/basketball-scoring.js +4 -4
  132. package/dist/sports/basketball-scoring.js.map +1 -1
  133. package/dist/sports/bulk-entry.js +1 -1
  134. package/dist/sports/bulk-entry.js.map +1 -1
  135. package/dist/sports/contribution.d.ts +5 -5
  136. package/dist/sports/contribution.d.ts.map +1 -1
  137. package/dist/sports/contribution.js +4 -4
  138. package/dist/sports/contribution.js.map +1 -1
  139. package/dist/sports/curling-scoring.js +2 -2
  140. package/dist/sports/curling-scoring.js.map +1 -1
  141. package/dist/sports/football-scoring.js +2 -2
  142. package/dist/sports/football-scoring.js.map +1 -1
  143. package/dist/sports/index.js +1 -1
  144. package/dist/sports/index.js.map +1 -1
  145. package/dist/sports/ledger-continuity.js +1 -1
  146. package/dist/sports/ledger-continuity.js.map +1 -1
  147. package/dist/sports/registry.js +2 -2
  148. package/dist/sports/registry.js.map +1 -1
  149. package/dist/sports/replay.js +1 -1
  150. package/dist/sports/replay.js.map +1 -1
  151. package/dist/sports/rollup-check.js +1 -1
  152. package/dist/sports/rollup-check.js.map +1 -1
  153. package/dist/sports/score-mirror.js +1 -1
  154. package/dist/sports/score-mirror.js.map +1 -1
  155. package/dist/sports/scoring-display.d.ts +1 -1
  156. package/dist/sports/scoring-display.js +2 -2
  157. package/dist/sports/scoring-display.js.map +1 -1
  158. package/dist/sports/scoring.d.ts +5 -5
  159. package/dist/sports/scoring.d.ts.map +1 -1
  160. package/dist/sports/scoring.js +2 -2
  161. package/dist/sports/scoring.js.map +1 -1
  162. package/dist/sports/sport-adapter.d.ts +3 -3
  163. package/dist/sports/sport-adapter.d.ts.map +1 -1
  164. package/dist/sports/sport-adapter.js +2 -2
  165. package/dist/sports/sport-adapter.js.map +1 -1
  166. package/dist/sports/store-driver.d.ts +1 -1
  167. package/dist/sports/store-driver.d.ts.map +1 -1
  168. package/dist/sports/store-driver.js +2 -2
  169. package/dist/sports/store-driver.js.map +1 -1
  170. package/dist/types/audit.js +4 -4
  171. package/dist/types/audit.js.map +1 -1
  172. package/dist/types/claims.js +1 -1
  173. package/dist/types/claims.js.map +1 -1
  174. package/dist/types/picture-owners.js +1 -1
  175. package/dist/types/picture-owners.js.map +1 -1
  176. package/dist/types/schedules.js +1 -1
  177. package/dist/types/schedules.js.map +1 -1
  178. package/dist/types/sports.js +2 -2
  179. package/dist/types/sports.js.map +1 -1
  180. package/dist/types/stats.js +5 -5
  181. package/dist/types/stats.js.map +1 -1
  182. package/dist/types/unions.js +2 -2
  183. package/dist/types/unions.js.map +1 -1
  184. package/dist/types/uploads.js +1 -1
  185. package/dist/types/uploads.js.map +1 -1
  186. package/dist/upload-variables/sign-hardware-contract-variables.js +1 -1
  187. package/dist/upload-variables/sign-hardware-contract-variables.js.map +1 -1
  188. package/dist/upload-variables/sign-waiver-variables.js +1 -1
  189. package/dist/upload-variables/sign-waiver-variables.js.map +1 -1
  190. package/dist/utils/account-status.js +1 -1
  191. package/dist/utils/account-status.js.map +1 -1
  192. package/dist/utils/app-routes.js +1 -1
  193. package/dist/utils/app-routes.js.map +1 -1
  194. package/dist/utils/league-join-warning.js +1 -1
  195. package/dist/utils/league-join-warning.js.map +1 -1
  196. package/dist/utils/league-joining.js +1 -1
  197. package/dist/utils/league-joining.js.map +1 -1
  198. package/dist/utils/league-mode.js +1 -1
  199. package/dist/utils/league-mode.js.map +1 -1
  200. package/dist/utils/team-creation.js +1 -1
  201. package/dist/utils/team-creation.js.map +1 -1
  202. package/dist/utils/team-member-actions.js +1 -1
  203. package/dist/utils/team-member-actions.js.map +1 -1
  204. package/package.json +4 -4
@@ -1,5 +1,5 @@
1
1
  // Business limits and policy values both app roots enforce. A form reads these for its
2
- // `maxLength` / zod bound and the callable's input schema reads the same constant (ENG-005).
2
+ // `maxLength` / zod bound and the callable's input schema reads the same constant (RULE-STD-ENG-005).
3
3
  // Display names, team names, and ghost player names share one length policy.
4
4
  export const MIN_DISPLAY_NAME_LENGTH = 3;
5
5
  export const MAX_DISPLAY_NAME_LENGTH = 50;
@@ -31,7 +31,7 @@ export const MAX_AWARD_GRANT_TARGETS = 20;
31
31
  // its public mirror, the person's record on the grant, and the audit event — beside the grant record.
32
32
  export const AWARD_GROUP_GRANT_PAGE_SIZE = 100;
33
33
  // The reason an admin gives for a repair action — a backfill, a recalculation, a reopen — recorded on
34
- // its audit event (BACKEND-212).
34
+ // its audit event (RULE-STD-BACKEND-212).
35
35
  export const MIN_REPAIR_REASON_LENGTH = 3;
36
36
  export const MAX_REPAIR_REASON_LENGTH = 500;
37
37
  // The most uids one `updateAdminList` call may grant or revoke per list.
@@ -1 +1 @@
1
- {"version":3,"file":"business.js","sourceRoot":"","sources":["../../src/constants/business.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,6FAA6F;AAE7F,6EAA6E;AAC7E,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AACzC,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAC1C,qEAAqE;AACrE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AACrC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AACtC,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEvC,MAAM,CAAC,MAAM,oCAAoC,GAAG,EAAE,CAAC;AACvD,MAAM,CAAC,MAAM,oCAAoC,GAAG,GAAG,CAAC;AACxD,MAAM,CAAC,MAAM,kCAAkC,GAAG,GAAG,CAAC;AAEtD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AACzC,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,CAAC;AAC3C,MAAM,CAAC,MAAM,6BAA6B,GAAG,EAAE,CAAC;AAChD,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC;AAClD,gGAAgG;AAChG,2EAA2E;AAC3E,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;IAC5B,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,EAAE;CACpC,CAAC;AAEX,MAAM,CAAC,MAAM,yBAAyB,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAG9E,CAAC;AAEF,kCAAkC;AAClC,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC;AACvC,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAExC,gEAAgE;AAChE,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAC1C,iGAAiG;AACjG,oGAAoG;AACpG,sGAAsG;AACtG,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,sGAAsG;AACtG,iCAAiC;AACjC,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAC1C,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAE5C,yEAAyE;AACzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAEzC,wFAAwF;AACxF,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AAE5C,qEAAqE;AACrE,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAE3C,+FAA+F;AAC/F,MAAM,CAAC,MAAM,sCAAsC,GAAG,GAAG,CAAC;AAE1D,sCAAsC;AACtC,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;AACnD,MAAM,CAAC,MAAM,+BAA+B,GAAG,GAAG,CAAC;AACnD,mFAAmF;AACnF,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAEzC,iCAAiC;AACjC,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AACjD,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC;AAElD,mGAAmG;AACnG,4CAA4C;AAC5C,MAAM,CAAC,MAAM,gCAAgC,GAAG,GAAG,CAAC;AACpD,MAAM,CAAC,MAAM,+BAA+B,GAAG,KAAK,CAAC;AAErD,mGAAmG;AACnG,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AACjD,MAAM,CAAC,MAAM,uCAAuC,GAAG,GAAG,CAAC;AAC3D,MAAM,CAAC,MAAM,+BAA+B,GAAG,EAAE,CAAC;AAClD,MAAM,CAAC,MAAM,qCAAqC,GAAG,EAAE,CAAC;AACxD,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AAEjD,kCAAkC;AAClC,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAElD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAEnD,+DAA+D;AAC/D,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEvC,4FAA4F;AAC5F,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,OAAe,EAAE,KAAa,EAAE,IAAY,EAAU,EAAE,CAC7F,GAAG,OAAO,+BAA+B,KAAK,IAAI,IAAI,GAAG,CAAC;AAE5D,iGAAiG;AACjG,oGAAoG;AACpG,0EAA0E;AAE1E,8FAA8F;AAC9F,4EAA4E;AAC5E,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAC1C,MAAM,CAAC,MAAM,iBAAiB,GAAG,sBAAsB,CACrD,WAAW,EACX,uBAAuB,EACvB,SAAS,CACV,CAAC;AACF,iGAAiG;AACjG,MAAM,CAAC,MAAM,oCAAoC,GAAG,uBAAuB,CAAC;AAC5E,oGAAoG;AACpG,8DAA8D;AAC9D,MAAM,CAAC,MAAM,oCAAoC,GAAG,EAAE,CAAC;AACvD,qGAAqG;AACrG,MAAM,CAAC,MAAM,2BAA2B,GACtC,uBAAuB,GAAG,oCAAoC,CAAC;AAEjE,gGAAgG;AAChG,mGAAmG;AACnG,qDAAqD;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AACvC,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AACxC,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAC7D,aAAa,EACb,oBAAoB,EACpB,OAAO,CACR,CAAC;AACF,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAC7D,aAAa,EACb,oBAAoB,EACpB,OAAO,CACR,CAAC;AAEF,oGAAoG;AACpG,8CAA8C;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAC1C,MAAM,CAAC,MAAM,0BAA0B,GAAG,sBAAsB,CAC9D,WAAW,EACX,uBAAuB,EACvB,OAAO,CACR,CAAC;AAEF,sGAAsG;AACtG,8CAA8C;AAC9C,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAC;AAEvD,kGAAkG;AAClG,mEAAmE;AACnE,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAC7C,MAAM,CAAC,MAAM,0BAA0B,GAAG,sBAAsB,CAC9D,WAAW,EACX,wBAAwB,EACxB,gBAAgB,CACjB,CAAC;AAEF,uGAAuG;AACvG,qGAAqG;AACrG,mEAAmE;AACnE,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AACrC,MAAM,CAAC,MAAM,2BAA2B,GAAG,sBAAsB,CAC/D,UAAU,EACV,kBAAkB,EAClB,WAAW,CACZ,CAAC;AAEF,mGAAmG;AACnG,wGAAwG;AACxG,kFAAkF;AAClF,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,CAAC;AAC/C,MAAM,CAAC,MAAM,6BAA6B,GAAG,sBAAsB,CACjE,UAAU,EACV,4BAA4B,EAC5B,qCAAqC,CACtC,CAAC;AAEF,oGAAoG;AACpG,wFAAwF;AACxF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B;QACE,GAAG,EAAE,aAAa;QAClB,KAAK,EAAE,2CAA2C;QAClD,KAAK,EAAE,uBAAuB;KAC/B;IACD;QACE,GAAG,EAAE,gBAAgB;QACrB,KAAK,EAAE,mCAAmC,uBAAuB,iBAAiB,oCAAoC,gDAAgD;QACtK,KAAK,EAAE,2BAA2B;KACnC;IACD,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,oBAAoB,EAAE;IAC/E;QACE,GAAG,EAAE,aAAa;QAClB,KAAK,EAAE,2CAA2C;QAClD,KAAK,EAAE,oBAAoB;KAC5B;IACD,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,mCAAmC,EAAE,KAAK,EAAE,uBAAuB,EAAE;IAChG,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,4BAA4B,EAAE,KAAK,EAAE,wBAAwB,EAAE;IAC1F;QACE,GAAG,EAAE,eAAe;QACpB,KAAK,EAAE,8DAA8D;QACrE,KAAK,EAAE,kBAAkB;KAC1B;IACD;QACE,GAAG,EAAE,eAAe;QACpB,KAAK,EAAE,qEAAqE;QAC5E,KAAK,EAAE,kBAAkB;KAC1B;IACD;QACE,GAAG,EAAE,wBAAwB;QAC7B,KAAK,EAAE,+DAA+D;QACtE,KAAK,EAAE,4BAA4B;KACpC;CAC0E,CAAC","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// 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(\n 'This team',\n MAX_ACTIVE_TEAM_MEMBERS,\n 'players',\n);\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 =\n 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(\n 'This league',\n MAX_TEAMS_PER_LEAGUE,\n 'teams',\n);\nexport const LEAGUE_GAMES_FULL_MESSAGE = countCapReachedMessage(\n 'This league',\n MAX_GAMES_PER_LEAGUE,\n 'games',\n);\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(\n 'This week',\n MAX_SCHEDULE_WEEK_GAMES,\n 'games',\n);\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(\n 'This game',\n MAX_STAT_EVENTS_PER_GAME,\n 'recorded stats',\n);\n\n// The venues games are played at. Every venue picker and the public locations page read the whole set,\n// so creating a venue is refused at this many. A venue's courts, sheets, or rooms are part of it and\n// count toward MAX_GAME_LOCATION_SUB_LOCATIONS, never toward this.\nexport const MAX_GAME_LOCATIONS = 30;\nexport const GAME_LOCATIONS_FULL_MESSAGE = countCapReachedMessage(\n 'Q-Sports',\n MAX_GAME_LOCATIONS,\n 'locations',\n);\n\n// The waiver templates a league can be given: every template not retired. The offered list and the\n// league form's picker read the whole set, so creating a template is refused at this many; retiring one\n// frees its place. A retired template is kept for what names it and never counts.\nexport const MAX_OFFERED_WAIVER_TEMPLATES = 30;\nexport const WAIVER_TEMPLATES_FULL_MESSAGE = countCapReachedMessage(\n 'Q-Sports',\n MAX_OFFERED_WAIVER_TEMPLATES,\n 'waiver templates offered to leagues',\n);\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 {\n key: 'teamPlayers',\n label: 'Players on a team, ghost players included',\n limit: MAX_ACTIVE_TEAM_MEMBERS,\n },\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 {\n key: 'leagueGames',\n label: \"Games in a league, so also a team's games\",\n limit: MAX_GAMES_PER_LEAGUE,\n },\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 {\n key: 'playoffCutoff',\n label: \"Teams reaching the playoffs, at most the league's team limit\",\n limit: MAX_PLAYOFF_CUTOFF,\n },\n {\n key: 'gameLocations',\n label: \"Game locations, each venue's courts or sheets counted as part of it\",\n limit: MAX_GAME_LOCATIONS,\n },\n {\n key: 'offeredWaiverTemplates',\n label: 'Waiver templates offered to leagues, retired ones not counted',\n limit: MAX_OFFERED_WAIVER_TEMPLATES,\n },\n] as const satisfies readonly { key: string; label: string; limit: number }[];\nexport type CapacityLimitKey = (typeof CAPACITY_LIMITS)[number]['key'];\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 (RULE-STD-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 (RULE-STD-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// 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(\n 'This team',\n MAX_ACTIVE_TEAM_MEMBERS,\n 'players',\n);\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 =\n 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(\n 'This league',\n MAX_TEAMS_PER_LEAGUE,\n 'teams',\n);\nexport const LEAGUE_GAMES_FULL_MESSAGE = countCapReachedMessage(\n 'This league',\n MAX_GAMES_PER_LEAGUE,\n 'games',\n);\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(\n 'This week',\n MAX_SCHEDULE_WEEK_GAMES,\n 'games',\n);\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(\n 'This game',\n MAX_STAT_EVENTS_PER_GAME,\n 'recorded stats',\n);\n\n// The venues games are played at. Every venue picker and the public locations page read the whole set,\n// so creating a venue is refused at this many. A venue's courts, sheets, or rooms are part of it and\n// count toward MAX_GAME_LOCATION_SUB_LOCATIONS, never toward this.\nexport const MAX_GAME_LOCATIONS = 30;\nexport const GAME_LOCATIONS_FULL_MESSAGE = countCapReachedMessage(\n 'Q-Sports',\n MAX_GAME_LOCATIONS,\n 'locations',\n);\n\n// The waiver templates a league can be given: every template not retired. The offered list and the\n// league form's picker read the whole set, so creating a template is refused at this many; retiring one\n// frees its place. A retired template is kept for what names it and never counts.\nexport const MAX_OFFERED_WAIVER_TEMPLATES = 30;\nexport const WAIVER_TEMPLATES_FULL_MESSAGE = countCapReachedMessage(\n 'Q-Sports',\n MAX_OFFERED_WAIVER_TEMPLATES,\n 'waiver templates offered to leagues',\n);\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 {\n key: 'teamPlayers',\n label: 'Players on a team, ghost players included',\n limit: MAX_ACTIVE_TEAM_MEMBERS,\n },\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 {\n key: 'leagueGames',\n label: \"Games in a league, so also a team's games\",\n limit: MAX_GAMES_PER_LEAGUE,\n },\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 {\n key: 'playoffCutoff',\n label: \"Teams reaching the playoffs, at most the league's team limit\",\n limit: MAX_PLAYOFF_CUTOFF,\n },\n {\n key: 'gameLocations',\n label: \"Game locations, each venue's courts or sheets counted as part of it\",\n limit: MAX_GAME_LOCATIONS,\n },\n {\n key: 'offeredWaiverTemplates',\n label: 'Waiver templates offered to leagues, retired ones not counted',\n limit: MAX_OFFERED_WAIVER_TEMPLATES,\n },\n] as const satisfies readonly { key: string; label: string; limit: number }[];\nexport type CapacityLimitKey = (typeof CAPACITY_LIMITS)[number]['key'];\n"]}
@@ -0,0 +1,2 @@
1
+ export declare const FUNCTIONS_REGION = "us-east1";
2
+ //# sourceMappingURL=deployment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/constants/deployment.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export const FUNCTIONS_REGION = 'us-east1';
2
+ //# sourceMappingURL=deployment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment.js","sourceRoot":"","sources":["../../src/constants/deployment.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC","sourcesContent":["export const FUNCTIONS_REGION = 'us-east1';\n"]}
@@ -4,4 +4,5 @@ export * from './storage-keys.js';
4
4
  export * from './media.js';
5
5
  export * from './telemetry.js';
6
6
  export * from './scheduled-jobs.js';
7
+ export * from './deployment.js';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC"}
@@ -4,4 +4,5 @@ export * from './storage-keys.js';
4
4
  export * from './media.js';
5
5
  export * from './telemetry.js';
6
6
  export * from './scheduled-jobs.js';
7
+ export * from './deployment.js';
7
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC","sourcesContent":["export * from './business.js';\nexport * from './sports.js';\nexport * from './storage-keys.js';\nexport * from './media.js';\nexport * from './telemetry.js';\nexport * from './scheduled-jobs.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC","sourcesContent":["export * from './business.js';\nexport * from './sports.js';\nexport * from './storage-keys.js';\nexport * from './media.js';\nexport * from './telemetry.js';\nexport * from './scheduled-jobs.js';\nexport * from './deployment.js';\n"]}
@@ -15,7 +15,7 @@ export const STALE_PENDING_MEDIA_AGE_MS = 30 * MINUTE_MS;
15
15
  // A staged object this old with no `pending` or `processing` upload row is abandoned — never
16
16
  // accepted, consumed by a signing, or left by a finished upload — and the sweep deletes it.
17
17
  export const ABANDONED_STAGING_AGE_MS = 24 * HOUR_MS;
18
- // The maintenance sweep's per-run bounds (BACKEND-112). Each pass stops at its bound and the next
18
+ // The maintenance sweep's per-run bounds (RULE-STD-BACKEND-112). Each pass stops at its bound and the next
19
19
  // scheduled run continues.
20
20
  export const STALE_PENDING_MEDIA_PAGE_SIZE = 100;
21
21
  export const MAX_STALE_PENDING_MEDIA_PAGES_PER_RUN = 5;
@@ -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,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;AAElD,uGAAuG;AACvG,sGAAsG;AACtG,gCAAgC;AAChC,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,OAAO,CAAC;AAElD,8FAA8F;AAC9F,MAAM,CAAC,MAAM,gCAAgC,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\n// How far back an admin's upload tray follows their uploads: only rows accepted within this window are\n// listened to. Every unfinished upload is inside it, since the sweep fails one still unfinished after\n// `STALE_PENDING_MEDIA_AGE_MS`.\nexport const UPLOAD_TRAY_WINDOW_MS = 24 * HOUR_MS;\n\n// The most uploads one call marks seen in the tray: the finished rows the tray shows at once.\nexport const MAX_UPLOADS_MARKED_SEEN_PER_CALL = 200;\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,2GAA2G;AAC3G,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;AAElD,uGAAuG;AACvG,sGAAsG;AACtG,gCAAgC;AAChC,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,OAAO,CAAC;AAElD,8FAA8F;AAC9F,MAAM,CAAC,MAAM,gCAAgC,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 (RULE-STD-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\n// How far back an admin's upload tray follows their uploads: only rows accepted within this window are\n// listened to. Every unfinished upload is inside it, since the sweep fails one still unfinished after\n// `STALE_PENDING_MEDIA_AGE_MS`.\nexport const UPLOAD_TRAY_WINDOW_MS = 24 * HOUR_MS;\n\n// The most uploads one call marks seen in the tray: the finished rows the tray shows at once.\nexport const MAX_UPLOADS_MARKED_SEEN_PER_CALL = 200;\n"]}
@@ -1,5 +1,5 @@
1
1
  // Sport rule values that are persisted or validated on both sides of the boundary. What a sport
2
- // DOES with them — applying an event, recomputing a score — belongs to its adapter (SPORT-003).
2
+ // DOES with them — applying an event, recomputing a score — belongs to its adapter (RULE-QS-SPORT-903).
3
3
  import { BASKETBALL_ASSIST_EVENT_TYPE, BASKETBALL_BULK_EDIT_EVENT_TYPE, BASKETBALL_ONE_POINT_EVENT_TYPE, BASKETBALL_REBOUND_EVENT_TYPE, BASKETBALL_THREE_POINT_EVENT_TYPE, BASKETBALL_TIMEOUT_EVENT_TYPE, BASKETBALL_TWO_POINT_EVENT_TYPE, FOOTBALL_BULK_EDIT_EVENT_TYPE, FOOTBALL_ONE_POINT_EVENT_TYPE, FOOTBALL_SIX_POINT_EVENT_TYPE, FOOTBALL_TWO_POINT_EVENT_TYPE, } from '../types/sports.js';
4
4
  // Points a basketball stat event adds to its team's score. BULK_EDIT carries its own `value`.
5
5
  export const BASKETBALL_POINTS_BY_EVENT_TYPE = {
@@ -1 +1 @@
1
- {"version":3,"file":"sports.js","sourceRoot":"","sources":["../../src/constants/sports.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,gGAAgG;AAEhG,OAAO,EACL,4BAA4B,EAC5B,+BAA+B,EAC/B,+BAA+B,EAC/B,6BAA6B,EAC7B,iCAAiC,EACjC,6BAA6B,EAC7B,+BAA+B,EAC/B,6BAA6B,EAC7B,6BAA6B,EAC7B,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,oBAAoB,CAAC;AAa5B,8FAA8F;AAC9F,MAAM,CAAC,MAAM,+BAA+B,GAAG;IAC7C,CAAC,+BAA+B,CAAC,EAAE,CAAC;IACpC,CAAC,+BAA+B,CAAC,EAAE,CAAC;IACpC,CAAC,iCAAiC,CAAC,EAAE,CAAC;IACtC,CAAC,6BAA6B,CAAC,EAAE,CAAC;IAClC,CAAC,4BAA4B,CAAC,EAAE,CAAC;IACjC,CAAC,6BAA6B,CAAC,EAAE,CAAC;IAClC,CAAC,+BAA+B,CAAC,EAAE,CAAC;CACsB,CAAC;AAE7D,mGAAmG;AACnG,mFAAmF;AACnF,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,MAAM,EAAE,CAAC;IACT,QAAQ,EAAE,GAAG;IACb,OAAO,EAAE,CAAC;CACgD,CAAC;AAE7D,iFAAiF;AACjF,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,4DAA4D;AAC5D,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAE1C,mGAAmG;AACnG,uEAAuE;AACvE,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAE7C,qFAAqF;AACrF,MAAM,CAAC,MAAM,iCAAiC,GAAG,GAAG,CAAC;AAErD,kGAAkG;AAClG,MAAM,CAAC,MAAM,+BAA+B,GAAG,GAAG,CAAC;AAEnD,iGAAiG;AACjG,MAAM,CAAC,MAAM,kCAAkC,GAAG,GAAG,CAAC;AAEtD,yEAAyE;AACzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAE3C,qGAAqG;AACrG,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,CAAC,6BAA6B,CAAC,EAAE,CAAC;IAClC,CAAC,6BAA6B,CAAC,EAAE,CAAC;IAClC,CAAC,6BAA6B,CAAC,EAAE,CAAC;IAClC,CAAC,6BAA6B,CAAC,EAAE,CAAC;CACsB,CAAC;AAE3D,qGAAqG;AACrG,8EAA8E;AAC9E,MAAM,CAAC,MAAM,wBAAwB,GAAG,wCAAwC,CAAC;AAEjF,kGAAkG;AAClG,+BAA+B;AAC/B,MAAM,CAAC,MAAM,0BAA0B,GAAG,2CAA2C,CAAC;AAEtF,sEAAsE;AACtE,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,mBAAmB,EAAE,iDAAiD;IACtE,aAAa,EAAE,wBAAwB;IACvC,iBAAiB,EAAE,yDAAyD;IAC5E,mBAAmB,EAAE,kEAAkE;IACvF,gBAAgB,EACd,qFAAqF;IACvF,gBAAgB,EAAE,mDAAmD;IACrE,YAAY,EAAE,2CAA2C;IACzD,cAAc,EAAE,kCAAkC;IAClD,gBAAgB,EACd,mFAAmF;IACrF,SAAS,EAAE,sFAAsF;IACjG,eAAe,EACb,mFAAmF;IACrF,uBAAuB,EAAE,0CAA0C;IACnE,qBAAqB,EAAE,mEAAmE;IAC1F,aAAa,EACX,4FAA4F;CACvC,CAAC;AAE1D,qGAAqG;AACrG,oGAAoG;AACpG,+FAA+F;AAC/F,MAAM,CAAC,MAAM,4BAA4B,GAAG,WAAW,CAAC;AACxD,MAAM,CAAC,MAAM,iCAAiC,GAC5C,gHAAgH,CAAC;AAEnH,4DAA4D;AAC5D,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,eAAe,EACb,8GAA8G;IAChH,gBAAgB,EACd,gIAAgI;IAClI,eAAe,EACb,mJAAmJ;IACrJ,cAAc,EACZ,kHAAkH;IACpH,mBAAmB,EACjB,4HAA4H;IAC9H,eAAe,EAAE,2CAA2C;IAC5D,aAAa,EAAE,wCAAwC;IACvD,YAAY,EAAE,2CAA2C;CACL,CAAC;AAEvD,gGAAgG;AAChG,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,cAAc,EACZ,4HAA4H;IAC9H,kBAAkB,EAChB,yGAAyG;IAC3G,kBAAkB,EAChB,qIAAqI;CAC/E,CAAC;AAE3D,mGAAmG;AACnG,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,gBAAgB,EAAE,gEAAgE;CACrB,CAAC;AAEhE,iGAAiG;AACjG,kGAAkG;AAClG,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,CAAU,CAAC","sourcesContent":["// Sport rule values that are persisted or validated on both sides of the boundary. What a sport\n// DOES with them — applying an event, recomputing a score — belongs to its adapter (SPORT-003).\n\nimport {\n BASKETBALL_ASSIST_EVENT_TYPE,\n BASKETBALL_BULK_EDIT_EVENT_TYPE,\n BASKETBALL_ONE_POINT_EVENT_TYPE,\n BASKETBALL_REBOUND_EVENT_TYPE,\n BASKETBALL_THREE_POINT_EVENT_TYPE,\n BASKETBALL_TIMEOUT_EVENT_TYPE,\n BASKETBALL_TWO_POINT_EVENT_TYPE,\n FOOTBALL_BULK_EDIT_EVENT_TYPE,\n FOOTBALL_ONE_POINT_EVENT_TYPE,\n FOOTBALL_SIX_POINT_EVENT_TYPE,\n FOOTBALL_TWO_POINT_EVENT_TYPE,\n} from '../types/sports.js';\nimport type {\n BasketballPlayerStatKey,\n BasketballStatEventType,\n FootballStatEventType,\n} from '../types/sports.js';\nimport type {\n FinalizationNotReadyReason,\n GameRepairRefusal,\n StatEventRefusalCode,\n StatsIntegrityRefusal,\n} from '../types/stats.js';\n\n// Points a basketball stat event adds to its team's score. BULK_EDIT carries its own `value`.\nexport const BASKETBALL_POINTS_BY_EVENT_TYPE = {\n [BASKETBALL_ONE_POINT_EVENT_TYPE]: 1,\n [BASKETBALL_TWO_POINT_EVENT_TYPE]: 2,\n [BASKETBALL_THREE_POINT_EVENT_TYPE]: 3,\n [BASKETBALL_REBOUND_EVENT_TYPE]: 0,\n [BASKETBALL_ASSIST_EVENT_TYPE]: 0,\n [BASKETBALL_TIMEOUT_EVENT_TYPE]: 0,\n [BASKETBALL_BULK_EDIT_EVENT_TYPE]: 0,\n} as const satisfies Record<BasketballStatEventType, number>;\n\n// Basketball fantasy points (FP): each player counter times its weight. Every weight is a multiple\n// of one half, so FP summed across games equals FP of the summed counters exactly.\nexport const BASKETBALL_FANTASY_POINT_WEIGHTS = {\n points: 1,\n rebounds: 1.5,\n assists: 2,\n} as const satisfies Record<BasketballPlayerStatKey, number>;\n\n// A curling game is this many rounds; `roundScores` has exactly this many slots.\nexport const CURLING_ROUND_COUNT = 14;\n\n// A scored curling round is worth 1..MAX; a blank end is 0.\nexport const MAX_CURLING_ROUND_POINTS = 4;\n\n// The most events one scoring write may carry: each event is a document created in the write's one\n// transaction, and a Firestore transaction commits at most 500 writes.\nexport const MAX_STAT_EVENTS_PER_WRITE = 200;\n\n// The most lines and fields one Correct a game call may set; a game holds far fewer.\nexport const MAX_GAME_STATS_CORRECTION_TARGETS = 200;\n\n// The most destination changes one rebuild call lists in its answer; the audit events record all.\nexport const MAX_STATS_REPAIR_LISTED_CHANGES = 500;\n\n// The most findings one stats check keeps on its report; every finding is still counted by kind.\nexport const MAX_STATS_VALIDATION_DISCREPANCIES = 500;\n\n// The most events one page of a completed game's finalized record holds.\nexport const STAT_HISTORY_PAGE_SIZE = 1000;\n\n// Points a football stat event adds to its team's score. FOOTBALL_BULK_EDIT carries its own `value`.\nexport const FOOTBALL_POINTS_BY_EVENT_TYPE = {\n [FOOTBALL_SIX_POINT_EVENT_TYPE]: 6,\n [FOOTBALL_TWO_POINT_EVENT_TYPE]: 2,\n [FOOTBALL_ONE_POINT_EVENT_TYPE]: 1,\n [FOOTBALL_BULK_EDIT_EVENT_TYPE]: 0,\n} as const satisfies Record<FootballStatEventType, number>;\n\n// A game write's answer — a scoring event, a bulk-entry form, absence, the possession arrow, a ghost\n// added from the game — when the request names a team the game does not hold.\nexport const TEAM_NOT_IN_GAME_MESSAGE = 'That team is not playing in this game.';\n\n// A game write's answer — a bulk-entry row, absence, a memo — when the request names a person the\n// game's roster does not hold.\nexport const NOT_ON_GAME_ROSTER_MESSAGE = \"That player is not on this game's roster.\";\n\n// What the scorekeeper reads beside an event a scoring write refused.\nexport const STAT_EVENT_REFUSAL_COPY = {\n eventTypeNotInSport: \"That stat does not belong to this game's sport.\",\n teamNotInGame: TEAM_NOT_IN_GAME_MESSAGE,\n playerNotRostered: \"That player is not on this game's roster for that team.\",\n assisterNotRostered: \"The assisting player is not on this game's roster for that team.\",\n assistNotAllowed:\n 'Only a 2- or 3-point basket can be assisted, and never by the player who scored it.',\n playerNotAllowed: \"That stat is the team's; it cannot name a player.\",\n roundNotNext: 'That round is not the next one to record.',\n roundsComplete: 'Every round is already recorded.',\n bulkValueInvalid:\n 'That entry is not a real result: stats are whole numbers and never go below zero.',\n bulkStale: 'The numbers changed while you were editing. Check the flagged values and save again.',\n eventIdConflict:\n 'A different stat was already saved under this one. Discard it and enter it again.',\n voidTargetNotSportEvent: 'There is no recorded stat there to undo.',\n correctionNotAccepted: 'Corrections are made from Fix stats, not from the scoring screen.',\n undoBelowZero:\n 'Undoing that play would leave a stat below zero, because a later entry already lowered it.',\n} as const satisfies Record<StatEventRefusalCode, string>;\n\n// The operation every correction the game copy appends is recorded under, and the reason it carries.\n// The copy carries each recorded result exactly: where a game's recorded lines differ from what its\n// recorded plays replay to, one correction per field closes the difference, and these name it.\nexport const STATS_MIGRATION_OPERATION_ID = 'migration';\nexport const STATS_MIGRATION_CORRECTION_REASON =\n 'The recorded result differs from what its recorded plays add up to; this correction keeps the recorded result.';\n\n// What an admin reads when Fix stats refuses a game repair.\nexport const GAME_REPAIR_REFUSAL_COPY = {\n eventUnreadable:\n \"One of this game's recorded stats can no longer be read. Fix the event in the console, then repair the game.\",\n eventSequenceGap:\n \"This game's recorded stats are numbered with a gap or a repeat. Fix the event documents in the console, then restore the game.\",\n sourcesDisagree:\n \"This game's record, its stored numbers, and its recorded stats all disagree, so nothing says what was applied. Fix the game in the console first.\",\n scoreAmbiguous:\n \"The team's score was edited in two places to different values. Enter the score you want as a correction instead.\",\n fieldNotCorrectable:\n 'That number cannot be corrected for this sport. Restore the game from its recorded stats, or reopen it and score it again.',\n lineNotRostered: \"That player is not on this game's roster.\",\n teamNotInGame: 'That team is not playing in this game.',\n gameTooLarge: 'This repair is too large to save at once.',\n} as const satisfies Record<GameRepairRefusal, string>;\n\n// What the caller reads when a stats path refuses a game whose numbers changed outside the app.\nexport const STATS_INTEGRITY_REFUSAL_COPY = {\n statsOutOfBand:\n \"This game's stored numbers no longer match the stats recorded for it. An admin must reconcile the game in Fix stats first.\",\n finalRecordMissing:\n \"This finalized game's record of its stats is missing. An admin must repair the game in Fix stats first.\",\n aggregateBelowZero:\n 'A league, career, or hall-of-fame total holds less than this game added to it. An admin must rebuild the totals in Fix stats first.',\n} as const satisfies Record<StatsIntegrityRefusal, string>;\n\n// What the finish control shows while a game may not be finalized, and what finalize refuses with.\nexport const FINALIZATION_NOT_READY_COPY = {\n roundsIncomplete: 'Every round must be recorded before the game can be finalized.',\n} as const satisfies Record<FinalizationNotReadyReason, string>;\n\n// The hall of fame's minimum-games filter: each choice keeps the careers with at least that many\n// games played. None is chosen until the viewer picks one, so the hall of fame opens on everyone.\nexport const HALL_OF_FAME_MINIMUM_GAMES_CHOICES = [25, 50, 100] as const;\nexport type HallOfFameMinimumGames = (typeof HALL_OF_FAME_MINIMUM_GAMES_CHOICES)[number];\n"]}
1
+ {"version":3,"file":"sports.js","sourceRoot":"","sources":["../../src/constants/sports.ts"],"names":[],"mappings":"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","sourcesContent":["// Sport rule values that are persisted or validated on both sides of the boundary. What a sport\n// DOES with them — applying an event, recomputing a score — belongs to its adapter (RULE-QS-SPORT-903).\n\nimport {\n BASKETBALL_ASSIST_EVENT_TYPE,\n BASKETBALL_BULK_EDIT_EVENT_TYPE,\n BASKETBALL_ONE_POINT_EVENT_TYPE,\n BASKETBALL_REBOUND_EVENT_TYPE,\n BASKETBALL_THREE_POINT_EVENT_TYPE,\n BASKETBALL_TIMEOUT_EVENT_TYPE,\n BASKETBALL_TWO_POINT_EVENT_TYPE,\n FOOTBALL_BULK_EDIT_EVENT_TYPE,\n FOOTBALL_ONE_POINT_EVENT_TYPE,\n FOOTBALL_SIX_POINT_EVENT_TYPE,\n FOOTBALL_TWO_POINT_EVENT_TYPE,\n} from '../types/sports.js';\nimport type {\n BasketballPlayerStatKey,\n BasketballStatEventType,\n FootballStatEventType,\n} from '../types/sports.js';\nimport type {\n FinalizationNotReadyReason,\n GameRepairRefusal,\n StatEventRefusalCode,\n StatsIntegrityRefusal,\n} from '../types/stats.js';\n\n// Points a basketball stat event adds to its team's score. BULK_EDIT carries its own `value`.\nexport const BASKETBALL_POINTS_BY_EVENT_TYPE = {\n [BASKETBALL_ONE_POINT_EVENT_TYPE]: 1,\n [BASKETBALL_TWO_POINT_EVENT_TYPE]: 2,\n [BASKETBALL_THREE_POINT_EVENT_TYPE]: 3,\n [BASKETBALL_REBOUND_EVENT_TYPE]: 0,\n [BASKETBALL_ASSIST_EVENT_TYPE]: 0,\n [BASKETBALL_TIMEOUT_EVENT_TYPE]: 0,\n [BASKETBALL_BULK_EDIT_EVENT_TYPE]: 0,\n} as const satisfies Record<BasketballStatEventType, number>;\n\n// Basketball fantasy points (FP): each player counter times its weight. Every weight is a multiple\n// of one half, so FP summed across games equals FP of the summed counters exactly.\nexport const BASKETBALL_FANTASY_POINT_WEIGHTS = {\n points: 1,\n rebounds: 1.5,\n assists: 2,\n} as const satisfies Record<BasketballPlayerStatKey, number>;\n\n// A curling game is this many rounds; `roundScores` has exactly this many slots.\nexport const CURLING_ROUND_COUNT = 14;\n\n// A scored curling round is worth 1..MAX; a blank end is 0.\nexport const MAX_CURLING_ROUND_POINTS = 4;\n\n// The most events one scoring write may carry: each event is a document created in the write's one\n// transaction, and a Firestore transaction commits at most 500 writes.\nexport const MAX_STAT_EVENTS_PER_WRITE = 200;\n\n// The most lines and fields one Correct a game call may set; a game holds far fewer.\nexport const MAX_GAME_STATS_CORRECTION_TARGETS = 200;\n\n// The most destination changes one rebuild call lists in its answer; the audit events record all.\nexport const MAX_STATS_REPAIR_LISTED_CHANGES = 500;\n\n// The most findings one stats check keeps on its report; every finding is still counted by kind.\nexport const MAX_STATS_VALIDATION_DISCREPANCIES = 500;\n\n// The most events one page of a completed game's finalized record holds.\nexport const STAT_HISTORY_PAGE_SIZE = 1000;\n\n// Points a football stat event adds to its team's score. FOOTBALL_BULK_EDIT carries its own `value`.\nexport const FOOTBALL_POINTS_BY_EVENT_TYPE = {\n [FOOTBALL_SIX_POINT_EVENT_TYPE]: 6,\n [FOOTBALL_TWO_POINT_EVENT_TYPE]: 2,\n [FOOTBALL_ONE_POINT_EVENT_TYPE]: 1,\n [FOOTBALL_BULK_EDIT_EVENT_TYPE]: 0,\n} as const satisfies Record<FootballStatEventType, number>;\n\n// A game write's answer — a scoring event, a bulk-entry form, absence, the possession arrow, a ghost\n// added from the game — when the request names a team the game does not hold.\nexport const TEAM_NOT_IN_GAME_MESSAGE = 'That team is not playing in this game.';\n\n// A game write's answer — a bulk-entry row, absence, a memo — when the request names a person the\n// game's roster does not hold.\nexport const NOT_ON_GAME_ROSTER_MESSAGE = \"That player is not on this game's roster.\";\n\n// What the scorekeeper reads beside an event a scoring write refused.\nexport const STAT_EVENT_REFUSAL_COPY = {\n eventTypeNotInSport: \"That stat does not belong to this game's sport.\",\n teamNotInGame: TEAM_NOT_IN_GAME_MESSAGE,\n playerNotRostered: \"That player is not on this game's roster for that team.\",\n assisterNotRostered: \"The assisting player is not on this game's roster for that team.\",\n assistNotAllowed:\n 'Only a 2- or 3-point basket can be assisted, and never by the player who scored it.',\n playerNotAllowed: \"That stat is the team's; it cannot name a player.\",\n roundNotNext: 'That round is not the next one to record.',\n roundsComplete: 'Every round is already recorded.',\n bulkValueInvalid:\n 'That entry is not a real result: stats are whole numbers and never go below zero.',\n bulkStale: 'The numbers changed while you were editing. Check the flagged values and save again.',\n eventIdConflict:\n 'A different stat was already saved under this one. Discard it and enter it again.',\n voidTargetNotSportEvent: 'There is no recorded stat there to undo.',\n correctionNotAccepted: 'Corrections are made from Fix stats, not from the scoring screen.',\n undoBelowZero:\n 'Undoing that play would leave a stat below zero, because a later entry already lowered it.',\n} as const satisfies Record<StatEventRefusalCode, string>;\n\n// The operation every correction the game copy appends is recorded under, and the reason it carries.\n// The copy carries each recorded result exactly: where a game's recorded lines differ from what its\n// recorded plays replay to, one correction per field closes the difference, and these name it.\nexport const STATS_MIGRATION_OPERATION_ID = 'migration';\nexport const STATS_MIGRATION_CORRECTION_REASON =\n 'The recorded result differs from what its recorded plays add up to; this correction keeps the recorded result.';\n\n// What an admin reads when Fix stats refuses a game repair.\nexport const GAME_REPAIR_REFUSAL_COPY = {\n eventUnreadable:\n \"One of this game's recorded stats can no longer be read. Fix the event in the console, then repair the game.\",\n eventSequenceGap:\n \"This game's recorded stats are numbered with a gap or a repeat. Fix the event documents in the console, then restore the game.\",\n sourcesDisagree:\n \"This game's record, its stored numbers, and its recorded stats all disagree, so nothing says what was applied. Fix the game in the console first.\",\n scoreAmbiguous:\n \"The team's score was edited in two places to different values. Enter the score you want as a correction instead.\",\n fieldNotCorrectable:\n 'That number cannot be corrected for this sport. Restore the game from its recorded stats, or reopen it and score it again.',\n lineNotRostered: \"That player is not on this game's roster.\",\n teamNotInGame: 'That team is not playing in this game.',\n gameTooLarge: 'This repair is too large to save at once.',\n} as const satisfies Record<GameRepairRefusal, string>;\n\n// What the caller reads when a stats path refuses a game whose numbers changed outside the app.\nexport const STATS_INTEGRITY_REFUSAL_COPY = {\n statsOutOfBand:\n \"This game's stored numbers no longer match the stats recorded for it. An admin must reconcile the game in Fix stats first.\",\n finalRecordMissing:\n \"This finalized game's record of its stats is missing. An admin must repair the game in Fix stats first.\",\n aggregateBelowZero:\n 'A league, career, or hall-of-fame total holds less than this game added to it. An admin must rebuild the totals in Fix stats first.',\n} as const satisfies Record<StatsIntegrityRefusal, string>;\n\n// What the finish control shows while a game may not be finalized, and what finalize refuses with.\nexport const FINALIZATION_NOT_READY_COPY = {\n roundsIncomplete: 'Every round must be recorded before the game can be finalized.',\n} as const satisfies Record<FinalizationNotReadyReason, string>;\n\n// The hall of fame's minimum-games filter: each choice keeps the careers with at least that many\n// games played. None is chosen until the viewer picks one, so the hall of fame opens on everyone.\nexport const HALL_OF_FAME_MINIMUM_GAMES_CHOICES = [25, 50, 100] as const;\nexport type HallOfFameMinimumGames = (typeof HALL_OF_FAME_MINIMUM_GAMES_CHOICES)[number];\n"]}
@@ -1,11 +1,11 @@
1
- // The Q-Sports telemetry content policy (QUALITY-106): which keys of a monitoring event may carry
1
+ // The Q-Sports telemetry content policy (RULE-STD-QUALITY-106): which keys of a monitoring event may carry
2
2
  // text. monitoring-core's `createTelemetryContentPolicy` applies it, so the Functions backend and the
3
3
  // Next server and edge runtimes cut every event to the same allowlist. Plain data with no
4
- // monitoring-core import, so `q-core` binds no monitoring at all (QUALITY-102); the shape matches
4
+ // monitoring-core import, so `q-core` binds no monitoring at all (RULE-STD-QUALITY-102); the shape matches
5
5
  // that factory's options.
6
6
  //
7
7
  // Widening either list to a key that can hold prose, a stored document, a file name, or a storage or
8
- // document path breaks QUALITY-106: only identifiers, codes, and enum values may ride as text.
8
+ // document path breaks RULE-STD-QUALITY-106: only identifiers, codes, and enum values may ride as text.
9
9
  export const TELEMETRY_CONTENT_POLICY = {
10
10
  /** Keys whose text value is diagnostic: the descriptors the capture sites set. */
11
11
  diagnosticKeys: [
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/constants/telemetry.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,sGAAsG;AACtG,0FAA0F;AAC1F,kGAAkG;AAClG,0BAA0B;AAC1B,EAAE;AACF,qGAAqG;AACrG,+FAA+F;AAE/F,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,kFAAkF;IAClF,cAAc,EAAE;QACd,IAAI;QACJ,MAAM;QACN,WAAW;QACX,MAAM;QACN,MAAM;QACN,MAAM;QACN,QAAQ;QACR,OAAO;QACP,OAAO;QACP,MAAM;QACN,QAAQ;QACR,+FAA+F;QAC/F,uEAAuE;QACvE,OAAO;KACR;IACD,mGAAmG;IACnG,qBAAqB,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC;IAC3F,6GAA6G;IAC7G,gBAAgB,EAAE,CAAC,UAAU,CAAC;CACtB,CAAC","sourcesContent":["// The Q-Sports telemetry content policy (QUALITY-106): which keys of a monitoring event may carry\n// text. monitoring-core's `createTelemetryContentPolicy` applies it, so the Functions backend and the\n// Next server and edge runtimes cut every event to the same allowlist. Plain data with no\n// monitoring-core import, so `q-core` binds no monitoring at all (QUALITY-102); the shape matches\n// that factory's options.\n//\n// Widening either list to a key that can hold prose, a stored document, a file name, or a storage or\n// document path breaks QUALITY-106: only identifiers, codes, and enum values may ride as text.\n\nexport const TELEMETRY_CONTENT_POLICY = {\n /** Keys whose text value is diagnostic: the descriptors the capture sites set. */\n diagnosticKeys: [\n 'fn',\n 'kind',\n 'operation',\n 'step',\n 'pass',\n 'area',\n 'action',\n 'phase',\n 'stage',\n 'code',\n 'reason',\n // `captureException(error, { extra: {…} })` lands as an extra named `extra` (monitoring-core's\n // capture context): the container passes and its own keys are checked.\n 'extra',\n ],\n /** An identifier, code, or enum value named by its suffix (`pendingMediaId`, `auditEventType`). */\n diagnosticKeySuffixes: ['Id', 'Ids', 'Type', 'Kind', 'Status', 'Code', 'Outcome', 'Origin'],\n /** The context the capture sites name the running function in (`scope.setContext('function', { name })`). */\n codeNameContexts: ['function'],\n} as const;\n"]}
1
+ {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/constants/telemetry.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAC3G,sGAAsG;AACtG,0FAA0F;AAC1F,2GAA2G;AAC3G,0BAA0B;AAC1B,EAAE;AACF,qGAAqG;AACrG,wGAAwG;AAExG,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,kFAAkF;IAClF,cAAc,EAAE;QACd,IAAI;QACJ,MAAM;QACN,WAAW;QACX,MAAM;QACN,MAAM;QACN,MAAM;QACN,QAAQ;QACR,OAAO;QACP,OAAO;QACP,MAAM;QACN,QAAQ;QACR,+FAA+F;QAC/F,uEAAuE;QACvE,OAAO;KACR;IACD,mGAAmG;IACnG,qBAAqB,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC;IAC3F,6GAA6G;IAC7G,gBAAgB,EAAE,CAAC,UAAU,CAAC;CACtB,CAAC","sourcesContent":["// The Q-Sports telemetry content policy (RULE-STD-QUALITY-106): which keys of a monitoring event may carry\n// text. monitoring-core's `createTelemetryContentPolicy` applies it, so the Functions backend and the\n// Next server and edge runtimes cut every event to the same allowlist. Plain data with no\n// monitoring-core import, so `q-core` binds no monitoring at all (RULE-STD-QUALITY-102); the shape matches\n// that factory's options.\n//\n// Widening either list to a key that can hold prose, a stored document, a file name, or a storage or\n// document path breaks RULE-STD-QUALITY-106: only identifiers, codes, and enum values may ride as text.\n\nexport const TELEMETRY_CONTENT_POLICY = {\n /** Keys whose text value is diagnostic: the descriptors the capture sites set. */\n diagnosticKeys: [\n 'fn',\n 'kind',\n 'operation',\n 'step',\n 'pass',\n 'area',\n 'action',\n 'phase',\n 'stage',\n 'code',\n 'reason',\n // `captureException(error, { extra: {…} })` lands as an extra named `extra` (monitoring-core's\n // capture context): the container passes and its own keys are checked.\n 'extra',\n ],\n /** An identifier, code, or enum value named by its suffix (`pendingMediaId`, `auditEventType`). */\n diagnosticKeySuffixes: ['Id', 'Ids', 'Type', 'Kind', 'Status', 'Code', 'Outcome', 'Origin'],\n /** The context the capture sites name the running function in (`scope.setContext('function', { name })`). */\n codeNameContexts: ['function'],\n} as const;\n"]}
@@ -6,10 +6,10 @@
6
6
  // `NotificationDocSchema` and narrowed to Q-Sports' one shared category and its notification types.
7
7
  // The history, ledger, and job documents are written by notification-core's server helpers and by
8
8
  // the functions around them; their schemas are declared here because the package exports no schema
9
- // for them. A card, a history entry, and a ledger row each hold ids only, never a name (ARCH-103).
9
+ // for them. A card, a history entry, and a ledger row each hold ids only, never a name (RULE-STD-ARCH-103).
10
10
  //
11
11
  // `expireAt` is the one Firestore `Timestamp` any of them holds: a native-TTL policy honours only a
12
- // real Timestamp, and nothing reads the field for display or order (ARCH-105).
12
+ // real Timestamp, and nothing reads the field for display or order (RULE-STD-ARCH-105).
13
13
  import { z } from 'zod';
14
14
  import { NotificationDocSchema } from '@ttt-productions/notification-core';
15
15
  import { ADMIN_NOTIFICATION_CATEGORY, ADMIN_NOTIFICATION_TYPE_KEYS, NOTIFICATION_ARCHIVE_ALL_JOB_STATE_KEYS, NOTIFICATION_DELIVERY_PAYLOAD_VERSION, NOTIFICATION_DELIVERY_SKIP_REASON_KEYS, NOTIFICATION_DELIVERY_STATE_KEYS, NOTIFICATION_MATERIALIZATION_CLASS_KEYS, SHARED_NOTIFICATION_AUDIENCE_SCOPE, } from '../types/notifications.js';
@@ -115,7 +115,7 @@ export const NotificationDeliverySchema = z
115
115
  export const NotificationArchiveAllJobSchema = z.object({
116
116
  jobId: z.string().min(1),
117
117
  // The admin who asked. Each card the job archives is recorded as archived by them; the event the
118
- // request wrote when the job was created carries the request's origin (BACKEND-211).
118
+ // request wrote when the job was created carries the request's origin (RULE-STD-BACKEND-211).
119
119
  userId: userIdSchema,
120
120
  category: z.literal(ADMIN_NOTIFICATION_CATEGORY),
121
121
  state: z.enum(NOTIFICATION_ARCHIVE_ALL_JOB_STATE_KEYS),
@@ -1 +1 @@
1
- {"version":3,"file":"admin-notifications.js","sourceRoot":"","sources":["../../src/doc-schemas/admin-notifications.ts"],"names":[],"mappings":"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","sourcesContent":["// The admin notification documents: the shared cards, their archived history, the delivery ledger\n// the owning callables write occurrences into, and the archive-all jobs. Every one is written only by\n// Cloud Functions.\n//\n// notification-core owns the generic card shape, so the card schema is composed from its\n// `NotificationDocSchema` and narrowed to Q-Sports' one shared category and its notification types.\n// The history, ledger, and job documents are written by notification-core's server helpers and by\n// the functions around them; their schemas are declared here because the package exports no schema\n// for them. A card, a history entry, and a ledger row each hold ids only, never a name (ARCH-103).\n//\n// `expireAt` is the one Firestore `Timestamp` any of them holds: a native-TTL policy honours only a\n// real Timestamp, and nothing reads the field for display or order (ARCH-105).\n\nimport { z } from 'zod';\nimport { NotificationDocSchema } from '@ttt-productions/notification-core';\nimport {\n ADMIN_NOTIFICATION_CATEGORY,\n ADMIN_NOTIFICATION_TYPE_KEYS,\n NOTIFICATION_ARCHIVE_ALL_JOB_STATE_KEYS,\n NOTIFICATION_DELIVERY_PAYLOAD_VERSION,\n NOTIFICATION_DELIVERY_SKIP_REASON_KEYS,\n NOTIFICATION_DELIVERY_STATE_KEYS,\n NOTIFICATION_MATERIALIZATION_CLASS_KEYS,\n SHARED_NOTIFICATION_AUDIENCE_SCOPE,\n} from '../types/notifications.js';\nimport type { AdminNotificationType } from '../types/notifications.js';\nimport { countSchema, epochMsSchema, leagueIdSchema, teamIdSchema, userIdSchema } from './atoms.js';\nimport { FirestoreTimestampSchema } from './firestore-primitives.js';\n\nexport const adminNotificationTypeSchema = z.enum(ADMIN_NOTIFICATION_TYPE_KEYS);\n\n// What each type's card and ledger row name, by id. The card's title, link, and every name it shows\n// are derived from these at read time.\nexport const LeagueTeamCreatedNotificationMetadataSchema = z\n .object({ leagueId: leagueIdSchema, teamId: teamIdSchema })\n .strict();\nexport type LeagueTeamCreatedNotificationMetadata = z.infer<\n typeof LeagueTeamCreatedNotificationMetadataSchema\n>;\n\nexport const LeagueFreeAgentSignedUpNotificationMetadataSchema = z\n .object({ leagueId: leagueIdSchema, userId: userIdSchema })\n .strict();\nexport type LeagueFreeAgentSignedUpNotificationMetadata = z.infer<\n typeof LeagueFreeAgentSignedUpNotificationMetadataSchema\n>;\n\nexport const ADMIN_NOTIFICATION_METADATA_SCHEMAS = {\n leagueTeamCreated: LeagueTeamCreatedNotificationMetadataSchema,\n leagueFreeAgentSignedUp: LeagueFreeAgentSignedUpNotificationMetadataSchema,\n} as const satisfies Record<AdminNotificationType, z.ZodTypeAny>;\n\nexport type AdminNotificationMetadataByType = {\n [T in AdminNotificationType]: z.infer<(typeof ADMIN_NOTIFICATION_METADATA_SCHEMAS)[T]>;\n};\n\n/** Whether `metadata` is the metadata `type` declares. */\nexport function isAdminNotificationMetadata(\n type: AdminNotificationType,\n metadata: unknown,\n): boolean {\n return ADMIN_NOTIFICATION_METADATA_SCHEMAS[type].safeParse(metadata).success;\n}\n\nconst METADATA_MISMATCH = {\n message: \"The metadata is not the notification type's.\",\n path: ['metadata'],\n};\n\n// activeAdminNotifications/{notificationId} — one shared card, as notification-core stores it. The id\n// is notification-core's, hashed from the type and the aggregation key, and is not a field: a reader\n// is handed it beside the document. `seenAt` is the package's field: a shared card has no seen state,\n// so no admin marks it and it stays 0.\nexport const ActiveAdminNotificationSchema = NotificationDocSchema.omit({ id: true })\n .extend({\n type: adminNotificationTypeSchema,\n category: z.literal(ADMIN_NOTIFICATION_CATEGORY),\n targetUserId: z.null(),\n count: countSchema,\n latestActorIds: z.array(userIdSchema),\n seenAt: epochMsSchema,\n createdAt: epochMsSchema,\n updatedAt: epochMsSchema,\n })\n .refine((card) => isAdminNotificationMetadata(card.type, card.metadata), METADATA_MISMATCH);\nexport type ActiveAdminNotification = z.infer<typeof ActiveAdminNotificationSchema>;\n\n// adminNotificationHistory/{historyId} — an archived card: the card as it stood when archived, kept\n// whole under `archivedSnapshot`, with who archived it and the archive's replay facts.\nexport const AdminNotificationHistorySchema = z.object({\n archiveOccurrenceId: z.string().min(1),\n requestId: z.string().min(1),\n // The archive's replay key: the card and the generation the archiving admin saw.\n payloadHash: z.string().min(1),\n activeId: z.string().min(1),\n observedActivityGeneration: z.string().min(1),\n category: z.literal(ADMIN_NOTIFICATION_CATEGORY),\n audienceScope: z.literal(SHARED_NOTIFICATION_AUDIENCE_SCOPE),\n archivedSnapshot: ActiveAdminNotificationSchema,\n archivedAt: epochMsSchema,\n expireAt: FirestoreTimestampSchema,\n handledBy: userIdSchema,\n});\nexport type AdminNotificationHistory = z.infer<typeof AdminNotificationHistorySchema>;\n\nexport const NotificationDeliveryPayloadSchema = z.object({\n // Who did what the occurrence records; null for an occurrence with no acting account.\n actorId: userIdSchema.nullable(),\n metadata: z.record(z.string(), z.unknown()),\n occurrenceAt: epochMsSchema,\n});\nexport type NotificationDeliveryPayload = z.infer<typeof NotificationDeliveryPayloadSchema>;\n\n// notificationDeliveries/{deliveryId} — one occurrence: the queue row the worker materializes and the\n// record that it happened, in one document, so the occurrence and its delivery cannot come apart.\n// Written in the owning callable's own transaction. A queued or dead-lettered row never expires:\n// notification-core sets `expireAt` only when the row materializes or is skipped, and an admin's\n// replay of a dead-lettered row sets it back to null. A row is skipped only when it names a recipient\n// who may no longer receive, which no Q-Sports row does; the skip's fields are declared because the\n// package writes them.\nexport const NotificationDeliverySchema = z\n .object({\n deliveryId: z.string().min(1),\n state: z.enum(NOTIFICATION_DELIVERY_STATE_KEYS),\n notificationType: adminNotificationTypeSchema,\n eventId: z.string().min(1),\n // Every Q-Sports notification is shared, so no row names a recipient.\n recipientUid: z.null(),\n aggregationKey: z.string().min(1),\n strategy: z.literal('increment'),\n payload: NotificationDeliveryPayloadSchema,\n payloadVersion: z.literal(NOTIFICATION_DELIVERY_PAYLOAD_VERSION),\n materializationClass: z.enum(NOTIFICATION_MATERIALIZATION_CLASS_KEYS),\n createdAt: epochMsSchema,\n attemptCount: countSchema,\n nextAttemptAt: epochMsSchema,\n lastError: z.string().nullable(),\n materializedAt: epochMsSchema.nullable(),\n deadLetteredAt: epochMsSchema.nullable(),\n skipReason: z.enum(NOTIFICATION_DELIVERY_SKIP_REASON_KEYS).optional(),\n skippedAt: epochMsSchema.optional(),\n expireAt: FirestoreTimestampSchema.nullable().optional(),\n })\n .refine((row) => isAdminNotificationMetadata(row.notificationType, row.payload.metadata), {\n message: METADATA_MISMATCH.message,\n path: ['payload', 'metadata'],\n });\nexport type NotificationDelivery = z.infer<typeof NotificationDeliverySchema>;\n\n// notificationArchiveAllJobs/{jobId} — one admin's \"archive all\": the server drains every active card\n// in pages until none is left, whether or not that admin's browser is still open. Backend-only: the\n// admin learns its state through the status callable. A completed job expires; one that is still\n// running, stopped short, or failed never does.\nexport const NotificationArchiveAllJobSchema = z.object({\n jobId: z.string().min(1),\n // The admin who asked. Each card the job archives is recorded as archived by them; the event the\n // request wrote when the job was created carries the request's origin (BACKEND-211).\n userId: userIdSchema,\n category: z.literal(ADMIN_NOTIFICATION_CATEGORY),\n state: z.enum(NOTIFICATION_ARCHIVE_ALL_JOB_STATE_KEYS),\n // Moves on every claim, so an overlapping run that claimed an older revision cannot settle the job.\n revision: countSchema,\n archived: countSchema,\n // The cards the COMPLETING drain could not archive — each failed on its own and is still in the\n // list. Set (never summed) when the job completes, because that drain walked the whole active set\n // once. Optional, never defaulted: absent until a job completes with cards left behind, read as 0.\n notArchived: countSchema.optional(),\n error: z.string().nullable(),\n attemptCount: countSchema,\n nextAttemptAt: epochMsSchema,\n deadLettered: z.boolean(),\n createdAt: epochMsSchema,\n updatedAt: epochMsSchema,\n completedAt: epochMsSchema.nullable(),\n deadLetteredAt: epochMsSchema.nullable(),\n expireAt: FirestoreTimestampSchema.optional(),\n});\nexport type NotificationArchiveAllJob = z.infer<typeof NotificationArchiveAllJobSchema>;\n"]}
1
+ {"version":3,"file":"admin-notifications.js","sourceRoot":"","sources":["../../src/doc-schemas/admin-notifications.ts"],"names":[],"mappings":"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","sourcesContent":["// The admin notification documents: the shared cards, their archived history, the delivery ledger\n// the owning callables write occurrences into, and the archive-all jobs. Every one is written only by\n// Cloud Functions.\n//\n// notification-core owns the generic card shape, so the card schema is composed from its\n// `NotificationDocSchema` and narrowed to Q-Sports' one shared category and its notification types.\n// The history, ledger, and job documents are written by notification-core's server helpers and by\n// the functions around them; their schemas are declared here because the package exports no schema\n// for them. A card, a history entry, and a ledger row each hold ids only, never a name (RULE-STD-ARCH-103).\n//\n// `expireAt` is the one Firestore `Timestamp` any of them holds: a native-TTL policy honours only a\n// real Timestamp, and nothing reads the field for display or order (RULE-STD-ARCH-105).\n\nimport { z } from 'zod';\nimport { NotificationDocSchema } from '@ttt-productions/notification-core';\nimport {\n ADMIN_NOTIFICATION_CATEGORY,\n ADMIN_NOTIFICATION_TYPE_KEYS,\n NOTIFICATION_ARCHIVE_ALL_JOB_STATE_KEYS,\n NOTIFICATION_DELIVERY_PAYLOAD_VERSION,\n NOTIFICATION_DELIVERY_SKIP_REASON_KEYS,\n NOTIFICATION_DELIVERY_STATE_KEYS,\n NOTIFICATION_MATERIALIZATION_CLASS_KEYS,\n SHARED_NOTIFICATION_AUDIENCE_SCOPE,\n} from '../types/notifications.js';\nimport type { AdminNotificationType } from '../types/notifications.js';\nimport { countSchema, epochMsSchema, leagueIdSchema, teamIdSchema, userIdSchema } from './atoms.js';\nimport { FirestoreTimestampSchema } from './firestore-primitives.js';\n\nexport const adminNotificationTypeSchema = z.enum(ADMIN_NOTIFICATION_TYPE_KEYS);\n\n// What each type's card and ledger row name, by id. The card's title, link, and every name it shows\n// are derived from these at read time.\nexport const LeagueTeamCreatedNotificationMetadataSchema = z\n .object({ leagueId: leagueIdSchema, teamId: teamIdSchema })\n .strict();\nexport type LeagueTeamCreatedNotificationMetadata = z.infer<\n typeof LeagueTeamCreatedNotificationMetadataSchema\n>;\n\nexport const LeagueFreeAgentSignedUpNotificationMetadataSchema = z\n .object({ leagueId: leagueIdSchema, userId: userIdSchema })\n .strict();\nexport type LeagueFreeAgentSignedUpNotificationMetadata = z.infer<\n typeof LeagueFreeAgentSignedUpNotificationMetadataSchema\n>;\n\nexport const ADMIN_NOTIFICATION_METADATA_SCHEMAS = {\n leagueTeamCreated: LeagueTeamCreatedNotificationMetadataSchema,\n leagueFreeAgentSignedUp: LeagueFreeAgentSignedUpNotificationMetadataSchema,\n} as const satisfies Record<AdminNotificationType, z.ZodTypeAny>;\n\nexport type AdminNotificationMetadataByType = {\n [T in AdminNotificationType]: z.infer<(typeof ADMIN_NOTIFICATION_METADATA_SCHEMAS)[T]>;\n};\n\n/** Whether `metadata` is the metadata `type` declares. */\nexport function isAdminNotificationMetadata(\n type: AdminNotificationType,\n metadata: unknown,\n): boolean {\n return ADMIN_NOTIFICATION_METADATA_SCHEMAS[type].safeParse(metadata).success;\n}\n\nconst METADATA_MISMATCH = {\n message: \"The metadata is not the notification type's.\",\n path: ['metadata'],\n};\n\n// activeAdminNotifications/{notificationId} — one shared card, as notification-core stores it. The id\n// is notification-core's, hashed from the type and the aggregation key, and is not a field: a reader\n// is handed it beside the document. `seenAt` is the package's field: a shared card has no seen state,\n// so no admin marks it and it stays 0.\nexport const ActiveAdminNotificationSchema = NotificationDocSchema.omit({ id: true })\n .extend({\n type: adminNotificationTypeSchema,\n category: z.literal(ADMIN_NOTIFICATION_CATEGORY),\n targetUserId: z.null(),\n count: countSchema,\n latestActorIds: z.array(userIdSchema),\n seenAt: epochMsSchema,\n createdAt: epochMsSchema,\n updatedAt: epochMsSchema,\n })\n .refine((card) => isAdminNotificationMetadata(card.type, card.metadata), METADATA_MISMATCH);\nexport type ActiveAdminNotification = z.infer<typeof ActiveAdminNotificationSchema>;\n\n// adminNotificationHistory/{historyId} — an archived card: the card as it stood when archived, kept\n// whole under `archivedSnapshot`, with who archived it and the archive's replay facts.\nexport const AdminNotificationHistorySchema = z.object({\n archiveOccurrenceId: z.string().min(1),\n requestId: z.string().min(1),\n // The archive's replay key: the card and the generation the archiving admin saw.\n payloadHash: z.string().min(1),\n activeId: z.string().min(1),\n observedActivityGeneration: z.string().min(1),\n category: z.literal(ADMIN_NOTIFICATION_CATEGORY),\n audienceScope: z.literal(SHARED_NOTIFICATION_AUDIENCE_SCOPE),\n archivedSnapshot: ActiveAdminNotificationSchema,\n archivedAt: epochMsSchema,\n expireAt: FirestoreTimestampSchema,\n handledBy: userIdSchema,\n});\nexport type AdminNotificationHistory = z.infer<typeof AdminNotificationHistorySchema>;\n\nexport const NotificationDeliveryPayloadSchema = z.object({\n // Who did what the occurrence records; null for an occurrence with no acting account.\n actorId: userIdSchema.nullable(),\n metadata: z.record(z.string(), z.unknown()),\n occurrenceAt: epochMsSchema,\n});\nexport type NotificationDeliveryPayload = z.infer<typeof NotificationDeliveryPayloadSchema>;\n\n// notificationDeliveries/{deliveryId} — one occurrence: the queue row the worker materializes and the\n// record that it happened, in one document, so the occurrence and its delivery cannot come apart.\n// Written in the owning callable's own transaction. A queued or dead-lettered row never expires:\n// notification-core sets `expireAt` only when the row materializes or is skipped, and an admin's\n// replay of a dead-lettered row sets it back to null. A row is skipped only when it names a recipient\n// who may no longer receive, which no Q-Sports row does; the skip's fields are declared because the\n// package writes them.\nexport const NotificationDeliverySchema = z\n .object({\n deliveryId: z.string().min(1),\n state: z.enum(NOTIFICATION_DELIVERY_STATE_KEYS),\n notificationType: adminNotificationTypeSchema,\n eventId: z.string().min(1),\n // Every Q-Sports notification is shared, so no row names a recipient.\n recipientUid: z.null(),\n aggregationKey: z.string().min(1),\n strategy: z.literal('increment'),\n payload: NotificationDeliveryPayloadSchema,\n payloadVersion: z.literal(NOTIFICATION_DELIVERY_PAYLOAD_VERSION),\n materializationClass: z.enum(NOTIFICATION_MATERIALIZATION_CLASS_KEYS),\n createdAt: epochMsSchema,\n attemptCount: countSchema,\n nextAttemptAt: epochMsSchema,\n lastError: z.string().nullable(),\n materializedAt: epochMsSchema.nullable(),\n deadLetteredAt: epochMsSchema.nullable(),\n skipReason: z.enum(NOTIFICATION_DELIVERY_SKIP_REASON_KEYS).optional(),\n skippedAt: epochMsSchema.optional(),\n expireAt: FirestoreTimestampSchema.nullable().optional(),\n })\n .refine((row) => isAdminNotificationMetadata(row.notificationType, row.payload.metadata), {\n message: METADATA_MISMATCH.message,\n path: ['payload', 'metadata'],\n });\nexport type NotificationDelivery = z.infer<typeof NotificationDeliverySchema>;\n\n// notificationArchiveAllJobs/{jobId} — one admin's \"archive all\": the server drains every active card\n// in pages until none is left, whether or not that admin's browser is still open. Backend-only: the\n// admin learns its state through the status callable. A completed job expires; one that is still\n// running, stopped short, or failed never does.\nexport const NotificationArchiveAllJobSchema = z.object({\n jobId: z.string().min(1),\n // The admin who asked. Each card the job archives is recorded as archived by them; the event the\n // request wrote when the job was created carries the request's origin (RULE-STD-BACKEND-211).\n userId: userIdSchema,\n category: z.literal(ADMIN_NOTIFICATION_CATEGORY),\n state: z.enum(NOTIFICATION_ARCHIVE_ALL_JOB_STATE_KEYS),\n // Moves on every claim, so an overlapping run that claimed an older revision cannot settle the job.\n revision: countSchema,\n archived: countSchema,\n // The cards the COMPLETING drain could not archive — each failed on its own and is still in the\n // list. Set (never summed) when the job completes, because that drain walked the whole active set\n // once. Optional, never defaulted: absent until a job completes with cards left behind, read as 0.\n notArchived: countSchema.optional(),\n error: z.string().nullable(),\n attemptCount: countSchema,\n nextAttemptAt: epochMsSchema,\n deadLettered: z.boolean(),\n createdAt: epochMsSchema,\n updatedAt: epochMsSchema,\n completedAt: epochMsSchema.nullable(),\n deadLetteredAt: epochMsSchema.nullable(),\n expireAt: FirestoreTimestampSchema.optional(),\n});\nexport type NotificationArchiveAllJob = z.infer<typeof NotificationArchiveAllJobSchema>;\n"]}
@@ -1,5 +1,5 @@
1
1
  // The aggregate tier of the stats model: every league-wide and career number, each a derived cache
2
- // of the completed games' finalized records (SPORT-201). None is ever written by a client.
2
+ // of the completed games' finalized records (RULE-QS-SPORT-921). None is ever written by a client.
3
3
  //
4
4
  // leagueTeams/{teamId}.seasonStats the team's standings line
5
5
  // leagueTeams/{teamId}.playerSeasonStats.{userId} one membership's row — the team's league and
@@ -12,7 +12,7 @@
12
12
  // holding lines of those kinds is built from it — the game's `stats` (`SPORT_GAME_STATS_SCHEMAS`),
13
13
  // the season lines, the membership rows, the lifetime lines, and the hall-of-fame entries. A
14
14
  // document on its own does not say which sport it counts, so a stored line is read as one sport's
15
- // line only through that sport's adapter, never by its shape (SPORT-002). The lines are strict, so a
15
+ // line only through that sport's adapter, never by its shape (RULE-QS-SPORT-902). The lines are strict, so a
16
16
  // line from one sport is never mistaken for another's with fields silently dropped.
17
17
  import { z } from 'zod';
18
18
  import { LEAGUE_TYPE_KEYS, SPORT_KEY_BY_LEAGUE_TYPE } from '../types/sports.js';
@@ -91,7 +91,7 @@ export const UserLifetimeStatsSchema = z.strictObject(Object.fromEntries(LEAGUE_
91
91
  SPORT_LINE_SCHEMAS[leagueType].lifetime.optional(),
92
92
  ])));
93
93
  // `sportHallOfFame/{sportKey}` — kept only for a sport whose adapter keeps a hall of fame. Real users
94
- // only (SPORT-208); an entry equals that user's lifetime line for the sport.
94
+ // only (RULE-QS-SPORT-928); an entry equals that user's lifetime line for the sport.
95
95
  export const SportHallOfFameSchema = z.object({
96
96
  playerStats: z.record(userIdSchema, lineOfAnySport('hallOfFame')),
97
97
  updatedAt: epochMsSchema,
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate-stats.js","sourceRoot":"","sources":["../../src/doc-schemas/aggregate-stats.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,2FAA2F;AAC3F,EAAE;AACF,kFAAkF;AAClF,qGAAqG;AACrG,kGAAkG;AAClG,8FAA8F;AAC9F,uFAAuF;AACvF,EAAE;AACF,gGAAgG;AAChG,iGAAiG;AACjG,mGAAmG;AACnG,6FAA6F;AAC7F,kGAAkG;AAClG,qGAAqG;AACrG,oFAAoF;AAEpF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAGhF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAEzB,gGAAgG;AAChG,oBAAoB;AACpB,gGAAgG;AAEhG,gGAAgG;AAChG,MAAM,mBAAmB,GAAG;IAC1B,WAAW,EAAE,WAAW;IACxB,IAAI,EAAE,WAAW;IACjB,MAAM,EAAE,WAAW;IACnB,IAAI,EAAE,WAAW;IACjB,SAAS,EAAE,WAAW;IACtB,aAAa,EAAE,WAAW;CAC3B,CAAC;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC;AAG7E,gGAAgG;AAChG,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAC,YAAY,CAAC;IAC5D,GAAG,mBAAmB;IACtB,QAAQ,EAAE,WAAW;IACrB,OAAO,EAAE,WAAW;CACrB,CAAC,CAAC;AAGH,gGAAgG;AAChG,8FAA8F;AAC9F,gGAAgG;AAEhG,6DAA6D;AAC7D,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,YAAY,CAAC;IACnD,WAAW,EAAE,WAAW;CACzB,CAAC,CAAC;AAGH,sEAAsE;AACtE,MAAM,CAAC,MAAM,4BAA4B,GAAG,2BAA2B,CAAC,MAAM,CAAC;IAC7E,WAAW,EAAE,WAAW;CACzB,CAAC,CAAC;AAGH,gGAAgG;AAChG,+BAA+B;AAC/B,gGAAgG;AAEhG,oGAAoG;AACpG,mBAAmB;AACnB,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,UAAU,EAAE;QACV,UAAU,EAAE,2BAA2B;QACvC,aAAa,EAAE,4BAA4B;QAC3C,QAAQ,EAAE,4BAA4B;QACtC,UAAU,EAAE,4BAA4B;QACxC,QAAQ,EAAE,yBAAyB;QACnC,UAAU,EAAE,+BAA+B;KAC5C;IACD,OAAO,EAAE;QACP,UAAU,EAAE,CAAC,CAAC,KAAK,EAAE;QACrB,aAAa,EAAE,sBAAsB;QACrC,QAAQ,EAAE,sBAAsB;QAChC,UAAU,EAAE,CAAC,CAAC,KAAK,EAAE;QACrB,QAAQ,EAAE,sBAAsB;QAChC,UAAU,EAAE,yBAAyB;KACtC;IACD,QAAQ,EAAE;QACR,UAAU,EAAE,CAAC,CAAC,KAAK,EAAE;QACrB,aAAa,EAAE,sBAAsB;QACrC,QAAQ,EAAE,sBAAsB;QAChC,UAAU,EAAE,CAAC,CAAC,KAAK,EAAE;QACrB,QAAQ,EAAE,uBAAuB;QACjC,UAAU,EAAE,yBAAyB;KACtC;CACqE,CAAC;AAEzE,wDAAwD;AACxD,SAAS,cAAc,CAAyB,IAAO;IACrD,OAAO,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC7F,CAAC;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;AAGlE,oFAAoF;AACpF,MAAM,CAAC,MAAM,uBAAuB,GAAG,cAAc,CAAC,eAAe,CAAC,CAAC;AAWvE,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,YAAY,CACnD,MAAM,CAAC,WAAW,CAChB,gBAAgB,CAAC,GAAG,CAClB,CAAC,UAAU,EAAE,EAAE,CACb;IACE,wBAAwB,CAAC,UAAU,CAAC;IACpC,kBAAkB,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE;CAC1C,CACb,CACoB,CACxB,CAAC;AAGF,sGAAsG;AACtG,6EAA6E;AAC7E,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,YAAY,CAAC,CAAC;IACjE,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC","sourcesContent":["// The aggregate tier of the stats model: every league-wide and career number, each a derived cache\n// of the completed games' finalized records (SPORT-201). None is ever written by a client.\n//\n// leagueTeams/{teamId}.seasonStats the team's standings line\n// leagueTeams/{teamId}.playerSeasonStats.{userId} one membership's row — the team's league and\n// the key name the membership it belongs to\n// userProfiles/{userId}.lifetimeStats.{sportKey} a real player's career line per sport\n// sportHallOfFame/{sportKey} a sport's all-time leaderboard\n//\n// Which schema each sport's line of each kind uses — game lines included — is declared once, in\n// `SPORT_LINE_SCHEMAS`: the sport's adapter reads its lines through it, and every document field\n// holding lines of those kinds is built from it — the game's `stats` (`SPORT_GAME_STATS_SCHEMAS`),\n// the season lines, the membership rows, the lifetime lines, and the hall-of-fame entries. A\n// document on its own does not say which sport it counts, so a stored line is read as one sport's\n// line only through that sport's adapter, never by its shape (SPORT-002). The lines are strict, so a\n// line from one sport is never mistaken for another's with fields silently dropped.\n\nimport { z } from 'zod';\nimport { LEAGUE_TYPE_KEYS, SPORT_KEY_BY_LEAGUE_TYPE } from '../types/sports.js';\nimport type { LeagueType } from '../types/sports.js';\nimport type { StatLineKind } from '../types/stats.js';\nimport { countSchema, epochMsSchema, userIdSchema } from './atoms.js';\nimport {\n BasketballPlayerStatsSchema,\n BasketballTeamStatsSchema,\n CurlingTeamStatsSchema,\n FootballTeamStatsSchema,\n} from './game-stats.js';\n\n// ---------------------------------------------------------------------------------------------\n// Team season lines\n// ---------------------------------------------------------------------------------------------\n\n// The standings base every sport's season line carries. The point differential derives at read.\nconst teamSeasonBaseShape = {\n gamesPlayed: countSchema,\n wins: countSchema,\n losses: countSchema,\n ties: countSchema,\n pointsFor: countSchema,\n pointsAgainst: countSchema,\n};\n\nexport const TeamSeasonBaseStatsSchema = z.strictObject(teamSeasonBaseShape);\nexport type TeamSeasonBaseStats = z.infer<typeof TeamSeasonBaseStatsSchema>;\n\n// Rebounds and assists are summed from the team's players' game lines; no team line carries FP.\nexport const BasketballTeamSeasonStatsSchema = z.strictObject({\n ...teamSeasonBaseShape,\n rebounds: countSchema,\n assists: countSchema,\n});\nexport type BasketballTeamSeasonStats = z.infer<typeof BasketballTeamSeasonStatsSchema>;\n\n// ---------------------------------------------------------------------------------------------\n// Player lines that carry games played: membership rows, lifetime lines, hall-of-fame entries\n// ---------------------------------------------------------------------------------------------\n\n// A sport with no individual stats counts games played only.\nexport const GamesPlayedStatsSchema = z.strictObject({\n gamesPlayed: countSchema,\n});\nexport type GamesPlayedStats = z.infer<typeof GamesPlayedStatsSchema>;\n\n// The basketball game line — its counters and FP — plus games played.\nexport const BasketballPlayerTotalsSchema = BasketballPlayerStatsSchema.extend({\n gamesPlayed: countSchema,\n});\nexport type BasketballPlayerTotals = z.infer<typeof BasketballPlayerTotalsSchema>;\n\n// ---------------------------------------------------------------------------------------------\n// Every sport's lines, by kind\n// ---------------------------------------------------------------------------------------------\n\n// A kind a sport keeps no line of is `z.never()`: curling and football keep no player game line and\n// no hall of fame.\nexport const SPORT_LINE_SCHEMAS = {\n Basketball: {\n gamePlayer: BasketballPlayerStatsSchema,\n membershipRow: BasketballPlayerTotalsSchema,\n lifetime: BasketballPlayerTotalsSchema,\n hallOfFame: BasketballPlayerTotalsSchema,\n gameTeam: BasketballTeamStatsSchema,\n teamSeason: BasketballTeamSeasonStatsSchema,\n },\n Curling: {\n gamePlayer: z.never(),\n membershipRow: GamesPlayedStatsSchema,\n lifetime: GamesPlayedStatsSchema,\n hallOfFame: z.never(),\n gameTeam: CurlingTeamStatsSchema,\n teamSeason: TeamSeasonBaseStatsSchema,\n },\n Football: {\n gamePlayer: z.never(),\n membershipRow: GamesPlayedStatsSchema,\n lifetime: GamesPlayedStatsSchema,\n hallOfFame: z.never(),\n gameTeam: FootballTeamStatsSchema,\n teamSeason: TeamSeasonBaseStatsSchema,\n },\n} as const satisfies Record<LeagueType, Record<StatLineKind, z.ZodType>>;\n\n// A stored line of one kind, whichever sport it counts.\nfunction lineOfAnySport<K extends StatLineKind>(kind: K) {\n return z.union(LEAGUE_TYPE_KEYS.map((leagueType) => SPORT_LINE_SCHEMAS[leagueType][kind]));\n}\n\nexport const TeamSeasonStatsSchema = lineOfAnySport('teamSeason');\nexport type TeamSeasonStats = z.infer<typeof TeamSeasonStatsSchema>;\n\n// A row exists because a game roster named the player, and outlives the membership.\nexport const PlayerSeasonStatsSchema = lineOfAnySport('membershipRow');\nexport type PlayerSeasonStats = z.infer<typeof PlayerSeasonStatsSchema>;\n\n// Keyed by sport key, each sport's lifetime line. A sport the user never played is absent, not\n// zeroed. Ghosts never hold one (SPORT-208).\ntype LifetimeLinesShape = {\n [L in LeagueType as (typeof SPORT_KEY_BY_LEAGUE_TYPE)[L]]: z.ZodOptional<\n (typeof SPORT_LINE_SCHEMAS)[L]['lifetime']\n >;\n};\n\nexport const UserLifetimeStatsSchema = z.strictObject(\n Object.fromEntries(\n LEAGUE_TYPE_KEYS.map(\n (leagueType) =>\n [\n SPORT_KEY_BY_LEAGUE_TYPE[leagueType],\n SPORT_LINE_SCHEMAS[leagueType].lifetime.optional(),\n ] as const,\n ),\n ) as LifetimeLinesShape,\n);\nexport type UserLifetimeStats = z.infer<typeof UserLifetimeStatsSchema>;\n\n// `sportHallOfFame/{sportKey}` — kept only for a sport whose adapter keeps a hall of fame. Real users\n// only (SPORT-208); an entry equals that user's lifetime line for the sport.\nexport const SportHallOfFameSchema = z.object({\n playerStats: z.record(userIdSchema, lineOfAnySport('hallOfFame')),\n updatedAt: epochMsSchema,\n});\nexport type SportHallOfFame = z.infer<typeof SportHallOfFameSchema>;\n"]}
1
+ {"version":3,"file":"aggregate-stats.js","sourceRoot":"","sources":["../../src/doc-schemas/aggregate-stats.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,mGAAmG;AACnG,EAAE;AACF,kFAAkF;AAClF,qGAAqG;AACrG,kGAAkG;AAClG,8FAA8F;AAC9F,uFAAuF;AACvF,EAAE;AACF,gGAAgG;AAChG,iGAAiG;AACjG,mGAAmG;AACnG,6FAA6F;AAC7F,kGAAkG;AAClG,6GAA6G;AAC7G,oFAAoF;AAEpF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAGhF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAEzB,gGAAgG;AAChG,oBAAoB;AACpB,gGAAgG;AAEhG,gGAAgG;AAChG,MAAM,mBAAmB,GAAG;IAC1B,WAAW,EAAE,WAAW;IACxB,IAAI,EAAE,WAAW;IACjB,MAAM,EAAE,WAAW;IACnB,IAAI,EAAE,WAAW;IACjB,SAAS,EAAE,WAAW;IACtB,aAAa,EAAE,WAAW;CAC3B,CAAC;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC;AAG7E,gGAAgG;AAChG,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAC,YAAY,CAAC;IAC5D,GAAG,mBAAmB;IACtB,QAAQ,EAAE,WAAW;IACrB,OAAO,EAAE,WAAW;CACrB,CAAC,CAAC;AAGH,gGAAgG;AAChG,8FAA8F;AAC9F,gGAAgG;AAEhG,6DAA6D;AAC7D,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,YAAY,CAAC;IACnD,WAAW,EAAE,WAAW;CACzB,CAAC,CAAC;AAGH,sEAAsE;AACtE,MAAM,CAAC,MAAM,4BAA4B,GAAG,2BAA2B,CAAC,MAAM,CAAC;IAC7E,WAAW,EAAE,WAAW;CACzB,CAAC,CAAC;AAGH,gGAAgG;AAChG,+BAA+B;AAC/B,gGAAgG;AAEhG,oGAAoG;AACpG,mBAAmB;AACnB,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,UAAU,EAAE;QACV,UAAU,EAAE,2BAA2B;QACvC,aAAa,EAAE,4BAA4B;QAC3C,QAAQ,EAAE,4BAA4B;QACtC,UAAU,EAAE,4BAA4B;QACxC,QAAQ,EAAE,yBAAyB;QACnC,UAAU,EAAE,+BAA+B;KAC5C;IACD,OAAO,EAAE;QACP,UAAU,EAAE,CAAC,CAAC,KAAK,EAAE;QACrB,aAAa,EAAE,sBAAsB;QACrC,QAAQ,EAAE,sBAAsB;QAChC,UAAU,EAAE,CAAC,CAAC,KAAK,EAAE;QACrB,QAAQ,EAAE,sBAAsB;QAChC,UAAU,EAAE,yBAAyB;KACtC;IACD,QAAQ,EAAE;QACR,UAAU,EAAE,CAAC,CAAC,KAAK,EAAE;QACrB,aAAa,EAAE,sBAAsB;QACrC,QAAQ,EAAE,sBAAsB;QAChC,UAAU,EAAE,CAAC,CAAC,KAAK,EAAE;QACrB,QAAQ,EAAE,uBAAuB;QACjC,UAAU,EAAE,yBAAyB;KACtC;CACqE,CAAC;AAEzE,wDAAwD;AACxD,SAAS,cAAc,CAAyB,IAAO;IACrD,OAAO,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC7F,CAAC;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;AAGlE,oFAAoF;AACpF,MAAM,CAAC,MAAM,uBAAuB,GAAG,cAAc,CAAC,eAAe,CAAC,CAAC;AAWvE,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,YAAY,CACnD,MAAM,CAAC,WAAW,CAChB,gBAAgB,CAAC,GAAG,CAClB,CAAC,UAAU,EAAE,EAAE,CACb;IACE,wBAAwB,CAAC,UAAU,CAAC;IACpC,kBAAkB,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE;CAC1C,CACb,CACoB,CACxB,CAAC;AAGF,sGAAsG;AACtG,qFAAqF;AACrF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,YAAY,CAAC,CAAC;IACjE,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC","sourcesContent":["// The aggregate tier of the stats model: every league-wide and career number, each a derived cache\n// of the completed games' finalized records (RULE-QS-SPORT-921). None is ever written by a client.\n//\n// leagueTeams/{teamId}.seasonStats the team's standings line\n// leagueTeams/{teamId}.playerSeasonStats.{userId} one membership's row — the team's league and\n// the key name the membership it belongs to\n// userProfiles/{userId}.lifetimeStats.{sportKey} a real player's career line per sport\n// sportHallOfFame/{sportKey} a sport's all-time leaderboard\n//\n// Which schema each sport's line of each kind uses — game lines included — is declared once, in\n// `SPORT_LINE_SCHEMAS`: the sport's adapter reads its lines through it, and every document field\n// holding lines of those kinds is built from it — the game's `stats` (`SPORT_GAME_STATS_SCHEMAS`),\n// the season lines, the membership rows, the lifetime lines, and the hall-of-fame entries. A\n// document on its own does not say which sport it counts, so a stored line is read as one sport's\n// line only through that sport's adapter, never by its shape (RULE-QS-SPORT-902). The lines are strict, so a\n// line from one sport is never mistaken for another's with fields silently dropped.\n\nimport { z } from 'zod';\nimport { LEAGUE_TYPE_KEYS, SPORT_KEY_BY_LEAGUE_TYPE } from '../types/sports.js';\nimport type { LeagueType } from '../types/sports.js';\nimport type { StatLineKind } from '../types/stats.js';\nimport { countSchema, epochMsSchema, userIdSchema } from './atoms.js';\nimport {\n BasketballPlayerStatsSchema,\n BasketballTeamStatsSchema,\n CurlingTeamStatsSchema,\n FootballTeamStatsSchema,\n} from './game-stats.js';\n\n// ---------------------------------------------------------------------------------------------\n// Team season lines\n// ---------------------------------------------------------------------------------------------\n\n// The standings base every sport's season line carries. The point differential derives at read.\nconst teamSeasonBaseShape = {\n gamesPlayed: countSchema,\n wins: countSchema,\n losses: countSchema,\n ties: countSchema,\n pointsFor: countSchema,\n pointsAgainst: countSchema,\n};\n\nexport const TeamSeasonBaseStatsSchema = z.strictObject(teamSeasonBaseShape);\nexport type TeamSeasonBaseStats = z.infer<typeof TeamSeasonBaseStatsSchema>;\n\n// Rebounds and assists are summed from the team's players' game lines; no team line carries FP.\nexport const BasketballTeamSeasonStatsSchema = z.strictObject({\n ...teamSeasonBaseShape,\n rebounds: countSchema,\n assists: countSchema,\n});\nexport type BasketballTeamSeasonStats = z.infer<typeof BasketballTeamSeasonStatsSchema>;\n\n// ---------------------------------------------------------------------------------------------\n// Player lines that carry games played: membership rows, lifetime lines, hall-of-fame entries\n// ---------------------------------------------------------------------------------------------\n\n// A sport with no individual stats counts games played only.\nexport const GamesPlayedStatsSchema = z.strictObject({\n gamesPlayed: countSchema,\n});\nexport type GamesPlayedStats = z.infer<typeof GamesPlayedStatsSchema>;\n\n// The basketball game line — its counters and FP — plus games played.\nexport const BasketballPlayerTotalsSchema = BasketballPlayerStatsSchema.extend({\n gamesPlayed: countSchema,\n});\nexport type BasketballPlayerTotals = z.infer<typeof BasketballPlayerTotalsSchema>;\n\n// ---------------------------------------------------------------------------------------------\n// Every sport's lines, by kind\n// ---------------------------------------------------------------------------------------------\n\n// A kind a sport keeps no line of is `z.never()`: curling and football keep no player game line and\n// no hall of fame.\nexport const SPORT_LINE_SCHEMAS = {\n Basketball: {\n gamePlayer: BasketballPlayerStatsSchema,\n membershipRow: BasketballPlayerTotalsSchema,\n lifetime: BasketballPlayerTotalsSchema,\n hallOfFame: BasketballPlayerTotalsSchema,\n gameTeam: BasketballTeamStatsSchema,\n teamSeason: BasketballTeamSeasonStatsSchema,\n },\n Curling: {\n gamePlayer: z.never(),\n membershipRow: GamesPlayedStatsSchema,\n lifetime: GamesPlayedStatsSchema,\n hallOfFame: z.never(),\n gameTeam: CurlingTeamStatsSchema,\n teamSeason: TeamSeasonBaseStatsSchema,\n },\n Football: {\n gamePlayer: z.never(),\n membershipRow: GamesPlayedStatsSchema,\n lifetime: GamesPlayedStatsSchema,\n hallOfFame: z.never(),\n gameTeam: FootballTeamStatsSchema,\n teamSeason: TeamSeasonBaseStatsSchema,\n },\n} as const satisfies Record<LeagueType, Record<StatLineKind, z.ZodType>>;\n\n// A stored line of one kind, whichever sport it counts.\nfunction lineOfAnySport<K extends StatLineKind>(kind: K) {\n return z.union(LEAGUE_TYPE_KEYS.map((leagueType) => SPORT_LINE_SCHEMAS[leagueType][kind]));\n}\n\nexport const TeamSeasonStatsSchema = lineOfAnySport('teamSeason');\nexport type TeamSeasonStats = z.infer<typeof TeamSeasonStatsSchema>;\n\n// A row exists because a game roster named the player, and outlives the membership.\nexport const PlayerSeasonStatsSchema = lineOfAnySport('membershipRow');\nexport type PlayerSeasonStats = z.infer<typeof PlayerSeasonStatsSchema>;\n\n// Keyed by sport key, each sport's lifetime line. A sport the user never played is absent, not\n// zeroed. Ghosts never hold one (RULE-QS-SPORT-928).\ntype LifetimeLinesShape = {\n [L in LeagueType as (typeof SPORT_KEY_BY_LEAGUE_TYPE)[L]]: z.ZodOptional<\n (typeof SPORT_LINE_SCHEMAS)[L]['lifetime']\n >;\n};\n\nexport const UserLifetimeStatsSchema = z.strictObject(\n Object.fromEntries(\n LEAGUE_TYPE_KEYS.map(\n (leagueType) =>\n [\n SPORT_KEY_BY_LEAGUE_TYPE[leagueType],\n SPORT_LINE_SCHEMAS[leagueType].lifetime.optional(),\n ] as const,\n ),\n ) as LifetimeLinesShape,\n);\nexport type UserLifetimeStats = z.infer<typeof UserLifetimeStatsSchema>;\n\n// `sportHallOfFame/{sportKey}` — kept only for a sport whose adapter keeps a hall of fame. Real users\n// only (RULE-QS-SPORT-928); an entry equals that user's lifetime line for the sport.\nexport const SportHallOfFameSchema = z.object({\n playerStats: z.record(userIdSchema, lineOfAnySport('hallOfFame')),\n updatedAt: epochMsSchema,\n});\nexport type SportHallOfFame = z.infer<typeof SportHallOfFameSchema>;\n"]}
@@ -13,7 +13,7 @@ export declare const TEXT_FIELD_ISSUE_MESSAGES: Readonly<Record<InputFormatIssue
13
13
  /**
14
14
  * A text field's wire schema, built from the field's one declaration: it runs input-format-core's
15
15
  * check — trim, then min, max, and the format's characters — and parses to the text the check keeps.
16
- * The form's input reads the same declaration, so the two accept exactly the same text (ARCH-102). A
16
+ * The form's input reads the same declaration, so the two accept exactly the same text (RULE-STD-ARCH-102). A
17
17
  * refusal carries the issue and the declaration's bounds, so a form can say which bound was missed.
18
18
  */
19
19
  /** A text field's declaration, as a form and its wire schema both read it. */
@@ -8,7 +8,7 @@ import { isStagingId } from '../ids/picture-ids.js';
8
8
  import { isDocumentIdSegment } from '../paths/utils.js';
9
9
  import { BASKETBALL_BULK_STAT_KEYS, LEAGUE_TYPE_KEYS, SPORT_KEYS, STAT_COUNTER_KEYS, } from '../types/sports.js';
10
10
  import { GAME_STATUS_KEYS, HARDWARE_LOAN_CONTRACT_STATUS_KEYS, MEMBERSHIP_ROSTER_SYNC_STATUS_KEYS, TEAM_JOIN_REQUEST_STATUS_KEYS, LEAGUE_MODE_KEYS, LEAGUE_STATUS_KEYS, STATS_BEACON_ACTION_KEYS, STATS_DISCREPANCY_KIND_KEYS, TEAM_ICON_KEYS, TEAM_INVITATION_STATUS_KEYS, TEAM_JOIN_METHOD_KEYS, TEAM_MEMBERSHIP_STATUS_KEYS, TEAM_ROLE_KEYS, SYSTEM_ROLE_KEYS, TEAM_TYPE_KEYS, USER_STATUS_KEYS, WAIVER_WITNESS_AUTHORITY_KEYS, } from '../types/unions.js';
11
- // Every persisted moment is an epoch-millisecond number (ARCH-105).
11
+ // Every persisted moment is an epoch-millisecond number (RULE-STD-ARCH-105).
12
12
  export const epochMsSchema = z.number().int().nonnegative();
13
13
  // A tally: a stat line's counter, a games-played count, a run's page counts.
14
14
  export const countSchema = z.number().int().nonnegative();
@@ -50,7 +50,7 @@ export function textFieldSchema(declaration) {
50
50
  return z.NEVER;
51
51
  });
52
52
  }
53
- // Required on every repair action and recorded with the state it replaced (BACKEND-212): the
53
+ // Required on every repair action and recorded with the state it replaced (RULE-STD-BACKEND-212): the
54
54
  // callable's input, and the correction event and stats operation that store it.
55
55
  export const repairReasonSchema = textFieldSchema(REPAIR_REASON_INPUT_FORMAT);
56
56
  /** The shape of a calendar date, `YYYY-MM-DD`; a form names the issue it raises by this pattern. */
@@ -66,7 +66,7 @@ export const calendarDateSchema = z
66
66
  const day = new Date(`${value}T00:00:00.000Z`);
67
67
  return !Number.isNaN(day.getTime()) && day.toISOString().startsWith(value);
68
68
  }, 'Not a real calendar date.');
69
- // Every id a document path is built from is exactly one path segment (ARCH-106): a client-sent id
69
+ // Every id a document path is built from is exactly one path segment (RULE-STD-ARCH-106): a client-sent id
70
70
  // holding a `/` or empty would address another document, a subcollection, or the collection itself.
71
71
  // Each entity's atom builds on this one; there is no generic idSchema for a field to use instead. An
72
72
  // empty id stops at its one length issue — a form's "Required." for a choice left unmade — never a
@@ -1 +1 @@
1
- {"version":3,"file":"atoms.js","sourceRoot":"","sources":["../../src/doc-schemas/atoms.ts"],"names":[],"mappings":"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","sourcesContent":["// Field atoms shared by the document schemas and the callable schemas. Every enum derives from its\n// one canonical key array in ../types — never re-declared.\n\nimport { z } from 'zod';\nimport {\n checkInputFormat,\n type DeclaredInputFormat,\n type InputFormatIssue,\n} from '@ttt-productions/input-format-core';\nimport { REPAIR_REASON_INPUT_FORMAT } from '../input-formats/input-formats.js';\nimport { parseGameRosterPlayerId } from '../ids/compound-ids.js';\nimport { isStagingId } from '../ids/picture-ids.js';\nimport { isDocumentIdSegment } from '../paths/utils.js';\nimport {\n BASKETBALL_BULK_STAT_KEYS,\n LEAGUE_TYPE_KEYS,\n SPORT_KEYS,\n STAT_COUNTER_KEYS,\n} from '../types/sports.js';\nimport {\n GAME_STATUS_KEYS,\n HARDWARE_LOAN_CONTRACT_STATUS_KEYS,\n MEMBERSHIP_ROSTER_SYNC_STATUS_KEYS,\n TEAM_JOIN_REQUEST_STATUS_KEYS,\n LEAGUE_MODE_KEYS,\n LEAGUE_STATUS_KEYS,\n STATS_BEACON_ACTION_KEYS,\n STATS_DISCREPANCY_KIND_KEYS,\n TEAM_ICON_KEYS,\n TEAM_INVITATION_STATUS_KEYS,\n TEAM_JOIN_METHOD_KEYS,\n TEAM_MEMBERSHIP_STATUS_KEYS,\n TEAM_ROLE_KEYS,\n SYSTEM_ROLE_KEYS,\n TEAM_TYPE_KEYS,\n USER_STATUS_KEYS,\n WAIVER_WITNESS_AUTHORITY_KEYS,\n} from '../types/unions.js';\n\n// Every persisted moment is an epoch-millisecond number (ARCH-105).\nexport const epochMsSchema = z.number().int().nonnegative();\n\n// A tally: a stat line's counter, a games-played count, a run's page counts.\nexport const countSchema = z.number().int().nonnegative();\n\n// A change revision: advanced by one with every change to what it versions, so the first writes 1.\nexport const revisionSchema = z.number().int().positive();\n\n// The revision a reconciler last finished: 0 before its first finish, never past the revision.\nexport const appliedRevisionSchema = z.number().int().nonnegative();\n\n/**\n * Why `checkInputFormat` refused a text, as a field's wire schema words it, with the declaration's\n * bounds: a required field left blank is \"Required.\", a short one names its minimum, a long one its\n * maximum.\n */\nexport const TEXT_FIELD_ISSUE_MESSAGES: Readonly<\n Record<InputFormatIssue, (declaration: DeclaredInputFormat) => string>\n> = {\n tooShort: ({ min }) => (min <= 1 ? 'Required.' : `At least ${min} characters.`),\n tooLong: ({ max }) => `At most ${max} characters.`,\n invalidCharacters: () => 'Holds a character this field does not allow.',\n};\n\n/**\n * A text field's wire schema, built from the field's one declaration: it runs input-format-core's\n * check — trim, then min, max, and the format's characters — and parses to the text the check keeps.\n * The form's input reads the same declaration, so the two accept exactly the same text (ARCH-102). A\n * refusal carries the issue and the declaration's bounds, so a form can say which bound was missed.\n */\n/** A text field's declaration, as a form and its wire schema both read it. */\nexport type TextFieldDeclaration = DeclaredInputFormat;\n\nexport type TextFieldResult =\n { readonly ok: true; readonly value: string } | { readonly ok: false; readonly reason: string };\n\n/**\n * Checks `text` against its field's declaration: the value the field keeps, or the sentence that\n * refuses it — the same words `textFieldSchema` gives the wire.\n */\nexport function checkTextField(declaration: TextFieldDeclaration, text: string): TextFieldResult {\n const result = checkInputFormat(text, declaration);\n if (result.ok) return { ok: true, value: result.value };\n return { ok: false, reason: TEXT_FIELD_ISSUE_MESSAGES[result.issue](declaration) };\n}\n\nexport function textFieldSchema(declaration: DeclaredInputFormat) {\n return z.string().transform((text, ctx) => {\n const result = checkInputFormat(text, declaration);\n if (result.ok) return result.value;\n ctx.addIssue({\n code: 'custom',\n message: TEXT_FIELD_ISSUE_MESSAGES[result.issue](declaration),\n input: text,\n params: { issue: result.issue, min: declaration.min, max: declaration.max },\n });\n return z.NEVER;\n });\n}\n\n// Required on every repair action and recorded with the state it replaced (BACKEND-212): the\n// callable's input, and the correction event and stats operation that store it.\nexport const repairReasonSchema = textFieldSchema(REPAIR_REASON_INPUT_FORMAT);\n\n/** The shape of a calendar date, `YYYY-MM-DD`; a form names the issue it raises by this pattern. */\nexport const CALENDAR_DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n// A calendar date with no time or zone: `YYYY-MM-DD`, naming a day that exists — the pattern alone\n// admits `2026-02-31`. League start/end dates and a game's dayKey. Two of them compare correctly as\n// strings. A value that fails the pattern stops there: an empty or misshapen value is not a date at\n// all, so it carries the one pattern issue (a form's \"Choose a date.\"), never a second about the day.\nexport const calendarDateSchema = z\n .string()\n .regex(CALENDAR_DATE_PATTERN, { abort: true })\n .refine((value) => {\n const day = new Date(`${value}T00:00:00.000Z`);\n return !Number.isNaN(day.getTime()) && day.toISOString().startsWith(value);\n }, 'Not a real calendar date.');\n\n// Every id a document path is built from is exactly one path segment (ARCH-106): a client-sent id\n// holding a `/` or empty would address another document, a subcollection, or the collection itself.\n// Each entity's atom builds on this one; there is no generic idSchema for a field to use instead. An\n// empty id stops at its one length issue — a form's \"Required.\" for a choice left unmade — never a\n// second about the segment.\nexport const documentIdSegmentSchema = z\n .string()\n .min(1, { abort: true })\n .refine(isDocumentIdSegment, 'Not a document id.');\n\nexport const userIdSchema = documentIdSegmentSchema;\nexport const leagueIdSchema = documentIdSegmentSchema;\nexport const teamIdSchema = documentIdSegmentSchema;\nexport const gameIdSchema = documentIdSegmentSchema;\n// A week of a league's schedule. A week has no document: its games carry the id and the week's\n// shared fields, and the league's weeks are rebuilt by grouping its games on it.\nexport const scheduleWeekIdSchema = documentIdSegmentSchema;\n// A game roster entry, `{teamId}_{userId}`: its roster document id, the key of its line in the\n// game's `stats.playerStats`, and its member of the game's absence and participation lists.\nexport const gameRosterPlayerIdSchema = documentIdSegmentSchema.refine(\n (id) => parseGameRosterPlayerId(id) !== null,\n 'Not a game roster entry id.',\n);\n// Equal to the stat event's document id.\nexport const statEventIdSchema = documentIdSegmentSchema;\nexport const statsOperationIdSchema = documentIdSegmentSchema;\n// A stats check's report is keyed by the check's operation id.\nexport const statsValidationReportIdSchema = statsOperationIdSchema;\n// A hardware loan contract's id, minted by the creating callable.\nexport const hardwareLoanContractIdSchema = documentIdSegmentSchema;\n// The id a client mints for one upload, the last segment of its staging path: a lowercase UUID. A\n// picture's id is its upload's staging id, so `pictureIdSchema` is this atom.\nexport const stagingIdSchema = z.string().refine(isStagingId, 'Not a staging id.');\n\nexport const leagueTypeSchema = z.enum(LEAGUE_TYPE_KEYS);\nexport const sportKeySchema = z.enum(SPORT_KEYS);\nexport const leagueStatusSchema = z.enum(LEAGUE_STATUS_KEYS);\nexport const leagueModeSchema = z.enum(LEAGUE_MODE_KEYS);\nexport const teamTypeSchema = z.enum(TEAM_TYPE_KEYS);\nexport const teamIconSchema = z.enum(TEAM_ICON_KEYS);\nexport const teamRoleSchema = z.enum(TEAM_ROLE_KEYS);\nexport const systemRoleSchema = z.enum(SYSTEM_ROLE_KEYS);\nexport const userStatusSchema = z.enum(USER_STATUS_KEYS);\nexport const teamMembershipStatusSchema = z.enum(TEAM_MEMBERSHIP_STATUS_KEYS);\nexport const teamJoinMethodSchema = z.enum(TEAM_JOIN_METHOD_KEYS);\nexport const gameStatusSchema = z.enum(GAME_STATUS_KEYS);\nexport const teamInvitationStatusSchema = z.enum(TEAM_INVITATION_STATUS_KEYS);\nexport const teamJoinRequestStatusSchema = z.enum(TEAM_JOIN_REQUEST_STATUS_KEYS);\nexport const hardwareLoanContractStatusSchema = z.enum(HARDWARE_LOAN_CONTRACT_STATUS_KEYS);\nexport const membershipRosterSyncStatusSchema = z.enum(MEMBERSHIP_ROSTER_SYNC_STATUS_KEYS);\nexport const waiverWitnessAuthoritySchema = z.enum(WAIVER_WITNESS_AUTHORITY_KEYS);\nexport const statsBeaconActionSchema = z.enum(STATS_BEACON_ACTION_KEYS);\nexport const statsDiscrepancyKindSchema = z.enum(STATS_DISCREPANCY_KIND_KEYS);\nexport const basketballBulkStatKeySchema = z.enum(BASKETBALL_BULK_STAT_KEYS);\nexport const statCounterKeySchema = z.enum(STAT_COUNTER_KEYS);\n"]}
1
+ {"version":3,"file":"atoms.js","sourceRoot":"","sources":["../../src/doc-schemas/atoms.ts"],"names":[],"mappings":"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","sourcesContent":["// Field atoms shared by the document schemas and the callable schemas. Every enum derives from its\n// one canonical key array in ../types — never re-declared.\n\nimport { z } from 'zod';\nimport {\n checkInputFormat,\n type DeclaredInputFormat,\n type InputFormatIssue,\n} from '@ttt-productions/input-format-core';\nimport { REPAIR_REASON_INPUT_FORMAT } from '../input-formats/input-formats.js';\nimport { parseGameRosterPlayerId } from '../ids/compound-ids.js';\nimport { isStagingId } from '../ids/picture-ids.js';\nimport { isDocumentIdSegment } from '../paths/utils.js';\nimport {\n BASKETBALL_BULK_STAT_KEYS,\n LEAGUE_TYPE_KEYS,\n SPORT_KEYS,\n STAT_COUNTER_KEYS,\n} from '../types/sports.js';\nimport {\n GAME_STATUS_KEYS,\n HARDWARE_LOAN_CONTRACT_STATUS_KEYS,\n MEMBERSHIP_ROSTER_SYNC_STATUS_KEYS,\n TEAM_JOIN_REQUEST_STATUS_KEYS,\n LEAGUE_MODE_KEYS,\n LEAGUE_STATUS_KEYS,\n STATS_BEACON_ACTION_KEYS,\n STATS_DISCREPANCY_KIND_KEYS,\n TEAM_ICON_KEYS,\n TEAM_INVITATION_STATUS_KEYS,\n TEAM_JOIN_METHOD_KEYS,\n TEAM_MEMBERSHIP_STATUS_KEYS,\n TEAM_ROLE_KEYS,\n SYSTEM_ROLE_KEYS,\n TEAM_TYPE_KEYS,\n USER_STATUS_KEYS,\n WAIVER_WITNESS_AUTHORITY_KEYS,\n} from '../types/unions.js';\n\n// Every persisted moment is an epoch-millisecond number (RULE-STD-ARCH-105).\nexport const epochMsSchema = z.number().int().nonnegative();\n\n// A tally: a stat line's counter, a games-played count, a run's page counts.\nexport const countSchema = z.number().int().nonnegative();\n\n// A change revision: advanced by one with every change to what it versions, so the first writes 1.\nexport const revisionSchema = z.number().int().positive();\n\n// The revision a reconciler last finished: 0 before its first finish, never past the revision.\nexport const appliedRevisionSchema = z.number().int().nonnegative();\n\n/**\n * Why `checkInputFormat` refused a text, as a field's wire schema words it, with the declaration's\n * bounds: a required field left blank is \"Required.\", a short one names its minimum, a long one its\n * maximum.\n */\nexport const TEXT_FIELD_ISSUE_MESSAGES: Readonly<\n Record<InputFormatIssue, (declaration: DeclaredInputFormat) => string>\n> = {\n tooShort: ({ min }) => (min <= 1 ? 'Required.' : `At least ${min} characters.`),\n tooLong: ({ max }) => `At most ${max} characters.`,\n invalidCharacters: () => 'Holds a character this field does not allow.',\n};\n\n/**\n * A text field's wire schema, built from the field's one declaration: it runs input-format-core's\n * check — trim, then min, max, and the format's characters — and parses to the text the check keeps.\n * The form's input reads the same declaration, so the two accept exactly the same text (RULE-STD-ARCH-102). A\n * refusal carries the issue and the declaration's bounds, so a form can say which bound was missed.\n */\n/** A text field's declaration, as a form and its wire schema both read it. */\nexport type TextFieldDeclaration = DeclaredInputFormat;\n\nexport type TextFieldResult =\n { readonly ok: true; readonly value: string } | { readonly ok: false; readonly reason: string };\n\n/**\n * Checks `text` against its field's declaration: the value the field keeps, or the sentence that\n * refuses it — the same words `textFieldSchema` gives the wire.\n */\nexport function checkTextField(declaration: TextFieldDeclaration, text: string): TextFieldResult {\n const result = checkInputFormat(text, declaration);\n if (result.ok) return { ok: true, value: result.value };\n return { ok: false, reason: TEXT_FIELD_ISSUE_MESSAGES[result.issue](declaration) };\n}\n\nexport function textFieldSchema(declaration: DeclaredInputFormat) {\n return z.string().transform((text, ctx) => {\n const result = checkInputFormat(text, declaration);\n if (result.ok) return result.value;\n ctx.addIssue({\n code: 'custom',\n message: TEXT_FIELD_ISSUE_MESSAGES[result.issue](declaration),\n input: text,\n params: { issue: result.issue, min: declaration.min, max: declaration.max },\n });\n return z.NEVER;\n });\n}\n\n// Required on every repair action and recorded with the state it replaced (RULE-STD-BACKEND-212): the\n// callable's input, and the correction event and stats operation that store it.\nexport const repairReasonSchema = textFieldSchema(REPAIR_REASON_INPUT_FORMAT);\n\n/** The shape of a calendar date, `YYYY-MM-DD`; a form names the issue it raises by this pattern. */\nexport const CALENDAR_DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n// A calendar date with no time or zone: `YYYY-MM-DD`, naming a day that exists — the pattern alone\n// admits `2026-02-31`. League start/end dates and a game's dayKey. Two of them compare correctly as\n// strings. A value that fails the pattern stops there: an empty or misshapen value is not a date at\n// all, so it carries the one pattern issue (a form's \"Choose a date.\"), never a second about the day.\nexport const calendarDateSchema = z\n .string()\n .regex(CALENDAR_DATE_PATTERN, { abort: true })\n .refine((value) => {\n const day = new Date(`${value}T00:00:00.000Z`);\n return !Number.isNaN(day.getTime()) && day.toISOString().startsWith(value);\n }, 'Not a real calendar date.');\n\n// Every id a document path is built from is exactly one path segment (RULE-STD-ARCH-106): a client-sent id\n// holding a `/` or empty would address another document, a subcollection, or the collection itself.\n// Each entity's atom builds on this one; there is no generic idSchema for a field to use instead. An\n// empty id stops at its one length issue — a form's \"Required.\" for a choice left unmade — never a\n// second about the segment.\nexport const documentIdSegmentSchema = z\n .string()\n .min(1, { abort: true })\n .refine(isDocumentIdSegment, 'Not a document id.');\n\nexport const userIdSchema = documentIdSegmentSchema;\nexport const leagueIdSchema = documentIdSegmentSchema;\nexport const teamIdSchema = documentIdSegmentSchema;\nexport const gameIdSchema = documentIdSegmentSchema;\n// A week of a league's schedule. A week has no document: its games carry the id and the week's\n// shared fields, and the league's weeks are rebuilt by grouping its games on it.\nexport const scheduleWeekIdSchema = documentIdSegmentSchema;\n// A game roster entry, `{teamId}_{userId}`: its roster document id, the key of its line in the\n// game's `stats.playerStats`, and its member of the game's absence and participation lists.\nexport const gameRosterPlayerIdSchema = documentIdSegmentSchema.refine(\n (id) => parseGameRosterPlayerId(id) !== null,\n 'Not a game roster entry id.',\n);\n// Equal to the stat event's document id.\nexport const statEventIdSchema = documentIdSegmentSchema;\nexport const statsOperationIdSchema = documentIdSegmentSchema;\n// A stats check's report is keyed by the check's operation id.\nexport const statsValidationReportIdSchema = statsOperationIdSchema;\n// A hardware loan contract's id, minted by the creating callable.\nexport const hardwareLoanContractIdSchema = documentIdSegmentSchema;\n// The id a client mints for one upload, the last segment of its staging path: a lowercase UUID. A\n// picture's id is its upload's staging id, so `pictureIdSchema` is this atom.\nexport const stagingIdSchema = z.string().refine(isStagingId, 'Not a staging id.');\n\nexport const leagueTypeSchema = z.enum(LEAGUE_TYPE_KEYS);\nexport const sportKeySchema = z.enum(SPORT_KEYS);\nexport const leagueStatusSchema = z.enum(LEAGUE_STATUS_KEYS);\nexport const leagueModeSchema = z.enum(LEAGUE_MODE_KEYS);\nexport const teamTypeSchema = z.enum(TEAM_TYPE_KEYS);\nexport const teamIconSchema = z.enum(TEAM_ICON_KEYS);\nexport const teamRoleSchema = z.enum(TEAM_ROLE_KEYS);\nexport const systemRoleSchema = z.enum(SYSTEM_ROLE_KEYS);\nexport const userStatusSchema = z.enum(USER_STATUS_KEYS);\nexport const teamMembershipStatusSchema = z.enum(TEAM_MEMBERSHIP_STATUS_KEYS);\nexport const teamJoinMethodSchema = z.enum(TEAM_JOIN_METHOD_KEYS);\nexport const gameStatusSchema = z.enum(GAME_STATUS_KEYS);\nexport const teamInvitationStatusSchema = z.enum(TEAM_INVITATION_STATUS_KEYS);\nexport const teamJoinRequestStatusSchema = z.enum(TEAM_JOIN_REQUEST_STATUS_KEYS);\nexport const hardwareLoanContractStatusSchema = z.enum(HARDWARE_LOAN_CONTRACT_STATUS_KEYS);\nexport const membershipRosterSyncStatusSchema = z.enum(MEMBERSHIP_ROSTER_SYNC_STATUS_KEYS);\nexport const waiverWitnessAuthoritySchema = z.enum(WAIVER_WITNESS_AUTHORITY_KEYS);\nexport const statsBeaconActionSchema = z.enum(STATS_BEACON_ACTION_KEYS);\nexport const statsDiscrepancyKindSchema = z.enum(STATS_DISCREPANCY_KIND_KEYS);\nexport const basketballBulkStatKeySchema = z.enum(BASKETBALL_BULK_STAT_KEYS);\nexport const statCounterKeySchema = z.enum(STAT_COUNTER_KEYS);\n"]}
@@ -1,10 +1,10 @@
1
1
  // `auditEvents/{auditEventId}` — the append-only record of sensitive actions and their rejected
2
- // attempts (BACKEND-202..205). Written only by Cloud Functions through the audit writer, in the
2
+ // attempts (RULE-STD-BACKEND-202..205). Written only by Cloud Functions through the audit writer, in the
3
3
  // same transaction or batch as the action it records; no client reads or writes it.
4
4
  //
5
5
  // The envelope is `@ttt-productions/audit-core`'s `AuditEvent`; Q-Sports supplies the event-type
6
6
  // union and the actor / target shapes. The admin modes carry the global role they acted with and the
7
- // others carry none (BACKEND-205).
7
+ // others carry none (RULE-STD-BACKEND-205).
8
8
  import { z } from 'zod';
9
9
  import { AUDIT_ADMIN_ACTOR_MODE_KEYS, AUDIT_EVENT_TYPE_KEYS, AUDIT_NON_ADMIN_ACTOR_MODE_KEYS, AUDIT_RESULT_KEYS, } from '../types/audit.js';
10
10
  import { epochMsSchema, systemRoleSchema, userIdSchema } from './atoms.js';
@@ -1 +1 @@
1
- {"version":3,"file":"audit-event.js","sourceRoot":"","sources":["../../src/doc-schemas/audit-event.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,gGAAgG;AAChG,oFAAoF;AACpF,EAAE;AACF,iGAAiG;AACjG,qGAAqG;AACrG,mCAAmC;AAEnC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,+BAA+B,EAC/B,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE3E,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAElE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,kBAAkB,CAAC,WAAW,EAAE;IAChE,CAAC;SACE,MAAM,CAAC;QACN,GAAG,EAAE,YAAY,CAAC,QAAQ,EAAE;QAC5B,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;QACpB,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,+BAA+B,CAAC;KACnD,CAAC;SACD,MAAM,EAAE;IACX,CAAC;SACE,MAAM,CAAC;QACN,GAAG,EAAE,YAAY,CAAC,QAAQ,EAAE;QAC5B,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;QACpB,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC;QAC9C,UAAU,EAAE,gBAAgB;KAC7B,CAAC;SACD,MAAM,EAAE;CACZ,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,GAAG,EAAE,YAAY,CAAC,QAAQ,EAAE;IAC5B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACrB,IAAI,EAAE,oBAAoB;IAC1B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC1C,KAAK,EAAE,gBAAgB,CAAC,QAAQ,EAAE;IAClC,MAAM,EAAE,iBAAiB,CAAC,QAAQ,EAAE;IACpC,SAAS,EAAE,aAAa;IACxB,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,mFAAmF;IACnF,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IAC3C,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC;IACjC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC","sourcesContent":["// `auditEvents/{auditEventId}` — the append-only record of sensitive actions and their rejected\n// attempts (BACKEND-202..205). Written only by Cloud Functions through the audit writer, in the\n// same transaction or batch as the action it records; no client reads or writes it.\n//\n// The envelope is `@ttt-productions/audit-core`'s `AuditEvent`; Q-Sports supplies the event-type\n// union and the actor / target shapes. The admin modes carry the global role they acted with and the\n// others carry none (BACKEND-205).\n\nimport { z } from 'zod';\nimport {\n AUDIT_ADMIN_ACTOR_MODE_KEYS,\n AUDIT_EVENT_TYPE_KEYS,\n AUDIT_NON_ADMIN_ACTOR_MODE_KEYS,\n AUDIT_RESULT_KEYS,\n} from '../types/audit.js';\nimport { epochMsSchema, systemRoleSchema, userIdSchema } from './atoms.js';\n\nexport const auditEventTypeSchema = z.enum(AUDIT_EVENT_TYPE_KEYS);\n\nexport const AuditActorSchema = z.discriminatedUnion('actorMode', [\n z\n .object({\n uid: userIdSchema.nullable(),\n isAdmin: z.boolean(),\n actorMode: z.enum(AUDIT_NON_ADMIN_ACTOR_MODE_KEYS),\n })\n .strict(),\n z\n .object({\n uid: userIdSchema.nullable(),\n isAdmin: z.boolean(),\n actorMode: z.enum(AUDIT_ADMIN_ACTOR_MODE_KEYS),\n systemRole: systemRoleSchema,\n })\n .strict(),\n]);\n\nexport const AuditTargetSchema = z.object({\n uid: userIdSchema.nullable(),\n ref: z.string().nullable(),\n});\n\nexport const AuditEventSchema = z.object({\n id: z.string().min(1),\n type: auditEventTypeSchema,\n schemaVersion: z.number().int().positive(),\n actor: AuditActorSchema.nullable(),\n target: AuditTargetSchema.nullable(),\n timestamp: epochMsSchema,\n ip: z.string().nullable(),\n userAgent: z.string().nullable(),\n region: z.string().nullable(),\n // Event-specific facts: prior and new values, the role changed, a repair's reason.\n metadata: z.record(z.string(), z.unknown()),\n result: z.enum(AUDIT_RESULT_KEYS),\n failureReason: z.string().nullable(),\n correlationId: z.string().nullable(),\n});\nexport type AuditEvent = z.infer<typeof AuditEventSchema>;\n"]}
1
+ {"version":3,"file":"audit-event.js","sourceRoot":"","sources":["../../src/doc-schemas/audit-event.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,yGAAyG;AACzG,oFAAoF;AACpF,EAAE;AACF,iGAAiG;AACjG,qGAAqG;AACrG,4CAA4C;AAE5C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,+BAA+B,EAC/B,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE3E,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAElE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,kBAAkB,CAAC,WAAW,EAAE;IAChE,CAAC;SACE,MAAM,CAAC;QACN,GAAG,EAAE,YAAY,CAAC,QAAQ,EAAE;QAC5B,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;QACpB,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,+BAA+B,CAAC;KACnD,CAAC;SACD,MAAM,EAAE;IACX,CAAC;SACE,MAAM,CAAC;QACN,GAAG,EAAE,YAAY,CAAC,QAAQ,EAAE;QAC5B,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;QACpB,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC;QAC9C,UAAU,EAAE,gBAAgB;KAC7B,CAAC;SACD,MAAM,EAAE;CACZ,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,GAAG,EAAE,YAAY,CAAC,QAAQ,EAAE;IAC5B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACrB,IAAI,EAAE,oBAAoB;IAC1B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC1C,KAAK,EAAE,gBAAgB,CAAC,QAAQ,EAAE;IAClC,MAAM,EAAE,iBAAiB,CAAC,QAAQ,EAAE;IACpC,SAAS,EAAE,aAAa;IACxB,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,mFAAmF;IACnF,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IAC3C,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC;IACjC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC","sourcesContent":["// `auditEvents/{auditEventId}` — the append-only record of sensitive actions and their rejected\n// attempts (RULE-STD-BACKEND-202..205). Written only by Cloud Functions through the audit writer, in the\n// same transaction or batch as the action it records; no client reads or writes it.\n//\n// The envelope is `@ttt-productions/audit-core`'s `AuditEvent`; Q-Sports supplies the event-type\n// union and the actor / target shapes. The admin modes carry the global role they acted with and the\n// others carry none (RULE-STD-BACKEND-205).\n\nimport { z } from 'zod';\nimport {\n AUDIT_ADMIN_ACTOR_MODE_KEYS,\n AUDIT_EVENT_TYPE_KEYS,\n AUDIT_NON_ADMIN_ACTOR_MODE_KEYS,\n AUDIT_RESULT_KEYS,\n} from '../types/audit.js';\nimport { epochMsSchema, systemRoleSchema, userIdSchema } from './atoms.js';\n\nexport const auditEventTypeSchema = z.enum(AUDIT_EVENT_TYPE_KEYS);\n\nexport const AuditActorSchema = z.discriminatedUnion('actorMode', [\n z\n .object({\n uid: userIdSchema.nullable(),\n isAdmin: z.boolean(),\n actorMode: z.enum(AUDIT_NON_ADMIN_ACTOR_MODE_KEYS),\n })\n .strict(),\n z\n .object({\n uid: userIdSchema.nullable(),\n isAdmin: z.boolean(),\n actorMode: z.enum(AUDIT_ADMIN_ACTOR_MODE_KEYS),\n systemRole: systemRoleSchema,\n })\n .strict(),\n]);\n\nexport const AuditTargetSchema = z.object({\n uid: userIdSchema.nullable(),\n ref: z.string().nullable(),\n});\n\nexport const AuditEventSchema = z.object({\n id: z.string().min(1),\n type: auditEventTypeSchema,\n schemaVersion: z.number().int().positive(),\n actor: AuditActorSchema.nullable(),\n target: AuditTargetSchema.nullable(),\n timestamp: epochMsSchema,\n ip: z.string().nullable(),\n userAgent: z.string().nullable(),\n region: z.string().nullable(),\n // Event-specific facts: prior and new values, the role changed, a repair's reason.\n metadata: z.record(z.string(), z.unknown()),\n result: z.enum(AUDIT_RESULT_KEYS),\n failureReason: z.string().nullable(),\n correlationId: z.string().nullable(),\n});\nexport type AuditEvent = z.infer<typeof AuditEventSchema>;\n"]}