@ttt-productions/q-core 0.1.20 → 0.1.21

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 (196) hide show
  1. package/dist/constants/business.js +2 -2
  2. package/dist/constants/business.js.map +1 -1
  3. package/dist/constants/media.js +1 -1
  4. package/dist/constants/media.js.map +1 -1
  5. package/dist/constants/sports.js +1 -1
  6. package/dist/constants/sports.js.map +1 -1
  7. package/dist/constants/telemetry.js +3 -3
  8. package/dist/constants/telemetry.js.map +1 -1
  9. package/dist/doc-schemas/admin-notifications.js +3 -3
  10. package/dist/doc-schemas/admin-notifications.js.map +1 -1
  11. package/dist/doc-schemas/aggregate-stats.js +3 -3
  12. package/dist/doc-schemas/aggregate-stats.js.map +1 -1
  13. package/dist/doc-schemas/atoms.d.ts +1 -1
  14. package/dist/doc-schemas/atoms.js +3 -3
  15. package/dist/doc-schemas/atoms.js.map +1 -1
  16. package/dist/doc-schemas/audit-event.js +2 -2
  17. package/dist/doc-schemas/audit-event.js.map +1 -1
  18. package/dist/doc-schemas/firestore-primitives.js +1 -1
  19. package/dist/doc-schemas/firestore-primitives.js.map +1 -1
  20. package/dist/doc-schemas/game-location.js +2 -2
  21. package/dist/doc-schemas/game-location.js.map +1 -1
  22. package/dist/doc-schemas/game-stat-history.js +1 -1
  23. package/dist/doc-schemas/game-stat-history.js.map +1 -1
  24. package/dist/doc-schemas/game-stats.js +6 -6
  25. package/dist/doc-schemas/game-stats.js.map +1 -1
  26. package/dist/doc-schemas/league-team.js +4 -4
  27. package/dist/doc-schemas/league-team.js.map +1 -1
  28. package/dist/doc-schemas/membership-roster-sync.js +2 -2
  29. package/dist/doc-schemas/membership-roster-sync.js.map +1 -1
  30. package/dist/doc-schemas/pending-media.d.ts +6 -6
  31. package/dist/doc-schemas/publicUser.js +1 -1
  32. package/dist/doc-schemas/publicUser.js.map +1 -1
  33. package/dist/doc-schemas/registration-start.js +1 -1
  34. package/dist/doc-schemas/registration-start.js.map +1 -1
  35. package/dist/doc-schemas/registry.d.ts +3 -3
  36. package/dist/doc-schemas/sport-game.js +4 -4
  37. package/dist/doc-schemas/sport-game.js.map +1 -1
  38. package/dist/doc-schemas/sport-league.js +1 -1
  39. package/dist/doc-schemas/sport-league.js.map +1 -1
  40. package/dist/doc-schemas/stats-operation.js +2 -2
  41. package/dist/doc-schemas/stats-operation.js.map +1 -1
  42. package/dist/doc-schemas/status-reconcile-queue.js +1 -1
  43. package/dist/doc-schemas/status-reconcile-queue.js.map +1 -1
  44. package/dist/doc-schemas/sweep-state.js +2 -2
  45. package/dist/doc-schemas/sweep-state.js.map +1 -1
  46. package/dist/doc-schemas/team-membership.js +1 -1
  47. package/dist/doc-schemas/team-membership.js.map +1 -1
  48. package/dist/doc-schemas/user-profile.js +1 -1
  49. package/dist/doc-schemas/user-profile.js.map +1 -1
  50. package/dist/doc-schemas/waiver-template.js +1 -1
  51. package/dist/doc-schemas/waiver-template.js.map +1 -1
  52. package/dist/doc-schemas/waivers-and-hardware.js +3 -3
  53. package/dist/doc-schemas/waivers-and-hardware.js.map +1 -1
  54. package/dist/domain-events/domain-events.js +2 -2
  55. package/dist/domain-events/domain-events.js.map +1 -1
  56. package/dist/ids/award-grant-ids.js +1 -1
  57. package/dist/ids/award-grant-ids.js.map +1 -1
  58. package/dist/ids/compound-ids.js +1 -1
  59. package/dist/ids/compound-ids.js.map +1 -1
  60. package/dist/ids/minted-ids.js +1 -1
  61. package/dist/ids/minted-ids.js.map +1 -1
  62. package/dist/ids/notification-ids.js +1 -1
  63. package/dist/ids/notification-ids.js.map +1 -1
  64. package/dist/ids/stat-event-ids.d.ts +1 -1
  65. package/dist/ids/stat-event-ids.js +2 -2
  66. package/dist/ids/stat-event-ids.js.map +1 -1
  67. package/dist/ids/stats-operation-ids.js +1 -1
  68. package/dist/ids/stats-operation-ids.js.map +1 -1
  69. package/dist/index.js +1 -1
  70. package/dist/index.js.map +1 -1
  71. package/dist/input-formats/input-formats.js +1 -1
  72. package/dist/input-formats/input-formats.js.map +1 -1
  73. package/dist/media/file-origins.js +1 -1
  74. package/dist/media/file-origins.js.map +1 -1
  75. package/dist/media/picture-url.js +1 -1
  76. package/dist/media/picture-url.js.map +1 -1
  77. package/dist/migration-console/action-ids.js +1 -1
  78. package/dist/migration-console/action-ids.js.map +1 -1
  79. package/dist/migration-console/manifest.js +1 -1
  80. package/dist/migration-console/manifest.js.map +1 -1
  81. package/dist/notifications/admin-notification-config.js +1 -1
  82. package/dist/notifications/admin-notification-config.js.map +1 -1
  83. package/dist/notifications/occurrences.js +1 -1
  84. package/dist/notifications/occurrences.js.map +1 -1
  85. package/dist/paths/collections.js +3 -3
  86. package/dist/paths/collections.js.map +1 -1
  87. package/dist/paths/picture-owner-paths.js +1 -1
  88. package/dist/paths/picture-owner-paths.js.map +1 -1
  89. package/dist/paths/storage-paths.d.ts +1 -1
  90. package/dist/paths/storage-paths.js +1 -1
  91. package/dist/paths/storage-paths.js.map +1 -1
  92. package/dist/paths/utils.d.ts +1 -1
  93. package/dist/paths/utils.js +1 -1
  94. package/dist/paths/utils.js.map +1 -1
  95. package/dist/schemas/admin-notifications.js +1 -1
  96. package/dist/schemas/admin-notifications.js.map +1 -1
  97. package/dist/schemas/app-config.js +1 -1
  98. package/dist/schemas/app-config.js.map +1 -1
  99. package/dist/schemas/awards.js +1 -1
  100. package/dist/schemas/awards.js.map +1 -1
  101. package/dist/schemas/dead-letters.js +2 -2
  102. package/dist/schemas/dead-letters.js.map +1 -1
  103. package/dist/schemas/leagues.js +1 -1
  104. package/dist/schemas/leagues.js.map +1 -1
  105. package/dist/schemas/migration-console.js +1 -1
  106. package/dist/schemas/migration-console.js.map +1 -1
  107. package/dist/schemas/pictures.js +1 -1
  108. package/dist/schemas/pictures.js.map +1 -1
  109. package/dist/schemas/schedules.js +2 -2
  110. package/dist/schemas/schedules.js.map +1 -1
  111. package/dist/schemas/scoring.js +1 -1
  112. package/dist/schemas/scoring.js.map +1 -1
  113. package/dist/schemas/signed-waivers.js +1 -1
  114. package/dist/schemas/signed-waivers.js.map +1 -1
  115. package/dist/schemas/stats.js +6 -6
  116. package/dist/schemas/stats.js.map +1 -1
  117. package/dist/schemas/team-joining.js +3 -3
  118. package/dist/schemas/team-joining.js.map +1 -1
  119. package/dist/schemas/teams.js +7 -7
  120. package/dist/schemas/teams.js.map +1 -1
  121. package/dist/schemas/users.js +3 -3
  122. package/dist/schemas/users.js.map +1 -1
  123. package/dist/sports/basketball-scoring.js +4 -4
  124. package/dist/sports/basketball-scoring.js.map +1 -1
  125. package/dist/sports/bulk-entry.js +1 -1
  126. package/dist/sports/bulk-entry.js.map +1 -1
  127. package/dist/sports/contribution.d.ts +5 -5
  128. package/dist/sports/contribution.d.ts.map +1 -1
  129. package/dist/sports/contribution.js +4 -4
  130. package/dist/sports/contribution.js.map +1 -1
  131. package/dist/sports/curling-scoring.js +2 -2
  132. package/dist/sports/curling-scoring.js.map +1 -1
  133. package/dist/sports/football-scoring.js +2 -2
  134. package/dist/sports/football-scoring.js.map +1 -1
  135. package/dist/sports/index.js +1 -1
  136. package/dist/sports/index.js.map +1 -1
  137. package/dist/sports/ledger-continuity.js +1 -1
  138. package/dist/sports/ledger-continuity.js.map +1 -1
  139. package/dist/sports/registry.js +2 -2
  140. package/dist/sports/registry.js.map +1 -1
  141. package/dist/sports/replay.js +1 -1
  142. package/dist/sports/replay.js.map +1 -1
  143. package/dist/sports/rollup-check.js +1 -1
  144. package/dist/sports/rollup-check.js.map +1 -1
  145. package/dist/sports/score-mirror.js +1 -1
  146. package/dist/sports/score-mirror.js.map +1 -1
  147. package/dist/sports/scoring-display.d.ts +1 -1
  148. package/dist/sports/scoring-display.js +2 -2
  149. package/dist/sports/scoring-display.js.map +1 -1
  150. package/dist/sports/scoring.d.ts +5 -5
  151. package/dist/sports/scoring.d.ts.map +1 -1
  152. package/dist/sports/scoring.js +2 -2
  153. package/dist/sports/scoring.js.map +1 -1
  154. package/dist/sports/sport-adapter.d.ts +3 -3
  155. package/dist/sports/sport-adapter.d.ts.map +1 -1
  156. package/dist/sports/sport-adapter.js +2 -2
  157. package/dist/sports/sport-adapter.js.map +1 -1
  158. package/dist/sports/store-driver.d.ts +1 -1
  159. package/dist/sports/store-driver.d.ts.map +1 -1
  160. package/dist/sports/store-driver.js +2 -2
  161. package/dist/sports/store-driver.js.map +1 -1
  162. package/dist/types/audit.js +4 -4
  163. package/dist/types/audit.js.map +1 -1
  164. package/dist/types/claims.js +1 -1
  165. package/dist/types/claims.js.map +1 -1
  166. package/dist/types/picture-owners.js +1 -1
  167. package/dist/types/picture-owners.js.map +1 -1
  168. package/dist/types/schedules.js +1 -1
  169. package/dist/types/schedules.js.map +1 -1
  170. package/dist/types/sports.js +2 -2
  171. package/dist/types/sports.js.map +1 -1
  172. package/dist/types/stats.js +5 -5
  173. package/dist/types/stats.js.map +1 -1
  174. package/dist/types/unions.js +2 -2
  175. package/dist/types/unions.js.map +1 -1
  176. package/dist/types/uploads.js +1 -1
  177. package/dist/types/uploads.js.map +1 -1
  178. package/dist/upload-variables/sign-hardware-contract-variables.js +1 -1
  179. package/dist/upload-variables/sign-hardware-contract-variables.js.map +1 -1
  180. package/dist/upload-variables/sign-waiver-variables.js +1 -1
  181. package/dist/upload-variables/sign-waiver-variables.js.map +1 -1
  182. package/dist/utils/account-status.js +1 -1
  183. package/dist/utils/account-status.js.map +1 -1
  184. package/dist/utils/app-routes.js +1 -1
  185. package/dist/utils/app-routes.js.map +1 -1
  186. package/dist/utils/league-join-warning.js +1 -1
  187. package/dist/utils/league-join-warning.js.map +1 -1
  188. package/dist/utils/league-joining.js +1 -1
  189. package/dist/utils/league-joining.js.map +1 -1
  190. package/dist/utils/league-mode.js +1 -1
  191. package/dist/utils/league-mode.js.map +1 -1
  192. package/dist/utils/team-creation.js +1 -1
  193. package/dist/utils/team-creation.js.map +1 -1
  194. package/dist/utils/team-member-actions.js +1 -1
  195. package/dist/utils/team-member-actions.js.map +1 -1
  196. package/package.json +3 -3
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // Builders take the clock reading and the random suffix as arguments so they stay pure: the caller
4
4
  // supplies `Date.now()` and its own randomness.
5
- // A placeholder player carries a `ghost_`-prefixed id and has a publicUsers document (BACKEND-120).
5
+ // A placeholder player carries a `ghost_`-prefixed id and has a publicUsers document (RULE-QS-BACKEND-903).
6
6
  export const GHOST_USER_ID_PREFIX = 'ghost_';
7
7
  export function isGhostUserId(userId) {
8
8
  return userId.startsWith(GHOST_USER_ID_PREFIX);
@@ -1 +1 @@
1
- {"version":3,"file":"minted-ids.js","sourceRoot":"","sources":["../../src/ids/minted-ids.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,mGAAmG;AACnG,gDAAgD;AAEhD,oGAAoG;AACpG,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAE7C,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,MAAM,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,MAAc;IAC5D,OAAO,GAAG,oBAAoB,GAAG,KAAK,IAAI,MAAM,EAAE,CAAC;AACrD,CAAC;AAED,mGAAmG;AACnG,qGAAqG;AACrG,iGAAiG;AACjG,MAAM,UAAU,0BAA0B,CAAC,SAAiB;IAC1D,OAAO,GAAG,oBAAoB,OAAO,SAAS,EAAE,CAAC;AACnD,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,MAAc;IAC5D,OAAO,GAAG,KAAK,IAAI,MAAM,EAAE,CAAC;AAC9B,CAAC;AAED,+FAA+F;AAC/F,qGAAqG;AACrG,yDAAyD;AACzD,MAAM,UAAU,mBAAmB,CAAC,KAAa,EAAE,MAAc;IAC/D,OAAO,QAAQ,KAAK,IAAI,MAAM,EAAE,CAAC;AACnC,CAAC;AAED,4DAA4D;AAC5D,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,CAAC;AAEjC,gGAAgG;AAChG,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAC;AAEpC,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,OAAO,MAAM,KAAK,WAAW,CAAC;AAChC,CAAC","sourcesContent":["// Minted identifiers and identity sentinels.\n//\n// Builders take the clock reading and the random suffix as arguments so they stay pure: the caller\n// supplies `Date.now()` and its own randomness.\n\n// A placeholder player carries a `ghost_`-prefixed id and has a publicUsers document (BACKEND-120).\nexport const GHOST_USER_ID_PREFIX = 'ghost_';\n\nexport function isGhostUserId(userId: string): boolean {\n return userId.startsWith(GHOST_USER_ID_PREFIX);\n}\n\nexport function buildGhostUserId(nowMs: number, suffix: string): string {\n return `${GHOST_USER_ID_PREFIX}${nowMs}_${suffix}`;\n}\n\n// The ghost a ghost-add request creates: derived from the request id the client mints once per add\n// and re-sends on every retry, so a retry names the same ghost instead of creating a second one. Its\n// `req_` part never reads as a clock reading, so it cannot collide with a `buildGhostUserId` id.\nexport function buildGhostUserIdForRequest(requestId: string): string {\n return `${GHOST_USER_ID_PREFIX}req_${requestId}`;\n}\n\n// A new game's id, `{timestamp}_{suffix}`. A copied game keeps the id it was stored under.\nexport function buildSportGameId(nowMs: number, suffix: string): string {\n return `${nowMs}_${suffix}`;\n}\n\n// A new week's `weekId`, minted by the server when the week is saved. Some stored weeks hold a\n// date-derived `week-{YYYY-MM-DD}` id; a minted id never takes that form, and never derives from the\n// date, so two weeks starting on one day stay two weeks.\nexport function buildScheduleWeekId(nowMs: number, suffix: string): string {\n return `week_${nowMs}_${suffix}`;\n}\n\n// The team slot of a bye week. Never a real leagueTeams id.\nexport const BYE_TEAM_ID = 'bye';\n\n// How the bye slot reads wherever a game or a week names it — a matchup, a schedule, a refusal.\nexport const BYE_TEAM_LABEL = 'Bye';\n\nexport function isByeTeamId(teamId: string): boolean {\n return teamId === BYE_TEAM_ID;\n}\n"]}
1
+ {"version":3,"file":"minted-ids.js","sourceRoot":"","sources":["../../src/ids/minted-ids.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,mGAAmG;AACnG,gDAAgD;AAEhD,4GAA4G;AAC5G,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAE7C,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,MAAM,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,MAAc;IAC5D,OAAO,GAAG,oBAAoB,GAAG,KAAK,IAAI,MAAM,EAAE,CAAC;AACrD,CAAC;AAED,mGAAmG;AACnG,qGAAqG;AACrG,iGAAiG;AACjG,MAAM,UAAU,0BAA0B,CAAC,SAAiB;IAC1D,OAAO,GAAG,oBAAoB,OAAO,SAAS,EAAE,CAAC;AACnD,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,MAAc;IAC5D,OAAO,GAAG,KAAK,IAAI,MAAM,EAAE,CAAC;AAC9B,CAAC;AAED,+FAA+F;AAC/F,qGAAqG;AACrG,yDAAyD;AACzD,MAAM,UAAU,mBAAmB,CAAC,KAAa,EAAE,MAAc;IAC/D,OAAO,QAAQ,KAAK,IAAI,MAAM,EAAE,CAAC;AACnC,CAAC;AAED,4DAA4D;AAC5D,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,CAAC;AAEjC,gGAAgG;AAChG,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAC;AAEpC,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,OAAO,MAAM,KAAK,WAAW,CAAC;AAChC,CAAC","sourcesContent":["// Minted identifiers and identity sentinels.\n//\n// Builders take the clock reading and the random suffix as arguments so they stay pure: the caller\n// supplies `Date.now()` and its own randomness.\n\n// A placeholder player carries a `ghost_`-prefixed id and has a publicUsers document (RULE-QS-BACKEND-903).\nexport const GHOST_USER_ID_PREFIX = 'ghost_';\n\nexport function isGhostUserId(userId: string): boolean {\n return userId.startsWith(GHOST_USER_ID_PREFIX);\n}\n\nexport function buildGhostUserId(nowMs: number, suffix: string): string {\n return `${GHOST_USER_ID_PREFIX}${nowMs}_${suffix}`;\n}\n\n// The ghost a ghost-add request creates: derived from the request id the client mints once per add\n// and re-sends on every retry, so a retry names the same ghost instead of creating a second one. Its\n// `req_` part never reads as a clock reading, so it cannot collide with a `buildGhostUserId` id.\nexport function buildGhostUserIdForRequest(requestId: string): string {\n return `${GHOST_USER_ID_PREFIX}req_${requestId}`;\n}\n\n// A new game's id, `{timestamp}_{suffix}`. A copied game keeps the id it was stored under.\nexport function buildSportGameId(nowMs: number, suffix: string): string {\n return `${nowMs}_${suffix}`;\n}\n\n// A new week's `weekId`, minted by the server when the week is saved. Some stored weeks hold a\n// date-derived `week-{YYYY-MM-DD}` id; a minted id never takes that form, and never derives from the\n// date, so two weeks starting on one day stay two weeks.\nexport function buildScheduleWeekId(nowMs: number, suffix: string): string {\n return `week_${nowMs}_${suffix}`;\n}\n\n// The team slot of a bye week. Never a real leagueTeams id.\nexport const BYE_TEAM_ID = 'bye';\n\n// How the bye slot reads wherever a game or a week names it — a matchup, a schedule, a refusal.\nexport const BYE_TEAM_LABEL = 'Bye';\n\nexport function isByeTeamId(teamId: string): boolean {\n return teamId === BYE_TEAM_ID;\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  // Admin notification ids. Each is deterministic from what it names, so a retried write lands on the
2
- // document the first attempt wrote and never beside it (BACKEND-304, BACKEND-306).
2
+ // document the first attempt wrote and never beside it (RULE-STD-BACKEND-304, RULE-STD-BACKEND-306).
3
3
  //
4
4
  // A shared card's own id is not here: notification-core derives it from the notification type and
5
5
  // the aggregation key.
@@ -1 +1 @@
1
- {"version":3,"file":"notification-ids.js","sourceRoot":"","sources":["../../src/ids/notification-ids.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,mFAAmF;AACnF,EAAE;AACF,kGAAkG;AAClG,uBAAuB;AAEvB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AAEjC,qGAAqG;AACrG,wEAAwE;AACxE,MAAM,UAAU,2BAA2B,CACzC,gBAAuC,EACvC,OAAe;IAEf,OAAO,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AACjE,CAAC;AAED,mGAAmG;AACnG,mGAAmG;AACnG,4CAA4C;AAC5C,MAAM,UAAU,mCAAmC,CACjD,QAAgB,EAChB,MAAc,EACd,UAAkB;IAElB,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAC5E,CAAC;AAED,iGAAiG;AACjG,iFAAiF;AACjF,MAAM,UAAU,+BAA+B,CAAC,SAAiB,EAAE,cAAsB;IACvF,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AACjE,CAAC;AAED,gGAAgG;AAChG,qGAAqG;AACrG,WAAW;AACX,MAAM,UAAU,gCAAgC,CAC9C,cAAsB,EACtB,0BAAkC;IAElC,OAAO,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAClF,CAAC;AAUD,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B,MAAM,UAAU,gCAAgC,CAAC,MAAc,EAAE,SAAiB;IAChF,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,gCAAgC,CAC9C,EAAU;IAEV,MAAM,WAAW,GAAG,EAAE,CAAC,MAAM,GAAG,iBAAiB,GAAG,qBAAqB,CAAC,MAAM,CAAC;IACjF,IAAI,WAAW,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,qBAAqB,EAAE,WAAW,CAAC;QAAE,OAAO,IAAI,CAAC;IACvF,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,EAAE,CAAC,KAAK,CAAC,WAAW,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACvE,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IACvF,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC/B,CAAC","sourcesContent":["// Admin notification ids. Each is deterministic from what it names, so a retried write lands on the\n// document the first attempt wrote and never beside it (BACKEND-304, BACKEND-306).\n//\n// A shared card's own id is not here: notification-core derives it from the notification type and\n// the aggregation key.\n\nimport { z } from 'zod';\nimport type { AdminNotificationType } from '../types/notifications.js';\nimport { COMPOUND_ID_SEPARATOR } from './compound-ids.js';\n\nconst requestIdSchema = z.uuid();\n\n// notificationDeliveries/{notificationType}_{eventId} — one occurrence of one notification type. The\n// type leads, so two types recording the same action never share a row.\nexport function buildNotificationDeliveryId(\n notificationType: AdminNotificationType,\n eventId: string,\n): string {\n return [notificationType, eventId].join(COMPOUND_ID_SEPARATOR);\n}\n\n// A player's free-agent signup in a league. A player can sign up, be placed on a team, and sign up\n// again later, so the signup's time is part of the id: a later signup is a new occurrence, while a\n// retry of the same signup is the same one.\nexport function buildLeagueFreeAgentSignedUpEventId(\n leagueId: string,\n userId: string,\n signedUpAt: number,\n): string {\n return [leagueId, String(signedUpAt), userId].join(COMPOUND_ID_SEPARATOR);\n}\n\n// adminNotificationHistory/{requestId}_{notificationId} — one archive of one card. The archive's\n// request id alone would let one \"archive all\" run's cards collide on one entry.\nexport function buildAdminNotificationHistoryId(requestId: string, notificationId: string): string {\n return [requestId, notificationId].join(COMPOUND_ID_SEPARATOR);\n}\n\n// The replay key an archive stores on its history entry: the card, and the generation of it the\n// admin saw. An archive replayed with the same request finds its entry and the same key, and changes\n// nothing.\nexport function buildAdminNotificationArchiveKey(\n notificationId: string,\n observedActivityGeneration: string,\n): string {\n return [notificationId, observedActivityGeneration].join(COMPOUND_ID_SEPARATOR);\n}\n\n// notificationArchiveAllJobs/{userId}_{requestId} — one admin's one \"archive all\" request. A retry of\n// the request finds the job its first attempt created. The request id is a UUID, so it is the part\n// read from the end.\nexport interface NotificationArchiveAllJobIdParts {\n userId: string;\n requestId: string;\n}\n\nconst REQUEST_ID_LENGTH = 36;\n\nexport function buildNotificationArchiveAllJobId(userId: string, requestId: string): string {\n return [userId, requestId].join(COMPOUND_ID_SEPARATOR);\n}\n\nexport function parseNotificationArchiveAllJobId(\n id: string,\n): NotificationArchiveAllJobIdParts | null {\n const separatorAt = id.length - REQUEST_ID_LENGTH - COMPOUND_ID_SEPARATOR.length;\n if (separatorAt < 1 || !id.startsWith(COMPOUND_ID_SEPARATOR, separatorAt)) return null;\n const userId = id.slice(0, separatorAt);\n const requestId = id.slice(separatorAt + COMPOUND_ID_SEPARATOR.length);\n if (userId.includes('/') || !requestIdSchema.safeParse(requestId).success) return null;\n return { userId, requestId };\n}\n"]}
1
+ {"version":3,"file":"notification-ids.js","sourceRoot":"","sources":["../../src/ids/notification-ids.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,qGAAqG;AACrG,EAAE;AACF,kGAAkG;AAClG,uBAAuB;AAEvB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AAEjC,qGAAqG;AACrG,wEAAwE;AACxE,MAAM,UAAU,2BAA2B,CACzC,gBAAuC,EACvC,OAAe;IAEf,OAAO,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AACjE,CAAC;AAED,mGAAmG;AACnG,mGAAmG;AACnG,4CAA4C;AAC5C,MAAM,UAAU,mCAAmC,CACjD,QAAgB,EAChB,MAAc,EACd,UAAkB;IAElB,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAC5E,CAAC;AAED,iGAAiG;AACjG,iFAAiF;AACjF,MAAM,UAAU,+BAA+B,CAAC,SAAiB,EAAE,cAAsB;IACvF,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AACjE,CAAC;AAED,gGAAgG;AAChG,qGAAqG;AACrG,WAAW;AACX,MAAM,UAAU,gCAAgC,CAC9C,cAAsB,EACtB,0BAAkC;IAElC,OAAO,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAClF,CAAC;AAUD,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B,MAAM,UAAU,gCAAgC,CAAC,MAAc,EAAE,SAAiB;IAChF,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,gCAAgC,CAC9C,EAAU;IAEV,MAAM,WAAW,GAAG,EAAE,CAAC,MAAM,GAAG,iBAAiB,GAAG,qBAAqB,CAAC,MAAM,CAAC;IACjF,IAAI,WAAW,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,qBAAqB,EAAE,WAAW,CAAC;QAAE,OAAO,IAAI,CAAC;IACvF,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,EAAE,CAAC,KAAK,CAAC,WAAW,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACvE,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IACvF,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC/B,CAAC","sourcesContent":["// Admin notification ids. Each is deterministic from what it names, so a retried write lands on the\n// document the first attempt wrote and never beside it (RULE-STD-BACKEND-304, RULE-STD-BACKEND-306).\n//\n// A shared card's own id is not here: notification-core derives it from the notification type and\n// the aggregation key.\n\nimport { z } from 'zod';\nimport type { AdminNotificationType } from '../types/notifications.js';\nimport { COMPOUND_ID_SEPARATOR } from './compound-ids.js';\n\nconst requestIdSchema = z.uuid();\n\n// notificationDeliveries/{notificationType}_{eventId} — one occurrence of one notification type. The\n// type leads, so two types recording the same action never share a row.\nexport function buildNotificationDeliveryId(\n notificationType: AdminNotificationType,\n eventId: string,\n): string {\n return [notificationType, eventId].join(COMPOUND_ID_SEPARATOR);\n}\n\n// A player's free-agent signup in a league. A player can sign up, be placed on a team, and sign up\n// again later, so the signup's time is part of the id: a later signup is a new occurrence, while a\n// retry of the same signup is the same one.\nexport function buildLeagueFreeAgentSignedUpEventId(\n leagueId: string,\n userId: string,\n signedUpAt: number,\n): string {\n return [leagueId, String(signedUpAt), userId].join(COMPOUND_ID_SEPARATOR);\n}\n\n// adminNotificationHistory/{requestId}_{notificationId} — one archive of one card. The archive's\n// request id alone would let one \"archive all\" run's cards collide on one entry.\nexport function buildAdminNotificationHistoryId(requestId: string, notificationId: string): string {\n return [requestId, notificationId].join(COMPOUND_ID_SEPARATOR);\n}\n\n// The replay key an archive stores on its history entry: the card, and the generation of it the\n// admin saw. An archive replayed with the same request finds its entry and the same key, and changes\n// nothing.\nexport function buildAdminNotificationArchiveKey(\n notificationId: string,\n observedActivityGeneration: string,\n): string {\n return [notificationId, observedActivityGeneration].join(COMPOUND_ID_SEPARATOR);\n}\n\n// notificationArchiveAllJobs/{userId}_{requestId} — one admin's one \"archive all\" request. A retry of\n// the request finds the job its first attempt created. The request id is a UUID, so it is the part\n// read from the end.\nexport interface NotificationArchiveAllJobIdParts {\n userId: string;\n requestId: string;\n}\n\nconst REQUEST_ID_LENGTH = 36;\n\nexport function buildNotificationArchiveAllJobId(userId: string, requestId: string): string {\n return [userId, requestId].join(COMPOUND_ID_SEPARATOR);\n}\n\nexport function parseNotificationArchiveAllJobId(\n id: string,\n): NotificationArchiveAllJobIdParts | null {\n const separatorAt = id.length - REQUEST_ID_LENGTH - COMPOUND_ID_SEPARATOR.length;\n if (separatorAt < 1 || !id.startsWith(COMPOUND_ID_SEPARATOR, separatorAt)) return null;\n const userId = id.slice(0, separatorAt);\n const requestId = id.slice(separatorAt + COMPOUND_ID_SEPARATOR.length);\n if (userId.includes('/') || !requestIdSchema.safeParse(requestId).success) return null;\n return { userId, requestId };\n}\n"]}
@@ -5,7 +5,7 @@ export declare const MAX_SPORT_STAT_EVENT_ID_LENGTH = 200;
5
5
  * Whether `id` can be a sport event's id — minted at the tap, or kept by a copied event: it begins
6
6
  * with a digit and holds no path separator, so it names one document of the game's event
7
7
  * subcollection and never a void's or a correction's id. A scoring write accepts no other id from
8
- * its caller, because the id becomes a document path (ARCH-106).
8
+ * its caller, because the id becomes a document path (RULE-STD-ARCH-106).
9
9
  */
10
10
  export declare function isSportStatEventId(id: string): boolean;
11
11
  export declare function buildStatVoidId(targetEventId: string): string;
@@ -2,7 +2,7 @@
2
2
  // completed game's finalized record, `sportGames/{gameId}/gameStatHistory/{pageId}`.
3
3
  //
4
4
  // A sport event's id is minted once, at the tap, and every retry of that event reuses it — the id is
5
- // what makes a re-send an acknowledgement rather than a second application (SPORT-102). The two
5
+ // what makes a re-send an acknowledgement rather than a second application (RULE-QS-SPORT-912). The two
6
6
  // store-level events take ids derived from what they act on, so a target has at most one void and a
7
7
  // correction operation writes at most one correction per line and field.
8
8
  import { COMPOUND_ID_SEPARATOR } from './compound-ids.js';
@@ -15,7 +15,7 @@ export const MAX_SPORT_STAT_EVENT_ID_LENGTH = 200;
15
15
  * Whether `id` can be a sport event's id — minted at the tap, or kept by a copied event: it begins
16
16
  * with a digit and holds no path separator, so it names one document of the game's event
17
17
  * subcollection and never a void's or a correction's id. A scoring write accepts no other id from
18
- * its caller, because the id becomes a document path (ARCH-106).
18
+ * its caller, because the id becomes a document path (RULE-STD-ARCH-106).
19
19
  */
20
20
  export function isSportStatEventId(id) {
21
21
  return id.length <= MAX_SPORT_STAT_EVENT_ID_LENGTH && /^[0-9]/.test(id) && !id.includes('/');
@@ -1 +1 @@
1
- {"version":3,"file":"stat-event-ids.js","sourceRoot":"","sources":["../../src/ids/stat-event-ids.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,qFAAqF;AACrF,EAAE;AACF,qGAAqG;AACrG,gGAAgG;AAChG,oGAAoG;AACpG,yEAAyE;AAEzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,MAAc;IAC5D,OAAO,GAAG,KAAK,GAAG,qBAAqB,GAAG,MAAM,EAAE,CAAC;AACrD,CAAC;AAED,6FAA6F;AAC7F,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAElD;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAU;IAC3C,OAAO,EAAE,CAAC,MAAM,IAAI,8BAA8B,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC/F,CAAC;AAED,kGAAkG;AAClG,YAAY;AACZ,MAAM,mBAAmB,GAAG,MAAM,CAAC;AACnC,MAAM,yBAAyB,GAAG,YAAY,CAAC;AAE/C,MAAM,UAAU,eAAe,CAAC,aAAqB;IACnD,OAAO,CAAC,mBAAmB,EAAE,aAAa,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAC1E,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,qBAAqB,CACnC,WAAmB,EACnB,OAAe,EACf,KAAqB;IAErB,OAAO,CAAC,yBAAyB,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAC9F,CAAC;AAED,mEAAmE;AACnE,MAAM,6BAA6B,GAAG,CAAC,CAAC;AAExC,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAClD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,UAAU,CAAC,+DAA+D,KAAK,GAAG,CAAC,CAAC;IAChG,CAAC;IACD,OAAO,OAAO,qBAAqB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,6BAA6B,EAAE,GAAG,CAAC,EAAE,CAAC;AACrG,CAAC","sourcesContent":["// Ids of the stat event store: `sportGames/{gameId}/gameStatEvents/{eventId}` and the pages of a\n// completed game's finalized record, `sportGames/{gameId}/gameStatHistory/{pageId}`.\n//\n// A sport event's id is minted once, at the tap, and every retry of that event reuses it — the id is\n// what makes a re-send an acknowledgement rather than a second application (SPORT-102). The two\n// store-level events take ids derived from what they act on, so a target has at most one void and a\n// correction operation writes at most one correction per line and field.\n\nimport { COMPOUND_ID_SEPARATOR } from './compound-ids.js';\nimport type { StatCounterKey } from '../types/sports.js';\n\nexport function buildStatEventId(nowMs: number, suffix: string): string {\n return `${nowMs}${COMPOUND_ID_SEPARATOR}${suffix}`;\n}\n\n// The longest sport event id a scoring write accepts — far past a minted id or a copied one.\nexport const MAX_SPORT_STAT_EVENT_ID_LENGTH = 200;\n\n/**\n * Whether `id` can be a sport event's id — minted at the tap, or kept by a copied event: it begins\n * with a digit and holds no path separator, so it names one document of the game's event\n * subcollection and never a void's or a correction's id. A scoring write accepts no other id from\n * its caller, because the id becomes a document path (ARCH-106).\n */\nexport function isSportStatEventId(id: string): boolean {\n return id.length <= MAX_SPORT_STAT_EVENT_ID_LENGTH && /^[0-9]/.test(id) && !id.includes('/');\n}\n\n// Minted event ids — and the ids copied events keep — begin with a digit, so these prefixes never\n// meet one.\nconst STAT_VOID_ID_PREFIX = 'void';\nconst STAT_CORRECTION_ID_PREFIX = 'correction';\n\nexport function buildStatVoidId(targetEventId: string): string {\n return [STAT_VOID_ID_PREFIX, targetEventId].join(COMPOUND_ID_SEPARATOR);\n}\n\n// `lineKey` is the roster-entry id of a player line, or the team id of a team line.\nexport function buildStatCorrectionId(\n operationId: string,\n lineKey: string,\n field: StatCounterKey,\n): string {\n return [STAT_CORRECTION_ID_PREFIX, operationId, lineKey, field].join(COMPOUND_ID_SEPARATOR);\n}\n\n// Zero-padded so the pages of one record sort in page order by id.\nconst STAT_HISTORY_PAGE_INDEX_WIDTH = 4;\n\nexport function buildStatHistoryPageId(index: number): string {\n if (!Number.isSafeInteger(index) || index < 0) {\n throw new RangeError(`A finalized-record page index is a whole number from 0, not ${index}.`);\n }\n return `page${COMPOUND_ID_SEPARATOR}${String(index).padStart(STAT_HISTORY_PAGE_INDEX_WIDTH, '0')}`;\n}\n"]}
1
+ {"version":3,"file":"stat-event-ids.js","sourceRoot":"","sources":["../../src/ids/stat-event-ids.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,qFAAqF;AACrF,EAAE;AACF,qGAAqG;AACrG,wGAAwG;AACxG,oGAAoG;AACpG,yEAAyE;AAEzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,MAAc;IAC5D,OAAO,GAAG,KAAK,GAAG,qBAAqB,GAAG,MAAM,EAAE,CAAC;AACrD,CAAC;AAED,6FAA6F;AAC7F,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAElD;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAU;IAC3C,OAAO,EAAE,CAAC,MAAM,IAAI,8BAA8B,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC/F,CAAC;AAED,kGAAkG;AAClG,YAAY;AACZ,MAAM,mBAAmB,GAAG,MAAM,CAAC;AACnC,MAAM,yBAAyB,GAAG,YAAY,CAAC;AAE/C,MAAM,UAAU,eAAe,CAAC,aAAqB;IACnD,OAAO,CAAC,mBAAmB,EAAE,aAAa,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAC1E,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,qBAAqB,CACnC,WAAmB,EACnB,OAAe,EACf,KAAqB;IAErB,OAAO,CAAC,yBAAyB,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAC9F,CAAC;AAED,mEAAmE;AACnE,MAAM,6BAA6B,GAAG,CAAC,CAAC;AAExC,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAClD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,UAAU,CAAC,+DAA+D,KAAK,GAAG,CAAC,CAAC;IAChG,CAAC;IACD,OAAO,OAAO,qBAAqB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,6BAA6B,EAAE,GAAG,CAAC,EAAE,CAAC;AACrG,CAAC","sourcesContent":["// Ids of the stat event store: `sportGames/{gameId}/gameStatEvents/{eventId}` and the pages of a\n// completed game's finalized record, `sportGames/{gameId}/gameStatHistory/{pageId}`.\n//\n// A sport event's id is minted once, at the tap, and every retry of that event reuses it — the id is\n// what makes a re-send an acknowledgement rather than a second application (RULE-QS-SPORT-912). The two\n// store-level events take ids derived from what they act on, so a target has at most one void and a\n// correction operation writes at most one correction per line and field.\n\nimport { COMPOUND_ID_SEPARATOR } from './compound-ids.js';\nimport type { StatCounterKey } from '../types/sports.js';\n\nexport function buildStatEventId(nowMs: number, suffix: string): string {\n return `${nowMs}${COMPOUND_ID_SEPARATOR}${suffix}`;\n}\n\n// The longest sport event id a scoring write accepts — far past a minted id or a copied one.\nexport const MAX_SPORT_STAT_EVENT_ID_LENGTH = 200;\n\n/**\n * Whether `id` can be a sport event's id — minted at the tap, or kept by a copied event: it begins\n * with a digit and holds no path separator, so it names one document of the game's event\n * subcollection and never a void's or a correction's id. A scoring write accepts no other id from\n * its caller, because the id becomes a document path (RULE-STD-ARCH-106).\n */\nexport function isSportStatEventId(id: string): boolean {\n return id.length <= MAX_SPORT_STAT_EVENT_ID_LENGTH && /^[0-9]/.test(id) && !id.includes('/');\n}\n\n// Minted event ids — and the ids copied events keep — begin with a digit, so these prefixes never\n// meet one.\nconst STAT_VOID_ID_PREFIX = 'void';\nconst STAT_CORRECTION_ID_PREFIX = 'correction';\n\nexport function buildStatVoidId(targetEventId: string): string {\n return [STAT_VOID_ID_PREFIX, targetEventId].join(COMPOUND_ID_SEPARATOR);\n}\n\n// `lineKey` is the roster-entry id of a player line, or the team id of a team line.\nexport function buildStatCorrectionId(\n operationId: string,\n lineKey: string,\n field: StatCounterKey,\n): string {\n return [STAT_CORRECTION_ID_PREFIX, operationId, lineKey, field].join(COMPOUND_ID_SEPARATOR);\n}\n\n// Zero-padded so the pages of one record sort in page order by id.\nconst STAT_HISTORY_PAGE_INDEX_WIDTH = 4;\n\nexport function buildStatHistoryPageId(index: number): string {\n if (!Number.isSafeInteger(index) || index < 0) {\n throw new RangeError(`A finalized-record page index is a whole number from 0, not ${index}.`);\n }\n return `page${COMPOUND_ID_SEPARATOR}${String(index).padStart(STAT_HISTORY_PAGE_INDEX_WIDTH, '0')}`;\n}\n"]}
@@ -3,7 +3,7 @@
3
3
  import { COMPOUND_ID_SEPARATOR } from './compound-ids.js';
4
4
  const GAME_DELETION_OPERATION_ID_PREFIX = 'gameDeletion';
5
5
  // A game is deleted at most once, so its id names the one deletion it can have; a client-minted id
6
- // would let a caller address another action's operation document (BACKEND-102).
6
+ // would let a caller address another action's operation document (RULE-STD-BACKEND-102).
7
7
  export function buildGameDeletionOperationId(gameId) {
8
8
  return [GAME_DELETION_OPERATION_ID_PREFIX, gameId].join(COMPOUND_ID_SEPARATOR);
9
9
  }
@@ -1 +1 @@
1
- {"version":3,"file":"stats-operation-ids.js","sourceRoot":"","sources":["../../src/ids/stats-operation-ids.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,+FAA+F;AAE/F,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,iCAAiC,GAAG,cAAc,CAAC;AAEzD,mGAAmG;AACnG,gFAAgF;AAChF,MAAM,UAAU,4BAA4B,CAAC,MAAc;IACzD,OAAO,CAAC,iCAAiC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AACjF,CAAC;AAED,MAAM,gCAAgC,GAAG,aAAa,CAAC;AAEvD,gGAAgG;AAChG,kGAAkG;AAClG,8FAA8F;AAC9F,MAAM,UAAU,yBAAyB,CAAC,MAAc,EAAE,WAAmB;IAC3E,OAAO,CAAC,gCAAgC,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAC7F,CAAC","sourcesContent":["// `statsOperations` ids the server derives from what the operation acts on, so a retried call finds\n// the operation it started instead of starting a second one, and no client names the document.\n\nimport { COMPOUND_ID_SEPARATOR } from './compound-ids.js';\n\nconst GAME_DELETION_OPERATION_ID_PREFIX = 'gameDeletion';\n\n// A game is deleted at most once, so its id names the one deletion it can have; a client-minted id\n// would let a caller address another action's operation document (BACKEND-102).\nexport function buildGameDeletionOperationId(gameId: string): string {\n return [GAME_DELETION_OPERATION_ID_PREFIX, gameId].join(COMPOUND_ID_SEPARATOR);\n}\n\nconst GHOST_PLAYER_OPERATION_ID_PREFIX = 'ghostPlayer';\n\n// A ghost has exactly one operation for both of its ways out — a link to a real account, or its\n// removal — so two links of one ghost, or a link and a removal, find the same document and refuse\n// each other instead of interleaving. The team is part of it because a link acts on one team.\nexport function buildGhostLinkOperationId(teamId: string, ghostUserId: string): string {\n return [GHOST_PLAYER_OPERATION_ID_PREFIX, teamId, ghostUserId].join(COMPOUND_ID_SEPARATOR);\n}\n"]}
1
+ {"version":3,"file":"stats-operation-ids.js","sourceRoot":"","sources":["../../src/ids/stats-operation-ids.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,+FAA+F;AAE/F,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,iCAAiC,GAAG,cAAc,CAAC;AAEzD,mGAAmG;AACnG,yFAAyF;AACzF,MAAM,UAAU,4BAA4B,CAAC,MAAc;IACzD,OAAO,CAAC,iCAAiC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AACjF,CAAC;AAED,MAAM,gCAAgC,GAAG,aAAa,CAAC;AAEvD,gGAAgG;AAChG,kGAAkG;AAClG,8FAA8F;AAC9F,MAAM,UAAU,yBAAyB,CAAC,MAAc,EAAE,WAAmB;IAC3E,OAAO,CAAC,gCAAgC,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAC7F,CAAC","sourcesContent":["// `statsOperations` ids the server derives from what the operation acts on, so a retried call finds\n// the operation it started instead of starting a second one, and no client names the document.\n\nimport { COMPOUND_ID_SEPARATOR } from './compound-ids.js';\n\nconst GAME_DELETION_OPERATION_ID_PREFIX = 'gameDeletion';\n\n// A game is deleted at most once, so its id names the one deletion it can have; a client-minted id\n// would let a caller address another action's operation document (RULE-STD-BACKEND-102).\nexport function buildGameDeletionOperationId(gameId: string): string {\n return [GAME_DELETION_OPERATION_ID_PREFIX, gameId].join(COMPOUND_ID_SEPARATOR);\n}\n\nconst GHOST_PLAYER_OPERATION_ID_PREFIX = 'ghostPlayer';\n\n// A ghost has exactly one operation for both of its ways out — a link to a real account, or its\n// removal — so two links of one ghost, or a link and a removal, find the same document and refuse\n// each other instead of interleaving. The team is part of it because a link acts on one team.\nexport function buildGhostLinkOperationId(teamId: string, ghostUserId: string): string {\n return [GHOST_PLAYER_OPERATION_ID_PREFIX, teamId, ghostUserId].join(COMPOUND_ID_SEPARATOR);\n}\n"]}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // The package root carries only the modules no folder entry owns; every other definition is exported
2
- // by its own folder's entry alone (ARCH-203).
2
+ // by its own folder's entry alone (RULE-STD-ARCH-203).
3
3
  export * from './input-formats/input-formats.js';
4
4
  export * from './migration-console/action-ids.js';
5
5
  export * from './migration-console/manifest.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,8CAA8C;AAE9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC","sourcesContent":["// The package root carries only the modules no folder entry owns; every other definition is exported\n// by its own folder's entry alone (ARCH-203).\n\nexport * from './input-formats/input-formats.js';\nexport * from './migration-console/action-ids.js';\nexport * from './migration-console/manifest.js';\nexport * from './migration-console/run-state.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,uDAAuD;AAEvD,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC","sourcesContent":["// The package root carries only the modules no folder entry owns; every other definition is exported\n// by its own folder's entry alone (RULE-STD-ARCH-203).\n\nexport * from './input-formats/input-formats.js';\nexport * from './migration-console/action-ids.js';\nexport * from './migration-console/manifest.js';\nexport * from './migration-console/run-state.js';\n"]}
@@ -1,5 +1,5 @@
1
1
  // The declared formats of the free-text fields a form takes, each built from the limits the field
2
- // enforces (ARCH-102). ui-core's text inputs and `ConsequenceDialog`'s reason field take only a
2
+ // enforces (RULE-STD-ARCH-102). ui-core's text inputs and `ConsequenceDialog`'s reason field take only a
3
3
  // declaration, never a bare bound, and a field's wire schema is built from the same declaration
4
4
  // through `textFieldSchema`, so the form and the wire run the one check and accept exactly the same
5
5
  // text. `input-format-core` is server-safe and has no dependencies, so the declarations sit in the
@@ -1 +1 @@
1
- {"version":3,"file":"input-formats.js","sourceRoot":"","sources":["../../src/input-formats/input-formats.ts"],"names":[],"mappings":"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","sourcesContent":["// The declared formats of the free-text fields a form takes, each built from the limits the field\n// enforces (ARCH-102). ui-core's text inputs and `ConsequenceDialog`'s reason field take only a\n// declaration, never a bare bound, and a field's wire schema is built from the same declaration\n// through `textFieldSchema`, so the form and the wire run the one check and accept exactly the same\n// text. `input-format-core` is server-safe and has no dependencies, so the declarations sit in the\n// package root, which both app roots import.\n\nimport { defineInputFormat } from '@ttt-productions/input-format-core';\nimport {\n MAX_ANNOUNCEMENT_MESSAGE_LENGTH,\n MAX_APP_VERSION_LENGTH,\n MAX_AWARD_NAME_LENGTH,\n MAX_CONTACT_PHONE_LENGTH,\n MAX_DISPLAY_NAME_LENGTH,\n MAX_GAME_LOCATION_MEMO_LENGTH,\n MAX_GAME_LOCATION_NAME_LENGTH,\n MAX_GAME_LOCATION_STREET_ADDRESS_LENGTH,\n MAX_GAME_LOCATION_SUB_LOCATION_LENGTH,\n MAX_GAME_MEMO_LENGTH,\n MAX_LANDING_ABOUT_TEXT_LENGTH,\n MAX_LANDING_HERO_TITLE_LENGTH,\n MAX_LEAGUE_DESCRIPTION_LENGTH,\n MAX_LEAGUE_TITLE_LENGTH,\n MAX_MAINTENANCE_MESSAGE_LENGTH,\n MAX_REPAIR_REASON_LENGTH,\n MAX_SCHEDULE_WEEK_TITLE_LENGTH,\n MAX_TEAM_INVITATION_MESSAGE_LENGTH,\n MAX_TEAM_JOIN_REQUEST_MESSAGE_LENGTH,\n MAX_TEAM_NAME_LENGTH,\n MAX_WAIVER_TEMPLATE_TEXT_LENGTH,\n MAX_WAIVER_TEMPLATE_TITLE_LENGTH,\n MIN_AWARD_NAME_LENGTH,\n MIN_DISPLAY_NAME_LENGTH,\n MIN_LEAGUE_DESCRIPTION_LENGTH,\n MIN_LEAGUE_TITLE_LENGTH,\n MIN_REPAIR_REASON_LENGTH,\n MIN_TEAM_JOIN_REQUEST_MESSAGE_LENGTH,\n MIN_TEAM_NAME_LENGTH,\n} from '../constants/business.js';\n\n/**\n * The reason an admin gives for a repair action, as `repairReasonSchema` reads it: trimmed, then\n * bounded by the repair-reason limits, with any character and line breaks allowed.\n */\nexport const REPAIR_REASON_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: MIN_REPAIR_REASON_LENGTH,\n max: MAX_REPAIR_REASON_LENGTH,\n});\n\n/**\n * The evidence an operator records for a cutover step the console cannot see done — an export's\n * operation name, a revocation's console record: trimmed, then bounded by the repair-reason limits,\n * line breaks allowed.\n */\nexport const MIGRATION_EVIDENCE_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: MIN_REPAIR_REASON_LENGTH,\n max: MAX_REPAIR_REASON_LENGTH,\n});\n\n/**\n * A player's message to the team they ask to join, as `teamJoinRequestMessageSchema` reads it:\n * trimmed, then bounded by the join-request message limits, with any character and line breaks\n * allowed.\n */\nexport const TEAM_JOIN_REQUEST_MESSAGE_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: MIN_TEAM_JOIN_REQUEST_MESSAGE_LENGTH,\n max: MAX_TEAM_JOIN_REQUEST_MESSAGE_LENGTH,\n});\n\n/**\n * The optional note a team sends with an invitation, as `teamInvitationMessageSchema` reads it:\n * trimmed, empty allowed, then bounded by the invitation note limit, line breaks allowed.\n */\nexport const TEAM_INVITATION_MESSAGE_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: 0,\n max: MAX_TEAM_INVITATION_MESSAGE_LENGTH,\n});\n\n/**\n * A scorer's note on one person in a live game (\"red shirt\", \"#9\"), as `gameMemoSchema` reads it:\n * trimmed, one line, empty allowed — an empty memo clears the note — then bounded by the memo cap.\n */\nexport const GAME_MEMO_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 0,\n max: MAX_GAME_MEMO_LENGTH,\n});\n\n/**\n * A person's display name — an account's, or a ghost player's, as both ghost doors and the admin's\n * ghost captain ask for it — as `displayNameSchema` reads it: trimmed, one line, then bounded by the\n * display-name limits.\n */\nexport const DISPLAY_NAME_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: MIN_DISPLAY_NAME_LENGTH,\n max: MAX_DISPLAY_NAME_LENGTH,\n});\n\n/**\n * A team's name, as `teamNameSchema` reads it at creation and at an admin's rename: trimmed, one\n * line, then bounded by the team-name limits.\n */\nexport const TEAM_NAME_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: MIN_TEAM_NAME_LENGTH,\n max: MAX_TEAM_NAME_LENGTH,\n});\n\n/**\n * The number the league reaches a team on, as `contactPhoneSchema` reads it: trimmed, one line,\n * required, and bounded by the contact-phone cap. It is kept as typed — no format is imposed.\n */\nexport const CONTACT_PHONE_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 1,\n max: MAX_CONTACT_PHONE_LENGTH,\n});\n\n/**\n * The account's optional default contact phone on the settings page: the contact-phone format with\n * empty allowed. Empty means remove the saved number — the form sends it as\n * `UpdateAccountSettingsInput.defaultContactPhone: null` — and any other value is held to\n * `CONTACT_PHONE_INPUT_FORMAT`'s trim, one line, and cap.\n */\nexport const OPTIONAL_CONTACT_PHONE_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 0,\n max: MAX_CONTACT_PHONE_LENGTH,\n});\n\n/**\n * An award's name in the catalog, as `awardNameSchema` reads it — at creation, sent with the award's\n * first badge: trimmed, one line, then bounded by the award-name limits.\n */\nexport const AWARD_NAME_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: MIN_AWARD_NAME_LENGTH,\n max: MAX_AWARD_NAME_LENGTH,\n});\n\n/**\n * A league's title, as `leagueTitleSchema` reads it at creation and at the settings edit: trimmed,\n * one line, then bounded by the league-title limits.\n */\nexport const LEAGUE_TITLE_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: MIN_LEAGUE_TITLE_LENGTH,\n max: MAX_LEAGUE_TITLE_LENGTH,\n});\n\n/**\n * A league's description, as `leagueDescriptionSchema` reads it at creation and at the settings\n * edit: trimmed, then bounded by the league-description limits, line breaks allowed.\n */\nexport const LEAGUE_DESCRIPTION_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: MIN_LEAGUE_DESCRIPTION_LENGTH,\n max: MAX_LEAGUE_DESCRIPTION_LENGTH,\n});\n\n/** A venue's name, as `gameLocationNameSchema` reads it: trimmed, one line, required, within its cap. */\nexport const GAME_LOCATION_NAME_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 1,\n max: MAX_GAME_LOCATION_NAME_LENGTH,\n});\n\n/**\n * A venue's street address, as `gameLocationStreetAddressSchema` reads it: trimmed, one line,\n * required, within its cap.\n */\nexport const GAME_LOCATION_STREET_ADDRESS_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 1,\n max: MAX_GAME_LOCATION_STREET_ADDRESS_LENGTH,\n});\n\n/**\n * One court, sheet, or room of a venue, as `gameLocationSubLocationSchema` reads it: trimmed, one\n * line, required, within its cap.\n */\nexport const GAME_LOCATION_SUB_LOCATION_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 1,\n max: MAX_GAME_LOCATION_SUB_LOCATION_LENGTH,\n});\n\n/**\n * A venue's memo, as `gameLocationMemoSchema` reads it: trimmed, line breaks allowed, empty allowed —\n * an empty memo means the venue has none — within its cap.\n */\nexport const GAME_LOCATION_MEMO_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: 0,\n max: MAX_GAME_LOCATION_MEMO_LENGTH,\n});\n\n/** A schedule week's name, as `scheduleWeekTitleSchema` reads it: trimmed, one line, required. */\nexport const SCHEDULE_WEEK_TITLE_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 1,\n max: MAX_SCHEDULE_WEEK_TITLE_LENGTH,\n});\n\n/**\n * A waiver template's title, as `waiverTemplateTitleSchema` reads it. A waiver is a legal artifact a\n * player signs, so its words are kept exactly as typed; it still cannot be blank.\n */\nexport const WAIVER_TEMPLATE_TITLE_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 1,\n max: MAX_WAIVER_TEMPLATE_TITLE_LENGTH,\n keepAsTyped: true,\n});\n\n/**\n * A waiver template's wording, as `waiverTemplateTextSchema` reads it: the legal text a player signs,\n * kept exactly as typed, line breaks allowed, never blank.\n */\nexport const WAIVER_TEMPLATE_TEXT_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: 1,\n max: MAX_WAIVER_TEMPLATE_TEXT_LENGTH,\n keepAsTyped: true,\n});\n\n/** The landing page's hero title: trimmed, one line; blank shows the default heading. */\nexport const LANDING_HERO_TITLE_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 0,\n max: MAX_LANDING_HERO_TITLE_LENGTH,\n});\n\n/** The landing page's about text: trimmed, line breaks allowed, empty allowed. */\nexport const LANDING_ABOUT_TEXT_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: 0,\n max: MAX_LANDING_ABOUT_TEXT_LENGTH,\n});\n\n/** The message shown while the app is in maintenance; empty shows none. */\nexport const MAINTENANCE_MESSAGE_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: 0,\n max: MAX_MAINTENANCE_MESSAGE_LENGTH,\n});\n\n/** The site announcement banner; empty clears it. */\nexport const ANNOUNCEMENT_MESSAGE_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: 0,\n max: MAX_ANNOUNCEMENT_MESSAGE_LENGTH,\n});\n\n/** The published app version the version gate compares against; never blank. */\nexport const APP_VERSION_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: 1,\n max: MAX_APP_VERSION_LENGTH,\n});\n"]}
1
+ {"version":3,"file":"input-formats.js","sourceRoot":"","sources":["../../src/input-formats/input-formats.ts"],"names":[],"mappings":"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","sourcesContent":["// The declared formats of the free-text fields a form takes, each built from the limits the field\n// enforces (RULE-STD-ARCH-102). ui-core's text inputs and `ConsequenceDialog`'s reason field take only a\n// declaration, never a bare bound, and a field's wire schema is built from the same declaration\n// through `textFieldSchema`, so the form and the wire run the one check and accept exactly the same\n// text. `input-format-core` is server-safe and has no dependencies, so the declarations sit in the\n// package root, which both app roots import.\n\nimport { defineInputFormat } from '@ttt-productions/input-format-core';\nimport {\n MAX_ANNOUNCEMENT_MESSAGE_LENGTH,\n MAX_APP_VERSION_LENGTH,\n MAX_AWARD_NAME_LENGTH,\n MAX_CONTACT_PHONE_LENGTH,\n MAX_DISPLAY_NAME_LENGTH,\n MAX_GAME_LOCATION_MEMO_LENGTH,\n MAX_GAME_LOCATION_NAME_LENGTH,\n MAX_GAME_LOCATION_STREET_ADDRESS_LENGTH,\n MAX_GAME_LOCATION_SUB_LOCATION_LENGTH,\n MAX_GAME_MEMO_LENGTH,\n MAX_LANDING_ABOUT_TEXT_LENGTH,\n MAX_LANDING_HERO_TITLE_LENGTH,\n MAX_LEAGUE_DESCRIPTION_LENGTH,\n MAX_LEAGUE_TITLE_LENGTH,\n MAX_MAINTENANCE_MESSAGE_LENGTH,\n MAX_REPAIR_REASON_LENGTH,\n MAX_SCHEDULE_WEEK_TITLE_LENGTH,\n MAX_TEAM_INVITATION_MESSAGE_LENGTH,\n MAX_TEAM_JOIN_REQUEST_MESSAGE_LENGTH,\n MAX_TEAM_NAME_LENGTH,\n MAX_WAIVER_TEMPLATE_TEXT_LENGTH,\n MAX_WAIVER_TEMPLATE_TITLE_LENGTH,\n MIN_AWARD_NAME_LENGTH,\n MIN_DISPLAY_NAME_LENGTH,\n MIN_LEAGUE_DESCRIPTION_LENGTH,\n MIN_LEAGUE_TITLE_LENGTH,\n MIN_REPAIR_REASON_LENGTH,\n MIN_TEAM_JOIN_REQUEST_MESSAGE_LENGTH,\n MIN_TEAM_NAME_LENGTH,\n} from '../constants/business.js';\n\n/**\n * The reason an admin gives for a repair action, as `repairReasonSchema` reads it: trimmed, then\n * bounded by the repair-reason limits, with any character and line breaks allowed.\n */\nexport const REPAIR_REASON_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: MIN_REPAIR_REASON_LENGTH,\n max: MAX_REPAIR_REASON_LENGTH,\n});\n\n/**\n * The evidence an operator records for a cutover step the console cannot see done — an export's\n * operation name, a revocation's console record: trimmed, then bounded by the repair-reason limits,\n * line breaks allowed.\n */\nexport const MIGRATION_EVIDENCE_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: MIN_REPAIR_REASON_LENGTH,\n max: MAX_REPAIR_REASON_LENGTH,\n});\n\n/**\n * A player's message to the team they ask to join, as `teamJoinRequestMessageSchema` reads it:\n * trimmed, then bounded by the join-request message limits, with any character and line breaks\n * allowed.\n */\nexport const TEAM_JOIN_REQUEST_MESSAGE_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: MIN_TEAM_JOIN_REQUEST_MESSAGE_LENGTH,\n max: MAX_TEAM_JOIN_REQUEST_MESSAGE_LENGTH,\n});\n\n/**\n * The optional note a team sends with an invitation, as `teamInvitationMessageSchema` reads it:\n * trimmed, empty allowed, then bounded by the invitation note limit, line breaks allowed.\n */\nexport const TEAM_INVITATION_MESSAGE_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: 0,\n max: MAX_TEAM_INVITATION_MESSAGE_LENGTH,\n});\n\n/**\n * A scorer's note on one person in a live game (\"red shirt\", \"#9\"), as `gameMemoSchema` reads it:\n * trimmed, one line, empty allowed — an empty memo clears the note — then bounded by the memo cap.\n */\nexport const GAME_MEMO_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 0,\n max: MAX_GAME_MEMO_LENGTH,\n});\n\n/**\n * A person's display name — an account's, or a ghost player's, as both ghost doors and the admin's\n * ghost captain ask for it — as `displayNameSchema` reads it: trimmed, one line, then bounded by the\n * display-name limits.\n */\nexport const DISPLAY_NAME_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: MIN_DISPLAY_NAME_LENGTH,\n max: MAX_DISPLAY_NAME_LENGTH,\n});\n\n/**\n * A team's name, as `teamNameSchema` reads it at creation and at an admin's rename: trimmed, one\n * line, then bounded by the team-name limits.\n */\nexport const TEAM_NAME_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: MIN_TEAM_NAME_LENGTH,\n max: MAX_TEAM_NAME_LENGTH,\n});\n\n/**\n * The number the league reaches a team on, as `contactPhoneSchema` reads it: trimmed, one line,\n * required, and bounded by the contact-phone cap. It is kept as typed — no format is imposed.\n */\nexport const CONTACT_PHONE_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 1,\n max: MAX_CONTACT_PHONE_LENGTH,\n});\n\n/**\n * The account's optional default contact phone on the settings page: the contact-phone format with\n * empty allowed. Empty means remove the saved number — the form sends it as\n * `UpdateAccountSettingsInput.defaultContactPhone: null` — and any other value is held to\n * `CONTACT_PHONE_INPUT_FORMAT`'s trim, one line, and cap.\n */\nexport const OPTIONAL_CONTACT_PHONE_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 0,\n max: MAX_CONTACT_PHONE_LENGTH,\n});\n\n/**\n * An award's name in the catalog, as `awardNameSchema` reads it — at creation, sent with the award's\n * first badge: trimmed, one line, then bounded by the award-name limits.\n */\nexport const AWARD_NAME_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: MIN_AWARD_NAME_LENGTH,\n max: MAX_AWARD_NAME_LENGTH,\n});\n\n/**\n * A league's title, as `leagueTitleSchema` reads it at creation and at the settings edit: trimmed,\n * one line, then bounded by the league-title limits.\n */\nexport const LEAGUE_TITLE_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: MIN_LEAGUE_TITLE_LENGTH,\n max: MAX_LEAGUE_TITLE_LENGTH,\n});\n\n/**\n * A league's description, as `leagueDescriptionSchema` reads it at creation and at the settings\n * edit: trimmed, then bounded by the league-description limits, line breaks allowed.\n */\nexport const LEAGUE_DESCRIPTION_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: MIN_LEAGUE_DESCRIPTION_LENGTH,\n max: MAX_LEAGUE_DESCRIPTION_LENGTH,\n});\n\n/** A venue's name, as `gameLocationNameSchema` reads it: trimmed, one line, required, within its cap. */\nexport const GAME_LOCATION_NAME_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 1,\n max: MAX_GAME_LOCATION_NAME_LENGTH,\n});\n\n/**\n * A venue's street address, as `gameLocationStreetAddressSchema` reads it: trimmed, one line,\n * required, within its cap.\n */\nexport const GAME_LOCATION_STREET_ADDRESS_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 1,\n max: MAX_GAME_LOCATION_STREET_ADDRESS_LENGTH,\n});\n\n/**\n * One court, sheet, or room of a venue, as `gameLocationSubLocationSchema` reads it: trimmed, one\n * line, required, within its cap.\n */\nexport const GAME_LOCATION_SUB_LOCATION_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 1,\n max: MAX_GAME_LOCATION_SUB_LOCATION_LENGTH,\n});\n\n/**\n * A venue's memo, as `gameLocationMemoSchema` reads it: trimmed, line breaks allowed, empty allowed —\n * an empty memo means the venue has none — within its cap.\n */\nexport const GAME_LOCATION_MEMO_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: 0,\n max: MAX_GAME_LOCATION_MEMO_LENGTH,\n});\n\n/** A schedule week's name, as `scheduleWeekTitleSchema` reads it: trimmed, one line, required. */\nexport const SCHEDULE_WEEK_TITLE_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 1,\n max: MAX_SCHEDULE_WEEK_TITLE_LENGTH,\n});\n\n/**\n * A waiver template's title, as `waiverTemplateTitleSchema` reads it. A waiver is a legal artifact a\n * player signs, so its words are kept exactly as typed; it still cannot be blank.\n */\nexport const WAIVER_TEMPLATE_TITLE_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 1,\n max: MAX_WAIVER_TEMPLATE_TITLE_LENGTH,\n keepAsTyped: true,\n});\n\n/**\n * A waiver template's wording, as `waiverTemplateTextSchema` reads it: the legal text a player signs,\n * kept exactly as typed, line breaks allowed, never blank.\n */\nexport const WAIVER_TEMPLATE_TEXT_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: 1,\n max: MAX_WAIVER_TEMPLATE_TEXT_LENGTH,\n keepAsTyped: true,\n});\n\n/** The landing page's hero title: trimmed, one line; blank shows the default heading. */\nexport const LANDING_HERO_TITLE_INPUT_FORMAT = defineInputFormat({\n format: 'singleLine',\n min: 0,\n max: MAX_LANDING_HERO_TITLE_LENGTH,\n});\n\n/** The landing page's about text: trimmed, line breaks allowed, empty allowed. */\nexport const LANDING_ABOUT_TEXT_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: 0,\n max: MAX_LANDING_ABOUT_TEXT_LENGTH,\n});\n\n/** The message shown while the app is in maintenance; empty shows none. */\nexport const MAINTENANCE_MESSAGE_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: 0,\n max: MAX_MAINTENANCE_MESSAGE_LENGTH,\n});\n\n/** The site announcement banner; empty clears it. */\nexport const ANNOUNCEMENT_MESSAGE_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: 0,\n max: MAX_ANNOUNCEMENT_MESSAGE_LENGTH,\n});\n\n/** The published app version the version gate compares against; never blank. */\nexport const APP_VERSION_INPUT_FORMAT = defineInputFormat({\n format: 'none',\n min: 1,\n max: MAX_APP_VERSION_LENGTH,\n});\n"]}
@@ -1,4 +1,4 @@
1
- // The upload origin registry (MEDIA-001, MEDIA-006). One origin value binds everything an
1
+ // The upload origin registry (RULE-STD-MEDIA-001, RULE-STD-MEDIA-006). One origin value binds everything an
2
2
  // upload touches: the picker spec, the staging path and its `storage.rules` cap and content type,
3
3
  // the callable that accepts the upload, and — for a picture — the processing spec and the owner the
4
4
  // picture is published to. The generic packages treat an origin id as opaque; the policy is here.
@@ -1 +1 @@
1
- {"version":3,"file":"file-origins.js","sourceRoot":"","sources":["../../src/media/file-origins.ts"],"names":[],"mappings":"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","sourcesContent":["// The upload origin registry (MEDIA-001, MEDIA-006). One origin value binds everything an\n// upload touches: the picker spec, the staging path and its `storage.rules` cap and content type,\n// the callable that accepts the upload, and — for a picture — the processing spec and the owner the\n// picture is published to. The generic packages treat an origin id as opaque; the policy is here.\n//\n// Picture origins are processed into their kind's declared sizes and keep no master. Signature\n// origins are kept byte-exact (`preserveOriginal`) by the callable that records them and are never\n// processed. Adding an origin is one change across this registry, `storage.rules`, the processor\n// routing, and their tests; `storage.rules` restates the caps and content types, and a guard pins it\n// to this file.\n\nimport type { MediaAccept, MediaOriginSpec } from '@ttt-productions/media-schemas';\nimport { PICTURE_VARIANT_KEYS } from '../types/unions.js';\nimport type { PictureVariantKey } from '../types/unions.js';\nimport { PICTURE_ASPECT_RATIOS, pictureAspectRatio } from './picture-aspects.js';\nimport type { PictureAspect } from './picture-aspects.js';\n\nexport const PICTURE_ORIGIN_KEYS = [\n 'league-picture',\n 'team-picture',\n 'landing-picture',\n 'location-picture',\n 'award-badge',\n] as const;\nexport type PictureOrigin = (typeof PICTURE_ORIGIN_KEYS)[number];\n\nexport const SIGNATURE_ORIGIN_KEYS = ['waiver-signature', 'hardware-signature'] as const;\nexport type SignatureOrigin = (typeof SIGNATURE_ORIGIN_KEYS)[number];\n\nexport const FILE_ORIGIN_KEYS = [...PICTURE_ORIGIN_KEYS, ...SIGNATURE_ORIGIN_KEYS] as const;\nexport type FileOrigin = PictureOrigin | SignatureOrigin;\n\nconst MB = 1024 * 1024;\n\n// The raw cap is an abuse bound: the browser uploads a picture already cropped and shrunk to its\n// kind's largest size, so a real upload is far below it.\nexport const PICTURE_UPLOAD_MAX_BYTES = 25 * MB;\n\n// A photo's browser crop and every stored size are JPEG at this quality.\nexport const PICTURE_JPEG_QUALITY = 85;\n\n// What a picture kind is stored as: a photo as JPEG, a badge as PNG, which keeps the transparent\n// corners of a badge drawn round.\nexport const PICTURE_STORED_FORMAT_KEYS = ['jpeg', 'png'] as const;\nexport type PictureStoredFormat = (typeof PICTURE_STORED_FORMAT_KEYS)[number];\n\n// The cropper's mask: a rectangle, or a circle for a badge — which is how a badge is drawn, though\n// the stored image is square.\nexport const PICTURE_CROP_SHAPE_KEYS = ['rect', 'round'] as const;\nexport type PictureCropShape = (typeof PICTURE_CROP_SHAPE_KEYS)[number];\n\n// Picture formats the server's byte inspection accepts. Scriptable or unproven formats (svg, heic,\n// avif) are not accepted.\nexport const PICTURE_ACCEPTED_FORMATS = ['jpeg', 'png', 'gif', 'webp', 'bmp', 'tiff'] as const;\n\nexport const PICTURE_ACCEPT: MediaAccept = {\n kinds: ['image'],\n formats: [...PICTURE_ACCEPTED_FORMATS],\n};\n\nexport interface PictureSize {\n readonly width: number;\n readonly height: number;\n}\n\nexport interface PictureKind {\n /** The shape the browser crops to, the server re-crops to, and every display frames. */\n readonly aspect: PictureAspect;\n /** Each stored size, at the kind's shape. `lg` is also the size the browser crops to. */\n readonly sizes: Readonly<Record<PictureVariantKey, PictureSize>>;\n /** The format of the browser's crop and of every stored size. */\n readonly format: PictureStoredFormat;\n readonly cropShape: PictureCropShape;\n}\n\nexport const PICTURE_KINDS = {\n 'league-picture': {\n aspect: 'landscape',\n format: 'jpeg',\n cropShape: 'rect',\n sizes: {\n sm: { width: 480, height: 270 },\n md: { width: 960, height: 540 },\n lg: { width: 1920, height: 1080 },\n },\n },\n 'team-picture': {\n aspect: 'classic',\n format: 'jpeg',\n cropShape: 'rect',\n sizes: {\n sm: { width: 480, height: 360 },\n md: { width: 960, height: 720 },\n lg: { width: 1600, height: 1200 },\n },\n },\n 'landing-picture': {\n aspect: 'banner',\n format: 'jpeg',\n cropShape: 'rect',\n sizes: {\n sm: { width: 720, height: 240 },\n md: { width: 1440, height: 480 },\n lg: { width: 2400, height: 800 },\n },\n },\n 'location-picture': {\n aspect: 'landscape',\n format: 'jpeg',\n cropShape: 'rect',\n sizes: {\n sm: { width: 480, height: 270 },\n md: { width: 960, height: 540 },\n lg: { width: 1280, height: 720 },\n },\n },\n // A badge is cropped in a round mask and kept square, at the sizes every award has always had.\n 'award-badge': {\n aspect: 'square',\n format: 'png',\n cropShape: 'round',\n sizes: {\n sm: { width: 64, height: 64 },\n md: { width: 256, height: 256 },\n lg: { width: 512, height: 512 },\n },\n },\n} as const satisfies Record<PictureOrigin, PictureKind>;\n\nfunction pictureOriginSpec(kind: PictureKind): MediaOriginSpec {\n const ratio = pictureAspectRatio(kind.aspect);\n const { width, height } = PICTURE_ASPECT_RATIOS[kind.aspect];\n const variants = PICTURE_VARIANT_KEYS.map((key) => ({\n key,\n crop: {\n width: kind.sizes[key].width,\n height: kind.sizes[key].height,\n gravity: 'center' as const,\n },\n format: kind.format,\n ...(kind.format === 'jpeg' ? { quality: PICTURE_JPEG_QUALITY } : {}),\n }));\n return {\n kind: 'image',\n accept: PICTURE_ACCEPT,\n maxBytes: PICTURE_UPLOAD_MAX_BYTES,\n imageCrop: {\n aspectRatio: ratio,\n outputWidth: kind.sizes.lg.width,\n outputHeight: kind.sizes.lg.height,\n shape: kind.cropShape,\n format: kind.format,\n ...(kind.format === 'jpeg' ? { quality: PICTURE_JPEG_QUALITY } : {}),\n aspectRatioDisplay: `${width}:${height}`,\n },\n client: {\n allowPick: true,\n allowCapturePhoto: true,\n allowRecordVideo: false,\n allowRecordAudio: false,\n },\n processing: {\n // The server trusts nothing the browser did: an upload that bypassed the app's cropper is\n // centre-cropped to the kind's shape (`requiredAspectRatio` with `allowAutoFormat`) before\n // every size is cut, and a source smaller than a size is never enlarged.\n image: {\n kind: 'image',\n accept: PICTURE_ACCEPT,\n maxBytes: PICTURE_UPLOAD_MAX_BYTES,\n requiredAspectRatio: ratio,\n allowAutoFormat: true,\n image: { variants, stripMetadata: true },\n },\n },\n };\n}\n\n// A signature is drawn on a canvas and exported as PNG, so exactly `image/png` is accepted — no\n// other image type, and never the neutral octet-stream lane. It is kept byte-exact by the callable\n// that records it, and nothing processes it.\nexport const SIGNATURE_UPLOAD_MAX_BYTES = 1 * MB;\nexport const SIGNATURE_CONTENT_TYPE = 'image/png';\n\nexport const SIGNATURE_ACCEPT: MediaAccept = {\n kinds: ['file'],\n mimes: [SIGNATURE_CONTENT_TYPE],\n};\n\nfunction signatureOriginSpec(): MediaOriginSpec {\n return {\n kind: 'generic',\n accept: SIGNATURE_ACCEPT,\n maxBytes: SIGNATURE_UPLOAD_MAX_BYTES,\n client: {\n allowPick: false,\n allowCapturePhoto: false,\n allowRecordVideo: false,\n allowRecordAudio: false,\n },\n preserveOriginal: true,\n };\n}\n\nexport const MEDIA_ORIGIN_SPECS: Readonly<Record<FileOrigin, MediaOriginSpec>> = {\n 'league-picture': pictureOriginSpec(PICTURE_KINDS['league-picture']),\n 'team-picture': pictureOriginSpec(PICTURE_KINDS['team-picture']),\n 'landing-picture': pictureOriginSpec(PICTURE_KINDS['landing-picture']),\n 'location-picture': pictureOriginSpec(PICTURE_KINDS['location-picture']),\n 'award-badge': pictureOriginSpec(PICTURE_KINDS['award-badge']),\n // Promoted to its kept path by the waiver-signing callable.\n 'waiver-signature': signatureOriginSpec(),\n // Promoted to its kept path by the hardware contract's signing callable.\n 'hardware-signature': signatureOriginSpec(),\n};\n\nexport function isFileOrigin(value: string): value is FileOrigin {\n return (FILE_ORIGIN_KEYS as readonly string[]).includes(value);\n}\n\nexport function isPictureOrigin(value: string): value is PictureOrigin {\n return (PICTURE_ORIGIN_KEYS as readonly string[]).includes(value);\n}\n\nexport function isSignatureOrigin(value: string): value is SignatureOrigin {\n return (SIGNATURE_ORIGIN_KEYS as readonly string[]).includes(value);\n}\n"]}
1
+ {"version":3,"file":"file-origins.js","sourceRoot":"","sources":["../../src/media/file-origins.ts"],"names":[],"mappings":"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","sourcesContent":["// The upload origin registry (RULE-STD-MEDIA-001, RULE-STD-MEDIA-006). One origin value binds everything an\n// upload touches: the picker spec, the staging path and its `storage.rules` cap and content type,\n// the callable that accepts the upload, and — for a picture — the processing spec and the owner the\n// picture is published to. The generic packages treat an origin id as opaque; the policy is here.\n//\n// Picture origins are processed into their kind's declared sizes and keep no master. Signature\n// origins are kept byte-exact (`preserveOriginal`) by the callable that records them and are never\n// processed. Adding an origin is one change across this registry, `storage.rules`, the processor\n// routing, and their tests; `storage.rules` restates the caps and content types, and a guard pins it\n// to this file.\n\nimport type { MediaAccept, MediaOriginSpec } from '@ttt-productions/media-schemas';\nimport { PICTURE_VARIANT_KEYS } from '../types/unions.js';\nimport type { PictureVariantKey } from '../types/unions.js';\nimport { PICTURE_ASPECT_RATIOS, pictureAspectRatio } from './picture-aspects.js';\nimport type { PictureAspect } from './picture-aspects.js';\n\nexport const PICTURE_ORIGIN_KEYS = [\n 'league-picture',\n 'team-picture',\n 'landing-picture',\n 'location-picture',\n 'award-badge',\n] as const;\nexport type PictureOrigin = (typeof PICTURE_ORIGIN_KEYS)[number];\n\nexport const SIGNATURE_ORIGIN_KEYS = ['waiver-signature', 'hardware-signature'] as const;\nexport type SignatureOrigin = (typeof SIGNATURE_ORIGIN_KEYS)[number];\n\nexport const FILE_ORIGIN_KEYS = [...PICTURE_ORIGIN_KEYS, ...SIGNATURE_ORIGIN_KEYS] as const;\nexport type FileOrigin = PictureOrigin | SignatureOrigin;\n\nconst MB = 1024 * 1024;\n\n// The raw cap is an abuse bound: the browser uploads a picture already cropped and shrunk to its\n// kind's largest size, so a real upload is far below it.\nexport const PICTURE_UPLOAD_MAX_BYTES = 25 * MB;\n\n// A photo's browser crop and every stored size are JPEG at this quality.\nexport const PICTURE_JPEG_QUALITY = 85;\n\n// What a picture kind is stored as: a photo as JPEG, a badge as PNG, which keeps the transparent\n// corners of a badge drawn round.\nexport const PICTURE_STORED_FORMAT_KEYS = ['jpeg', 'png'] as const;\nexport type PictureStoredFormat = (typeof PICTURE_STORED_FORMAT_KEYS)[number];\n\n// The cropper's mask: a rectangle, or a circle for a badge — which is how a badge is drawn, though\n// the stored image is square.\nexport const PICTURE_CROP_SHAPE_KEYS = ['rect', 'round'] as const;\nexport type PictureCropShape = (typeof PICTURE_CROP_SHAPE_KEYS)[number];\n\n// Picture formats the server's byte inspection accepts. Scriptable or unproven formats (svg, heic,\n// avif) are not accepted.\nexport const PICTURE_ACCEPTED_FORMATS = ['jpeg', 'png', 'gif', 'webp', 'bmp', 'tiff'] as const;\n\nexport const PICTURE_ACCEPT: MediaAccept = {\n kinds: ['image'],\n formats: [...PICTURE_ACCEPTED_FORMATS],\n};\n\nexport interface PictureSize {\n readonly width: number;\n readonly height: number;\n}\n\nexport interface PictureKind {\n /** The shape the browser crops to, the server re-crops to, and every display frames. */\n readonly aspect: PictureAspect;\n /** Each stored size, at the kind's shape. `lg` is also the size the browser crops to. */\n readonly sizes: Readonly<Record<PictureVariantKey, PictureSize>>;\n /** The format of the browser's crop and of every stored size. */\n readonly format: PictureStoredFormat;\n readonly cropShape: PictureCropShape;\n}\n\nexport const PICTURE_KINDS = {\n 'league-picture': {\n aspect: 'landscape',\n format: 'jpeg',\n cropShape: 'rect',\n sizes: {\n sm: { width: 480, height: 270 },\n md: { width: 960, height: 540 },\n lg: { width: 1920, height: 1080 },\n },\n },\n 'team-picture': {\n aspect: 'classic',\n format: 'jpeg',\n cropShape: 'rect',\n sizes: {\n sm: { width: 480, height: 360 },\n md: { width: 960, height: 720 },\n lg: { width: 1600, height: 1200 },\n },\n },\n 'landing-picture': {\n aspect: 'banner',\n format: 'jpeg',\n cropShape: 'rect',\n sizes: {\n sm: { width: 720, height: 240 },\n md: { width: 1440, height: 480 },\n lg: { width: 2400, height: 800 },\n },\n },\n 'location-picture': {\n aspect: 'landscape',\n format: 'jpeg',\n cropShape: 'rect',\n sizes: {\n sm: { width: 480, height: 270 },\n md: { width: 960, height: 540 },\n lg: { width: 1280, height: 720 },\n },\n },\n // A badge is cropped in a round mask and kept square, at the sizes every award has always had.\n 'award-badge': {\n aspect: 'square',\n format: 'png',\n cropShape: 'round',\n sizes: {\n sm: { width: 64, height: 64 },\n md: { width: 256, height: 256 },\n lg: { width: 512, height: 512 },\n },\n },\n} as const satisfies Record<PictureOrigin, PictureKind>;\n\nfunction pictureOriginSpec(kind: PictureKind): MediaOriginSpec {\n const ratio = pictureAspectRatio(kind.aspect);\n const { width, height } = PICTURE_ASPECT_RATIOS[kind.aspect];\n const variants = PICTURE_VARIANT_KEYS.map((key) => ({\n key,\n crop: {\n width: kind.sizes[key].width,\n height: kind.sizes[key].height,\n gravity: 'center' as const,\n },\n format: kind.format,\n ...(kind.format === 'jpeg' ? { quality: PICTURE_JPEG_QUALITY } : {}),\n }));\n return {\n kind: 'image',\n accept: PICTURE_ACCEPT,\n maxBytes: PICTURE_UPLOAD_MAX_BYTES,\n imageCrop: {\n aspectRatio: ratio,\n outputWidth: kind.sizes.lg.width,\n outputHeight: kind.sizes.lg.height,\n shape: kind.cropShape,\n format: kind.format,\n ...(kind.format === 'jpeg' ? { quality: PICTURE_JPEG_QUALITY } : {}),\n aspectRatioDisplay: `${width}:${height}`,\n },\n client: {\n allowPick: true,\n allowCapturePhoto: true,\n allowRecordVideo: false,\n allowRecordAudio: false,\n },\n processing: {\n // The server trusts nothing the browser did: an upload that bypassed the app's cropper is\n // centre-cropped to the kind's shape (`requiredAspectRatio` with `allowAutoFormat`) before\n // every size is cut, and a source smaller than a size is never enlarged.\n image: {\n kind: 'image',\n accept: PICTURE_ACCEPT,\n maxBytes: PICTURE_UPLOAD_MAX_BYTES,\n requiredAspectRatio: ratio,\n allowAutoFormat: true,\n image: { variants, stripMetadata: true },\n },\n },\n };\n}\n\n// A signature is drawn on a canvas and exported as PNG, so exactly `image/png` is accepted — no\n// other image type, and never the neutral octet-stream lane. It is kept byte-exact by the callable\n// that records it, and nothing processes it.\nexport const SIGNATURE_UPLOAD_MAX_BYTES = 1 * MB;\nexport const SIGNATURE_CONTENT_TYPE = 'image/png';\n\nexport const SIGNATURE_ACCEPT: MediaAccept = {\n kinds: ['file'],\n mimes: [SIGNATURE_CONTENT_TYPE],\n};\n\nfunction signatureOriginSpec(): MediaOriginSpec {\n return {\n kind: 'generic',\n accept: SIGNATURE_ACCEPT,\n maxBytes: SIGNATURE_UPLOAD_MAX_BYTES,\n client: {\n allowPick: false,\n allowCapturePhoto: false,\n allowRecordVideo: false,\n allowRecordAudio: false,\n },\n preserveOriginal: true,\n };\n}\n\nexport const MEDIA_ORIGIN_SPECS: Readonly<Record<FileOrigin, MediaOriginSpec>> = {\n 'league-picture': pictureOriginSpec(PICTURE_KINDS['league-picture']),\n 'team-picture': pictureOriginSpec(PICTURE_KINDS['team-picture']),\n 'landing-picture': pictureOriginSpec(PICTURE_KINDS['landing-picture']),\n 'location-picture': pictureOriginSpec(PICTURE_KINDS['location-picture']),\n 'award-badge': pictureOriginSpec(PICTURE_KINDS['award-badge']),\n // Promoted to its kept path by the waiver-signing callable.\n 'waiver-signature': signatureOriginSpec(),\n // Promoted to its kept path by the hardware contract's signing callable.\n 'hardware-signature': signatureOriginSpec(),\n};\n\nexport function isFileOrigin(value: string): value is FileOrigin {\n return (FILE_ORIGIN_KEYS as readonly string[]).includes(value);\n}\n\nexport function isPictureOrigin(value: string): value is PictureOrigin {\n return (PICTURE_ORIGIN_KEYS as readonly string[]).includes(value);\n}\n\nexport function isSignatureOrigin(value: string): value is SignatureOrigin {\n return (SIGNATURE_ORIGIN_KEYS as readonly string[]).includes(value);\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  // Render-time picture URLs, pure and configuration-injected: q-core reads no environment, and the
2
2
  // app resolves its Storage origin and bucket once and builds one builder. A document stores picture
3
3
  // ids and never a URL; every render builds the URL from the id and the size it needs
4
- // (MEDIA-101, BACKEND-001).
4
+ // (RULE-STD-MEDIA-101, RULE-STD-BACKEND-001).
5
5
  import { buildPictureObjectPath } from '../paths/storage-paths.js';
6
6
  /**
7
7
  * The URL is the object's media endpoint with no download token, so it resolves only through a
@@ -1 +1 @@
1
- {"version":3,"file":"picture-url.js","sourceRoot":"","sources":["../../src/media/picture-url.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,oGAAoG;AACpG,qFAAqF;AACrF,4BAA4B;AAE5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAWnE;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAA+B;IACrE,MAAM,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACxD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC1B,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,CAAC,SAAS,EAAE,UAAU,EAAE,EAAE;QAC/B,MAAM,UAAU,GAAG,kBAAkB,CAAC,sBAAsB,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;QACrF,OAAO,GAAG,MAAM,SAAS,MAAM,MAAM,UAAU,YAAY,CAAC;IAC9D,CAAC,CAAC;AACJ,CAAC","sourcesContent":["// Render-time picture URLs, pure and configuration-injected: q-core reads no environment, and the\n// app resolves its Storage origin and bucket once and builds one builder. A document stores picture\n// ids and never a URL; every render builds the URL from the id and the size it needs\n// (MEDIA-101, BACKEND-001).\n\nimport { buildPictureObjectPath } from '../paths/storage-paths.js';\nimport type { PictureVariantKey } from '../types/unions.js';\n\nexport interface PictureUrlBuilderConfig {\n /** The origin serving the bucket's objects: Firebase Storage, or the local Storage emulator. */\n storageOrigin: string;\n bucket: string;\n}\n\nexport type PictureUrlBuilder = (pictureId: string, variantKey: PictureVariantKey) => string;\n\n/**\n * The URL is the object's media endpoint with no download token, so it resolves only through a\n * public-read `storage.rules` grant on the picture prefix — which is what lets every viewer, signed\n * in or not, load it.\n */\nexport function createPictureUrlBuilder(config: PictureUrlBuilderConfig): PictureUrlBuilder {\n const origin = config.storageOrigin.replace(/\\/+$/, '');\n const { bucket } = config;\n if (!origin || !bucket) {\n throw new Error('createPictureUrlBuilder: storageOrigin and bucket are required');\n }\n return (pictureId, variantKey) => {\n const objectPath = encodeURIComponent(buildPictureObjectPath(pictureId, variantKey));\n return `${origin}/v0/b/${bucket}/o/${objectPath}?alt=media`;\n };\n}\n"]}
1
+ {"version":3,"file":"picture-url.js","sourceRoot":"","sources":["../../src/media/picture-url.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,oGAAoG;AACpG,qFAAqF;AACrF,8CAA8C;AAE9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAWnE;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAA+B;IACrE,MAAM,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACxD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC1B,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,CAAC,SAAS,EAAE,UAAU,EAAE,EAAE;QAC/B,MAAM,UAAU,GAAG,kBAAkB,CAAC,sBAAsB,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;QACrF,OAAO,GAAG,MAAM,SAAS,MAAM,MAAM,UAAU,YAAY,CAAC;IAC9D,CAAC,CAAC;AACJ,CAAC","sourcesContent":["// Render-time picture URLs, pure and configuration-injected: q-core reads no environment, and the\n// app resolves its Storage origin and bucket once and builds one builder. A document stores picture\n// ids and never a URL; every render builds the URL from the id and the size it needs\n// (RULE-STD-MEDIA-101, RULE-STD-BACKEND-001).\n\nimport { buildPictureObjectPath } from '../paths/storage-paths.js';\nimport type { PictureVariantKey } from '../types/unions.js';\n\nexport interface PictureUrlBuilderConfig {\n /** The origin serving the bucket's objects: Firebase Storage, or the local Storage emulator. */\n storageOrigin: string;\n bucket: string;\n}\n\nexport type PictureUrlBuilder = (pictureId: string, variantKey: PictureVariantKey) => string;\n\n/**\n * The URL is the object's media endpoint with no download token, so it resolves only through a\n * public-read `storage.rules` grant on the picture prefix — which is what lets every viewer, signed\n * in or not, load it.\n */\nexport function createPictureUrlBuilder(config: PictureUrlBuilderConfig): PictureUrlBuilder {\n const origin = config.storageOrigin.replace(/\\/+$/, '');\n const { bucket } = config;\n if (!origin || !bucket) {\n throw new Error('createPictureUrlBuilder: storageOrigin and bucket are required');\n }\n return (pictureId, variantKey) => {\n const objectPath = encodeURIComponent(buildPictureObjectPath(pictureId, variantKey));\n return `${origin}/v0/b/${bucket}/o/${objectPath}?alt=media`;\n };\n}\n"]}
@@ -1,4 +1,4 @@
1
- // The one declaration of every cutover action id (ENG-002). The manifest, the console's callables
1
+ // The one declaration of every cutover action id (RULE-STD-ENG-002). The manifest, the console's callables
2
2
  // and screen, and every delete gate name an action by the id declared here — never by a string
3
3
  // literal at the call site, where a typo is a gate that silently never matches.
4
4
  //
@@ -1 +1 @@
1
- {"version":3,"file":"action-ids.js","sourceRoot":"","sources":["../../src/migration-console/action-ids.ts"],"names":[],"mappings":"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","sourcesContent":["// The one declaration of every cutover action id (ENG-002). The manifest, the console's callables\n// and screen, and every delete gate name an action by the id declared here — never by a string\n// literal at the call site, where a typo is a gate that silently never matches.\n//\n// An id is declared even before its action exists: a destructive core requires its verifications\n// by id, so it refuses to run until each one exists and has passed — failing closed, rather than\n// resting on a precondition written in prose that nothing enforces.\n\nexport const MIGRATION_ACTION_IDS = {\n // Accounts, identity, and global roles.\n copyUserProfiles: 'copy.userProfiles',\n copyHistoricalStats: 'copy.historicalStats',\n deriveReservedDisplayNames: 'derive.reservedDisplayNames',\n migratePublicUsers: 'migrate.publicUsers',\n copyAdminList: 'copy.adminList',\n verifyUserProfiles: 'verify.userProfiles',\n verifyHistoricalStats: 'verify.historicalStats',\n verifyReservedDisplayNames: 'verify.reservedDisplayNames',\n verifyPublicUsers: 'verify.publicUsers',\n verifyAdminList: 'verify.adminList',\n cleanupOrphanGhostPublicUsers: 'cleanup.orphanGhostPublicUsers',\n deleteV1Users: 'delete.v1Users',\n deleteV1AdminRoles: 'delete.v1AdminRoles',\n\n // Required by the ghost cleanup, whose probes read `teamMemberships` and `gameRosterPlayers`:\n // until those copies are verified complete, every ghost reads as unreferenced.\n verifyTeamMemberships: 'verify.teamMemberships',\n verifyGameRosterPlayers: 'verify.gameRosterPlayers',\n\n // Teams, memberships, and game rosters.\n copyLeagueTeams: 'copy.leagueTeams',\n verifyLeagueTeams: 'verify.leagueTeams',\n deleteV1Teams: 'delete.v1Teams',\n copyTeamMemberships: 'copy.teamMemberships',\n deleteV1TeamMembers: 'delete.v1TeamMembers',\n // The ways onto a team. Each copy reads the copied `teamMemberships` to close a pending invitation\n // or request its player already answered by joining, or to leave out a signup joining left behind,\n // so each runs after `copy.teamMemberships`.\n copyTeamInvitations: 'copy.teamInvitations',\n verifyTeamInvitations: 'verify.teamInvitations',\n deleteV1TeamInvites: 'delete.v1TeamInvites',\n copyTeamJoinRequests: 'copy.teamJoinRequests',\n verifyTeamJoinRequests: 'verify.teamJoinRequests',\n deleteV1JoinRequests: 'delete.v1JoinRequests',\n copyLeagueFreeAgents: 'copy.leagueFreeAgents',\n verifyLeagueFreeAgents: 'verify.leagueFreeAgents',\n deleteV1FreeAgentSignups: 'delete.v1FreeAgentSignups',\n copyGameRosterPlayers: 'copy.gameRosterPlayers',\n deleteV1GameRosters: 'delete.v1GameRosters',\n // The games open at the cutover, reconciled once against the copied memberships. It removes the\n // stale entries a copied roster holds, so it waits for the memberships and the game stats — which\n // pin an entry holding a recorded stat — to be copied and verified.\n reconcileOpenGameRosters: 'reconcile.openGameRosters',\n verifyGameStats: 'verify.gameStats',\n // Informational: each user whose V1 league total differs from the sum of their rebuilt per-membership\n // rows. It gates nothing; a summary is rebuilt by design.\n verifyLeaguePlayerStats: 'verify.leaguePlayerStats',\n\n // Games. `copy.sportGames` is the one reader of each V1 game, writing its schedule fields and its\n // stats documents together; the V1 game document goes last, after its roster.\n copySportGames: 'copy.sportGames',\n // A recorded stat line no roster entry or play places: the admin names its team, and the game's\n // copy runs again.\n resolveUnrosteredStatLine: 'resolve.unrosteredStatLine',\n verifySportGames: 'verify.sportGames',\n deleteV1LeagueGames: 'delete.v1LeagueGames',\n\n // Signed waivers: the records, and their signature files, which stay where V1 put them.\n copySignedWaivers: 'copy.signedWaivers',\n verifySignedWaivers: 'verify.signedWaivers',\n verifySignatureFilesResolvable: 'verify.signatureFilesResolvable',\n revokeV1SignatureDownloadTokens: 'revoke.v1SignatureDownloadTokens',\n deleteV1WaiverSignatures: 'delete.v1WaiverSignatures',\n\n // Hardware loan contracts: the records, and their signature files, which stay where V1 put them.\n // The two signature-file actions above walk the contracts after the waivers.\n copyHardwareLoanContracts: 'copy.hardwareLoanContracts',\n verifyHardwareLoanContracts: 'verify.hardwareLoanContracts',\n deleteV1HardwareContracts: 'delete.v1HardwareContracts',\n\n // Leagues.\n copySportLeagues: 'copy.sportLeagues',\n verifySportLeagues: 'verify.sportLeagues',\n deleteV1Leagues: 'delete.v1Leagues',\n // The parked chat documents under a V1 league, which keep the league alive until they go.\n deleteV1LeagueChats: 'delete.v1LeagueChats',\n\n // The V1 league, landing, venue, and award badge images, fed through the upload pipeline. The\n // verification is required by every V1 delete whose documents the carry reads images from.\n carryV1Pictures: 'carry.v1Pictures',\n verifyV1Pictures: 'verify.v1Pictures',\n\n // App configuration: `landing/base` splits into the three `_appConfig` documents.\n copyAppConfig: 'copy.appConfig',\n copyLandingPageConfig: 'copy.landingPageConfig',\n copyStatsBeacon: 'copy.statsBeacon',\n verifyAppConfig: 'verify.appConfig',\n verifyLandingPageConfig: 'verify.landingPageConfig',\n verifyStatsBeacon: 'verify.statsBeacon',\n deleteV1LandingBase: 'delete.v1LandingBase',\n\n // Waiver templates, each carried with its wording as version 1.\n copyWaiverTemplates: 'copy.waiverTemplates',\n verifyWaiverTemplates: 'verify.waiverTemplates',\n deleteV1Waivers: 'delete.v1Waivers',\n\n // The award catalog: the `landing/admin` awards array becomes one document per award, its badge\n // carried by `carry.v1Pictures`.\n copyAwardDefinitions: 'copy.awardDefinitions',\n verifyAwardDefinitions: 'verify.awardDefinitions',\n deleteV1AwardCatalog: 'delete.v1AwardCatalog',\n\n // Game locations: the `landing/admin` locations array becomes one document per venue.\n copyGameLocations: 'copy.gameLocations',\n verifyGameLocations: 'verify.gameLocations',\n deleteV1GameLocations: 'delete.v1GameLocations',\n\n // The admin dashboard counts are counted live, never stored; the V1 collection is deleted.\n deleteV1AdminStats: 'delete.v1AdminStats',\n\n // The store and team payments are deleted, not copied, once nothing in them is a completed\n // purchase.\n verifyV1CommerceDiscardable: 'verify.v1CommerceDiscardable',\n deleteV1Commerce: 'delete.v1Commerce',\n\n // The derived statistics, rebuilt from the copied games by the Fix stats actions, then validated.\n // The console records each run the operator makes from the Stats group; it never runs a second\n // calculator of its own.\n rebuildLeagueStats: 'rebuild.leagueStats',\n rebuildLifetimeStats: 'rebuild.lifetimeStats',\n rebuildSportHallOfFame: 'rebuild.sportHallOfFame',\n validateStats: 'validate.stats',\n // The V1 hall of fame is rebuilt, never copied: its delete waits for the rebuilt documents.\n verifySportHallOfFame: 'verify.sportHallOfFame',\n deleteV1HallOfFame: 'delete.v1HallOfFame',\n // V1 validation reports describe V1 aggregates at a past moment; nothing is copied.\n deleteV1ValidationReports: 'delete.v1ValidationReports',\n\n // The console's own marker: from here on the product owns every destination.\n recordCutoverLive: 'record.cutoverLive',\n\n // Operator checkpoints — external work the app cannot perform, recorded with its evidence.\n checkpointV1FirestoreExport: 'checkpoint.v1FirestoreExport',\n checkpointPaymentCredentialsRetired: 'checkpoint.paymentCredentialsRetired',\n checkpointServiceAccountKeyRevoked: 'checkpoint.serviceAccountKeyRevoked',\n checkpointUnattachedPicturesReuploaded: 'checkpoint.unattachedPicturesReuploaded',\n // The gate the abandoned-registration sweep reads: it deletes nothing until this is recorded.\n checkpointAbandonedRegistrationSweepEnabled: 'checkpoint.abandonedRegistrationSweepEnabled',\n} as const;\n\nexport type MigrationActionId = (typeof MIGRATION_ACTION_IDS)[keyof typeof MIGRATION_ACTION_IDS];\n\n/** Every action id, for the wire schemas that name one. */\nexport const MIGRATION_ACTION_ID_KEYS = Object.values(MIGRATION_ACTION_IDS) as [\n MigrationActionId,\n ...MigrationActionId[],\n];\n"]}
1
+ {"version":3,"file":"action-ids.js","sourceRoot":"","sources":["../../src/migration-console/action-ids.ts"],"names":[],"mappings":"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","sourcesContent":["// The one declaration of every cutover action id (RULE-STD-ENG-002). The manifest, the console's callables\n// and screen, and every delete gate name an action by the id declared here — never by a string\n// literal at the call site, where a typo is a gate that silently never matches.\n//\n// An id is declared even before its action exists: a destructive core requires its verifications\n// by id, so it refuses to run until each one exists and has passed — failing closed, rather than\n// resting on a precondition written in prose that nothing enforces.\n\nexport const MIGRATION_ACTION_IDS = {\n // Accounts, identity, and global roles.\n copyUserProfiles: 'copy.userProfiles',\n copyHistoricalStats: 'copy.historicalStats',\n deriveReservedDisplayNames: 'derive.reservedDisplayNames',\n migratePublicUsers: 'migrate.publicUsers',\n copyAdminList: 'copy.adminList',\n verifyUserProfiles: 'verify.userProfiles',\n verifyHistoricalStats: 'verify.historicalStats',\n verifyReservedDisplayNames: 'verify.reservedDisplayNames',\n verifyPublicUsers: 'verify.publicUsers',\n verifyAdminList: 'verify.adminList',\n cleanupOrphanGhostPublicUsers: 'cleanup.orphanGhostPublicUsers',\n deleteV1Users: 'delete.v1Users',\n deleteV1AdminRoles: 'delete.v1AdminRoles',\n\n // Required by the ghost cleanup, whose probes read `teamMemberships` and `gameRosterPlayers`:\n // until those copies are verified complete, every ghost reads as unreferenced.\n verifyTeamMemberships: 'verify.teamMemberships',\n verifyGameRosterPlayers: 'verify.gameRosterPlayers',\n\n // Teams, memberships, and game rosters.\n copyLeagueTeams: 'copy.leagueTeams',\n verifyLeagueTeams: 'verify.leagueTeams',\n deleteV1Teams: 'delete.v1Teams',\n copyTeamMemberships: 'copy.teamMemberships',\n deleteV1TeamMembers: 'delete.v1TeamMembers',\n // The ways onto a team. Each copy reads the copied `teamMemberships` to close a pending invitation\n // or request its player already answered by joining, or to leave out a signup joining left behind,\n // so each runs after `copy.teamMemberships`.\n copyTeamInvitations: 'copy.teamInvitations',\n verifyTeamInvitations: 'verify.teamInvitations',\n deleteV1TeamInvites: 'delete.v1TeamInvites',\n copyTeamJoinRequests: 'copy.teamJoinRequests',\n verifyTeamJoinRequests: 'verify.teamJoinRequests',\n deleteV1JoinRequests: 'delete.v1JoinRequests',\n copyLeagueFreeAgents: 'copy.leagueFreeAgents',\n verifyLeagueFreeAgents: 'verify.leagueFreeAgents',\n deleteV1FreeAgentSignups: 'delete.v1FreeAgentSignups',\n copyGameRosterPlayers: 'copy.gameRosterPlayers',\n deleteV1GameRosters: 'delete.v1GameRosters',\n // The games open at the cutover, reconciled once against the copied memberships. It removes the\n // stale entries a copied roster holds, so it waits for the memberships and the game stats — which\n // pin an entry holding a recorded stat — to be copied and verified.\n reconcileOpenGameRosters: 'reconcile.openGameRosters',\n verifyGameStats: 'verify.gameStats',\n // Informational: each user whose V1 league total differs from the sum of their rebuilt per-membership\n // rows. It gates nothing; a summary is rebuilt by design.\n verifyLeaguePlayerStats: 'verify.leaguePlayerStats',\n\n // Games. `copy.sportGames` is the one reader of each V1 game, writing its schedule fields and its\n // stats documents together; the V1 game document goes last, after its roster.\n copySportGames: 'copy.sportGames',\n // A recorded stat line no roster entry or play places: the admin names its team, and the game's\n // copy runs again.\n resolveUnrosteredStatLine: 'resolve.unrosteredStatLine',\n verifySportGames: 'verify.sportGames',\n deleteV1LeagueGames: 'delete.v1LeagueGames',\n\n // Signed waivers: the records, and their signature files, which stay where V1 put them.\n copySignedWaivers: 'copy.signedWaivers',\n verifySignedWaivers: 'verify.signedWaivers',\n verifySignatureFilesResolvable: 'verify.signatureFilesResolvable',\n revokeV1SignatureDownloadTokens: 'revoke.v1SignatureDownloadTokens',\n deleteV1WaiverSignatures: 'delete.v1WaiverSignatures',\n\n // Hardware loan contracts: the records, and their signature files, which stay where V1 put them.\n // The two signature-file actions above walk the contracts after the waivers.\n copyHardwareLoanContracts: 'copy.hardwareLoanContracts',\n verifyHardwareLoanContracts: 'verify.hardwareLoanContracts',\n deleteV1HardwareContracts: 'delete.v1HardwareContracts',\n\n // Leagues.\n copySportLeagues: 'copy.sportLeagues',\n verifySportLeagues: 'verify.sportLeagues',\n deleteV1Leagues: 'delete.v1Leagues',\n // The parked chat documents under a V1 league, which keep the league alive until they go.\n deleteV1LeagueChats: 'delete.v1LeagueChats',\n\n // The V1 league, landing, venue, and award badge images, fed through the upload pipeline. The\n // verification is required by every V1 delete whose documents the carry reads images from.\n carryV1Pictures: 'carry.v1Pictures',\n verifyV1Pictures: 'verify.v1Pictures',\n\n // App configuration: `landing/base` splits into the three `_appConfig` documents.\n copyAppConfig: 'copy.appConfig',\n copyLandingPageConfig: 'copy.landingPageConfig',\n copyStatsBeacon: 'copy.statsBeacon',\n verifyAppConfig: 'verify.appConfig',\n verifyLandingPageConfig: 'verify.landingPageConfig',\n verifyStatsBeacon: 'verify.statsBeacon',\n deleteV1LandingBase: 'delete.v1LandingBase',\n\n // Waiver templates, each carried with its wording as version 1.\n copyWaiverTemplates: 'copy.waiverTemplates',\n verifyWaiverTemplates: 'verify.waiverTemplates',\n deleteV1Waivers: 'delete.v1Waivers',\n\n // The award catalog: the `landing/admin` awards array becomes one document per award, its badge\n // carried by `carry.v1Pictures`.\n copyAwardDefinitions: 'copy.awardDefinitions',\n verifyAwardDefinitions: 'verify.awardDefinitions',\n deleteV1AwardCatalog: 'delete.v1AwardCatalog',\n\n // Game locations: the `landing/admin` locations array becomes one document per venue.\n copyGameLocations: 'copy.gameLocations',\n verifyGameLocations: 'verify.gameLocations',\n deleteV1GameLocations: 'delete.v1GameLocations',\n\n // The admin dashboard counts are counted live, never stored; the V1 collection is deleted.\n deleteV1AdminStats: 'delete.v1AdminStats',\n\n // The store and team payments are deleted, not copied, once nothing in them is a completed\n // purchase.\n verifyV1CommerceDiscardable: 'verify.v1CommerceDiscardable',\n deleteV1Commerce: 'delete.v1Commerce',\n\n // The derived statistics, rebuilt from the copied games by the Fix stats actions, then validated.\n // The console records each run the operator makes from the Stats group; it never runs a second\n // calculator of its own.\n rebuildLeagueStats: 'rebuild.leagueStats',\n rebuildLifetimeStats: 'rebuild.lifetimeStats',\n rebuildSportHallOfFame: 'rebuild.sportHallOfFame',\n validateStats: 'validate.stats',\n // The V1 hall of fame is rebuilt, never copied: its delete waits for the rebuilt documents.\n verifySportHallOfFame: 'verify.sportHallOfFame',\n deleteV1HallOfFame: 'delete.v1HallOfFame',\n // V1 validation reports describe V1 aggregates at a past moment; nothing is copied.\n deleteV1ValidationReports: 'delete.v1ValidationReports',\n\n // The console's own marker: from here on the product owns every destination.\n recordCutoverLive: 'record.cutoverLive',\n\n // Operator checkpoints — external work the app cannot perform, recorded with its evidence.\n checkpointV1FirestoreExport: 'checkpoint.v1FirestoreExport',\n checkpointPaymentCredentialsRetired: 'checkpoint.paymentCredentialsRetired',\n checkpointServiceAccountKeyRevoked: 'checkpoint.serviceAccountKeyRevoked',\n checkpointUnattachedPicturesReuploaded: 'checkpoint.unattachedPicturesReuploaded',\n // The gate the abandoned-registration sweep reads: it deletes nothing until this is recorded.\n checkpointAbandonedRegistrationSweepEnabled: 'checkpoint.abandonedRegistrationSweepEnabled',\n} as const;\n\nexport type MigrationActionId = (typeof MIGRATION_ACTION_IDS)[keyof typeof MIGRATION_ACTION_IDS];\n\n/** Every action id, for the wire schemas that name one. */\nexport const MIGRATION_ACTION_ID_KEYS = Object.values(MIGRATION_ACTION_IDS) as [\n MigrationActionId,\n ...MigrationActionId[],\n];\n"]}
@@ -1,7 +1,7 @@
1
1
  // The 2.0 cutover manifest: every step of the coordinated migration, in the order the operator runs
2
2
  // them, with what each step needs first. The console's callables enforce it — an action whose
3
3
  // dependencies are not satisfied refuses to run — and its screen renders it, so both read this one
4
- // declaration (ARCH-101, ENG-002).
4
+ // declaration (RULE-STD-ARCH-101, RULE-STD-ENG-002).
5
5
  //
6
6
  // Four kinds of step:
7
7
  // executable a console action with its own admin callable: admin-asserted, audited,