@ttt-productions/ttt-core 0.79.57 → 0.79.59

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 (191) hide show
  1. package/dist/constants/app-mode.d.ts +14 -1
  2. package/dist/constants/app-mode.d.ts.map +1 -1
  3. package/dist/constants/app-mode.js +24 -1
  4. package/dist/constants/app-mode.js.map +1 -1
  5. package/dist/constants/business-admin.d.ts +26 -0
  6. package/dist/constants/business-admin.d.ts.map +1 -1
  7. package/dist/constants/business-admin.js +36 -0
  8. package/dist/constants/business-admin.js.map +1 -1
  9. package/dist/constants/business-user.d.ts +6 -0
  10. package/dist/constants/business-user.d.ts.map +1 -1
  11. package/dist/constants/business-user.js +7 -0
  12. package/dist/constants/business-user.js.map +1 -1
  13. package/dist/constants/public-documents.d.ts +6 -0
  14. package/dist/constants/public-documents.d.ts.map +1 -1
  15. package/dist/constants/public-documents.js +6 -0
  16. package/dist/constants/public-documents.js.map +1 -1
  17. package/dist/constants/retention.d.ts +5 -1
  18. package/dist/constants/retention.d.ts.map +1 -1
  19. package/dist/constants/retention.js +13 -4
  20. package/dist/constants/retention.js.map +1 -1
  21. package/dist/constants/scheduled-jobs.d.ts +17 -9
  22. package/dist/constants/scheduled-jobs.d.ts.map +1 -1
  23. package/dist/constants/scheduled-jobs.js +33 -9
  24. package/dist/constants/scheduled-jobs.js.map +1 -1
  25. package/dist/doc-schemas/backend-state.d.ts +51 -29
  26. package/dist/doc-schemas/backend-state.d.ts.map +1 -1
  27. package/dist/doc-schemas/backend-state.js +82 -81
  28. package/dist/doc-schemas/backend-state.js.map +1 -1
  29. package/dist/doc-schemas/chat-sync.d.ts +1 -1
  30. package/dist/doc-schemas/commissions.d.ts +20 -0
  31. package/dist/doc-schemas/commissions.d.ts.map +1 -1
  32. package/dist/doc-schemas/commissions.js +5 -0
  33. package/dist/doc-schemas/commissions.js.map +1 -1
  34. package/dist/doc-schemas/content.d.ts +6 -3
  35. package/dist/doc-schemas/content.d.ts.map +1 -1
  36. package/dist/doc-schemas/content.js +8 -5
  37. package/dist/doc-schemas/content.js.map +1 -1
  38. package/dist/doc-schemas/erasure-fates.d.ts +199 -0
  39. package/dist/doc-schemas/erasure-fates.d.ts.map +1 -0
  40. package/dist/doc-schemas/erasure-fates.js +278 -0
  41. package/dist/doc-schemas/erasure-fates.js.map +1 -0
  42. package/dist/doc-schemas/index.d.ts +1 -0
  43. package/dist/doc-schemas/index.d.ts.map +1 -1
  44. package/dist/doc-schemas/index.js +2 -0
  45. package/dist/doc-schemas/index.js.map +1 -1
  46. package/dist/doc-schemas/media-activation-jobs.d.ts +6 -6
  47. package/dist/doc-schemas/media-assets.d.ts +119 -41
  48. package/dist/doc-schemas/media-assets.d.ts.map +1 -1
  49. package/dist/doc-schemas/media-assets.js +97 -5
  50. package/dist/doc-schemas/media-assets.js.map +1 -1
  51. package/dist/doc-schemas/media-copy-intents.d.ts +87 -5
  52. package/dist/doc-schemas/media-copy-intents.d.ts.map +1 -1
  53. package/dist/doc-schemas/media-copy-intents.js +45 -19
  54. package/dist/doc-schemas/media-copy-intents.js.map +1 -1
  55. package/dist/doc-schemas/messaging.d.ts +0 -2
  56. package/dist/doc-schemas/messaging.d.ts.map +1 -1
  57. package/dist/doc-schemas/messaging.js +0 -2
  58. package/dist/doc-schemas/messaging.js.map +1 -1
  59. package/dist/doc-schemas/moderation.d.ts +144 -14
  60. package/dist/doc-schemas/moderation.d.ts.map +1 -1
  61. package/dist/doc-schemas/moderation.js +151 -4
  62. package/dist/doc-schemas/moderation.js.map +1 -1
  63. package/dist/doc-schemas/ncii/allegations.d.ts +1 -1
  64. package/dist/doc-schemas/ncii/cases.d.ts +2 -2
  65. package/dist/doc-schemas/ncii/config.d.ts +9 -0
  66. package/dist/doc-schemas/ncii/config.d.ts.map +1 -1
  67. package/dist/doc-schemas/ncii/config.js +12 -7
  68. package/dist/doc-schemas/ncii/config.js.map +1 -1
  69. package/dist/doc-schemas/ncii/removal.d.ts +3 -3
  70. package/dist/doc-schemas/ncii/requests.d.ts +9 -9
  71. package/dist/doc-schemas/notification-archive-all-jobs.d.ts +2 -2
  72. package/dist/doc-schemas/notification-ledger.d.ts +22 -0
  73. package/dist/doc-schemas/notification-ledger.d.ts.map +1 -1
  74. package/dist/doc-schemas/notification-ledger.js +28 -1
  75. package/dist/doc-schemas/notification-ledger.js.map +1 -1
  76. package/dist/doc-schemas/operational.d.ts +40 -2
  77. package/dist/doc-schemas/operational.d.ts.map +1 -1
  78. package/dist/doc-schemas/operational.js +25 -2
  79. package/dist/doc-schemas/operational.js.map +1 -1
  80. package/dist/doc-schemas/payments.d.ts +2 -2
  81. package/dist/doc-schemas/public-documents.d.ts +13 -1
  82. package/dist/doc-schemas/public-documents.d.ts.map +1 -1
  83. package/dist/doc-schemas/public-documents.js +8 -1
  84. package/dist/doc-schemas/public-documents.js.map +1 -1
  85. package/dist/doc-schemas/registry.d.ts +259 -140
  86. package/dist/doc-schemas/registry.d.ts.map +1 -1
  87. package/dist/doc-schemas/registry.js +3 -5
  88. package/dist/doc-schemas/registry.js.map +1 -1
  89. package/dist/doc-schemas/safety/age.d.ts +0 -28
  90. package/dist/doc-schemas/safety/age.d.ts.map +1 -1
  91. package/dist/doc-schemas/safety/age.js +4 -24
  92. package/dist/doc-schemas/safety/age.js.map +1 -1
  93. package/dist/doc-schemas/safety/case.d.ts +6 -0
  94. package/dist/doc-schemas/safety/case.d.ts.map +1 -1
  95. package/dist/doc-schemas/safety/case.js +2 -0
  96. package/dist/doc-schemas/safety/case.js.map +1 -1
  97. package/dist/doc-schemas/safety/evidence.d.ts +40 -36
  98. package/dist/doc-schemas/safety/evidence.d.ts.map +1 -1
  99. package/dist/doc-schemas/safety/foundation.d.ts +19 -19
  100. package/dist/doc-schemas/safety/holds.d.ts +7 -7
  101. package/dist/doc-schemas/safety/report.d.ts +11 -9
  102. package/dist/doc-schemas/safety/report.d.ts.map +1 -1
  103. package/dist/doc-schemas/safety/report.js +10 -2
  104. package/dist/doc-schemas/safety/report.js.map +1 -1
  105. package/dist/doc-schemas/social.d.ts +4 -0
  106. package/dist/doc-schemas/social.d.ts.map +1 -1
  107. package/dist/doc-schemas/social.js +2 -0
  108. package/dist/doc-schemas/social.js.map +1 -1
  109. package/dist/doc-schemas/user.d.ts +16 -0
  110. package/dist/doc-schemas/user.d.ts.map +1 -1
  111. package/dist/doc-schemas/user.js +3 -0
  112. package/dist/doc-schemas/user.js.map +1 -1
  113. package/dist/doc-schemas/work-project.d.ts +14 -1
  114. package/dist/doc-schemas/work-project.d.ts.map +1 -1
  115. package/dist/doc-schemas/work-project.js +7 -0
  116. package/dist/doc-schemas/work-project.js.map +1 -1
  117. package/dist/ids/chat-ids.d.ts +2 -4
  118. package/dist/ids/chat-ids.d.ts.map +1 -1
  119. package/dist/ids/chat-ids.js +2 -4
  120. package/dist/ids/chat-ids.js.map +1 -1
  121. package/dist/media/download-filename.d.ts.map +1 -1
  122. package/dist/media/download-filename.js +1 -5
  123. package/dist/media/download-filename.js.map +1 -1
  124. package/dist/media/edge-serving-contract.d.ts +214 -49
  125. package/dist/media/edge-serving-contract.d.ts.map +1 -1
  126. package/dist/media/edge-serving-contract.js +125 -0
  127. package/dist/media/edge-serving-contract.js.map +1 -1
  128. package/dist/media/hall-library-target-fields.d.ts +6 -0
  129. package/dist/media/hall-library-target-fields.d.ts.map +1 -1
  130. package/dist/media/hall-library-target-fields.js +9 -0
  131. package/dist/media/hall-library-target-fields.js.map +1 -1
  132. package/dist/media/ttt-media-specs.d.ts.map +1 -1
  133. package/dist/media/ttt-media-specs.js +9 -8
  134. package/dist/media/ttt-media-specs.js.map +1 -1
  135. package/dist/paths/collections.d.ts +7 -3
  136. package/dist/paths/collections.d.ts.map +1 -1
  137. package/dist/paths/collections.js +13 -13
  138. package/dist/paths/collections.js.map +1 -1
  139. package/dist/paths/path-builders.d.ts +2 -4
  140. package/dist/paths/path-builders.d.ts.map +1 -1
  141. package/dist/paths/path-builders.js +3 -13
  142. package/dist/paths/path-builders.js.map +1 -1
  143. package/dist/schemas/admin.d.ts +11 -11
  144. package/dist/schemas/admin.d.ts.map +1 -1
  145. package/dist/schemas/admin.js +3 -3
  146. package/dist/schemas/admin.js.map +1 -1
  147. package/dist/schemas/chat.d.ts +1 -1
  148. package/dist/schemas/media.d.ts +5 -0
  149. package/dist/schemas/media.d.ts.map +1 -1
  150. package/dist/schemas/media.js +12 -1
  151. package/dist/schemas/media.js.map +1 -1
  152. package/dist/schemas/ncii.d.ts +3 -3
  153. package/dist/schemas/notification.d.ts +3 -3
  154. package/dist/schemas/public-documents.d.ts +37 -13
  155. package/dist/schemas/public-documents.d.ts.map +1 -1
  156. package/dist/schemas/public-documents.js +27 -9
  157. package/dist/schemas/public-documents.js.map +1 -1
  158. package/dist/schemas/safety.d.ts +1169 -0
  159. package/dist/schemas/safety.d.ts.map +1 -1
  160. package/dist/schemas/safety.js +163 -10
  161. package/dist/schemas/safety.js.map +1 -1
  162. package/dist/schemas/users.d.ts +21 -0
  163. package/dist/schemas/users.d.ts.map +1 -1
  164. package/dist/schemas/users.js +24 -10
  165. package/dist/schemas/users.js.map +1 -1
  166. package/dist/types/admin-ops.d.ts +9 -8
  167. package/dist/types/admin-ops.d.ts.map +1 -1
  168. package/dist/types/admin-ops.js.map +1 -1
  169. package/dist/types/audit.d.ts +1 -1
  170. package/dist/types/audit.d.ts.map +1 -1
  171. package/dist/types/audit.js.map +1 -1
  172. package/dist/types/moderation.d.ts +1 -1
  173. package/dist/types/moderation.d.ts.map +1 -1
  174. package/dist/types/moderation.js.map +1 -1
  175. package/dist/utils/age-derivation.d.ts +25 -0
  176. package/dist/utils/age-derivation.d.ts.map +1 -0
  177. package/dist/utils/age-derivation.js +40 -0
  178. package/dist/utils/age-derivation.js.map +1 -0
  179. package/dist/utils/hall-content.d.ts +10 -0
  180. package/dist/utils/hall-content.d.ts.map +1 -1
  181. package/dist/utils/hall-content.js +9 -0
  182. package/dist/utils/hall-content.js.map +1 -1
  183. package/dist/utils/index.d.ts +2 -0
  184. package/dist/utils/index.d.ts.map +1 -1
  185. package/dist/utils/index.js +2 -0
  186. package/dist/utils/index.js.map +1 -1
  187. package/dist/utils/pledge-totals.d.ts +14 -0
  188. package/dist/utils/pledge-totals.d.ts.map +1 -0
  189. package/dist/utils/pledge-totals.js +25 -0
  190. package/dist/utils/pledge-totals.js.map +1 -0
  191. package/package.json +5 -8
@@ -1,3 +1,4 @@
1
+ import type { WorkProjectType } from '../types/content.js';
1
2
  export type AppMode = 'charter' | 'full';
2
3
  /** The deployed mode. Changing this constant (and publishing) IS the flip. */
3
4
  export declare const APP_MODE: AppMode;
@@ -77,13 +78,25 @@ export interface TttLimits {
77
78
  LIBRARY_SUBMISSION: RateLimitValue;
78
79
  CHANNEL_CREATE: RateLimitValue;
79
80
  CHECKOUT_CREATE: RateLimitValue;
81
+ AGE_ATTEST: RateLimitValue;
80
82
  };
81
83
  }
82
84
  export declare const CHARTER_LIMITS: TttLimits;
83
85
  export declare const FULL_LIMITS: TttLimits;
84
86
  /** The resolved limit set for the deployed mode. Import THIS (or the alias
85
- * constants that re-export from it), never CHARTER_/FULL_ directly. */
87
+ * constants that re-export from it), never CHARTER_/FULL_ directly. A read bound
88
+ * that must hold whichever mode the data was written under uses
89
+ * HALL_SUB_ITEM_READ_BOUND_BY_WORK_TYPE below instead. */
86
90
  export declare const ACTIVE_LIMITS: TttLimits;
91
+ /**
92
+ * The most sub-items one Hall item can hold of each kind (Tale chapters, Tune tracks, Television
93
+ * episodes) in ANY app mode — each kind's count cap at its largest across CHARTER_LIMITS and
94
+ * FULL_LIMITS. A READ bound, never an enforced limit: a Hall item keeps the sub-items it was
95
+ * published with when the mode changes, so a reader that must see all of them sizes its query
96
+ * from this and treats a read that returns more as uncertain. Enforcement reads the active caps
97
+ * (MAX_CHAPTERS, MAX_TUNE_TRACKS, MAX_TELEVISION_EPISODES).
98
+ */
99
+ export declare const HALL_SUB_ITEM_READ_BOUND_BY_WORK_TYPE: Readonly<Record<WorkProjectType, number>>;
87
100
  /**
88
101
  * User-facing copy for a reached count cap — the ONE owner of the cap-message
89
102
  * shape, beside the limits it describes. Every callable refusing on a
@@ -1 +1 @@
1
- {"version":3,"file":"app-mode.d.ts","sourceRoot":"","sources":["../../src/constants/app-mode.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app-mode.d.ts","sourceRoot":"","sources":["../../src/constants/app-mode.ts"],"names":[],"mappings":"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"}
@@ -128,6 +128,11 @@ export const CHARTER_LIMITS = {
128
128
  LIBRARY_SUBMISSION: { maxRequests: 10, window: '1 h' },
129
129
  CHANNEL_CREATE: { maxRequests: 5, window: '1 h' },
130
130
  CHECKOUT_CREATE: { maxRequests: 5, window: '1 h' },
131
+ // The registration age step, keyed per hashed client IP (pre-account, no uid). One call per
132
+ // attempt — a corrected date or a retry at most doubles it — so 30/h gives one IP the same
133
+ // registrants per hour as DISPLAY_NAME_CHECK's 120/h at its 3–8 debounced checks each, while a
134
+ // script minting age-attestation records gets far less headroom.
135
+ AGE_ATTEST: { maxRequests: 30, window: '1 h' },
131
136
  },
132
137
  };
133
138
  export const FULL_LIMITS = {
@@ -216,11 +221,29 @@ export const FULL_LIMITS = {
216
221
  LIBRARY_SUBMISSION: { maxRequests: 20, window: '1 h' },
217
222
  CHANNEL_CREATE: { maxRequests: 20, window: '1 h' },
218
223
  CHECKOUT_CREATE: { maxRequests: 20, window: '1 h' },
224
+ // Scaled with DISPLAY_NAME_CHECK's full-mode 300/h — see the CHARTER_LIMITS note.
225
+ AGE_ATTEST: { maxRequests: 75, window: '1 h' },
219
226
  },
220
227
  };
221
228
  /** The resolved limit set for the deployed mode. Import THIS (or the alias
222
- * constants that re-export from it), never CHARTER_/FULL_ directly. */
229
+ * constants that re-export from it), never CHARTER_/FULL_ directly. A read bound
230
+ * that must hold whichever mode the data was written under uses
231
+ * HALL_SUB_ITEM_READ_BOUND_BY_WORK_TYPE below instead. */
223
232
  export const ACTIVE_LIMITS = byMode(CHARTER_LIMITS, FULL_LIMITS);
233
+ const largestInAnyMode = (pick) => Math.max(pick(CHARTER_LIMITS), pick(FULL_LIMITS));
234
+ /**
235
+ * The most sub-items one Hall item can hold of each kind (Tale chapters, Tune tracks, Television
236
+ * episodes) in ANY app mode — each kind's count cap at its largest across CHARTER_LIMITS and
237
+ * FULL_LIMITS. A READ bound, never an enforced limit: a Hall item keeps the sub-items it was
238
+ * published with when the mode changes, so a reader that must see all of them sizes its query
239
+ * from this and treats a read that returns more as uncertain. Enforcement reads the active caps
240
+ * (MAX_CHAPTERS, MAX_TUNE_TRACKS, MAX_TELEVISION_EPISODES).
241
+ */
242
+ export const HALL_SUB_ITEM_READ_BOUND_BY_WORK_TYPE = {
243
+ Tales: largestInAnyMode((limits) => limits.workProject.maxChapters),
244
+ Tunes: largestInAnyMode((limits) => limits.workProject.maxTuneTracks),
245
+ Television: largestInAnyMode((limits) => limits.workProject.maxTelevisionEpisodes),
246
+ };
224
247
  /**
225
248
  * User-facing copy for a reached count cap — the ONE owner of the cap-message
226
249
  * shape, beside the limits it describes. Every callable refusing on a
@@ -1 +1 @@
1
- {"version":3,"file":"app-mode.js","sourceRoot":"","sources":["../../src/constants/app-mode.ts"],"names":[],"mappings":"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","sourcesContent":["// App mode + the charter/full limit sets — the single published constant that\n// drives the whole app's cost posture. The flip to full-live is: change\n// APP_MODE → publish ttt-core → install in ttt-prod (root + functions) →\n// deploy. There is NO runtime mode flag and NO console toggle.\n// See ttt-prod docs/design/charter-season-and-app-mode.md.\n//\n// Only real cost/abuse drivers vary by mode: media specs (ttt-media-specs.ts),\n// rate-limit values, count caps, batch sizes. Text-length limits and payment\n// min/max are deliberately mode-invariant — with ONE ruled exception (DJ\n// 2026-07-13): chapter body content (maxChapterContentLength), because it\n// bounds threshold-review reading effort per item during charter season.\n\nexport type AppMode = 'charter' | 'full';\n\n/** The deployed mode. Changing this constant (and publishing) IS the flip. */\nexport const APP_MODE: AppMode = 'charter';\n\n/** Pick the active value for a mode-varied limit. */\nexport const byMode = <T>(charter: T, full: T): T =>\n (APP_MODE as AppMode) === 'charter' ? charter : full;\n\n/** Upstash-style sliding-window duration, e.g. '1 h'. */\nexport type RateLimitWindow = `${number} ${'ms' | 's' | 'm' | 'h' | 'd'}`;\n\nexport interface RateLimitValue {\n maxRequests: number;\n window: RateLimitWindow;\n}\n\nexport interface TttLimits {\n mode: AppMode;\n user: {\n maxCraftSkills: number;\n maxOwnedWorkProjects: number;\n maxAssociatedWorkProjects: number;\n };\n workProject: {\n maxGuildSize: number;\n maxFileFolders: number;\n maxWorkFiles: number;\n maxWorkFileStorageBytes: number;\n maxWorkProjectAuditions: number;\n maxCommissionListings: number;\n maxChapters: number;\n /** Chapter BODY length (chars) — the one mode-varied text limit (DJ 2026-07-13). */\n maxChapterContentLength: number;\n maxTuneTracks: number;\n maxTelevisionEpisodes: number;\n };\n /** Conversation Files container caps — PER CONVERSATION (one guild-invite\n * conversation or one admin-support dispatch thread). Both scopes share one cap\n * set; guild chat channels have no Conversation Files. DJ ruling 2026-07-25. */\n conversation: {\n /** Published active Conversation Files allowed in one conversation. */\n maxConversationFiles: number;\n /** Published STORED-OUTPUT bytes allowed in one conversation. */\n maxConversationFileStorageBytes: number;\n };\n hall: {\n maxSubmitBatch: number;\n };\n batches: {\n trendingFeedProcessLimit: number;\n maxFeedbackSubmitters: number;\n };\n /** Values for the backend rate limiters. Prefixes/factory stay in functions. */\n rateLimits: {\n UPLOAD: RateLimitValue;\n ADMIN_UPLOAD: RateLimitValue;\n CONTENT_WRITE: RateLimitValue;\n VOTE: RateLimitValue;\n MODERATION: RateLimitValue;\n SENSITIVE_ACTION: RateLimitValue;\n DISPLAY_NAME_CHECK: RateLimitValue;\n CHAT_MESSAGE: RateLimitValue;\n ADMIN_TASK: RateLimitValue;\n APPEAL_REVIEW: RateLimitValue;\n BAN_ACTION: RateLimitValue;\n SHARE_TRANSFER: RateLimitValue;\n CONTENT_REPORT: RateLimitValue;\n SHORT_LINK_CLICK: RateLimitValue;\n FOLLOW: RateLimitValue;\n MENTION_HISTORY: RateLimitValue;\n MEDIA_GRANT: RateLimitValue;\n CHAT_GRANT: RateLimitValue;\n OPERATOR_STEP_UP: RateLimitValue;\n ADMIN_MESSAGE_READ: RateLimitValue;\n USER_LOOKUP: RateLimitValue;\n NOTIFICATION_BROADCAST: RateLimitValue;\n NOTIFICATION_ARCHIVE: RateLimitValue;\n NOTIFICATION_MARK_SEEN: RateLimitValue;\n FEEDBACK: RateLimitValue;\n SHORT_LINK_CREATE: RateLimitValue;\n INVITE_ACTION: RateLimitValue;\n LIBRARY_SUBMISSION: RateLimitValue;\n CHANNEL_CREATE: RateLimitValue;\n CHECKOUT_CREATE: RateLimitValue;\n };\n}\n\nexport const CHARTER_LIMITS: TttLimits = {\n mode: 'charter',\n user: {\n maxCraftSkills: 8,\n maxOwnedWorkProjects: 3,\n maxAssociatedWorkProjects: 10,\n },\n workProject: {\n maxGuildSize: 25,\n maxFileFolders: 10,\n maxWorkFiles: 100,\n maxWorkFileStorageBytes: 2_684_354_560, // 2.5 GiB\n maxWorkProjectAuditions: 3,\n maxCommissionListings: 3,\n maxChapters: 5,\n // ~5,000 words — a full real chapter, while keeping the human threshold-review\n // reading effort per item bounded during charter season.\n maxChapterContentLength: 30_000,\n maxTuneTracks: 5,\n maxTelevisionEpisodes: 3,\n },\n // Conversation Files are QUOTA'D PER CONVERSATION (DJ ruling 2026-07-25). Counted on\n // STORED OUTPUT bytes (what actually sits in R2 after transcode), never on raw upload\n // bytes. Rationale: a per-file `maxBytes` cap alone bounds one upload but nothing stops\n // unbounded accumulation across a long-running conversation — this is the abuse brake\n // on that, and it is what `startUpload`'s reservation and the publication transfer both\n // check. Charter: 10 files / 500 MiB per conversation.\n conversation: {\n maxConversationFiles: 10,\n maxConversationFileStorageBytes: 524_288_000, // 500 MiB\n },\n hall: { maxSubmitBatch: 10 },\n batches: { trendingFeedProcessLimit: 250, maxFeedbackSubmitters: 25 },\n rateLimits: {\n // Uploads are charged ONCE per upload (in startUpload); the processor-side\n // check is a non-charging guard.\n UPLOAD: { maxRequests: 15, window: '1 h' },\n // A separate HIGHER bucket for uploads performed through the sanctioned admin\n // authority path — never an exemption. Bulk admin setup work (creating 3\n // auditions is ~5 uploads) exhausts the 15/h user bucket; 60/h covers a real\n // bulk admin session while keeping a brake on a compromised admin token and on\n // third-party quota/cost (Vision / Video Intelligence / R2). DJ ruling 2026-07-25.\n ADMIN_UPLOAD: { maxRequests: 60, window: '1 h' },\n // General content-write throttle for the non-upload write callables (text posts,\n // craft-skill edits, support-thread starts) that used to charge UPLOAD. Sharing\n // the 15/h charter upload window meant heavy text posting blocked real uploads\n // and vice versa; a separate 30/h bucket keeps each cost driver on its own\n // ceiling. DJ ruling 2026-07-25.\n CONTENT_WRITE: { maxRequests: 30, window: '1 h' },\n VOTE: { maxRequests: 100, window: '1 h' },\n MODERATION: { maxRequests: 50, window: '1 h' },\n // 30/h: one bucket is shared by ~35 callables (register, become-artisan,\n // create-work, invites, trades, appeals, …) — a real first-hour artisan does\n // 10-15 legitimate sensitive actions; 5/h blocked the intended golden path\n // (proven live by the hosted-dev E2E, 2026-07-07). DJ ruling 2026-07-08.\n SENSITIVE_ACTION: { maxRequests: 30, window: '1 h' },\n // 120/h (charter): checkDisplayNameAvailable is enforceAppCheck:true, so\n // reCAPTCHA-Enterprise attestation is the real abuse gate — this bucket is a\n // cost/DoS ceiling, not the security boundary. It is IP-keyed (pre-registration,\n // no uid yet) and the register form fires it debounced-LIVE as you type, so one\n // legit registrant spends ~3-8 checks and shared NAT/CGNAT multiplies that across\n // everyone on the IP. The old 20/h false-rejected legitimate shared-IP users and\n // could not fit a full hosted-dev E2E suite run on one IP. DJ ruling 2026-07-21.\n DISPLAY_NAME_CHECK: { maxRequests: 120, window: '1 h' },\n CHAT_MESSAGE: { maxRequests: 125, window: '1 h' },\n ADMIN_TASK: { maxRequests: 60, window: '1 h' },\n APPEAL_REVIEW: { maxRequests: 30, window: '1 h' },\n BAN_ACTION: { maxRequests: 20, window: '1 h' },\n SHARE_TRANSFER: { maxRequests: 10, window: '1 h' },\n CONTENT_REPORT: { maxRequests: 5, window: '1 h' },\n SHORT_LINK_CLICK: { maxRequests: 60, window: '1 h' },\n FOLLOW: { maxRequests: 20, window: '1 m' },\n MENTION_HISTORY: { maxRequests: 120, window: '1 h' },\n // Scoped-media grant minting (createMediaGrant). Its OWN bucket — it used to charge\n // MENTION_HISTORY, an unrelated surface, so neither limit meant what its name said and the\n // two shared a budget sized for neither (BACKEND-013). Refresh-driven, not user-initiated:\n // a grant is short-TTL (60 min work media, 15 min chat/folder scopes) and the client re-mints\n // under expiry, so a browsing session naturally spends several per hour PER SCOPE (each work,\n // each private file folder, each conversation). Cheap to serve (a Firestore permission read\n // plus an HMAC sign — no third-party cost), and a false 429 breaks media rendering outright,\n // so the ceiling is generous: it exists to stop a scripted grant-farming sweep, not to pace a\n // real reader. 120/h charter.\n MEDIA_GRANT: { maxRequests: 120, window: '1 h' },\n // Realtime chat grant minting (mintChatGrant) — previously NO limiter at all (BACKEND-013),\n // despite minting signed grants at concurrency: 20. Same refresh-driven shape as MEDIA_GRANT:\n // a 15-minute grant TTL means ~4 mints/hour for the inbox plus ~4 per open channel, doubled\n // again by socket reconnects on a flaky mobile network. It stays behind the per-channel\n // authorizeChannelScope gate, so this bucket is a cost/DoS ceiling rather than the\n // authorization boundary — sized to match MEDIA_GRANT so the two grant surfaces are paced\n // alike and neither can be starved by the other (separate Redis prefixes).\n CHAT_GRANT: { maxRequests: 120, window: '1 h' },\n // Operator step-up TOTP verification (confirmOperatorStepUp / verifyOperatorStepUp) —\n // previously unthrottled, so an attacker holding a stolen full-admin session could\n // brute-force the 6-digit second factor guarding the safety console (BACKEND-013).\n // DELIBERATELY THE TIGHTEST BUCKET IN THE SET: a real operator enrolls once and verifies\n // roughly twice an hour (the grant window is 30 minutes), so 10/h absorbs every legitimate\n // mistype while capping guessing at 10 attempts per hour against a code that rotates every\n // 30 seconds. Mode-INVARIANT — this is an admin security control, not a cost driver, so it\n // never loosens at the full-mode flip (same posture as MODERATION / BAN_ACTION / USER_LOOKUP).\n // The lockout-after-N-failures behaviour is a separate app-side concern; this is the throttle.\n OPERATOR_STEP_UP: { maxRequests: 10, window: '1 h' },\n ADMIN_MESSAGE_READ: { maxRequests: 60, window: '1 h' },\n // Admin exact-account lookup (searchPublicUsers / lookupUserByEmailOrUid) — a read, so a\n // generous per-minute bucket that still backstops a scripted enumeration sweep. Admin-only\n // and not a cost driver, so the value is the same in both modes (like MODERATION/ADMIN_TASK).\n USER_LOOKUP: { maxRequests: 30, window: '1 m' },\n NOTIFICATION_BROADCAST: { maxRequests: 10, window: '1 h' },\n NOTIFICATION_ARCHIVE: { maxRequests: 120, window: '1 h' },\n NOTIFICATION_MARK_SEEN: { maxRequests: 120, window: '1 h' },\n FEEDBACK: { maxRequests: 10, window: '1 h' },\n SHORT_LINK_CREATE: { maxRequests: 30, window: '1 h' },\n INVITE_ACTION: { maxRequests: 30, window: '1 h' },\n LIBRARY_SUBMISSION: { maxRequests: 10, window: '1 h' },\n CHANNEL_CREATE: { maxRequests: 5, window: '1 h' },\n CHECKOUT_CREATE: { maxRequests: 5, window: '1 h' },\n },\n};\n\nexport const FULL_LIMITS: TttLimits = {\n mode: 'full',\n user: {\n maxCraftSkills: 24,\n maxOwnedWorkProjects: 25,\n maxAssociatedWorkProjects: 100,\n },\n workProject: {\n maxGuildSize: 250,\n maxFileFolders: 25,\n maxWorkFiles: 1000,\n maxWorkFileStorageBytes: 26_843_545_600, // 25 GiB\n maxWorkProjectAuditions: 10,\n maxCommissionListings: 10,\n maxChapters: 50,\n // The intentional design ceiling (~17,000 words; trivially inside Firestore's\n // 1 MiB doc limit) — the audit trail deliberately stores field LENGTHS, not\n // content, because chapters can be this large (runUpdateChapterDetails).\n maxChapterContentLength: 100_000,\n maxTuneTracks: 50,\n maxTelevisionEpisodes: 25,\n },\n // 20 files / 1 GiB (full): the same per-conversation Conversation Files quota as\n // charter, scaled for a public-launch conversation. Still a hard ceiling on\n // unbounded R2 accumulation. See the CHARTER_LIMITS note above. DJ ruling 2026-07-25.\n conversation: {\n maxConversationFiles: 20,\n maxConversationFileStorageBytes: 1_073_741_824, // 1 GiB\n },\n hall: { maxSubmitBatch: 50 },\n batches: { trendingFeedProcessLimit: 1000, maxFeedbackSubmitters: 100 },\n rateLimits: {\n UPLOAD: { maxRequests: 30, window: '1 h' },\n // 120/h (full): the same sanctioned-admin-path upload bucket as charter, scaled\n // with UPLOAD — always HIGHER than the user bucket, never an exemption, so a\n // compromised admin token and third-party media cost stay bounded. See the\n // CHARTER_LIMITS note above. DJ ruling 2026-07-25.\n ADMIN_UPLOAD: { maxRequests: 120, window: '1 h' },\n // 60/h (full): the same non-upload content-write bucket as charter, scaled with\n // UPLOAD so text writes and media uploads never consume each other's ceiling.\n // See the CHARTER_LIMITS note above. DJ ruling 2026-07-25.\n CONTENT_WRITE: { maxRequests: 60, window: '1 h' },\n VOTE: { maxRequests: 200, window: '1 h' },\n MODERATION: { maxRequests: 50, window: '1 h' },\n // 60/h: full-mode power users managing several works do dozens of legitimate\n // management actions per hour; still a hard backstop against scripts.\n // DJ ruling 2026-07-08 (charter 30 / full 60, single bucket; a bucket split\n // is filed post-launch in ttt-prod docs/post-launch/).\n SENSITIVE_ACTION: { maxRequests: 60, window: '1 h' },\n // 300/h (full): same reasoning as the charter value above (App-Check-gated\n // cost ceiling, IP-keyed, debounced-live). Public-launch CGNAT exposure is far\n // larger — hundreds of concurrent legit registrants can sit behind one carrier\n // IP — so err generous; the only cost of \"too high\" is trivial Firestore reads,\n // while \"too low\" false-rejects real users. Raise on evidence (watch prod logs\n // for legit 429s) rather than pre-emptively tightening. DJ ruling 2026-07-21.\n DISPLAY_NAME_CHECK: { maxRequests: 300, window: '1 h' },\n CHAT_MESSAGE: { maxRequests: 150, window: '1 h' },\n ADMIN_TASK: { maxRequests: 60, window: '1 h' },\n APPEAL_REVIEW: { maxRequests: 30, window: '1 h' },\n BAN_ACTION: { maxRequests: 20, window: '1 h' },\n SHARE_TRANSFER: { maxRequests: 10, window: '1 h' },\n CONTENT_REPORT: { maxRequests: 20, window: '1 h' },\n SHORT_LINK_CLICK: { maxRequests: 60, window: '1 h' },\n FOLLOW: { maxRequests: 60, window: '1 m' },\n MENTION_HISTORY: { maxRequests: 120, window: '1 h' },\n // 240/h (full): the same grant-minting buckets as charter, doubled with the surrounding\n // content limits. Full mode raises the ceilings a grant is minted PER (250-guildmate guilds,\n // 25 file folders, 1000 work files, 10 auditions), so one session legitimately touches more\n // scopes and re-mints more often. See the CHARTER_LIMITS notes above.\n MEDIA_GRANT: { maxRequests: 240, window: '1 h' },\n CHAT_GRANT: { maxRequests: 240, window: '1 h' },\n // Operator step-up stays at 10/h in BOTH modes — an admin second-factor throttle is a\n // security control, not a cost lever, so it never loosens at the flip. See CHARTER_LIMITS.\n OPERATOR_STEP_UP: { maxRequests: 10, window: '1 h' },\n ADMIN_MESSAGE_READ: { maxRequests: 60, window: '1 h' },\n // Admin exact-account lookup — same read bucket in both modes (see CHARTER_LIMITS note).\n USER_LOOKUP: { maxRequests: 30, window: '1 m' },\n NOTIFICATION_BROADCAST: { maxRequests: 10, window: '1 h' },\n NOTIFICATION_ARCHIVE: { maxRequests: 120, window: '1 h' },\n NOTIFICATION_MARK_SEEN: { maxRequests: 120, window: '1 h' },\n FEEDBACK: { maxRequests: 10, window: '1 h' },\n SHORT_LINK_CREATE: { maxRequests: 30, window: '1 h' },\n INVITE_ACTION: { maxRequests: 60, window: '1 h' },\n LIBRARY_SUBMISSION: { maxRequests: 20, window: '1 h' },\n CHANNEL_CREATE: { maxRequests: 20, window: '1 h' },\n CHECKOUT_CREATE: { maxRequests: 20, window: '1 h' },\n },\n};\n\n/** The resolved limit set for the deployed mode. Import THIS (or the alias\n * constants that re-export from it), never CHARTER_/FULL_ directly. */\nexport const ACTIVE_LIMITS: TttLimits = byMode(CHARTER_LIMITS, FULL_LIMITS);\n\n/**\n * User-facing copy for a reached count cap — the ONE owner of the cap-message\n * shape, beside the limits it describes. Every callable refusing on a\n * mode-varied count cap formats through this. In charter mode the message\n * names Charter Season so the cap reads as the season's limit rather than the\n * platform's permanent shape; full mode stays neutral. `hint` is an optional\n * trailing sentence (e.g. \"Delete a file to add another.\").\n */\nexport const countCapReachedMessage = (\n subject: string,\n limit: number,\n noun: string,\n hint?: string,\n): string => {\n const base = byMode(\n `${subject} already has the Charter Season maximum of ${limit} ${noun} — caps expand when Charter Season ends.`,\n `${subject} already has the maximum of ${limit} ${noun}.`,\n );\n return hint ? `${base} ${hint}` : base;\n};\n"]}
1
+ {"version":3,"file":"app-mode.js","sourceRoot":"","sources":["../../src/constants/app-mode.ts"],"names":[],"mappings":"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","sourcesContent":["// App mode + the charter/full limit sets — the single published constant that\n// drives the whole app's cost posture. The flip to full-live is: change\n// APP_MODE → publish ttt-core → install in ttt-prod (root + functions) →\n// deploy. There is NO runtime mode flag and NO console toggle.\n// See ttt-prod docs/design/charter-season-and-app-mode.md.\n//\n// Only real cost/abuse drivers vary by mode: media specs (ttt-media-specs.ts),\n// rate-limit values, count caps, batch sizes. Text-length limits and payment\n// min/max are deliberately mode-invariant — with ONE ruled exception (DJ\n// 2026-07-13): chapter body content (maxChapterContentLength), because it\n// bounds threshold-review reading effort per item during charter season.\n\nimport type { WorkProjectType } from '../types/content.js';\n\nexport type AppMode = 'charter' | 'full';\n\n/** The deployed mode. Changing this constant (and publishing) IS the flip. */\nexport const APP_MODE: AppMode = 'charter';\n\n/** Pick the active value for a mode-varied limit. */\nexport const byMode = <T>(charter: T, full: T): T =>\n (APP_MODE as AppMode) === 'charter' ? charter : full;\n\n/** Upstash-style sliding-window duration, e.g. '1 h'. */\nexport type RateLimitWindow = `${number} ${'ms' | 's' | 'm' | 'h' | 'd'}`;\n\nexport interface RateLimitValue {\n maxRequests: number;\n window: RateLimitWindow;\n}\n\nexport interface TttLimits {\n mode: AppMode;\n user: {\n maxCraftSkills: number;\n maxOwnedWorkProjects: number;\n maxAssociatedWorkProjects: number;\n };\n workProject: {\n maxGuildSize: number;\n maxFileFolders: number;\n maxWorkFiles: number;\n maxWorkFileStorageBytes: number;\n maxWorkProjectAuditions: number;\n maxCommissionListings: number;\n maxChapters: number;\n /** Chapter BODY length (chars) — the one mode-varied text limit (DJ 2026-07-13). */\n maxChapterContentLength: number;\n maxTuneTracks: number;\n maxTelevisionEpisodes: number;\n };\n /** Conversation Files container caps — PER CONVERSATION (one guild-invite\n * conversation or one admin-support dispatch thread). Both scopes share one cap\n * set; guild chat channels have no Conversation Files. DJ ruling 2026-07-25. */\n conversation: {\n /** Published active Conversation Files allowed in one conversation. */\n maxConversationFiles: number;\n /** Published STORED-OUTPUT bytes allowed in one conversation. */\n maxConversationFileStorageBytes: number;\n };\n hall: {\n maxSubmitBatch: number;\n };\n batches: {\n trendingFeedProcessLimit: number;\n maxFeedbackSubmitters: number;\n };\n /** Values for the backend rate limiters. Prefixes/factory stay in functions. */\n rateLimits: {\n UPLOAD: RateLimitValue;\n ADMIN_UPLOAD: RateLimitValue;\n CONTENT_WRITE: RateLimitValue;\n VOTE: RateLimitValue;\n MODERATION: RateLimitValue;\n SENSITIVE_ACTION: RateLimitValue;\n DISPLAY_NAME_CHECK: RateLimitValue;\n CHAT_MESSAGE: RateLimitValue;\n ADMIN_TASK: RateLimitValue;\n APPEAL_REVIEW: RateLimitValue;\n BAN_ACTION: RateLimitValue;\n SHARE_TRANSFER: RateLimitValue;\n CONTENT_REPORT: RateLimitValue;\n SHORT_LINK_CLICK: RateLimitValue;\n FOLLOW: RateLimitValue;\n MENTION_HISTORY: RateLimitValue;\n MEDIA_GRANT: RateLimitValue;\n CHAT_GRANT: RateLimitValue;\n OPERATOR_STEP_UP: RateLimitValue;\n ADMIN_MESSAGE_READ: RateLimitValue;\n USER_LOOKUP: RateLimitValue;\n NOTIFICATION_BROADCAST: RateLimitValue;\n NOTIFICATION_ARCHIVE: RateLimitValue;\n NOTIFICATION_MARK_SEEN: RateLimitValue;\n FEEDBACK: RateLimitValue;\n SHORT_LINK_CREATE: RateLimitValue;\n INVITE_ACTION: RateLimitValue;\n LIBRARY_SUBMISSION: RateLimitValue;\n CHANNEL_CREATE: RateLimitValue;\n CHECKOUT_CREATE: RateLimitValue;\n AGE_ATTEST: RateLimitValue;\n };\n}\n\nexport const CHARTER_LIMITS: TttLimits = {\n mode: 'charter',\n user: {\n maxCraftSkills: 8,\n maxOwnedWorkProjects: 3,\n maxAssociatedWorkProjects: 10,\n },\n workProject: {\n maxGuildSize: 25,\n maxFileFolders: 10,\n maxWorkFiles: 100,\n maxWorkFileStorageBytes: 2_684_354_560, // 2.5 GiB\n maxWorkProjectAuditions: 3,\n maxCommissionListings: 3,\n maxChapters: 5,\n // ~5,000 words — a full real chapter, while keeping the human threshold-review\n // reading effort per item bounded during charter season.\n maxChapterContentLength: 30_000,\n maxTuneTracks: 5,\n maxTelevisionEpisodes: 3,\n },\n // Conversation Files are QUOTA'D PER CONVERSATION (DJ ruling 2026-07-25). Counted on\n // STORED OUTPUT bytes (what actually sits in R2 after transcode), never on raw upload\n // bytes. Rationale: a per-file `maxBytes` cap alone bounds one upload but nothing stops\n // unbounded accumulation across a long-running conversation — this is the abuse brake\n // on that, and it is what `startUpload`'s reservation and the publication transfer both\n // check. Charter: 10 files / 500 MiB per conversation.\n conversation: {\n maxConversationFiles: 10,\n maxConversationFileStorageBytes: 524_288_000, // 500 MiB\n },\n hall: { maxSubmitBatch: 10 },\n batches: { trendingFeedProcessLimit: 250, maxFeedbackSubmitters: 25 },\n rateLimits: {\n // Uploads are charged ONCE per upload (in startUpload); the processor-side\n // check is a non-charging guard.\n UPLOAD: { maxRequests: 15, window: '1 h' },\n // A separate HIGHER bucket for uploads performed through the sanctioned admin\n // authority path — never an exemption. Bulk admin setup work (creating 3\n // auditions is ~5 uploads) exhausts the 15/h user bucket; 60/h covers a real\n // bulk admin session while keeping a brake on a compromised admin token and on\n // third-party quota/cost (Vision / Video Intelligence / R2). DJ ruling 2026-07-25.\n ADMIN_UPLOAD: { maxRequests: 60, window: '1 h' },\n // General content-write throttle for the non-upload write callables (text posts,\n // craft-skill edits, support-thread starts) that used to charge UPLOAD. Sharing\n // the 15/h charter upload window meant heavy text posting blocked real uploads\n // and vice versa; a separate 30/h bucket keeps each cost driver on its own\n // ceiling. DJ ruling 2026-07-25.\n CONTENT_WRITE: { maxRequests: 30, window: '1 h' },\n VOTE: { maxRequests: 100, window: '1 h' },\n MODERATION: { maxRequests: 50, window: '1 h' },\n // 30/h: one bucket is shared by ~35 callables (register, become-artisan,\n // create-work, invites, trades, appeals, …) — a real first-hour artisan does\n // 10-15 legitimate sensitive actions; 5/h blocked the intended golden path\n // (proven live by the hosted-dev E2E, 2026-07-07). DJ ruling 2026-07-08.\n SENSITIVE_ACTION: { maxRequests: 30, window: '1 h' },\n // 120/h (charter): checkDisplayNameAvailable is enforceAppCheck:true, so\n // reCAPTCHA-Enterprise attestation is the real abuse gate — this bucket is a\n // cost/DoS ceiling, not the security boundary. It is IP-keyed (pre-registration,\n // no uid yet) and the register form fires it debounced-LIVE as you type, so one\n // legit registrant spends ~3-8 checks and shared NAT/CGNAT multiplies that across\n // everyone on the IP. The old 20/h false-rejected legitimate shared-IP users and\n // could not fit a full hosted-dev E2E suite run on one IP. DJ ruling 2026-07-21.\n DISPLAY_NAME_CHECK: { maxRequests: 120, window: '1 h' },\n CHAT_MESSAGE: { maxRequests: 125, window: '1 h' },\n ADMIN_TASK: { maxRequests: 60, window: '1 h' },\n APPEAL_REVIEW: { maxRequests: 30, window: '1 h' },\n BAN_ACTION: { maxRequests: 20, window: '1 h' },\n SHARE_TRANSFER: { maxRequests: 10, window: '1 h' },\n CONTENT_REPORT: { maxRequests: 5, window: '1 h' },\n SHORT_LINK_CLICK: { maxRequests: 60, window: '1 h' },\n FOLLOW: { maxRequests: 20, window: '1 m' },\n MENTION_HISTORY: { maxRequests: 120, window: '1 h' },\n // Scoped-media grant minting (createMediaGrant). Its OWN bucket — it used to charge\n // MENTION_HISTORY, an unrelated surface, so neither limit meant what its name said and the\n // two shared a budget sized for neither (BACKEND-013). Refresh-driven, not user-initiated:\n // a grant is short-TTL (60 min work media, 15 min chat/folder scopes) and the client re-mints\n // under expiry, so a browsing session naturally spends several per hour PER SCOPE (each work,\n // each private file folder, each conversation). Cheap to serve (a Firestore permission read\n // plus an HMAC sign — no third-party cost), and a false 429 breaks media rendering outright,\n // so the ceiling is generous: it exists to stop a scripted grant-farming sweep, not to pace a\n // real reader. 120/h charter.\n MEDIA_GRANT: { maxRequests: 120, window: '1 h' },\n // Realtime chat grant minting (mintChatGrant) — previously NO limiter at all (BACKEND-013),\n // despite minting signed grants at concurrency: 20. Same refresh-driven shape as MEDIA_GRANT:\n // a 15-minute grant TTL means ~4 mints/hour for the inbox plus ~4 per open channel, doubled\n // again by socket reconnects on a flaky mobile network. It stays behind the per-channel\n // authorizeChannelScope gate, so this bucket is a cost/DoS ceiling rather than the\n // authorization boundary — sized to match MEDIA_GRANT so the two grant surfaces are paced\n // alike and neither can be starved by the other (separate Redis prefixes).\n CHAT_GRANT: { maxRequests: 120, window: '1 h' },\n // Operator step-up TOTP verification (confirmOperatorStepUp / verifyOperatorStepUp) —\n // previously unthrottled, so an attacker holding a stolen full-admin session could\n // brute-force the 6-digit second factor guarding the safety console (BACKEND-013).\n // DELIBERATELY THE TIGHTEST BUCKET IN THE SET: a real operator enrolls once and verifies\n // roughly twice an hour (the grant window is 30 minutes), so 10/h absorbs every legitimate\n // mistype while capping guessing at 10 attempts per hour against a code that rotates every\n // 30 seconds. Mode-INVARIANT — this is an admin security control, not a cost driver, so it\n // never loosens at the full-mode flip (same posture as MODERATION / BAN_ACTION / USER_LOOKUP).\n // The lockout-after-N-failures behaviour is a separate app-side concern; this is the throttle.\n OPERATOR_STEP_UP: { maxRequests: 10, window: '1 h' },\n ADMIN_MESSAGE_READ: { maxRequests: 60, window: '1 h' },\n // Admin exact-account lookup (searchPublicUsers / lookupUserByEmailOrUid) — a read, so a\n // generous per-minute bucket that still backstops a scripted enumeration sweep. Admin-only\n // and not a cost driver, so the value is the same in both modes (like MODERATION/ADMIN_TASK).\n USER_LOOKUP: { maxRequests: 30, window: '1 m' },\n NOTIFICATION_BROADCAST: { maxRequests: 10, window: '1 h' },\n NOTIFICATION_ARCHIVE: { maxRequests: 120, window: '1 h' },\n NOTIFICATION_MARK_SEEN: { maxRequests: 120, window: '1 h' },\n FEEDBACK: { maxRequests: 10, window: '1 h' },\n SHORT_LINK_CREATE: { maxRequests: 30, window: '1 h' },\n INVITE_ACTION: { maxRequests: 30, window: '1 h' },\n LIBRARY_SUBMISSION: { maxRequests: 10, window: '1 h' },\n CHANNEL_CREATE: { maxRequests: 5, window: '1 h' },\n CHECKOUT_CREATE: { maxRequests: 5, window: '1 h' },\n // The registration age step, keyed per hashed client IP (pre-account, no uid). One call per\n // attempt — a corrected date or a retry at most doubles it — so 30/h gives one IP the same\n // registrants per hour as DISPLAY_NAME_CHECK's 120/h at its 3–8 debounced checks each, while a\n // script minting age-attestation records gets far less headroom.\n AGE_ATTEST: { maxRequests: 30, window: '1 h' },\n },\n};\n\nexport const FULL_LIMITS: TttLimits = {\n mode: 'full',\n user: {\n maxCraftSkills: 24,\n maxOwnedWorkProjects: 25,\n maxAssociatedWorkProjects: 100,\n },\n workProject: {\n maxGuildSize: 250,\n maxFileFolders: 25,\n maxWorkFiles: 1000,\n maxWorkFileStorageBytes: 26_843_545_600, // 25 GiB\n maxWorkProjectAuditions: 10,\n maxCommissionListings: 10,\n maxChapters: 50,\n // The intentional design ceiling (~17,000 words; trivially inside Firestore's\n // 1 MiB doc limit) — the audit trail deliberately stores field LENGTHS, not\n // content, because chapters can be this large (runUpdateChapterDetails).\n maxChapterContentLength: 100_000,\n maxTuneTracks: 50,\n maxTelevisionEpisodes: 25,\n },\n // 20 files / 1 GiB (full): the same per-conversation Conversation Files quota as\n // charter, scaled for a public-launch conversation. Still a hard ceiling on\n // unbounded R2 accumulation. See the CHARTER_LIMITS note above. DJ ruling 2026-07-25.\n conversation: {\n maxConversationFiles: 20,\n maxConversationFileStorageBytes: 1_073_741_824, // 1 GiB\n },\n hall: { maxSubmitBatch: 50 },\n batches: { trendingFeedProcessLimit: 1000, maxFeedbackSubmitters: 100 },\n rateLimits: {\n UPLOAD: { maxRequests: 30, window: '1 h' },\n // 120/h (full): the same sanctioned-admin-path upload bucket as charter, scaled\n // with UPLOAD — always HIGHER than the user bucket, never an exemption, so a\n // compromised admin token and third-party media cost stay bounded. See the\n // CHARTER_LIMITS note above. DJ ruling 2026-07-25.\n ADMIN_UPLOAD: { maxRequests: 120, window: '1 h' },\n // 60/h (full): the same non-upload content-write bucket as charter, scaled with\n // UPLOAD so text writes and media uploads never consume each other's ceiling.\n // See the CHARTER_LIMITS note above. DJ ruling 2026-07-25.\n CONTENT_WRITE: { maxRequests: 60, window: '1 h' },\n VOTE: { maxRequests: 200, window: '1 h' },\n MODERATION: { maxRequests: 50, window: '1 h' },\n // 60/h: full-mode power users managing several works do dozens of legitimate\n // management actions per hour; still a hard backstop against scripts.\n // DJ ruling 2026-07-08 (charter 30 / full 60, single bucket; a bucket split\n // is filed post-launch in ttt-prod docs/post-launch/).\n SENSITIVE_ACTION: { maxRequests: 60, window: '1 h' },\n // 300/h (full): same reasoning as the charter value above (App-Check-gated\n // cost ceiling, IP-keyed, debounced-live). Public-launch CGNAT exposure is far\n // larger — hundreds of concurrent legit registrants can sit behind one carrier\n // IP — so err generous; the only cost of \"too high\" is trivial Firestore reads,\n // while \"too low\" false-rejects real users. Raise on evidence (watch prod logs\n // for legit 429s) rather than pre-emptively tightening. DJ ruling 2026-07-21.\n DISPLAY_NAME_CHECK: { maxRequests: 300, window: '1 h' },\n CHAT_MESSAGE: { maxRequests: 150, window: '1 h' },\n ADMIN_TASK: { maxRequests: 60, window: '1 h' },\n APPEAL_REVIEW: { maxRequests: 30, window: '1 h' },\n BAN_ACTION: { maxRequests: 20, window: '1 h' },\n SHARE_TRANSFER: { maxRequests: 10, window: '1 h' },\n CONTENT_REPORT: { maxRequests: 20, window: '1 h' },\n SHORT_LINK_CLICK: { maxRequests: 60, window: '1 h' },\n FOLLOW: { maxRequests: 60, window: '1 m' },\n MENTION_HISTORY: { maxRequests: 120, window: '1 h' },\n // 240/h (full): the same grant-minting buckets as charter, doubled with the surrounding\n // content limits. Full mode raises the ceilings a grant is minted PER (250-guildmate guilds,\n // 25 file folders, 1000 work files, 10 auditions), so one session legitimately touches more\n // scopes and re-mints more often. See the CHARTER_LIMITS notes above.\n MEDIA_GRANT: { maxRequests: 240, window: '1 h' },\n CHAT_GRANT: { maxRequests: 240, window: '1 h' },\n // Operator step-up stays at 10/h in BOTH modes — an admin second-factor throttle is a\n // security control, not a cost lever, so it never loosens at the flip. See CHARTER_LIMITS.\n OPERATOR_STEP_UP: { maxRequests: 10, window: '1 h' },\n ADMIN_MESSAGE_READ: { maxRequests: 60, window: '1 h' },\n // Admin exact-account lookup — same read bucket in both modes (see CHARTER_LIMITS note).\n USER_LOOKUP: { maxRequests: 30, window: '1 m' },\n NOTIFICATION_BROADCAST: { maxRequests: 10, window: '1 h' },\n NOTIFICATION_ARCHIVE: { maxRequests: 120, window: '1 h' },\n NOTIFICATION_MARK_SEEN: { maxRequests: 120, window: '1 h' },\n FEEDBACK: { maxRequests: 10, window: '1 h' },\n SHORT_LINK_CREATE: { maxRequests: 30, window: '1 h' },\n INVITE_ACTION: { maxRequests: 60, window: '1 h' },\n LIBRARY_SUBMISSION: { maxRequests: 20, window: '1 h' },\n CHANNEL_CREATE: { maxRequests: 20, window: '1 h' },\n CHECKOUT_CREATE: { maxRequests: 20, window: '1 h' },\n // Scaled with DISPLAY_NAME_CHECK's full-mode 300/h — see the CHARTER_LIMITS note.\n AGE_ATTEST: { maxRequests: 75, window: '1 h' },\n },\n};\n\n/** The resolved limit set for the deployed mode. Import THIS (or the alias\n * constants that re-export from it), never CHARTER_/FULL_ directly. A read bound\n * that must hold whichever mode the data was written under uses\n * HALL_SUB_ITEM_READ_BOUND_BY_WORK_TYPE below instead. */\nexport const ACTIVE_LIMITS: TttLimits = byMode(CHARTER_LIMITS, FULL_LIMITS);\n\nconst largestInAnyMode = (pick: (limits: TttLimits) => number): number =>\n Math.max(pick(CHARTER_LIMITS), pick(FULL_LIMITS));\n\n/**\n * The most sub-items one Hall item can hold of each kind (Tale chapters, Tune tracks, Television\n * episodes) in ANY app mode — each kind's count cap at its largest across CHARTER_LIMITS and\n * FULL_LIMITS. A READ bound, never an enforced limit: a Hall item keeps the sub-items it was\n * published with when the mode changes, so a reader that must see all of them sizes its query\n * from this and treats a read that returns more as uncertain. Enforcement reads the active caps\n * (MAX_CHAPTERS, MAX_TUNE_TRACKS, MAX_TELEVISION_EPISODES).\n */\nexport const HALL_SUB_ITEM_READ_BOUND_BY_WORK_TYPE: Readonly<Record<WorkProjectType, number>> = {\n Tales: largestInAnyMode((limits) => limits.workProject.maxChapters),\n Tunes: largestInAnyMode((limits) => limits.workProject.maxTuneTracks),\n Television: largestInAnyMode((limits) => limits.workProject.maxTelevisionEpisodes),\n};\n\n/**\n * User-facing copy for a reached count cap — the ONE owner of the cap-message\n * shape, beside the limits it describes. Every callable refusing on a\n * mode-varied count cap formats through this. In charter mode the message\n * names Charter Season so the cap reads as the season's limit rather than the\n * platform's permanent shape; full mode stays neutral. `hint` is an optional\n * trailing sentence (e.g. \"Delete a file to add another.\").\n */\nexport const countCapReachedMessage = (\n subject: string,\n limit: number,\n noun: string,\n hint?: string,\n): string => {\n const base = byMode(\n `${subject} already has the Charter Season maximum of ${limit} ${noun} — caps expand when Charter Season ends.`,\n `${subject} already has the maximum of ${limit} ${noun}.`,\n );\n return hint ? `${base} ${hint}` : base;\n};\n"]}
@@ -156,4 +156,30 @@ export declare const ADMIN_TASK_STATUS: {
156
156
  readonly WORK_LATER: "workLater";
157
157
  readonly COMPLETED: "completed";
158
158
  };
159
+ /**
160
+ * Who resolves each kind of admin task. `checkin` means the generic queue check-in completes it
161
+ * (the task carries no decision of its own). Every other owner is the domain decision that closes
162
+ * the task — and deletes it in the same transaction — so a generic check-in must never mark one
163
+ * of those tasks resolved: that would drop the decision it exists to collect.
164
+ */
165
+ export declare const ADMIN_TASK_RESOLUTION_OWNERS: readonly ["checkin", "reportResolution", "thresholdReview", "appealReview", "refundDecision", "changeRequestReview", "dispatchClose"];
166
+ export type AdminTaskResolutionOwner = (typeof ADMIN_TASK_RESOLUTION_OWNERS)[number];
167
+ /** The resolution owner of every admin task type. A new task type fails the build until classified. */
168
+ export declare const ADMIN_TASK_RESOLUTION_OWNER_BY_TYPE: {
169
+ readonly adminDispatch: "dispatchClose";
170
+ readonly thresholdLibraryReview: "thresholdReview";
171
+ readonly userReport: "reportResolution";
172
+ readonly 'content-appeal': "appealReview";
173
+ readonly stakeShareAnomaly: "checkin";
174
+ readonly pledgeLedgerAnomaly: "checkin";
175
+ readonly pledgePaymentRepairNeeded: "checkin";
176
+ readonly pledgeDisputeOpened: "checkin";
177
+ readonly pledgeRefundRequested: "refundDecision";
178
+ readonly hallContentChangeRequest: "changeRequestReview";
179
+ };
180
+ /**
181
+ * Whether a generic check-in may mark a task of this stored type resolved. The type is read off an
182
+ * untrusted stored task as a string, so an unknown type answers `false` (fails closed).
183
+ */
184
+ export declare function isAdminTaskResolvedByCheckin(taskType: string): boolean;
159
185
  //# sourceMappingURL=business-admin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"business-admin.d.ts","sourceRoot":"","sources":["../../src/constants/business-admin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"business-admin.d.ts","sourceRoot":"","sources":["../../src/constants/business-admin.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,aAAa;;;;;CAKhB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC;AAEnF,eAAO,MAAM,oBAAoB;;;;;CAKvB,CAAC;AAEX;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB;;;;;IAK9B;;uDAEmD;;CAE3C,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,OAAO,mBAAmB,CAAC,CAAC;AAE/F,mFAAmF;AACnF,eAAO,MAAM,4BAA4B;;;;;CAK/B,CAAC;AAIX,yDAAyD;AACzD,eAAO,MAAM,iCAAiC,MAAM,CAAC;AAErD,qEAAqE;AACrE,eAAO,MAAM,sCAAsC,OAAO,CAAC;AAI3D,6DAA6D;AAC7D,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C;gEACgE;AAChE,eAAO,MAAM,iCAAiC,OAAO,CAAC;AAEtD;;yCAEyC;AACzC,eAAO,MAAM,0CAA0C,OAAO,CAAC;AAM/D,8FAA8F;AAC9F,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,6FAA6F;AAC7F,eAAO,MAAM,6BAA6B,MAAM,CAAC;AAEjD,sEAAsE;AACtE,eAAO,MAAM,oCAAoC,OAAO,CAAC;AAEzD,sDAAsD;AACtD,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAIjD,wFAAwF;AACxF,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAEhD;iFACiF;AACjF,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,iFAAiF;AACjF,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C;8EAC8E;AAC9E,eAAO,MAAM,iCAAiC,OAAO,CAAC;AAEtD,oFAAoF;AACpF,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAMhD,eAAO,MAAM,uBAAuB,qCAAqC,CAAC;AAC1E,eAAO,MAAM,gBAAgB,8BAA8B,CAAC;AAE5D,mFAAmF;AACnF,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAMpD,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,eAAO,MAAM,kCAAkC,OAAO,CAAC;AACvD,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAClD,eAAO,MAAM,oCAAoC,OAAO,CAAC;AACzD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAC/C,eAAO,MAAM,+BAA+B,MAAM,CAAC;AACnD,eAAO,MAAM,4BAA4B,QAAQ,CAAC;AAClD,+FAA+F;AAC/F,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD,wGAAwG;AACxG,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,6BAA6B,MAAM,CAAC;AACjD,eAAO,MAAM,6BAA6B,MAAM,CAAC;AACjD,eAAO,MAAM,8BAA8B,OAAO,CAAC;AAEnD,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC;;sEAEsE;AACtE,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,oEAAoE;AACpE,eAAO,MAAM,8BAA8B,OAAO,CAAC;AAEnD,0DAA0D;AAC1D,eAAO,MAAM,iCAAiC,MAAM,CAAC;AAErD,sEAAsE;AACtE,eAAO,MAAM,oCAAoC,OAAO,CAAC;AAEzD,wDAAwD;AACxD,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAEpD,qCAAqC;AACrC,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAIhD,0EAA0E;AAC1E,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAEpD,sFAAsF;AACtF,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,oFAAoF;AACpF,eAAO,MAAM,oCAAoC,OAAO,CAAC;AAEzD,8DAA8D;AAC9D,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C,gEAAgE;AAChE,eAAO,MAAM,gCAAgC,OAAO,CAAC;AAErD,oEAAoE;AACpE,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD,uFAAuF;AACvF,eAAO,MAAM,sCAAsC,MAAM,CAAC;AAE1D,2DAA2D;AAC3D,eAAO,MAAM,kCAAkC,OAAO,CAAC;AAEvD,4EAA4E;AAC5E,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD,uEAAuE;AACvE,eAAO,MAAM,uCAAuC,MAAM,CAAC;AAE3D,iFAAiF;AACjF,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAEpD,qFAAqF;AACrF,eAAO,MAAM,sCAAsC,MAAM,CAAC;AAE1D,gDAAgD;AAChD,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD,0FAA0F;AAC1F,eAAO,MAAM,uBAAuB,QAA8C,CAAC;AAEnF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,iKAMjB,CAAC;AAEX,yCAAyC;AACzC,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,gGAAgG;AAChG,eAAO,MAAM,6BAA6B,QAAoC,CAAC;AAI/E,qCAAqC;AACrC,eAAO,MAAM,iBAAiB;;;;;CAKpB,CAAC;AAEX;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,uIAQ/B,CAAC;AACX,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC;AAErF,uGAAuG;AACvG,eAAO,MAAM,mCAAmC;;;;;;;;;;;CAWoB,CAAC;AAErE;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAKtE"}
@@ -177,4 +177,40 @@ export const ADMIN_TASK_STATUS = {
177
177
  WORK_LATER: 'workLater',
178
178
  COMPLETED: 'completed',
179
179
  };
180
+ /**
181
+ * Who resolves each kind of admin task. `checkin` means the generic queue check-in completes it
182
+ * (the task carries no decision of its own). Every other owner is the domain decision that closes
183
+ * the task — and deletes it in the same transaction — so a generic check-in must never mark one
184
+ * of those tasks resolved: that would drop the decision it exists to collect.
185
+ */
186
+ export const ADMIN_TASK_RESOLUTION_OWNERS = [
187
+ 'checkin',
188
+ 'reportResolution',
189
+ 'thresholdReview',
190
+ 'appealReview',
191
+ 'refundDecision',
192
+ 'changeRequestReview',
193
+ 'dispatchClose',
194
+ ];
195
+ /** The resolution owner of every admin task type. A new task type fails the build until classified. */
196
+ export const ADMIN_TASK_RESOLUTION_OWNER_BY_TYPE = {
197
+ adminDispatch: 'dispatchClose',
198
+ thresholdLibraryReview: 'thresholdReview',
199
+ userReport: 'reportResolution',
200
+ 'content-appeal': 'appealReview',
201
+ stakeShareAnomaly: 'checkin',
202
+ pledgeLedgerAnomaly: 'checkin',
203
+ pledgePaymentRepairNeeded: 'checkin',
204
+ pledgeDisputeOpened: 'checkin',
205
+ pledgeRefundRequested: 'refundDecision',
206
+ hallContentChangeRequest: 'changeRequestReview',
207
+ };
208
+ /**
209
+ * Whether a generic check-in may mark a task of this stored type resolved. The type is read off an
210
+ * untrusted stored task as a string, so an unknown type answers `false` (fails closed).
211
+ */
212
+ export function isAdminTaskResolvedByCheckin(taskType) {
213
+ return (Object.hasOwn(ADMIN_TASK_RESOLUTION_OWNER_BY_TYPE, taskType) &&
214
+ ADMIN_TASK_RESOLUTION_OWNER_BY_TYPE[taskType] === 'checkin');
215
+ }
180
216
  //# sourceMappingURL=business-admin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"business-admin.js","sourceRoot":"","sources":["../../src/constants/business-admin.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,qCAAqC;AAErC,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,QAAQ,EAAE,CAAC;IACX,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,GAAG,EAAE,CAAC;CACE,CAAC;AAIX,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,CAAC,EAAE,UAAU;IACb,CAAC,EAAE,MAAM;IACT,CAAC,EAAE,QAAQ;IACX,CAAC,EAAE,KAAK;CACA,CAAC;AAEX;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,GAAG,EAAE,EAAE;IACP,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,GAAG;IACT,QAAQ,EAAE,IAAI;IACd;;uDAEmD;IACnD,aAAa,EAAE,IAAI;CACX,CAAC;AAIX,mFAAmF;AACnF,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,mBAAmB,CAAC,GAAG;IAC5C,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,mBAAmB,CAAC,MAAM;IAClD,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,mBAAmB,CAAC,IAAI;IAC9C,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,mBAAmB,CAAC,QAAQ;CAC9C,CAAC;AAEX,2BAA2B;AAE3B,yDAAyD;AACzD,MAAM,CAAC,MAAM,iCAAiC,GAAG,GAAG,CAAC;AAErD,qEAAqE;AACrE,MAAM,CAAC,MAAM,sCAAsC,GAAG,IAAI,CAAC;AAE3D,sCAAsC;AAEtC,6DAA6D;AAC7D,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAE9C;gEACgE;AAChE,MAAM,CAAC,MAAM,iCAAiC,GAAG,IAAI,CAAC;AAEtD;;yCAEyC;AACzC,MAAM,CAAC,MAAM,0CAA0C,GAAG,IAAI,CAAC;AAE/D,iDAAiD;AACjD,wFAAwF;AACxF,2FAA2F;AAE3F,8FAA8F;AAC9F,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;AAE/C,6FAA6F;AAC7F,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AAEjD,sEAAsE;AACtE,MAAM,CAAC,MAAM,oCAAoC,GAAG,IAAI,CAAC;AAEzD,sDAAsD;AACtD,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AAEjD,kCAAkC;AAElC,wFAAwF;AACxF,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;AAEhD;iFACiF;AACjF,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAE9C,iFAAiF;AACjF,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAE9C;8EAC8E;AAC9E,MAAM,CAAC,MAAM,iCAAiC,GAAG,IAAI,CAAC;AAEtD,oFAAoF;AACpF,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;AAEhD,gDAAgD;AAChD,qFAAqF;AACrF,sFAAsF;AACtF,sFAAsF;AACtF,MAAM,CAAC,MAAM,uBAAuB,GAAG,kCAAkC,CAAC;AAC1E,MAAM,CAAC,MAAM,gBAAgB,GAAG,2BAA2B,CAAC;AAE5D,mFAAmF;AACnF,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAC;AAEpD,iDAAiD;AACjD,mFAAmF;AACnF,4EAA4E;AAE5E,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAG,CAAC;AAChD,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAI,CAAC;AACvD,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAClD,MAAM,CAAC,MAAM,oCAAoC,GAAG,IAAI,CAAC;AACzD,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;AAC/C,MAAM,CAAC,MAAM,+BAA+B,GAAG,GAAG,CAAC;AACnD,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAClD,+FAA+F;AAC/F,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAC7C,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AACjD,wGAAwG;AACxG,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAC1C,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,CAAC;AACxC,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AACjD,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AACjD,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;AAEnD,2FAA2F;AAC3F,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAEzC;;sEAEsE;AACtE,MAAM,CAAC,MAAM,+BAA+B,GAAG,GAAG,CAAC;AAEnD,oEAAoE;AACpE,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;AAEnD,0DAA0D;AAC1D,MAAM,CAAC,MAAM,iCAAiC,GAAG,GAAG,CAAC;AAErD,sEAAsE;AACtE,MAAM,CAAC,MAAM,oCAAoC,GAAG,IAAI,CAAC;AAEzD,wDAAwD;AACxD,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAC;AAEpD,qCAAqC;AACrC,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAG,CAAC;AAEhD,6DAA6D;AAE7D,0EAA0E;AAC1E,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAC;AAEpD,sFAAsF;AACtF,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAE9C,oFAAoF;AACpF,MAAM,CAAC,MAAM,oCAAoC,GAAG,IAAI,CAAC;AAEzD,8DAA8D;AAC9D,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,gEAAgE;AAChE,MAAM,CAAC,MAAM,gCAAgC,GAAG,IAAI,CAAC;AAErD,oEAAoE;AACpE,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAElD,uFAAuF;AACvF,MAAM,CAAC,MAAM,sCAAsC,GAAG,GAAG,CAAC;AAE1D,2DAA2D;AAC3D,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAI,CAAC;AAEvD,4EAA4E;AAC5E,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAElD,uEAAuE;AACvE,MAAM,CAAC,MAAM,uCAAuC,GAAG,GAAG,CAAC;AAE3D,iFAAiF;AACjF,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAC;AAEpD,qFAAqF;AACrF,MAAM,CAAC,MAAM,sCAAsC,GAAG,GAAG,CAAC;AAE1D,gDAAgD;AAChD,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAElD,0FAA0F;AAC1F,MAAM,CAAC,MAAM,uBAAuB,GAAG,aAAa,CAAC,OAAO,CAAC,qBAAqB,CAAC;AAEnF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,4BAA4B;IAC5B,0BAA0B;IAC1B,2BAA2B;IAC3B,2BAA2B;IAC3B,gCAAgC;CACxB,CAAC;AAKX,gGAAgG;AAChG,MAAM,CAAC,MAAM,6BAA6B,GAAG,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC;AAE/E,sBAAsB;AAEtB,qCAAqC;AACrC,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,OAAO,EAAE,SAAS;IAClB,WAAW,EAAE,YAAY;IACzB,UAAU,EAAE,WAAW;IACvB,SAAS,EAAE,WAAW;CACd,CAAC","sourcesContent":["// Admin + content-moderation business-rule constants — task priority, dispatches,\n// the moderation/feedback workflow, and admin-task lifecycle.\nimport { ACTIVE_LIMITS } from './app-mode.js';\n\n// --- Admin Task Priority System ---\n\nexport const TASK_PRIORITY = {\n CRITICAL: 4,\n HIGH: 3,\n NORMAL: 2,\n LOW: 1,\n} as const;\n\nexport type TaskPriorityLevel = (typeof TASK_PRIORITY)[keyof typeof TASK_PRIORITY];\n\nexport const TASK_PRIORITY_LABELS = {\n 4: 'CRITICAL',\n 3: 'HIGH',\n 2: 'NORMAL',\n 1: 'LOW',\n} as const;\n\n/**\n * Canonical NUMERIC priority scale for `adminTasks.priority`, shared by BOTH\n * priority systems so `checkoutNextImportantTask` (orderBy priority desc across all\n * task types) compares commensurable values. The userReport report-core score formula\n * already writes on this scale (≈40–2250); the level-based types (thresholdLibraryReview,\n * content-appeal, adminDispatch) map their TASK_PRIORITY level onto these numbers instead\n * of writing the raw 1–4.\n *\n * Resulting order: safety reports (1200–2250) > money anomalies (1100) >\n * CRITICAL (1000) > HIGH (800) > routine reports interleave below > spam-tier (~80).\n */\nexport const TASK_PRIORITY_SCORE = {\n LOW: 50,\n NORMAL: 200,\n HIGH: 800,\n CRITICAL: 1000,\n /** Money-integrity anomaly (stake-share / pledge-ledger corruption signal) — outranks\n * everything except safety-class reports. The three anomaly creators that previously\n * hardcoded `priority: 1` (LOW) all write this. */\n MONEY_ANOMALY: 1100,\n} as const;\n\nexport type TaskPriorityScore = (typeof TASK_PRIORITY_SCORE)[keyof typeof TASK_PRIORITY_SCORE];\n\n/** Maps a level-based TASK_PRIORITY value (1–4) to its canonical numeric score. */\nexport const TASK_PRIORITY_LEVEL_TO_SCORE = {\n [TASK_PRIORITY.LOW]: TASK_PRIORITY_SCORE.LOW,\n [TASK_PRIORITY.NORMAL]: TASK_PRIORITY_SCORE.NORMAL,\n [TASK_PRIORITY.HIGH]: TASK_PRIORITY_SCORE.HIGH,\n [TASK_PRIORITY.CRITICAL]: TASK_PRIORITY_SCORE.CRITICAL,\n} as const;\n\n// --- Admin Dispatches ---\n\n/** Maximum length for an admin-dispatch subject line. */\nexport const MAX_ADMIN_DISPATCH_SUBJECT_LENGTH = 100;\n\n/** Maximum length for the initial body text of an admin dispatch. */\nexport const MAX_ADMIN_DISPATCH_INITIAL_TEXT_LENGTH = 1000;\n\n// --- Content Moderation Workflow ---\n\n/** Maximum length for a content-violation appeal message. */\nexport const MAX_APPEAL_MESSAGE_LENGTH = 1000;\n\n/** Threshold-review reviewer notes (revision notes shown to the author). One owner for\n * the review callable schema AND the admin work-view input. */\nexport const MAX_THRESHOLD_REVIEW_NOTES_LENGTH = 2000;\n\n/** Operator-facing reason stamped on a threshold item whose publish parked terminally\n * (`publishParkedReason`). Server-authored text, bounded so a long copy-failure message\n * cannot grow the doc without limit. */\nexport const MAX_THRESHOLD_PUBLISH_PARKED_REASON_LENGTH = 2000;\n\n// --- Moderation / safety operator text caps ---\n// Each is declared ONCE here and derived by every input schema AND persisted doc schema\n// that carries the field — the numbers must never be re-hardcoded at an enforcement point.\n\n/** Operator-facing INTERNAL reason/rationale (LE-loggable; account actions, case reopens). */\nexport const MAX_INTERNAL_REASON_LENGTH = 2000;\n\n/** The generic owner-readable (user-facing) reason — deliberately short, no detail leaks. */\nexport const MAX_USER_FACING_REASON_LENGTH = 280;\n\n/** Structured close-out summary on report/safety-case resolutions. */\nexport const MAX_SAFETY_RESOLUTION_SUMMARY_LENGTH = 2000;\n\n/** Optional operator note attached to a close-out. */\nexport const MAX_SAFETY_ADMIN_NOTE_LENGTH = 4000;\n\n// --- Report intake text caps ---\n\n/** Reporter free-text narrative/comment (segregated private doc; never on the root). */\nexport const MAX_REPORT_NARRATIVE_LENGTH = 4000;\n\n/** The report DIALOG's comment field cap (the client-side compose bound — tighter than\n * the server narrative cap above, which also absorbs upgrade concatenations). */\nexport const MAX_REPORT_COMMENT_LENGTH = 1000;\n\n/** Minimum operator rationale/reason length on safety-console staged actions. */\nexport const MIN_SAFETY_RATIONALE_LENGTH = 10;\n\n/** Operator reason on an admin chat-moderation action (adminModerateChatMessage /\n * adminReadChannelContext). ONE owner for both callables' `reason` bound. */\nexport const MAX_CHAT_MODERATION_REASON_LENGTH = 1000;\n\n/** Broadcast explicitUids selector audience cap (schema + composer both derive). */\nexport const MAX_BROADCAST_EXPLICIT_UIDS = 2000;\n\n// --- Operator step-up sentinel error codes ---\n// The two backend HttpsError messages the operator TOTP step-up throws; the frontend\n// matches them to open the step-up dialog in ENROLL vs VERIFY mode. ONE declaration —\n// both the callable (functions operatorStepUp.ts) and the frontend hook import these.\nexport const STEP_UP_ENROLL_REQUIRED = 'OPERATOR_STEP_UP_ENROLL_REQUIRED';\nexport const STEP_UP_REQUIRED = 'OPERATOR_STEP_UP_REQUIRED';\n\n/** The frozen report-time captured text snapshot (edit-to-evade guard; NO PII). */\nexport const MAX_REPORT_SNAPSHOT_TEXT_LENGTH = 4000;\n\n// --- Admin console config-content text caps ---\n// Every number the system uses lives HERE — even a field only one surface consumes\n// today, because any future client (TV / phone / admin app) reads ttt-core.\n\nexport const MAX_FUTURE_PLAN_TITLE_LENGTH = 200;\nexport const MAX_FUTURE_PLAN_DESCRIPTION_LENGTH = 4000;\nexport const MAX_PLATFORM_RULE_TITLE_LENGTH = 200;\nexport const MAX_PLATFORM_RULE_DESCRIPTION_LENGTH = 4000;\nexport const MAX_AGREEMENT_POINT_LENGTH = 2000;\nexport const MAX_CONTENT_PAGE_HEADING_LENGTH = 300;\nexport const MAX_CONTENT_PAGE_BODY_LENGTH = 20000;\n/** Sections in one long-form content page (Terms, Privacy, the DMCA policy's process text). */\nexport const MAX_CONTENT_PAGE_SECTIONS = 300;\nexport const MAX_TAKE_IT_DOWN_COPY_LENGTH = 8000;\n/** DMCA policy page: the labeled contact blocks (service provider, designated agent) and their rows. */\nexport const MAX_DMCA_CONTACT_BLOCKS = 10;\nexport const MAX_DMCA_CONTACT_ROWS = 20;\nexport const MAX_DMCA_CONTACT_LABEL_LENGTH = 100;\nexport const MAX_DMCA_CONTACT_VALUE_LENGTH = 500;\nexport const MAX_MAINTENANCE_MESSAGE_LENGTH = 2000;\n\n/** `_appConfig/app.appVersion` — the force-refresh version string VersionGate compares. */\nexport const MAX_APP_VERSION_LENGTH = 64;\n\n/** Announcement-banner copy on `_appConfig/app.announcementMessage`. Deliberately far\n * shorter than the maintenance message: the announcement renders as a one-line banner\n * on the landing page and login screen, not a full-page takeover. */\nexport const MAX_ANNOUNCEMENT_MESSAGE_LENGTH = 300;\n\n/** Admin notes on a content-appeal review (reviewContentAppeal). */\nexport const MAX_APPEAL_REVIEW_NOTES_LENGTH = 2000;\n\n/** Admin reason on a Work/Realm forced-retitle remedy. */\nexport const MAX_REQUIRE_RETITLE_REASON_LENGTH = 500;\n\n/** Optional user-facing DETAIL text on a normal-report resolution. */\nexport const MAX_USER_FACING_REASON_DETAIL_LENGTH = 2000;\n\n/** Admin broadcast / user notification message body. */\nexport const MAX_NOTIFICATION_MESSAGE_LENGTH = 2000;\n\n/** Admin user-search query input. */\nexport const MAX_USER_SEARCH_QUERY_LENGTH = 100;\n\n// --- NCII / child-safety statutory + operator text caps ---\n\n/** Operator reason on the admin \"mark as NCII linked evidence\" intake. */\nexport const MAX_NCII_EVIDENCE_REASON_LENGTH = 1000;\n\n/** Operator written rationale on a TAKE IT DOWN validity decision (immutable row). */\nexport const MAX_NCII_RATIONALE_LENGTH = 4000;\n\n/** Requester's statement of nonconsent (statutory intake — in-app and no-login). */\nexport const MAX_NCII_NONCONSENT_STATEMENT_LENGTH = 4000;\n\n/** Typed-name electronic signature on a statutory request. */\nexport const MAX_NCII_SIGNED_NAME_LENGTH = 256;\n\n/** Requester's optional supporting facts (statutory intake). */\nexport const MAX_NCII_SUPPORTING_FACTS_LENGTH = 8000;\n\n/** Bounded, non-sensitive summary on an NCII request action row. */\nexport const MAX_NCII_ACTION_SUMMARY_LENGTH = 280;\n\n/** Operator description of a registered evidence artifact (e.g. portal screenshot). */\nexport const MAX_SAFETY_ARTIFACT_DESCRIPTION_LENGTH = 500;\n\n/** Operator proof note on a manual NCMEC portal filing. */\nexport const MAX_NCMEC_PORTAL_PROOF_TEXT_LENGTH = 2000;\n\n/** Requester's typed name on the no-login TAKE IT DOWN statutory intake. */\nexport const MAX_NCII_REQUESTER_NAME_LENGTH = 256;\n\n/** Name of the depicted person a representative files on behalf of. */\nexport const MAX_NCII_REPRESENTED_PERSON_NAME_LENGTH = 256;\n\n/** Representative's stated basis of authority (parent/guardian/counsel etc.). */\nexport const MAX_NCII_AUTHORITY_BASIS_LENGTH = 2000;\n\n/** Optional pointer/reference to authority evidence supplied by a representative. */\nexport const MAX_NCII_AUTHORITY_EVIDENCE_REF_LENGTH = 512;\n\n/** Maximum length for a feedback suggestion. */\nexport const MAX_FEEDBACK_SUGGESTION_LENGTH = 100;\n\n/** Maximum number of distinct submitters tracked per feedback suggestion. Mode-varied. */\nexport const MAX_FEEDBACK_SUBMITTERS = ACTIVE_LIMITS.batches.maxFeedbackSubmitters;\n\n/**\n * Canonical list of feedback types accepted by `submitFeedback`. Used both\n * for runtime validation in the schema (`SubmitFeedbackInputSchema`) and\n * for type-safe consumer props on the frontend `<FeedbackSubmission />`\n * component. Adding a new type here is the single source of truth — no\n * mirroring in callers required.\n */\nexport const FEEDBACK_TYPES = [\n 'tradeProfessionSuggestions',\n 'craftSkillTagSuggestions',\n 'talesWorkGenreSuggestions',\n 'tunesWorkGenreSuggestions',\n 'televisionWorkGenreSuggestions',\n] as const;\n\n/** Union of canonical feedback types. */\nexport type FeedbackType = (typeof FEEDBACK_TYPES)[number];\n\n/** Maximum number of items submitted in a single hallLibrary-review submission. Mode-varied. */\nexport const MAX_HALL_LIBRARY_SUBMIT_BATCH = ACTIVE_LIMITS.hall.maxSubmitBatch;\n\n// --- Admin Tasks ---\n\n/** Admin task lifecycle statuses. */\nexport const ADMIN_TASK_STATUS = {\n PENDING: 'pending',\n CHECKED_OUT: 'checkedOut',\n WORK_LATER: 'workLater',\n COMPLETED: 'completed',\n} as const;\n"]}
1
+ {"version":3,"file":"business-admin.js","sourceRoot":"","sources":["../../src/constants/business-admin.ts"],"names":[],"mappings":"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","sourcesContent":["// Admin + content-moderation business-rule constants — task priority, dispatches,\n// the moderation/feedback workflow, and admin-task lifecycle.\nimport { ACTIVE_LIMITS } from './app-mode.js';\nimport type { AdminTaskType } from '../doc-schemas/report-docs.js';\n\n// --- Admin Task Priority System ---\n\nexport const TASK_PRIORITY = {\n CRITICAL: 4,\n HIGH: 3,\n NORMAL: 2,\n LOW: 1,\n} as const;\n\nexport type TaskPriorityLevel = (typeof TASK_PRIORITY)[keyof typeof TASK_PRIORITY];\n\nexport const TASK_PRIORITY_LABELS = {\n 4: 'CRITICAL',\n 3: 'HIGH',\n 2: 'NORMAL',\n 1: 'LOW',\n} as const;\n\n/**\n * Canonical NUMERIC priority scale for `adminTasks.priority`, shared by BOTH\n * priority systems so `checkoutNextImportantTask` (orderBy priority desc across all\n * task types) compares commensurable values. The userReport report-core score formula\n * already writes on this scale (≈40–2250); the level-based types (thresholdLibraryReview,\n * content-appeal, adminDispatch) map their TASK_PRIORITY level onto these numbers instead\n * of writing the raw 1–4.\n *\n * Resulting order: safety reports (1200–2250) > money anomalies (1100) >\n * CRITICAL (1000) > HIGH (800) > routine reports interleave below > spam-tier (~80).\n */\nexport const TASK_PRIORITY_SCORE = {\n LOW: 50,\n NORMAL: 200,\n HIGH: 800,\n CRITICAL: 1000,\n /** Money-integrity anomaly (stake-share / pledge-ledger corruption signal) — outranks\n * everything except safety-class reports. The three anomaly creators that previously\n * hardcoded `priority: 1` (LOW) all write this. */\n MONEY_ANOMALY: 1100,\n} as const;\n\nexport type TaskPriorityScore = (typeof TASK_PRIORITY_SCORE)[keyof typeof TASK_PRIORITY_SCORE];\n\n/** Maps a level-based TASK_PRIORITY value (1–4) to its canonical numeric score. */\nexport const TASK_PRIORITY_LEVEL_TO_SCORE = {\n [TASK_PRIORITY.LOW]: TASK_PRIORITY_SCORE.LOW,\n [TASK_PRIORITY.NORMAL]: TASK_PRIORITY_SCORE.NORMAL,\n [TASK_PRIORITY.HIGH]: TASK_PRIORITY_SCORE.HIGH,\n [TASK_PRIORITY.CRITICAL]: TASK_PRIORITY_SCORE.CRITICAL,\n} as const;\n\n// --- Admin Dispatches ---\n\n/** Maximum length for an admin-dispatch subject line. */\nexport const MAX_ADMIN_DISPATCH_SUBJECT_LENGTH = 100;\n\n/** Maximum length for the initial body text of an admin dispatch. */\nexport const MAX_ADMIN_DISPATCH_INITIAL_TEXT_LENGTH = 1000;\n\n// --- Content Moderation Workflow ---\n\n/** Maximum length for a content-violation appeal message. */\nexport const MAX_APPEAL_MESSAGE_LENGTH = 1000;\n\n/** Threshold-review reviewer notes (revision notes shown to the author). One owner for\n * the review callable schema AND the admin work-view input. */\nexport const MAX_THRESHOLD_REVIEW_NOTES_LENGTH = 2000;\n\n/** Operator-facing reason stamped on a threshold item whose publish parked terminally\n * (`publishParkedReason`). Server-authored text, bounded so a long copy-failure message\n * cannot grow the doc without limit. */\nexport const MAX_THRESHOLD_PUBLISH_PARKED_REASON_LENGTH = 2000;\n\n// --- Moderation / safety operator text caps ---\n// Each is declared ONCE here and derived by every input schema AND persisted doc schema\n// that carries the field — the numbers must never be re-hardcoded at an enforcement point.\n\n/** Operator-facing INTERNAL reason/rationale (LE-loggable; account actions, case reopens). */\nexport const MAX_INTERNAL_REASON_LENGTH = 2000;\n\n/** The generic owner-readable (user-facing) reason — deliberately short, no detail leaks. */\nexport const MAX_USER_FACING_REASON_LENGTH = 280;\n\n/** Structured close-out summary on report/safety-case resolutions. */\nexport const MAX_SAFETY_RESOLUTION_SUMMARY_LENGTH = 2000;\n\n/** Optional operator note attached to a close-out. */\nexport const MAX_SAFETY_ADMIN_NOTE_LENGTH = 4000;\n\n// --- Report intake text caps ---\n\n/** Reporter free-text narrative/comment (segregated private doc; never on the root). */\nexport const MAX_REPORT_NARRATIVE_LENGTH = 4000;\n\n/** The report DIALOG's comment field cap (the client-side compose bound — tighter than\n * the server narrative cap above, which also absorbs upgrade concatenations). */\nexport const MAX_REPORT_COMMENT_LENGTH = 1000;\n\n/** Minimum operator rationale/reason length on safety-console staged actions. */\nexport const MIN_SAFETY_RATIONALE_LENGTH = 10;\n\n/** Operator reason on an admin chat-moderation action (adminModerateChatMessage /\n * adminReadChannelContext). ONE owner for both callables' `reason` bound. */\nexport const MAX_CHAT_MODERATION_REASON_LENGTH = 1000;\n\n/** Broadcast explicitUids selector audience cap (schema + composer both derive). */\nexport const MAX_BROADCAST_EXPLICIT_UIDS = 2000;\n\n// --- Operator step-up sentinel error codes ---\n// The two backend HttpsError messages the operator TOTP step-up throws; the frontend\n// matches them to open the step-up dialog in ENROLL vs VERIFY mode. ONE declaration —\n// both the callable (functions operatorStepUp.ts) and the frontend hook import these.\nexport const STEP_UP_ENROLL_REQUIRED = 'OPERATOR_STEP_UP_ENROLL_REQUIRED';\nexport const STEP_UP_REQUIRED = 'OPERATOR_STEP_UP_REQUIRED';\n\n/** The frozen report-time captured text snapshot (edit-to-evade guard; NO PII). */\nexport const MAX_REPORT_SNAPSHOT_TEXT_LENGTH = 4000;\n\n// --- Admin console config-content text caps ---\n// Every number the system uses lives HERE — even a field only one surface consumes\n// today, because any future client (TV / phone / admin app) reads ttt-core.\n\nexport const MAX_FUTURE_PLAN_TITLE_LENGTH = 200;\nexport const MAX_FUTURE_PLAN_DESCRIPTION_LENGTH = 4000;\nexport const MAX_PLATFORM_RULE_TITLE_LENGTH = 200;\nexport const MAX_PLATFORM_RULE_DESCRIPTION_LENGTH = 4000;\nexport const MAX_AGREEMENT_POINT_LENGTH = 2000;\nexport const MAX_CONTENT_PAGE_HEADING_LENGTH = 300;\nexport const MAX_CONTENT_PAGE_BODY_LENGTH = 20000;\n/** Sections in one long-form content page (Terms, Privacy, the DMCA policy's process text). */\nexport const MAX_CONTENT_PAGE_SECTIONS = 300;\nexport const MAX_TAKE_IT_DOWN_COPY_LENGTH = 8000;\n/** DMCA policy page: the labeled contact blocks (service provider, designated agent) and their rows. */\nexport const MAX_DMCA_CONTACT_BLOCKS = 10;\nexport const MAX_DMCA_CONTACT_ROWS = 20;\nexport const MAX_DMCA_CONTACT_LABEL_LENGTH = 100;\nexport const MAX_DMCA_CONTACT_VALUE_LENGTH = 500;\nexport const MAX_MAINTENANCE_MESSAGE_LENGTH = 2000;\n\n/** `_appConfig/app.appVersion` — the force-refresh version string VersionGate compares. */\nexport const MAX_APP_VERSION_LENGTH = 64;\n\n/** Announcement-banner copy on `_appConfig/app.announcementMessage`. Deliberately far\n * shorter than the maintenance message: the announcement renders as a one-line banner\n * on the landing page and login screen, not a full-page takeover. */\nexport const MAX_ANNOUNCEMENT_MESSAGE_LENGTH = 300;\n\n/** Admin notes on a content-appeal review (reviewContentAppeal). */\nexport const MAX_APPEAL_REVIEW_NOTES_LENGTH = 2000;\n\n/** Admin reason on a Work/Realm forced-retitle remedy. */\nexport const MAX_REQUIRE_RETITLE_REASON_LENGTH = 500;\n\n/** Optional user-facing DETAIL text on a normal-report resolution. */\nexport const MAX_USER_FACING_REASON_DETAIL_LENGTH = 2000;\n\n/** Admin broadcast / user notification message body. */\nexport const MAX_NOTIFICATION_MESSAGE_LENGTH = 2000;\n\n/** Admin user-search query input. */\nexport const MAX_USER_SEARCH_QUERY_LENGTH = 100;\n\n// --- NCII / child-safety statutory + operator text caps ---\n\n/** Operator reason on the admin \"mark as NCII linked evidence\" intake. */\nexport const MAX_NCII_EVIDENCE_REASON_LENGTH = 1000;\n\n/** Operator written rationale on a TAKE IT DOWN validity decision (immutable row). */\nexport const MAX_NCII_RATIONALE_LENGTH = 4000;\n\n/** Requester's statement of nonconsent (statutory intake — in-app and no-login). */\nexport const MAX_NCII_NONCONSENT_STATEMENT_LENGTH = 4000;\n\n/** Typed-name electronic signature on a statutory request. */\nexport const MAX_NCII_SIGNED_NAME_LENGTH = 256;\n\n/** Requester's optional supporting facts (statutory intake). */\nexport const MAX_NCII_SUPPORTING_FACTS_LENGTH = 8000;\n\n/** Bounded, non-sensitive summary on an NCII request action row. */\nexport const MAX_NCII_ACTION_SUMMARY_LENGTH = 280;\n\n/** Operator description of a registered evidence artifact (e.g. portal screenshot). */\nexport const MAX_SAFETY_ARTIFACT_DESCRIPTION_LENGTH = 500;\n\n/** Operator proof note on a manual NCMEC portal filing. */\nexport const MAX_NCMEC_PORTAL_PROOF_TEXT_LENGTH = 2000;\n\n/** Requester's typed name on the no-login TAKE IT DOWN statutory intake. */\nexport const MAX_NCII_REQUESTER_NAME_LENGTH = 256;\n\n/** Name of the depicted person a representative files on behalf of. */\nexport const MAX_NCII_REPRESENTED_PERSON_NAME_LENGTH = 256;\n\n/** Representative's stated basis of authority (parent/guardian/counsel etc.). */\nexport const MAX_NCII_AUTHORITY_BASIS_LENGTH = 2000;\n\n/** Optional pointer/reference to authority evidence supplied by a representative. */\nexport const MAX_NCII_AUTHORITY_EVIDENCE_REF_LENGTH = 512;\n\n/** Maximum length for a feedback suggestion. */\nexport const MAX_FEEDBACK_SUGGESTION_LENGTH = 100;\n\n/** Maximum number of distinct submitters tracked per feedback suggestion. Mode-varied. */\nexport const MAX_FEEDBACK_SUBMITTERS = ACTIVE_LIMITS.batches.maxFeedbackSubmitters;\n\n/**\n * Canonical list of feedback types accepted by `submitFeedback`. Used both\n * for runtime validation in the schema (`SubmitFeedbackInputSchema`) and\n * for type-safe consumer props on the frontend `<FeedbackSubmission />`\n * component. Adding a new type here is the single source of truth — no\n * mirroring in callers required.\n */\nexport const FEEDBACK_TYPES = [\n 'tradeProfessionSuggestions',\n 'craftSkillTagSuggestions',\n 'talesWorkGenreSuggestions',\n 'tunesWorkGenreSuggestions',\n 'televisionWorkGenreSuggestions',\n] as const;\n\n/** Union of canonical feedback types. */\nexport type FeedbackType = (typeof FEEDBACK_TYPES)[number];\n\n/** Maximum number of items submitted in a single hallLibrary-review submission. Mode-varied. */\nexport const MAX_HALL_LIBRARY_SUBMIT_BATCH = ACTIVE_LIMITS.hall.maxSubmitBatch;\n\n// --- Admin Tasks ---\n\n/** Admin task lifecycle statuses. */\nexport const ADMIN_TASK_STATUS = {\n PENDING: 'pending',\n CHECKED_OUT: 'checkedOut',\n WORK_LATER: 'workLater',\n COMPLETED: 'completed',\n} as const;\n\n/**\n * Who resolves each kind of admin task. `checkin` means the generic queue check-in completes it\n * (the task carries no decision of its own). Every other owner is the domain decision that closes\n * the task — and deletes it in the same transaction — so a generic check-in must never mark one\n * of those tasks resolved: that would drop the decision it exists to collect.\n */\nexport const ADMIN_TASK_RESOLUTION_OWNERS = [\n 'checkin',\n 'reportResolution',\n 'thresholdReview',\n 'appealReview',\n 'refundDecision',\n 'changeRequestReview',\n 'dispatchClose',\n] as const;\nexport type AdminTaskResolutionOwner = (typeof ADMIN_TASK_RESOLUTION_OWNERS)[number];\n\n/** The resolution owner of every admin task type. A new task type fails the build until classified. */\nexport const ADMIN_TASK_RESOLUTION_OWNER_BY_TYPE = {\n adminDispatch: 'dispatchClose',\n thresholdLibraryReview: 'thresholdReview',\n userReport: 'reportResolution',\n 'content-appeal': 'appealReview',\n stakeShareAnomaly: 'checkin',\n pledgeLedgerAnomaly: 'checkin',\n pledgePaymentRepairNeeded: 'checkin',\n pledgeDisputeOpened: 'checkin',\n pledgeRefundRequested: 'refundDecision',\n hallContentChangeRequest: 'changeRequestReview',\n} as const satisfies Record<AdminTaskType, AdminTaskResolutionOwner>;\n\n/**\n * Whether a generic check-in may mark a task of this stored type resolved. The type is read off an\n * untrusted stored task as a string, so an unknown type answers `false` (fails closed).\n */\nexport function isAdminTaskResolvedByCheckin(taskType: string): boolean {\n return (\n Object.hasOwn(ADMIN_TASK_RESOLUTION_OWNER_BY_TYPE, taskType) &&\n ADMIN_TASK_RESOLUTION_OWNER_BY_TYPE[taskType as AdminTaskType] === 'checkin'\n );\n}\n"]}
@@ -32,6 +32,12 @@ export declare const PASSWORD_MIN_LENGTH = 7;
32
32
  * password-manager output while keeping the product input finite.
33
33
  */
34
34
  export declare const PASSWORD_MAX_LENGTH = 64;
35
+ /** Whole years of age at which a person may hold a (teen) account; younger is refused. */
36
+ export declare const AGE_TEEN_FLOOR_YEARS = 13;
37
+ /** Whole years of age at which an account is adult. */
38
+ export declare const AGE_ADULT_FLOOR_YEARS = 18;
39
+ /** The earliest birth year a date of birth may carry; an earlier year is not a real entry. */
40
+ export declare const DATE_OF_BIRTH_MIN_YEAR = 1900;
35
41
  /** Maximum number of craft-skills a user can upload to their profile. Mode-varied. */
36
42
  export declare const CRAFT_SKILL_LIMIT: number;
37
43
  /** Maximum number of work projects an artisan can own (steward) at once. */
@@ -1 +1 @@
1
- {"version":3,"file":"business-user.d.ts","sourceRoot":"","sources":["../../src/constants/business-user.ts"],"names":[],"mappings":"AAKA,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,2EAA2E;AAC3E,eAAO,MAAM,cAAc,QAAmB,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,kBAAkB,CAAC;AAE1D;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAI9C;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAMtC,sFAAsF;AACtF,eAAO,MAAM,iBAAiB,QAAoC,CAAC;AAInE,4EAA4E;AAC5E,eAAO,MAAM,uBAAuB,QAA0C,CAAC;AAE/E,0EAA0E;AAC1E,eAAO,MAAM,4BAA4B,QAA+C,CAAC;AAEzF,qDAAqD;AACrD,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,6CAA6C;AAC7C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C,yFAAyF;AACzF,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAI/C,iFAAiF;AACjF,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAIpC,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAIrC;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAC"}
1
+ {"version":3,"file":"business-user.d.ts","sourceRoot":"","sources":["../../src/constants/business-user.ts"],"names":[],"mappings":"AAKA,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,2EAA2E;AAC3E,eAAO,MAAM,cAAc,QAAmB,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,kBAAkB,CAAC;AAE1D;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAI9C;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAItC,0FAA0F;AAC1F,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,uDAAuD;AACvD,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,8FAA8F;AAC9F,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAM3C,sFAAsF;AACtF,eAAO,MAAM,iBAAiB,QAAoC,CAAC;AAInE,4EAA4E;AAC5E,eAAO,MAAM,uBAAuB,QAA0C,CAAC;AAE/E,0EAA0E;AAC1E,eAAO,MAAM,4BAA4B,QAA+C,CAAC;AAEzF,qDAAqD;AACrD,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,6CAA6C;AAC7C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C,yFAAyF;AACzF,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAI/C,iFAAiF;AACjF,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAIpC,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAIrC;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAC"}
@@ -36,6 +36,13 @@ export const PASSWORD_MIN_LENGTH = 7;
36
36
  * password-manager output while keeping the product input finite.
37
37
  */
38
38
  export const PASSWORD_MAX_LENGTH = 64;
39
+ // --- Age from a date of birth ---
40
+ /** Whole years of age at which a person may hold a (teen) account; younger is refused. */
41
+ export const AGE_TEEN_FLOOR_YEARS = 13;
42
+ /** Whole years of age at which an account is adult. */
43
+ export const AGE_ADULT_FLOOR_YEARS = 18;
44
+ /** The earliest birth year a date of birth may carry; an earlier year is not a real entry. */
45
+ export const DATE_OF_BIRTH_MIN_YEAR = 1900;
39
46
  import { ACTIVE_LIMITS } from './app-mode.js';
40
47
  // --- User Profile Craft Skills ---
41
48
  /** Maximum number of craft-skills a user can upload to their profile. Mode-varied. */
@@ -1 +1 @@
1
- {"version":3,"file":"business-user.js","sourceRoot":"","sources":["../../src/constants/business-user.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,+BAA+B;AAE/B,6BAA6B;AAE7B,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAE9C,4BAA4B;AAE5B;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,oCAAoC;AAEpC,sFAAsF;AACtF,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC;AAEnE,yEAAyE;AAEzE,4EAA4E;AAC5E,MAAM,CAAC,MAAM,uBAAuB,GAAG,aAAa,CAAC,IAAI,CAAC,oBAAoB,CAAC;AAE/E,0EAA0E;AAC1E,MAAM,CAAC,MAAM,4BAA4B,GAAG,aAAa,CAAC,IAAI,CAAC,yBAAyB,CAAC;AAEzF,qDAAqD;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAEtC,6CAA6C;AAC7C,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,yFAAyF;AACzF,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,0BAA0B;AAE1B,iFAAiF;AACjF,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAEpC,iBAAiB;AAEjB,6DAA6D;AAC7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,gCAAgC;AAEhC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC","sourcesContent":["// User identity + profile business-rule constants — display names, craft skills,\n// mention history, and search.\n\n// --- User Display Names ---\n\n/** Minimum length for a user display name (inclusive). */\nexport const USERNAME_MIN_LENGTH = 3;\n\n/** Maximum length for a user display name (inclusive). */\nexport const USERNAME_MAX_LENGTH = 20;\n\n/** Allowed characters in a user display name: letters and numbers only. */\nexport const USERNAME_REGEX = /^[a-zA-Z0-9]+$/;\n\n/**\n * Display-name sentinel for an erased account (N3 data-deletion / GDPR erasure).\n * The `publicUsers/{uid}` doc is kept (so uid→name resolvers keep working) with\n * `displayName` set to this and `anonymizedAt` stamped; uid-keyed authorship /\n * share-tombstone records render with this label.\n */\nexport const FORMER_MEMBER_DISPLAY_NAME = 'Former member';\n\n/**\n * N3 account deletion / GDPR erasure — the grace window (days) between the deletion\n * request and the destructive scrub. Logging back in during the window is the one cancel.\n * Persisted on each request as `graceDays` (compliance trail) and stated as product policy\n * in the user-facing data-deletion documents — ONE declaration; the callable and the copy\n * both derive from it.\n */\nexport const ACCOUNT_DELETION_GRACE_DAYS = 30;\n\n// --- Account Passwords ---\n\n/**\n * Minimum length (inclusive) for a TTT account password. Counted in JavaScript\n * string length units (UTF-16 code units), consistent with HTML input length\n * behavior and the Firebase Auth SDK. Above Firebase's hard floor of 6 and within\n * the supported hosted password-policy range, which is set from this declaration.\n */\nexport const PASSWORD_MIN_LENGTH = 7;\n\n/**\n * Maximum length (inclusive) for a TTT account password, in the same UTF-16\n * code-unit units as the minimum. Long enough for passphrases and\n * password-manager output while keeping the product input finite.\n */\nexport const PASSWORD_MAX_LENGTH = 64;\n\nimport { ACTIVE_LIMITS } from './app-mode.js';\n\n// --- User Profile Craft Skills ---\n\n/** Maximum number of craft-skills a user can upload to their profile. Mode-varied. */\nexport const CRAFT_SKILL_LIMIT = ACTIVE_LIMITS.user.maxCraftSkills;\n\n// --- Work-project participation caps (mode-varied, server-enforced) ---\n\n/** Maximum number of work projects an artisan can own (steward) at once. */\nexport const MAX_OWNED_WORK_PROJECTS = ACTIVE_LIMITS.user.maxOwnedWorkProjects;\n\n/** Maximum number of active guild memberships a user can hold at once. */\nexport const MAX_ASSOCIATED_WORK_PROJECTS = ACTIVE_LIMITS.user.maxAssociatedWorkProjects;\n\n/** Maximum number of tags allowed per craftSkill. */\nexport const MAX_CRAFT_SKILL_TAGS = 5;\n\n/** Maximum length for a craft-skill name. */\nexport const MAX_CRAFT_SKILL_NAME_LENGTH = 200;\n\n/** Non-US artisan-interest country/region free-text (input schema + privateData doc). */\nexport const MAX_ARTISAN_LOCATION_LENGTH = 100;\n\n// --- Mention History ---\n\n/** Maximum number of recent mentions kept in the user's mention-history hook. */\nexport const MAX_HISTORY_ITEMS = 10;\n\n// --- Search ---\n\n/** Maximum number of results returned by the search hook. */\nexport const SEARCH_RESULT_LIMIT = 6;\n\n// --- First-visit site tour ---\n\n/**\n * Current first-visit site-tour version. The `updateSiteTourPreference` callable stamps\n * this onto `privateData.siteTour.completedVersion` at completion, and the landing\n * eligibility check suppresses the automatic invitation while a member's\n * `completedVersion` equals it. Bump when the permanent-shell-controls tour changes\n * enough to re-invite every member. See\n * ttt-prod docs/design/landing-backstage-guide-and-first-visit-plan.md §10.\n */\nexport const SITE_TOUR_CURRENT_VERSION = 1;\n"]}
1
+ {"version":3,"file":"business-user.js","sourceRoot":"","sources":["../../src/constants/business-user.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,+BAA+B;AAE/B,6BAA6B;AAE7B,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAE9C,4BAA4B;AAE5B;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,mCAAmC;AAEnC,0FAA0F;AAC1F,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEvC,uDAAuD;AACvD,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAExC,8FAA8F;AAC9F,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,oCAAoC;AAEpC,sFAAsF;AACtF,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC;AAEnE,yEAAyE;AAEzE,4EAA4E;AAC5E,MAAM,CAAC,MAAM,uBAAuB,GAAG,aAAa,CAAC,IAAI,CAAC,oBAAoB,CAAC;AAE/E,0EAA0E;AAC1E,MAAM,CAAC,MAAM,4BAA4B,GAAG,aAAa,CAAC,IAAI,CAAC,yBAAyB,CAAC;AAEzF,qDAAqD;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAEtC,6CAA6C;AAC7C,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,yFAAyF;AACzF,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,0BAA0B;AAE1B,iFAAiF;AACjF,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAEpC,iBAAiB;AAEjB,6DAA6D;AAC7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,gCAAgC;AAEhC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC","sourcesContent":["// User identity + profile business-rule constants — display names, craft skills,\n// mention history, and search.\n\n// --- User Display Names ---\n\n/** Minimum length for a user display name (inclusive). */\nexport const USERNAME_MIN_LENGTH = 3;\n\n/** Maximum length for a user display name (inclusive). */\nexport const USERNAME_MAX_LENGTH = 20;\n\n/** Allowed characters in a user display name: letters and numbers only. */\nexport const USERNAME_REGEX = /^[a-zA-Z0-9]+$/;\n\n/**\n * Display-name sentinel for an erased account (N3 data-deletion / GDPR erasure).\n * The `publicUsers/{uid}` doc is kept (so uid→name resolvers keep working) with\n * `displayName` set to this and `anonymizedAt` stamped; uid-keyed authorship /\n * share-tombstone records render with this label.\n */\nexport const FORMER_MEMBER_DISPLAY_NAME = 'Former member';\n\n/**\n * N3 account deletion / GDPR erasure — the grace window (days) between the deletion\n * request and the destructive scrub. Logging back in during the window is the one cancel.\n * Persisted on each request as `graceDays` (compliance trail) and stated as product policy\n * in the user-facing data-deletion documents — ONE declaration; the callable and the copy\n * both derive from it.\n */\nexport const ACCOUNT_DELETION_GRACE_DAYS = 30;\n\n// --- Account Passwords ---\n\n/**\n * Minimum length (inclusive) for a TTT account password. Counted in JavaScript\n * string length units (UTF-16 code units), consistent with HTML input length\n * behavior and the Firebase Auth SDK. Above Firebase's hard floor of 6 and within\n * the supported hosted password-policy range, which is set from this declaration.\n */\nexport const PASSWORD_MIN_LENGTH = 7;\n\n/**\n * Maximum length (inclusive) for a TTT account password, in the same UTF-16\n * code-unit units as the minimum. Long enough for passphrases and\n * password-manager output while keeping the product input finite.\n */\nexport const PASSWORD_MAX_LENGTH = 64;\n\n// --- Age from a date of birth ---\n\n/** Whole years of age at which a person may hold a (teen) account; younger is refused. */\nexport const AGE_TEEN_FLOOR_YEARS = 13;\n\n/** Whole years of age at which an account is adult. */\nexport const AGE_ADULT_FLOOR_YEARS = 18;\n\n/** The earliest birth year a date of birth may carry; an earlier year is not a real entry. */\nexport const DATE_OF_BIRTH_MIN_YEAR = 1900;\n\nimport { ACTIVE_LIMITS } from './app-mode.js';\n\n// --- User Profile Craft Skills ---\n\n/** Maximum number of craft-skills a user can upload to their profile. Mode-varied. */\nexport const CRAFT_SKILL_LIMIT = ACTIVE_LIMITS.user.maxCraftSkills;\n\n// --- Work-project participation caps (mode-varied, server-enforced) ---\n\n/** Maximum number of work projects an artisan can own (steward) at once. */\nexport const MAX_OWNED_WORK_PROJECTS = ACTIVE_LIMITS.user.maxOwnedWorkProjects;\n\n/** Maximum number of active guild memberships a user can hold at once. */\nexport const MAX_ASSOCIATED_WORK_PROJECTS = ACTIVE_LIMITS.user.maxAssociatedWorkProjects;\n\n/** Maximum number of tags allowed per craftSkill. */\nexport const MAX_CRAFT_SKILL_TAGS = 5;\n\n/** Maximum length for a craft-skill name. */\nexport const MAX_CRAFT_SKILL_NAME_LENGTH = 200;\n\n/** Non-US artisan-interest country/region free-text (input schema + privateData doc). */\nexport const MAX_ARTISAN_LOCATION_LENGTH = 100;\n\n// --- Mention History ---\n\n/** Maximum number of recent mentions kept in the user's mention-history hook. */\nexport const MAX_HISTORY_ITEMS = 10;\n\n// --- Search ---\n\n/** Maximum number of results returned by the search hook. */\nexport const SEARCH_RESULT_LIMIT = 6;\n\n// --- First-visit site tour ---\n\n/**\n * Current first-visit site-tour version. The `updateSiteTourPreference` callable stamps\n * this onto `privateData.siteTour.completedVersion` at completion, and the landing\n * eligibility check suppresses the automatic invitation while a member's\n * `completedVersion` equals it. Bump when the permanent-shell-controls tour changes\n * enough to re-invite every member. See\n * ttt-prod docs/design/landing-backstage-guide-and-first-visit-plan.md §10.\n */\nexport const SITE_TOUR_CURRENT_VERSION = 1;\n"]}
@@ -9,6 +9,12 @@ export declare const PUBLIC_DOCUMENT_LABELS: Record<PublicDocumentId, string>;
9
9
  * `acceptance.claimKey`) — one name for both sides.
10
10
  */
11
11
  export declare const PUBLIC_DOCUMENTS_ACCEPTED_CLAIM = "docsAccepted";
12
+ /**
13
+ * The custom claim a finished registration carries (value `true`). Set when registration
14
+ * completes, and re-derived from the account's canonical state by the claim reconciler; the
15
+ * member-content read rules require it, so a bare login that skipped the age step reads nothing.
16
+ */
17
+ export declare const REGISTERED_MEMBER_CLAIM = "registeredMember";
12
18
  /**
13
19
  * How an acceptance was recorded: at registration, or through the re-acceptance prompt.
14
20
  * Both write the same private summary and the same `publicDocuments.accepted` audit event.
@@ -1 +1 @@
1
- {"version":3,"file":"public-documents.d.ts","sourceRoot":"","sources":["../../src/constants/public-documents.ts"],"names":[],"mappings":"AAkBA,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,uHAOtB,CAAC;AACX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAOnE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,iBAAiB,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,kCAAkC,2CAA4C,CAAC;AAC5F,MAAM,MAAM,8BAA8B,GAAG,CAAC,OAAO,kCAAkC,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjG;;;;;GAKG;AACH,eAAO,MAAM,uCAAuC,yFACoC,CAAC;AAEzF,2EAA2E;AAC3E,eAAO,MAAM,kCAAkC,KAAK,CAAC"}
1
+ {"version":3,"file":"public-documents.d.ts","sourceRoot":"","sources":["../../src/constants/public-documents.ts"],"names":[],"mappings":"AAkBA,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,uHAOtB,CAAC;AACX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAOnE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,iBAAiB,CAAC;AAE9D;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,qBAAqB,CAAC;AAE1D;;;GAGG;AACH,eAAO,MAAM,kCAAkC,2CAA4C,CAAC;AAC5F,MAAM,MAAM,8BAA8B,GAAG,CAAC,OAAO,kCAAkC,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjG;;;;;GAKG;AACH,eAAO,MAAM,uCAAuC,yFACoC,CAAC;AAEzF,2EAA2E;AAC3E,eAAO,MAAM,kCAAkC,KAAK,CAAC"}
@@ -38,6 +38,12 @@ export const PUBLIC_DOCUMENT_LABELS = {
38
38
  * `acceptance.claimKey`) — one name for both sides.
39
39
  */
40
40
  export const PUBLIC_DOCUMENTS_ACCEPTED_CLAIM = 'docsAccepted';
41
+ /**
42
+ * The custom claim a finished registration carries (value `true`). Set when registration
43
+ * completes, and re-derived from the account's canonical state by the claim reconciler; the
44
+ * member-content read rules require it, so a bare login that skipped the age step reads nothing.
45
+ */
46
+ export const REGISTERED_MEMBER_CLAIM = 'registeredMember';
41
47
  /**
42
48
  * How an acceptance was recorded: at registration, or through the re-acceptance prompt.
43
49
  * Both write the same private summary and the same `publicDocuments.accepted` audit event.
@@ -1 +1 @@
1
- {"version":3,"file":"public-documents.js","sourceRoot":"","sources":["../../src/constants/public-documents.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,gFAAgF;AAChF,0EAA0E;AAC1E,8EAA8E;AAC9E,iFAAiF;AACjF,kCAAkC;AAClC,EAAE;AACF,6EAA6E;AAC7E,iFAAiF;AACjF,2DAA2D;AAC3D,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,iEAAiE;AACjE,+EAA+E;AAE/E,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,yDAAyD;AACzD,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,YAAY,CAAC,UAAU;IACvB,YAAY,CAAC,YAAY;IACzB,YAAY,CAAC,oBAAoB;IACjC,YAAY,CAAC,YAAY;IACzB,YAAY,CAAC,WAAW;IACxB,YAAY,CAAC,sBAAsB;CAC3B,CAAC;AAGX,2FAA2F;AAC3F,MAAM,CAAC,MAAM,sBAAsB,GAAqC;IACtE,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,kBAAkB;IAC7C,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,gBAAgB;IAC7C,CAAC,YAAY,CAAC,oBAAoB,CAAC,EAAE,oBAAoB;IACzD,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,cAAc;IAC3C,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,aAAa;IACzC,CAAC,YAAY,CAAC,sBAAsB,CAAC,EAAE,cAAc;CACtD,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,cAAc,CAAC;AAE9D;;;GAGG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAAC,cAAc,EAAE,cAAc,CAAU,CAAC;AAG5F;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uCAAuC,GAClD,sFAAsF,CAAC;AAEzF,2EAA2E;AAC3E,MAAM,CAAC,MAAM,kCAAkC,GAAG,EAAE,CAAC","sourcesContent":["// ============================================================================\n// PUBLIC DOCUMENTS — the Admin-editable public web pages that are versioned and\n// published in release bundles: Terms of Service, Privacy Policy, Rules &\n// Agreements, Future Plans, the DMCA policy, and the Take It Down explanatory\n// copy. ONE owner for the document identity, display labels, and the numbers the\n// release / acceptance flow uses.\n//\n// Each PublicDocumentId is ALSO the doc id of that document's public current\n// projection under `_appConfig` (PATH_BUILDERS.publicDocumentProjection), so the\n// ids derive from SPECIAL_DOCS rather than being restated.\n//\n// Versions are whole numbers v1, v2, … per document. \"Require acceptance\" is a\n// property of a RELEASE, never a second version number: a release that requires\n// acceptance raises the single required-acceptance level by one.\n// ============================================================================\n\nimport { SPECIAL_DOCS } from '../paths/collections.js';\n\n/** Every public document, in canonical display order. */\nexport const PUBLIC_DOCUMENT_IDS = [\n SPECIAL_DOCS.TERMS_PAGE,\n SPECIAL_DOCS.PRIVACY_PAGE,\n SPECIAL_DOCS.RULES_AND_AGREEMENTS,\n SPECIAL_DOCS.FUTURE_PLANS,\n SPECIAL_DOCS.DMCA_POLICY,\n SPECIAL_DOCS.TAKE_IT_DOWN_PAGE_COPY,\n] as const;\nexport type PublicDocumentId = (typeof PUBLIC_DOCUMENT_IDS)[number];\n\n/** The user-facing name of each public document (Admin list, acceptance prompt, links). */\nexport const PUBLIC_DOCUMENT_LABELS: Record<PublicDocumentId, string> = {\n [SPECIAL_DOCS.TERMS_PAGE]: 'Terms of Service',\n [SPECIAL_DOCS.PRIVACY_PAGE]: 'Privacy Policy',\n [SPECIAL_DOCS.RULES_AND_AGREEMENTS]: 'Rules & Agreements',\n [SPECIAL_DOCS.FUTURE_PLANS]: 'Future Plans',\n [SPECIAL_DOCS.DMCA_POLICY]: 'DMCA Policy',\n [SPECIAL_DOCS.TAKE_IT_DOWN_PAGE_COPY]: 'Take It Down',\n};\n\n/**\n * The custom claim holding the caller's accepted level. Written by the acceptance core\n * (through `updateClaim`) and read by the backend acceptance gate (auth-core's\n * `acceptance.claimKey`) — one name for both sides.\n */\nexport const PUBLIC_DOCUMENTS_ACCEPTED_CLAIM = 'docsAccepted';\n\n/**\n * How an acceptance was recorded: at registration, or through the re-acceptance prompt.\n * Both write the same private summary and the same `publicDocuments.accepted` audit event.\n */\nexport const PUBLIC_DOCUMENT_ACCEPTANCE_SOURCES = ['registration', 'reacceptance'] as const;\nexport type PublicDocumentAcceptanceSource = (typeof PUBLIC_DOCUMENT_ACCEPTANCE_SOURCES)[number];\n\n/**\n * The re-acceptance prompt's explicit agreement line, shown above its Accept and Log out\n * choices. Verbatim and independent of the founder notice: it shows whether or not the notice\n * is active. What a person accepted is recorded as the document/version pairs the prompt\n * listed, so this line carries no revision of its own.\n */\nexport const PUBLIC_DOCUMENTS_REACCEPTANCE_STATEMENT =\n 'By choosing Accept, you agree to the current versions of the documents listed above.';\n\n/** Versions returned per page by the Admin history read (newest first). */\nexport const PUBLIC_DOCUMENT_HISTORY_PAGE_LIMIT = 10;\n"]}
1
+ {"version":3,"file":"public-documents.js","sourceRoot":"","sources":["../../src/constants/public-documents.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,gFAAgF;AAChF,0EAA0E;AAC1E,8EAA8E;AAC9E,iFAAiF;AACjF,kCAAkC;AAClC,EAAE;AACF,6EAA6E;AAC7E,iFAAiF;AACjF,2DAA2D;AAC3D,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,iEAAiE;AACjE,+EAA+E;AAE/E,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,yDAAyD;AACzD,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,YAAY,CAAC,UAAU;IACvB,YAAY,CAAC,YAAY;IACzB,YAAY,CAAC,oBAAoB;IACjC,YAAY,CAAC,YAAY;IACzB,YAAY,CAAC,WAAW;IACxB,YAAY,CAAC,sBAAsB;CAC3B,CAAC;AAGX,2FAA2F;AAC3F,MAAM,CAAC,MAAM,sBAAsB,GAAqC;IACtE,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,kBAAkB;IAC7C,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,gBAAgB;IAC7C,CAAC,YAAY,CAAC,oBAAoB,CAAC,EAAE,oBAAoB;IACzD,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,cAAc;IAC3C,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,aAAa;IACzC,CAAC,YAAY,CAAC,sBAAsB,CAAC,EAAE,cAAc;CACtD,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,cAAc,CAAC;AAE9D;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,kBAAkB,CAAC;AAE1D;;;GAGG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAAC,cAAc,EAAE,cAAc,CAAU,CAAC;AAG5F;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uCAAuC,GAClD,sFAAsF,CAAC;AAEzF,2EAA2E;AAC3E,MAAM,CAAC,MAAM,kCAAkC,GAAG,EAAE,CAAC","sourcesContent":["// ============================================================================\n// PUBLIC DOCUMENTS — the Admin-editable public web pages that are versioned and\n// published in release bundles: Terms of Service, Privacy Policy, Rules &\n// Agreements, Future Plans, the DMCA policy, and the Take It Down explanatory\n// copy. ONE owner for the document identity, display labels, and the numbers the\n// release / acceptance flow uses.\n//\n// Each PublicDocumentId is ALSO the doc id of that document's public current\n// projection under `_appConfig` (PATH_BUILDERS.publicDocumentProjection), so the\n// ids derive from SPECIAL_DOCS rather than being restated.\n//\n// Versions are whole numbers v1, v2, … per document. \"Require acceptance\" is a\n// property of a RELEASE, never a second version number: a release that requires\n// acceptance raises the single required-acceptance level by one.\n// ============================================================================\n\nimport { SPECIAL_DOCS } from '../paths/collections.js';\n\n/** Every public document, in canonical display order. */\nexport const PUBLIC_DOCUMENT_IDS = [\n SPECIAL_DOCS.TERMS_PAGE,\n SPECIAL_DOCS.PRIVACY_PAGE,\n SPECIAL_DOCS.RULES_AND_AGREEMENTS,\n SPECIAL_DOCS.FUTURE_PLANS,\n SPECIAL_DOCS.DMCA_POLICY,\n SPECIAL_DOCS.TAKE_IT_DOWN_PAGE_COPY,\n] as const;\nexport type PublicDocumentId = (typeof PUBLIC_DOCUMENT_IDS)[number];\n\n/** The user-facing name of each public document (Admin list, acceptance prompt, links). */\nexport const PUBLIC_DOCUMENT_LABELS: Record<PublicDocumentId, string> = {\n [SPECIAL_DOCS.TERMS_PAGE]: 'Terms of Service',\n [SPECIAL_DOCS.PRIVACY_PAGE]: 'Privacy Policy',\n [SPECIAL_DOCS.RULES_AND_AGREEMENTS]: 'Rules & Agreements',\n [SPECIAL_DOCS.FUTURE_PLANS]: 'Future Plans',\n [SPECIAL_DOCS.DMCA_POLICY]: 'DMCA Policy',\n [SPECIAL_DOCS.TAKE_IT_DOWN_PAGE_COPY]: 'Take It Down',\n};\n\n/**\n * The custom claim holding the caller's accepted level. Written by the acceptance core\n * (through `updateClaim`) and read by the backend acceptance gate (auth-core's\n * `acceptance.claimKey`) — one name for both sides.\n */\nexport const PUBLIC_DOCUMENTS_ACCEPTED_CLAIM = 'docsAccepted';\n\n/**\n * The custom claim a finished registration carries (value `true`). Set when registration\n * completes, and re-derived from the account's canonical state by the claim reconciler; the\n * member-content read rules require it, so a bare login that skipped the age step reads nothing.\n */\nexport const REGISTERED_MEMBER_CLAIM = 'registeredMember';\n\n/**\n * How an acceptance was recorded: at registration, or through the re-acceptance prompt.\n * Both write the same private summary and the same `publicDocuments.accepted` audit event.\n */\nexport const PUBLIC_DOCUMENT_ACCEPTANCE_SOURCES = ['registration', 'reacceptance'] as const;\nexport type PublicDocumentAcceptanceSource = (typeof PUBLIC_DOCUMENT_ACCEPTANCE_SOURCES)[number];\n\n/**\n * The re-acceptance prompt's explicit agreement line, shown above its Accept and Log out\n * choices. Verbatim and independent of the founder notice: it shows whether or not the notice\n * is active. What a person accepted is recorded as the document/version pairs the prompt\n * listed, so this line carries no revision of its own.\n */\nexport const PUBLIC_DOCUMENTS_REACCEPTANCE_STATEMENT =\n 'By choosing Accept, you agree to the current versions of the documents listed above.';\n\n/** Versions returned per page by the Admin history read (newest first). */\nexport const PUBLIC_DOCUMENT_HISTORY_PAGE_LIMIT = 10;\n"]}
@@ -5,8 +5,12 @@ export declare const PENDING_MEDIA_ARCHIVE_AFTER_DAYS = 7;
5
5
  /** Per-run cap on docs archived by the archivePendingMedia scheduled commission. */
6
6
  export declare const PENDING_MEDIA_ARCHIVE_BATCH_SIZE = 500;
7
7
  /** Days a moderation-rejected upload's quarantined bytes are retained before the
8
- * hold-aware sweep deletes them. */
8
+ * hold-aware sweep deletes them — and the days a member has to appeal the rejection. */
9
9
  export declare const REJECTED_MEDIA_RETENTION_DAYS = 90;
10
+ /** The epoch-ms moment the appeal window of a rejection made at `rejectedAt` closes. */
11
+ export declare function rejectedMediaAppealDeadline(rejectedAt: number): number;
12
+ /** Whether a rejection made at `rejectedAt` can still be appealed at `now`. */
13
+ export declare function isRejectedMediaAppealOpen(rejectedAt: number, now: number): boolean;
10
14
  /** TTL in hours after which an orphan storage upload becomes eligible for deletion. */
11
15
  export declare const ORPHAN_UPLOAD_TTL_HOURS = 24;
12
16
  /** Per-run cap on orphan storage deletions. */
@@ -1 +1 @@
1
- {"version":3,"file":"retention.d.ts","sourceRoot":"","sources":["../../src/constants/retention.ts"],"names":[],"mappings":"AAIA,gEAAgE;AAChE,eAAO,MAAM,sBAAsB,QAAiB,CAAC;AAIrD,oFAAoF;AACpF,eAAO,MAAM,gCAAgC,IAAI,CAAC;AAElD,oFAAoF;AACpF,eAAO,MAAM,gCAAgC,MAAM,CAAC;AAWpD;qCACqC;AACrC,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAIhD,uFAAuF;AACvF,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,+CAA+C;AAC/C,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,4CAA4C;AAC5C,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAIjD,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,QAAsB,CAAC;AAItD,+DAA+D;AAC/D,eAAO,MAAM,0BAA0B,QAAiB,CAAC;AAEzD,wDAAwD;AACxD,eAAO,MAAM,uBAAuB,QAAiB,CAAC;AAItD;qFACqF;AACrF,eAAO,MAAM,kCAAkC,QAAY,CAAC"}
1
+ {"version":3,"file":"retention.d.ts","sourceRoot":"","sources":["../../src/constants/retention.ts"],"names":[],"mappings":"AAIA,gEAAgE;AAChE,eAAO,MAAM,sBAAsB,QAAiB,CAAC;AAIrD,oFAAoF;AACpF,eAAO,MAAM,gCAAgC,IAAI,CAAC;AAElD,oFAAoF;AACpF,eAAO,MAAM,gCAAgC,MAAM,CAAC;AAWpD;yFACyF;AACzF,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAIhD,wFAAwF;AACxF,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,+EAA+E;AAC/E,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAElF;AAID,uFAAuF;AACvF,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,+CAA+C;AAC/C,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,4CAA4C;AAC5C,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAIjD,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,QAAsB,CAAC;AAItD,+DAA+D;AAC/D,eAAO,MAAM,0BAA0B,QAAiB,CAAC;AAEzD,wDAAwD;AACxD,eAAO,MAAM,uBAAuB,QAAiB,CAAC;AAItD;qFACqF;AACrF,eAAO,MAAM,kCAAkC,QAAY,CAAC"}
@@ -12,12 +12,21 @@ export const PENDING_MEDIA_ARCHIVE_BATCH_SIZE = 500;
12
12
  // (the violation's `rejectedFilePath`; owner + admin readable, never served through the media
13
13
  // gateway). They are not kept forever: after this window the hold-aware fail-closed sweep
14
14
  // deletes them. Fail-closed means a file it cannot prove is out of window — or that carries an
15
- // active Trust & Safety / legal hold — is LEFT IN PLACE, never deleted on a best guess.
16
- // 90 days sits comfortably past the appeal window while bounding indefinite retention of
17
- // often-adult rejected content. See ttt-prod docs/design/content-moderation-and-reporting.md.
15
+ // active Trust & Safety / legal hold, or a pending appeal — is LEFT IN PLACE, never deleted on a
16
+ // best guess. The appeal window is this same window, so an appeal can only ever be filed while
17
+ // its file is still kept.
18
18
  /** Days a moderation-rejected upload's quarantined bytes are retained before the
19
- * hold-aware sweep deletes them. */
19
+ * hold-aware sweep deletes them — and the days a member has to appeal the rejection. */
20
20
  export const REJECTED_MEDIA_RETENTION_DAYS = 90;
21
+ const REJECTED_MEDIA_RETENTION_MS = REJECTED_MEDIA_RETENTION_DAYS * 24 * 60 * 60 * 1000;
22
+ /** The epoch-ms moment the appeal window of a rejection made at `rejectedAt` closes. */
23
+ export function rejectedMediaAppealDeadline(rejectedAt) {
24
+ return rejectedAt + REJECTED_MEDIA_RETENTION_MS;
25
+ }
26
+ /** Whether a rejection made at `rejectedAt` can still be appealed at `now`. */
27
+ export function isRejectedMediaAppealOpen(rejectedAt, now) {
28
+ return now < rejectedMediaAppealDeadline(rejectedAt);
29
+ }
21
30
  // --- Orphan upload cleanup ---
22
31
  /** TTL in hours after which an orphan storage upload becomes eligible for deletion. */
23
32
  export const ORPHAN_UPLOAD_TTL_HOURS = 24;
@@ -1 +1 @@
1
- {"version":3,"file":"retention.js","sourceRoot":"","sources":["../../src/constants/retention.ts"],"names":[],"mappings":"AAAA,sDAAsD;AAEtD,oCAAoC;AAEpC,gEAAgE;AAChE,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC;AAErD,gCAAgC;AAEhC,oFAAoF;AACpF,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,CAAC;AAElD,oFAAoF;AACpF,MAAM,CAAC,MAAM,gCAAgC,GAAG,GAAG,CAAC;AAEpD,4DAA4D;AAC5D,4FAA4F;AAC5F,8FAA8F;AAC9F,0FAA0F;AAC1F,+FAA+F;AAC/F,wFAAwF;AACxF,yFAAyF;AACzF,8FAA8F;AAE9F;qCACqC;AACrC,MAAM,CAAC,MAAM,6BAA6B,GAAG,EAAE,CAAC;AAEhD,gCAAgC;AAEhC,uFAAuF;AACvF,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAE1C,+CAA+C;AAC/C,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAE5C,4CAA4C;AAC5C,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AAEjD,8CAA8C;AAE9C,4EAA4E;AAC5E,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEtD,yCAAyC;AAEzC,+DAA+D;AAC/D,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEzD,wDAAwD;AACxD,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEtD,8CAA8C;AAE9C;qFACqF;AACrF,MAAM,CAAC,MAAM,kCAAkC,GAAG,EAAE,GAAG,IAAI,CAAC","sourcesContent":["// Caching, retention, and cleanup TTLs / batch sizes.\n\n// --- Profanity word list cache ---\n\n/** TTL for the in-memory profanity word list cache (1 hour). */\nexport const WORD_LIST_CACHE_TTL_MS = 1000 * 60 * 60;\n\n// --- Pending media archive ---\n\n/** Days after which a terminal pendingMedia doc is moved to pendingMediaArchive. */\nexport const PENDING_MEDIA_ARCHIVE_AFTER_DAYS = 7;\n\n/** Per-run cap on docs archived by the archivePendingMedia scheduled commission. */\nexport const PENDING_MEDIA_ARCHIVE_BATCH_SIZE = 500;\n\n// --- Rejected (moderation-quarantined) media retention ---\n// Bytes a moderation decision rejected are kept at `rejected/{userId}/…` as APPEAL EVIDENCE\n// (the violation's `rejectedFilePath`; owner + admin readable, never served through the media\n// gateway). They are not kept forever: after this window the hold-aware fail-closed sweep\n// deletes them. Fail-closed means a file it cannot prove is out of window — or that carries an\n// active Trust & Safety / legal hold — is LEFT IN PLACE, never deleted on a best guess.\n// 90 days sits comfortably past the appeal window while bounding indefinite retention of\n// often-adult rejected content. See ttt-prod docs/design/content-moderation-and-reporting.md.\n\n/** Days a moderation-rejected upload's quarantined bytes are retained before the\n * hold-aware sweep deletes them. */\nexport const REJECTED_MEDIA_RETENTION_DAYS = 90;\n\n// --- Orphan upload cleanup ---\n\n/** TTL in hours after which an orphan storage upload becomes eligible for deletion. */\nexport const ORPHAN_UPLOAD_TTL_HOURS = 24;\n\n/** Per-run cap on orphan storage deletions. */\nexport const ORPHAN_UPLOAD_DELETE_CAP = 500;\n\n/** Page size for listing orphan uploads. */\nexport const ORPHAN_UPLOAD_LIST_PAGE_SIZE = 1000;\n\n// --- Frontend in-flight upload listeners ---\n\n/** Maximum lookback window for the in-flight upload listener (24 hours). */\nexport const LISTENER_WINDOW_MS = 24 * 60 * 60 * 1000;\n\n// --- Public users React Query cache ---\n\n/** Stale time for the publicUsers query cache (30 minutes). */\nexport const PUBLIC_USERS_STALE_TIME_MS = 30 * 60 * 1000;\n\n/** GC time for the publicUsers query cache (1 hour). */\nexport const PUBLIC_USERS_GC_TIME_MS = 60 * 60 * 1000;\n\n// --- Public-entity lookup negative cache ---\n\n/** Stale time for an ABSENT public-lookup document (20 seconds) — short, so a\n * just-created or just-published entity stops reading as missing almost at once. */\nexport const PUBLIC_LOOKUP_ABSENT_STALE_TIME_MS = 20 * 1000;\n"]}
1
+ {"version":3,"file":"retention.js","sourceRoot":"","sources":["../../src/constants/retention.ts"],"names":[],"mappings":"AAAA,sDAAsD;AAEtD,oCAAoC;AAEpC,gEAAgE;AAChE,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC;AAErD,gCAAgC;AAEhC,oFAAoF;AACpF,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,CAAC;AAElD,oFAAoF;AACpF,MAAM,CAAC,MAAM,gCAAgC,GAAG,GAAG,CAAC;AAEpD,4DAA4D;AAC5D,4FAA4F;AAC5F,8FAA8F;AAC9F,0FAA0F;AAC1F,+FAA+F;AAC/F,iGAAiG;AACjG,+FAA+F;AAC/F,0BAA0B;AAE1B;yFACyF;AACzF,MAAM,CAAC,MAAM,6BAA6B,GAAG,EAAE,CAAC;AAEhD,MAAM,2BAA2B,GAAG,6BAA6B,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAExF,wFAAwF;AACxF,MAAM,UAAU,2BAA2B,CAAC,UAAkB;IAC5D,OAAO,UAAU,GAAG,2BAA2B,CAAC;AAClD,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,yBAAyB,CAAC,UAAkB,EAAE,GAAW;IACvE,OAAO,GAAG,GAAG,2BAA2B,CAAC,UAAU,CAAC,CAAC;AACvD,CAAC;AAED,gCAAgC;AAEhC,uFAAuF;AACvF,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAE1C,+CAA+C;AAC/C,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAE5C,4CAA4C;AAC5C,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AAEjD,8CAA8C;AAE9C,4EAA4E;AAC5E,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEtD,yCAAyC;AAEzC,+DAA+D;AAC/D,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEzD,wDAAwD;AACxD,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEtD,8CAA8C;AAE9C;qFACqF;AACrF,MAAM,CAAC,MAAM,kCAAkC,GAAG,EAAE,GAAG,IAAI,CAAC","sourcesContent":["// Caching, retention, and cleanup TTLs / batch sizes.\n\n// --- Profanity word list cache ---\n\n/** TTL for the in-memory profanity word list cache (1 hour). */\nexport const WORD_LIST_CACHE_TTL_MS = 1000 * 60 * 60;\n\n// --- Pending media archive ---\n\n/** Days after which a terminal pendingMedia doc is moved to pendingMediaArchive. */\nexport const PENDING_MEDIA_ARCHIVE_AFTER_DAYS = 7;\n\n/** Per-run cap on docs archived by the archivePendingMedia scheduled commission. */\nexport const PENDING_MEDIA_ARCHIVE_BATCH_SIZE = 500;\n\n// --- Rejected (moderation-quarantined) media retention ---\n// Bytes a moderation decision rejected are kept at `rejected/{userId}/…` as APPEAL EVIDENCE\n// (the violation's `rejectedFilePath`; owner + admin readable, never served through the media\n// gateway). They are not kept forever: after this window the hold-aware fail-closed sweep\n// deletes them. Fail-closed means a file it cannot prove is out of window — or that carries an\n// active Trust & Safety / legal hold, or a pending appeal — is LEFT IN PLACE, never deleted on a\n// best guess. The appeal window is this same window, so an appeal can only ever be filed while\n// its file is still kept.\n\n/** Days a moderation-rejected upload's quarantined bytes are retained before the\n * hold-aware sweep deletes them — and the days a member has to appeal the rejection. */\nexport const REJECTED_MEDIA_RETENTION_DAYS = 90;\n\nconst REJECTED_MEDIA_RETENTION_MS = REJECTED_MEDIA_RETENTION_DAYS * 24 * 60 * 60 * 1000;\n\n/** The epoch-ms moment the appeal window of a rejection made at `rejectedAt` closes. */\nexport function rejectedMediaAppealDeadline(rejectedAt: number): number {\n return rejectedAt + REJECTED_MEDIA_RETENTION_MS;\n}\n\n/** Whether a rejection made at `rejectedAt` can still be appealed at `now`. */\nexport function isRejectedMediaAppealOpen(rejectedAt: number, now: number): boolean {\n return now < rejectedMediaAppealDeadline(rejectedAt);\n}\n\n// --- Orphan upload cleanup ---\n\n/** TTL in hours after which an orphan storage upload becomes eligible for deletion. */\nexport const ORPHAN_UPLOAD_TTL_HOURS = 24;\n\n/** Per-run cap on orphan storage deletions. */\nexport const ORPHAN_UPLOAD_DELETE_CAP = 500;\n\n/** Page size for listing orphan uploads. */\nexport const ORPHAN_UPLOAD_LIST_PAGE_SIZE = 1000;\n\n// --- Frontend in-flight upload listeners ---\n\n/** Maximum lookback window for the in-flight upload listener (24 hours). */\nexport const LISTENER_WINDOW_MS = 24 * 60 * 60 * 1000;\n\n// --- Public users React Query cache ---\n\n/** Stale time for the publicUsers query cache (30 minutes). */\nexport const PUBLIC_USERS_STALE_TIME_MS = 30 * 60 * 1000;\n\n/** GC time for the publicUsers query cache (1 hour). */\nexport const PUBLIC_USERS_GC_TIME_MS = 60 * 60 * 1000;\n\n// --- Public-entity lookup negative cache ---\n\n/** Stale time for an ABSENT public-lookup document (20 seconds) — short, so a\n * just-created or just-published entity stops reading as missing almost at once. */\nexport const PUBLIC_LOOKUP_ABSENT_STALE_TIME_MS = 20 * 1000;\n"]}