@ttt-productions/ttt-core 0.79.87 → 0.79.89

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.
@@ -126,6 +126,11 @@ export declare const INVITE_SOURCE_TYPE_LABELS: Record<InviteSourceType, string>
126
126
  export declare const SHORT_LINK_TARGET_TYPE_LABELS: Record<ShortLinkTargetType, string>;
127
127
  /** Support-thread statuses (a member's Messages, a Work's Correspondence, the admin browse list). */
128
128
  export declare const ADMIN_DISPATCH_STATUS_LABELS: Record<AdminDispatch['status'], string>;
129
+ /**
130
+ * Why a full admin cannot remove their own Admin standing: the server refuses it, and the roster
131
+ * panel withholds the control with the same words. Another full admin removes it.
132
+ */
133
+ export declare const ADMIN_SELF_REVOKE_REFUSAL = "You can't revoke your own admin standing. Ask another full admin to do it.";
129
134
  /** The marker beside the sender of a support-thread message sent with admin authority. */
130
135
  export declare const ADMIN_SENDER_MARKER_LABEL = "Admin";
131
136
  /** The sender label on a support thread's system line (a close notice) in the admin preview. */
@@ -1 +1 @@
1
- {"version":3,"file":"admin-labels.d.ts","sourceRoot":"","sources":["../../src/constants/admin-labels.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"admin-labels.d.ts","sourceRoot":"","sources":["../../src/constants/admin-labels.ts"],"names":[],"mappings":"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"}
@@ -219,6 +219,11 @@ export const ADMIN_DISPATCH_STATUS_LABELS = {
219
219
  closed_resolved: 'Resolved',
220
220
  closed_unresolved: 'Closed',
221
221
  };
222
+ /**
223
+ * Why a full admin cannot remove their own Admin standing: the server refuses it, and the roster
224
+ * panel withholds the control with the same words. Another full admin removes it.
225
+ */
226
+ export const ADMIN_SELF_REVOKE_REFUSAL = "You can't revoke your own admin standing. Ask another full admin to do it.";
222
227
  /** The marker beside the sender of a support-thread message sent with admin authority. */
223
228
  export const ADMIN_SENDER_MARKER_LABEL = 'Admin';
224
229
  /** The sender label on a support thread's system line (a close notice) in the admin preview. */
@@ -1 +1 @@
1
- {"version":3,"file":"admin-labels.js","sourceRoot":"","sources":["../../src/constants/admin-labels.ts"],"names":[],"mappings":"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","sourcesContent":["// ============================================================================\n// ADMIN-SURFACE DISPLAY LABELS — the ONE home for business display copy keyed\n// by canonical ttt-core enums (ARCH-102). Any client (web, phone, TV, admin app)\n// renders the same labels by importing these.\n//\n// A few entries here are copy for ADMIN-MANAGED content that a PUBLIC surface also\n// renders (the Rules & Agreements section titles). The organizing principle is one\n// declaration, not one consumer.\n// ============================================================================\n\nimport type { UserAccountStatus } from '../doc-schemas/user.js';\nimport type { FeedbackType } from './business-admin.js';\nimport type { ClearableTextFieldName } from './business-content.js';\n// Value import: the per-surface override type below indexes this map to restrict each surface's\n// override keys to the fields that surface actually exposes.\nimport { HALL_CONTENT_TEXT_FIELDS } from './business-content.js';\nimport type { HallContentTextSurface } from '../doc-schemas/content.js';\nimport type { ReportDisposition, NciiInternalStatus } from '../doc-schemas/safety/foundation.js';\nimport type { ChildSafetyWorkStatus } from '../doc-schemas/safety/case.js';\nimport type { BroadcastAudienceSelector } from '../schemas/notification.js';\nimport type { DeadLetterCollection } from '../schemas/admin.js';\nimport type { GuildInviteConversationStatus, ShortLinkTargetType } from '../schemas/atoms.js';\nimport type { AdminDispatch } from '../doc-schemas/messaging.js';\nimport type { CommissionProposalStatus } from '../schemas/commissions.js';\nimport type { InviteSourceType } from '../schemas/work-project-management.js';\nimport type { Audition } from '../doc-schemas/commissions.js';\nimport type { HallWingType, RuleGroup, WorkProjectType } from '../types/content.js';\nimport { HALL_WING_TYPES, WORK_PROJECT_TYPES } from './options.js';\nimport type { HallSubItemStatus } from '../utils/hall-content.js';\n\n/** Moderation status labels (user-management view, status badges). */\nexport const USER_ACCOUNT_STATUS_LABELS: Record<UserAccountStatus, string> = {\n active: 'Active',\n suspended: 'Suspended',\n banned: 'Banned',\n};\n\n/** Broadcast audience selector labels (announcement composer). */\nexport const BROADCAST_AUDIENCE_KIND_LABELS: Record<BroadcastAudienceSelector['kind'], string> = {\n allActiveUsers: 'All active users',\n allArtisans: 'All artisan creators',\n explicitUids: 'Specific users (paste uids)',\n workMembers: \"A Work's guild members\",\n realmMembers: \"A Realm's members\",\n};\n\n/** Human labels for the canonical feedback types — the admin feedback-suggestions views and\n * the user-facing feedback-submission surface render these instead of restating copy. Keyed\n * by FeedbackType, so a new FEEDBACK_TYPES member fails the build until its label exists.\n * UI copy uses the SHORT settled terms (Trade → \"Trades\", Craft → \"Craft Tags\", Genre →\n * \"<Kind> Genres\"), never the compound code identifiers (ARCH-107). */\nexport const FEEDBACK_TYPE_LABELS: Record<FeedbackType, string> = {\n tradeProfessionSuggestions: 'Trades',\n craftSkillTagSuggestions: 'Craft Tags',\n talesWorkGenreSuggestions: 'Tales Genres',\n tunesWorkGenreSuggestions: 'Tunes Genres',\n televisionWorkGenreSuggestions: 'Television Genres',\n};\n\n/**\n * Human labels for the raw clearable/changeable TEXT-FIELD names the moderation text-clear\n * remedy and the published \"Update details\" change-request flow work in. The backend stores and\n * validates the raw doc field names (`workingTitle`, `description`, `content`); these are the\n * only display strings for them — rendered by the member-facing \"text removed by moderation\"\n * banners, every admin clear-text field picker, the change-request dialog, and the admin\n * change-request work view. Keyed by the derived ClearableTextFieldName union, so a new\n * clearable field fails the build here until it has a label (ARCH-102).\n *\n * The `working*` pair belongs to the Work shell and the Realm doc; the labels stay the plain\n * short terms (ARCH-107 — the code identifier carries `working`, the UI never shows it).\n */\nexport const CLEARABLE_TEXT_FIELD_LABELS: Record<ClearableTextFieldName, string> = {\n workingTitle: 'Title',\n workingDescription: 'Description',\n title: 'Title',\n description: 'Description',\n content: 'Content',\n};\n\n/**\n * Per-surface label overrides for the field labels above: `[surface][field] → label`. Only a\n * surface that genuinely names a field differently appears here; everything else resolves to the\n * base map. Each surface's override keys are restricted to the fields THAT surface exposes\n * (indexed off `HALL_CONTENT_TEXT_FIELDS`), so an override for a field the surface does not have\n * is a compile error — as is an override for a surface outside `HallContentTextSurface`.\n */\nexport type ClearableTextFieldLabelOverrides = {\n [S in HallContentTextSurface]?: {\n [F in (typeof HALL_CONTENT_TEXT_FIELDS)[S][number]]?: string;\n };\n};\n\n/**\n * A Realm's identity field is called **Name** on every Realm surface (DJ ruling 2026-07-29,\n * settling a four-surface Name/Title conflict: the creation flow said \"Realm Name\", the inline\n * edit form \"Working Title\", the moderation-cleared banner \"Title\", and the request-update\n * dialog \"Name\"). The `workingTitle` doc field is shared with the Work shell, where it IS a\n * Title — so the difference is per-surface, not per-field, and lives here rather than flattening\n * one of the two settled terms.\n */\nexport const CLEARABLE_TEXT_FIELD_LABEL_OVERRIDES = {\n workRealm: { workingTitle: 'Name' },\n} as const satisfies ClearableTextFieldLabelOverrides;\n\n/**\n * The display label for one raw clearable text-field name: the surface's override when it has\n * one, otherwise the base label, otherwise the raw name. This is the ONE accessor every surface\n * calls — pass the surface whenever the caller knows it (a Realm surface must, or it renders\n * \"Title\" for a Name); omit it only for a genuinely surface-agnostic caller.\n *\n * Both parameters tolerate a plain `string` because callers read field names off stored documents\n * (`moderationClearedFields`) and callable payloads, and the surface may be the `workProject`\n * shell — which is a clearable surface but not a `HallContentTextSurface`, so it simply has no\n * overrides and falls through to the base map.\n */\nexport function clearableTextFieldLabel(field: string, surface?: string): string {\n const overrides = surface\n ? (CLEARABLE_TEXT_FIELD_LABEL_OVERRIDES as Record<string, Record<string, string> | undefined>)[\n surface\n ]\n : undefined;\n return (\n overrides?.[field] ?? (CLEARABLE_TEXT_FIELD_LABELS as Record<string, string>)[field] ?? field\n );\n}\n\n/** What the admin screens show for a thread whose `awaitingAdminReply` is set. */\nexport const ADMIN_DISPATCH_AWAITING_REPLY_LABEL = 'Awaiting reply';\n\n/** Human labels for the dead-letter replay ledgers (Ops Repairs dead-letter table). */\nexport const DEAD_LETTER_COLLECTION_LABELS: Record<DeadLetterCollection, string> = {\n chatSyncEvents: 'Chat sync event',\n chatAdminActionCommands: 'Chat moderation command',\n notificationDeliveries: 'Notification delivery',\n chatMessageOutbox: 'Chat message publish',\n squareAnnouncementJobs: 'Square announcement',\n chatSyncFanoutJobs: 'Chat access fanout',\n mediaAssets: 'Media authority sync',\n quarantineSagaJobs: 'Quarantine saga',\n nciiRemovalJobs: 'NCII removal',\n safetyEvidenceJobs: 'Safety evidence job',\n accountActionCommands: 'Account safety action',\n activeReportGroups: 'Report edge sync',\n hallSubItemEdgeSync: 'Hall sub-item edge sync',\n chatParkedDeliveries: 'Chat room delivery',\n};\n\n/** Operator-settable report dispositions with their console copy. 'undetermined' is the\n * initial state, never a settable target, so it carries no option entry. */\nexport const REPORT_DISPOSITION_OPTIONS: readonly {\n value: Exclude<ReportDisposition, 'undetermined'>;\n label: string;\n description: string;\n}[] = [\n {\n value: 'reportRequired',\n label: 'Report required (NCMEC)',\n description:\n 'Apparent CSAM — a report is legally required. Establishes actual knowledge + arms the clock.',\n },\n {\n value: 'notRequired',\n label: 'No report required',\n description: 'No reporting obligation ever arose. Disallowed once there is a validated match.',\n },\n {\n value: 'correctedNoApparentViolation',\n label: 'False positive — correct it',\n description: 'There was a triggering signal but on review it is a false positive.',\n },\n];\n\n/** One user-facing reason option (shown to the affected user for any user-affecting action). */\nexport interface UserFacingReasonOption {\n code: string;\n label: string;\n description: string;\n}\n\n/** User-facing reason picklist for the guided case-resolution flow — the affected user sees the\n * label for any user-affecting action. Canonical home (ARCH-102): the codes ARE this list's own\n * enumeration, so every client (web, phone, TV, admin app) renders the same options + labels by\n * importing from here. Final user-/legal-facing wording is subject to lawyer review; these are\n * functional defaults. */\nexport const USER_FACING_REASON_OPTIONS: readonly UserFacingReasonOption[] = [\n { code: 'harassment', label: 'Harassment or bullying', description: 'Targeted abuse, threats, or sustained harassment of a person.' },\n { code: 'hate', label: 'Hate speech', description: 'Attacks on people based on a protected characteristic.' },\n { code: 'adult-content', label: 'Explicit / adult content', description: 'Sexual or graphic content not allowed on the platform.' },\n { code: 'violence', label: 'Violence or threats', description: 'Threats of violence, or content glorifying or inciting it.' },\n { code: 'spam', label: 'Spam or scam', description: 'Unsolicited bulk content, deceptive links, or fraud.' },\n { code: 'impersonation', label: 'Impersonation', description: 'Pretending to be another person, brand, or entity.' },\n { code: 'ip', label: 'Intellectual property', description: 'Use of someone else’s work without the right to do so.' },\n { code: 'nonconsensual', label: 'Nonconsensual intimate imagery', description: 'Intimate or sexual imagery shared without the subject’s consent.' },\n { code: 'safety', label: 'Safety violation', description: 'A serious safety violation handled under our safety policies.' },\n { code: 'other', label: 'Other (explain)', description: 'A policy violation not covered above — describe it in the detail box.' },\n] as const;\n\n/** The user-facing label for a reason code (undefined when the code is unknown/absent). */\nexport function userFacingReasonLabel(code: string | undefined): string | undefined {\n return USER_FACING_REASON_OPTIONS.find((o) => o.code === code)?.label;\n}\n\n// --- Rules & Agreements section titles and type labels ---\n// The admin rules editor and the public rules page both render these (ENG-002).\n\n/** Each fixed rule group's heading. */\nexport const RULE_GROUP_TITLES: Record<RuleGroup, string> = {\n generic: 'General Site Rules',\n merchandising: 'Merchandising, Endorsements & Revenue Sharing',\n hallWing: 'Hall Wing Rules',\n workRealm: 'Realm Rules',\n};\nexport const ARTISAN_AGREEMENTS_TITLE = 'Artisan Agreements';\n\n/**\n * The label for a rule or agreement point narrowed to one type (\"Tales only\", \"Educational wing\n * only\", \"Tales, Educational wing only\"); undefined when it applies to every type.\n */\nexport function appliesToTypeLabel(entry: {\n workProjectType?: WorkProjectType;\n hallWingType?: HallWingType;\n}): string | undefined {\n const parts = [\n entry.workProjectType && WORK_PROJECT_TYPES[entry.workProjectType].label,\n entry.hallWingType && `${HALL_WING_TYPES[entry.hallWingType].label} wing`,\n ].filter((part): part is string => Boolean(part));\n return parts.length > 0 ? `${parts.join(', ')} only` : undefined;\n}\n\n// --- Stored-enum display labels ---\n// What a screen shows for a stored enum value, keyed by the canonical union so a new member fails\n// the build until it has its text. A screen renders the label, never the raw value.\n\n/** Guild invite statuses (a Work's invited-users list and an invite conversation's status line). */\nexport const GUILD_INVITE_STATUS_LABELS: Record<GuildInviteConversationStatus, string> = {\n pending: 'Pending',\n accepted: 'Accepted',\n declined: 'Declined',\n cancelled: 'Cancelled',\n finalized: 'Finalized',\n};\n\n/** Commission proposal statuses (a Work's proposals table). */\nexport const COMMISSION_PROPOSAL_STATUS_LABELS: Record<CommissionProposalStatus, string> = {\n open: 'Open',\n invited: 'Invited',\n accepted: 'Accepted',\n rejected: 'Rejected',\n};\n\n/** Chapter, track, and episode review statuses (the Tale, Tune, and Television section tables). */\nexport const HALL_SUB_ITEM_STATUS_LABELS: Record<HallSubItemStatus, string> = {\n unpublished: 'unpublished',\n pending_approval: 'Pending approval',\n published: 'published',\n};\n\n/** Audition statuses (the audition page). */\nexport const AUDITION_STATUS_LABELS: Record<Audition['status'], string> = {\n open: 'Open',\n closed: 'Closed',\n pendingReview: 'PendingReview',\n};\n\n/** Work types (Work lists, the Work overview, the Works popover). */\nexport const WORK_PROJECT_TYPE_LABELS: Record<WorkProjectType, string> = {\n Tales: 'Tales',\n Tunes: 'Tunes',\n Television: 'Television',\n};\n\n/** Invite source types, as the invite dialog names the context an invite was sent from. */\nexport const INVITE_SOURCE_TYPE_LABELS: Record<InviteSourceType, string> = {\n standalone: 'standalone',\n craftSkill: 'craftSkill',\n commission: 'commission',\n audition: 'audition',\n};\n\n/** Short-link target types (the admin Short Links list), in the target-type enum's order. */\nexport const SHORT_LINK_TARGET_TYPE_LABELS: Record<ShortLinkTargetType, string> = {\n audition: 'Audition',\n 'audition-entry': 'Audition entry',\n commission: 'Commission',\n 'hall-library-item': 'Hall entry',\n};\n\n/** Support-thread statuses (a member's Messages, a Work's Correspondence, the admin browse list). */\nexport const ADMIN_DISPATCH_STATUS_LABELS: Record<AdminDispatch['status'], string> = {\n open: 'Open',\n user_reply: 'User replied',\n admin_reply: 'Admin replied',\n closed_resolved: 'Resolved',\n closed_unresolved: 'Closed',\n};\n\n/** The marker beside the sender of a support-thread message sent with admin authority. */\nexport const ADMIN_SENDER_MARKER_LABEL = 'Admin';\n\n/** The sender label on a support thread's system line (a close notice) in the admin preview. */\nexport const ADMIN_DISPATCH_SYSTEM_SENDER_LABEL = 'System';\n\n/** Child-safety case work statuses (the safety case console's case rows and closed-case lookup). */\nexport const CHILD_SAFETY_CASE_WORK_STATUS_LABELS: Record<ChildSafetyWorkStatus, string> = {\n new: 'new',\n triaged: 'triaged',\n reporting: 'reporting',\n actioning: 'actioning',\n processing: 'processing',\n failed: 'failed',\n operationallyResolved: 'operationallyResolved',\n};\n\n/** NCII case statuses (the safety case console's case rows and closed-case lookup). */\nexport const NCII_CASE_STATUS_LABELS: Record<NciiInternalStatus, string> = {\n open: 'open',\n removalInProgress: 'removalInProgress',\n processing: 'processing',\n failed: 'failed',\n removed: 'removed',\n rejected: 'rejected',\n closed: 'closed',\n};\n"]}
1
+ {"version":3,"file":"admin-labels.js","sourceRoot":"","sources":["../../src/constants/admin-labels.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8EAA8E;AAC9E,iFAAiF;AACjF,8CAA8C;AAC9C,EAAE;AACF,mFAAmF;AACnF,mFAAmF;AACnF,iCAAiC;AACjC,+EAA+E;AAmB/E,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGnE,sEAAsE;AACtE,MAAM,CAAC,MAAM,0BAA0B,GAAsC;IAC3E,MAAM,EAAE,QAAQ;IAChB,SAAS,EAAE,WAAW;IACtB,MAAM,EAAE,QAAQ;CACjB,CAAC;AAEF,kEAAkE;AAClE,MAAM,CAAC,MAAM,8BAA8B,GAAsD;IAC/F,cAAc,EAAE,kBAAkB;IAClC,WAAW,EAAE,sBAAsB;IACnC,YAAY,EAAE,6BAA6B;IAC3C,WAAW,EAAE,wBAAwB;IACrC,YAAY,EAAE,mBAAmB;CAClC,CAAC;AAEF;;;;uEAIuE;AACvE,MAAM,CAAC,MAAM,oBAAoB,GAAiC;IAChE,0BAA0B,EAAE,QAAQ;IACpC,wBAAwB,EAAE,YAAY;IACtC,yBAAyB,EAAE,cAAc;IACzC,yBAAyB,EAAE,cAAc;IACzC,8BAA8B,EAAE,mBAAmB;CACpD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAA2C;IACjF,YAAY,EAAE,OAAO;IACrB,kBAAkB,EAAE,aAAa;IACjC,KAAK,EAAE,OAAO;IACd,WAAW,EAAE,aAAa;IAC1B,OAAO,EAAE,SAAS;CACnB,CAAC;AAeF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oCAAoC,GAAG;IAClD,SAAS,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE;CACgB,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAa,EAAE,OAAgB;IACrE,MAAM,SAAS,GAAG,OAAO;QACvB,CAAC,CAAE,oCAA2F,CAC1F,OAAO,CACR;QACH,CAAC,CAAC,SAAS,CAAC;IACd,OAAO,CACL,SAAS,EAAE,CAAC,KAAK,CAAC,IAAK,2BAAsD,CAAC,KAAK,CAAC,IAAI,KAAK,CAC9F,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,MAAM,mCAAmC,GAAG,gBAAgB,CAAC;AAEpE,uFAAuF;AACvF,MAAM,CAAC,MAAM,6BAA6B,GAAyC;IACjF,cAAc,EAAE,iBAAiB;IACjC,uBAAuB,EAAE,yBAAyB;IAClD,sBAAsB,EAAE,uBAAuB;IAC/C,iBAAiB,EAAE,sBAAsB;IACzC,sBAAsB,EAAE,qBAAqB;IAC7C,kBAAkB,EAAE,oBAAoB;IACxC,WAAW,EAAE,sBAAsB;IACnC,kBAAkB,EAAE,iBAAiB;IACrC,eAAe,EAAE,cAAc;IAC/B,kBAAkB,EAAE,qBAAqB;IACzC,qBAAqB,EAAE,uBAAuB;IAC9C,kBAAkB,EAAE,kBAAkB;IACtC,mBAAmB,EAAE,yBAAyB;IAC9C,oBAAoB,EAAE,oBAAoB;CAC3C,CAAC;AAEF;4EAC4E;AAC5E,MAAM,CAAC,MAAM,0BAA0B,GAIjC;IACJ;QACE,KAAK,EAAE,gBAAgB;QACvB,KAAK,EAAE,yBAAyB;QAChC,WAAW,EACT,8FAA8F;KACjG;IACD;QACE,KAAK,EAAE,aAAa;QACpB,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EAAE,iFAAiF;KAC/F;IACD;QACE,KAAK,EAAE,8BAA8B;QACrC,KAAK,EAAE,6BAA6B;QACpC,WAAW,EAAE,qEAAqE;KACnF;CACF,CAAC;AASF;;;;0BAI0B;AAC1B,MAAM,CAAC,MAAM,0BAA0B,GAAsC;IAC3E,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,wBAAwB,EAAE,WAAW,EAAE,+DAA+D,EAAE;IACrI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,wDAAwD,EAAE;IAC7G,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,0BAA0B,EAAE,WAAW,EAAE,wDAAwD,EAAE;IACnI,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,qBAAqB,EAAE,WAAW,EAAE,4DAA4D,EAAE;IAC7H,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,sDAAsD,EAAE;IAC5G,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,oDAAoD,EAAE;IACpH,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,uBAAuB,EAAE,WAAW,EAAE,wDAAwD,EAAE;IACrH,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,gCAAgC,EAAE,WAAW,EAAE,kEAAkE,EAAE;IACnJ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,+DAA+D,EAAE;IAC3H,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,uEAAuE,EAAE;CACzH,CAAC;AAEX,2FAA2F;AAC3F,MAAM,UAAU,qBAAqB,CAAC,IAAwB;IAC5D,OAAO,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,KAAK,CAAC;AACxE,CAAC;AAED,4DAA4D;AAC5D,gFAAgF;AAEhF,uCAAuC;AACvC,MAAM,CAAC,MAAM,iBAAiB,GAA8B;IAC1D,OAAO,EAAE,oBAAoB;IAC7B,aAAa,EAAE,+CAA+C;IAC9D,QAAQ,EAAE,iBAAiB;IAC3B,SAAS,EAAE,aAAa;CACzB,CAAC;AACF,MAAM,CAAC,MAAM,wBAAwB,GAAG,oBAAoB,CAAC;AAE7D;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAGlC;IACC,MAAM,KAAK,GAAG;QACZ,KAAK,CAAC,eAAe,IAAI,kBAAkB,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,KAAK;QACxE,KAAK,CAAC,YAAY,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,KAAK,OAAO;KAC1E,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAClD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACnE,CAAC;AAED,qCAAqC;AACrC,kGAAkG;AAClG,oFAAoF;AAEpF,oGAAoG;AACpG,MAAM,CAAC,MAAM,0BAA0B,GAAkD;IACvF,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,WAAW;IACtB,SAAS,EAAE,WAAW;CACvB,CAAC;AAEF,+DAA+D;AAC/D,MAAM,CAAC,MAAM,iCAAiC,GAA6C;IACzF,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;CACrB,CAAC;AAEF,mGAAmG;AACnG,MAAM,CAAC,MAAM,2BAA2B,GAAsC;IAC5E,WAAW,EAAE,aAAa;IAC1B,gBAAgB,EAAE,kBAAkB;IACpC,SAAS,EAAE,WAAW;CACvB,CAAC;AAEF,6CAA6C;AAC7C,MAAM,CAAC,MAAM,sBAAsB,GAAuC;IACxE,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,QAAQ;IAChB,aAAa,EAAE,eAAe;CAC/B,CAAC;AAEF,qEAAqE;AACrE,MAAM,CAAC,MAAM,wBAAwB,GAAoC;IACvE,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;IACd,UAAU,EAAE,YAAY;CACzB,CAAC;AAEF,2FAA2F;AAC3F,MAAM,CAAC,MAAM,yBAAyB,GAAqC;IACzE,UAAU,EAAE,YAAY;IACxB,UAAU,EAAE,YAAY;IACxB,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;CACrB,CAAC;AAEF,6FAA6F;AAC7F,MAAM,CAAC,MAAM,6BAA6B,GAAwC;IAChF,QAAQ,EAAE,UAAU;IACpB,gBAAgB,EAAE,gBAAgB;IAClC,UAAU,EAAE,YAAY;IACxB,mBAAmB,EAAE,YAAY;CAClC,CAAC;AAEF,qGAAqG;AACrG,MAAM,CAAC,MAAM,4BAA4B,GAA4C;IACnF,IAAI,EAAE,MAAM;IACZ,UAAU,EAAE,cAAc;IAC1B,WAAW,EAAE,eAAe;IAC5B,eAAe,EAAE,UAAU;IAC3B,iBAAiB,EAAE,QAAQ;CAC5B,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,4EAA4E,CAAC;AAEtH,0FAA0F;AAC1F,MAAM,CAAC,MAAM,yBAAyB,GAAG,OAAO,CAAC;AAEjD,gGAAgG;AAChG,MAAM,CAAC,MAAM,kCAAkC,GAAG,QAAQ,CAAC;AAE3D,oGAAoG;AACpG,MAAM,CAAC,MAAM,oCAAoC,GAA0C;IACzF,GAAG,EAAE,KAAK;IACV,OAAO,EAAE,SAAS;IAClB,SAAS,EAAE,WAAW;IACtB,SAAS,EAAE,WAAW;IACtB,UAAU,EAAE,YAAY;IACxB,MAAM,EAAE,QAAQ;IAChB,qBAAqB,EAAE,uBAAuB;CAC/C,CAAC;AAEF,uFAAuF;AACvF,MAAM,CAAC,MAAM,uBAAuB,GAAuC;IACzE,IAAI,EAAE,MAAM;IACZ,iBAAiB,EAAE,mBAAmB;IACtC,UAAU,EAAE,YAAY;IACxB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,UAAU;IACpB,MAAM,EAAE,QAAQ;CACjB,CAAC","sourcesContent":["// ============================================================================\n// ADMIN-SURFACE DISPLAY LABELS — the ONE home for business display copy keyed\n// by canonical ttt-core enums (ARCH-102). Any client (web, phone, TV, admin app)\n// renders the same labels by importing these.\n//\n// A few entries here are copy for ADMIN-MANAGED content that a PUBLIC surface also\n// renders (the Rules & Agreements section titles). The organizing principle is one\n// declaration, not one consumer.\n// ============================================================================\n\nimport type { UserAccountStatus } from '../doc-schemas/user.js';\nimport type { FeedbackType } from './business-admin.js';\nimport type { ClearableTextFieldName } from './business-content.js';\n// Value import: the per-surface override type below indexes this map to restrict each surface's\n// override keys to the fields that surface actually exposes.\nimport { HALL_CONTENT_TEXT_FIELDS } from './business-content.js';\nimport type { HallContentTextSurface } from '../doc-schemas/content.js';\nimport type { ReportDisposition, NciiInternalStatus } from '../doc-schemas/safety/foundation.js';\nimport type { ChildSafetyWorkStatus } from '../doc-schemas/safety/case.js';\nimport type { BroadcastAudienceSelector } from '../schemas/notification.js';\nimport type { DeadLetterCollection } from '../schemas/admin.js';\nimport type { GuildInviteConversationStatus, ShortLinkTargetType } from '../schemas/atoms.js';\nimport type { AdminDispatch } from '../doc-schemas/messaging.js';\nimport type { CommissionProposalStatus } from '../schemas/commissions.js';\nimport type { InviteSourceType } from '../schemas/work-project-management.js';\nimport type { Audition } from '../doc-schemas/commissions.js';\nimport type { HallWingType, RuleGroup, WorkProjectType } from '../types/content.js';\nimport { HALL_WING_TYPES, WORK_PROJECT_TYPES } from './options.js';\nimport type { HallSubItemStatus } from '../utils/hall-content.js';\n\n/** Moderation status labels (user-management view, status badges). */\nexport const USER_ACCOUNT_STATUS_LABELS: Record<UserAccountStatus, string> = {\n active: 'Active',\n suspended: 'Suspended',\n banned: 'Banned',\n};\n\n/** Broadcast audience selector labels (announcement composer). */\nexport const BROADCAST_AUDIENCE_KIND_LABELS: Record<BroadcastAudienceSelector['kind'], string> = {\n allActiveUsers: 'All active users',\n allArtisans: 'All artisan creators',\n explicitUids: 'Specific users (paste uids)',\n workMembers: \"A Work's guild members\",\n realmMembers: \"A Realm's members\",\n};\n\n/** Human labels for the canonical feedback types — the admin feedback-suggestions views and\n * the user-facing feedback-submission surface render these instead of restating copy. Keyed\n * by FeedbackType, so a new FEEDBACK_TYPES member fails the build until its label exists.\n * UI copy uses the SHORT settled terms (Trade → \"Trades\", Craft → \"Craft Tags\", Genre →\n * \"<Kind> Genres\"), never the compound code identifiers (ARCH-107). */\nexport const FEEDBACK_TYPE_LABELS: Record<FeedbackType, string> = {\n tradeProfessionSuggestions: 'Trades',\n craftSkillTagSuggestions: 'Craft Tags',\n talesWorkGenreSuggestions: 'Tales Genres',\n tunesWorkGenreSuggestions: 'Tunes Genres',\n televisionWorkGenreSuggestions: 'Television Genres',\n};\n\n/**\n * Human labels for the raw clearable/changeable TEXT-FIELD names the moderation text-clear\n * remedy and the published \"Update details\" change-request flow work in. The backend stores and\n * validates the raw doc field names (`workingTitle`, `description`, `content`); these are the\n * only display strings for them — rendered by the member-facing \"text removed by moderation\"\n * banners, every admin clear-text field picker, the change-request dialog, and the admin\n * change-request work view. Keyed by the derived ClearableTextFieldName union, so a new\n * clearable field fails the build here until it has a label (ARCH-102).\n *\n * The `working*` pair belongs to the Work shell and the Realm doc; the labels stay the plain\n * short terms (ARCH-107 — the code identifier carries `working`, the UI never shows it).\n */\nexport const CLEARABLE_TEXT_FIELD_LABELS: Record<ClearableTextFieldName, string> = {\n workingTitle: 'Title',\n workingDescription: 'Description',\n title: 'Title',\n description: 'Description',\n content: 'Content',\n};\n\n/**\n * Per-surface label overrides for the field labels above: `[surface][field] → label`. Only a\n * surface that genuinely names a field differently appears here; everything else resolves to the\n * base map. Each surface's override keys are restricted to the fields THAT surface exposes\n * (indexed off `HALL_CONTENT_TEXT_FIELDS`), so an override for a field the surface does not have\n * is a compile error — as is an override for a surface outside `HallContentTextSurface`.\n */\nexport type ClearableTextFieldLabelOverrides = {\n [S in HallContentTextSurface]?: {\n [F in (typeof HALL_CONTENT_TEXT_FIELDS)[S][number]]?: string;\n };\n};\n\n/**\n * A Realm's identity field is called **Name** on every Realm surface (DJ ruling 2026-07-29,\n * settling a four-surface Name/Title conflict: the creation flow said \"Realm Name\", the inline\n * edit form \"Working Title\", the moderation-cleared banner \"Title\", and the request-update\n * dialog \"Name\"). The `workingTitle` doc field is shared with the Work shell, where it IS a\n * Title — so the difference is per-surface, not per-field, and lives here rather than flattening\n * one of the two settled terms.\n */\nexport const CLEARABLE_TEXT_FIELD_LABEL_OVERRIDES = {\n workRealm: { workingTitle: 'Name' },\n} as const satisfies ClearableTextFieldLabelOverrides;\n\n/**\n * The display label for one raw clearable text-field name: the surface's override when it has\n * one, otherwise the base label, otherwise the raw name. This is the ONE accessor every surface\n * calls — pass the surface whenever the caller knows it (a Realm surface must, or it renders\n * \"Title\" for a Name); omit it only for a genuinely surface-agnostic caller.\n *\n * Both parameters tolerate a plain `string` because callers read field names off stored documents\n * (`moderationClearedFields`) and callable payloads, and the surface may be the `workProject`\n * shell — which is a clearable surface but not a `HallContentTextSurface`, so it simply has no\n * overrides and falls through to the base map.\n */\nexport function clearableTextFieldLabel(field: string, surface?: string): string {\n const overrides = surface\n ? (CLEARABLE_TEXT_FIELD_LABEL_OVERRIDES as Record<string, Record<string, string> | undefined>)[\n surface\n ]\n : undefined;\n return (\n overrides?.[field] ?? (CLEARABLE_TEXT_FIELD_LABELS as Record<string, string>)[field] ?? field\n );\n}\n\n/** What the admin screens show for a thread whose `awaitingAdminReply` is set. */\nexport const ADMIN_DISPATCH_AWAITING_REPLY_LABEL = 'Awaiting reply';\n\n/** Human labels for the dead-letter replay ledgers (Ops Repairs dead-letter table). */\nexport const DEAD_LETTER_COLLECTION_LABELS: Record<DeadLetterCollection, string> = {\n chatSyncEvents: 'Chat sync event',\n chatAdminActionCommands: 'Chat moderation command',\n notificationDeliveries: 'Notification delivery',\n chatMessageOutbox: 'Chat message publish',\n squareAnnouncementJobs: 'Square announcement',\n chatSyncFanoutJobs: 'Chat access fanout',\n mediaAssets: 'Media authority sync',\n quarantineSagaJobs: 'Quarantine saga',\n nciiRemovalJobs: 'NCII removal',\n safetyEvidenceJobs: 'Safety evidence job',\n accountActionCommands: 'Account safety action',\n activeReportGroups: 'Report edge sync',\n hallSubItemEdgeSync: 'Hall sub-item edge sync',\n chatParkedDeliveries: 'Chat room delivery',\n};\n\n/** Operator-settable report dispositions with their console copy. 'undetermined' is the\n * initial state, never a settable target, so it carries no option entry. */\nexport const REPORT_DISPOSITION_OPTIONS: readonly {\n value: Exclude<ReportDisposition, 'undetermined'>;\n label: string;\n description: string;\n}[] = [\n {\n value: 'reportRequired',\n label: 'Report required (NCMEC)',\n description:\n 'Apparent CSAM — a report is legally required. Establishes actual knowledge + arms the clock.',\n },\n {\n value: 'notRequired',\n label: 'No report required',\n description: 'No reporting obligation ever arose. Disallowed once there is a validated match.',\n },\n {\n value: 'correctedNoApparentViolation',\n label: 'False positive — correct it',\n description: 'There was a triggering signal but on review it is a false positive.',\n },\n];\n\n/** One user-facing reason option (shown to the affected user for any user-affecting action). */\nexport interface UserFacingReasonOption {\n code: string;\n label: string;\n description: string;\n}\n\n/** User-facing reason picklist for the guided case-resolution flow — the affected user sees the\n * label for any user-affecting action. Canonical home (ARCH-102): the codes ARE this list's own\n * enumeration, so every client (web, phone, TV, admin app) renders the same options + labels by\n * importing from here. Final user-/legal-facing wording is subject to lawyer review; these are\n * functional defaults. */\nexport const USER_FACING_REASON_OPTIONS: readonly UserFacingReasonOption[] = [\n { code: 'harassment', label: 'Harassment or bullying', description: 'Targeted abuse, threats, or sustained harassment of a person.' },\n { code: 'hate', label: 'Hate speech', description: 'Attacks on people based on a protected characteristic.' },\n { code: 'adult-content', label: 'Explicit / adult content', description: 'Sexual or graphic content not allowed on the platform.' },\n { code: 'violence', label: 'Violence or threats', description: 'Threats of violence, or content glorifying or inciting it.' },\n { code: 'spam', label: 'Spam or scam', description: 'Unsolicited bulk content, deceptive links, or fraud.' },\n { code: 'impersonation', label: 'Impersonation', description: 'Pretending to be another person, brand, or entity.' },\n { code: 'ip', label: 'Intellectual property', description: 'Use of someone else’s work without the right to do so.' },\n { code: 'nonconsensual', label: 'Nonconsensual intimate imagery', description: 'Intimate or sexual imagery shared without the subject’s consent.' },\n { code: 'safety', label: 'Safety violation', description: 'A serious safety violation handled under our safety policies.' },\n { code: 'other', label: 'Other (explain)', description: 'A policy violation not covered above — describe it in the detail box.' },\n] as const;\n\n/** The user-facing label for a reason code (undefined when the code is unknown/absent). */\nexport function userFacingReasonLabel(code: string | undefined): string | undefined {\n return USER_FACING_REASON_OPTIONS.find((o) => o.code === code)?.label;\n}\n\n// --- Rules & Agreements section titles and type labels ---\n// The admin rules editor and the public rules page both render these (ENG-002).\n\n/** Each fixed rule group's heading. */\nexport const RULE_GROUP_TITLES: Record<RuleGroup, string> = {\n generic: 'General Site Rules',\n merchandising: 'Merchandising, Endorsements & Revenue Sharing',\n hallWing: 'Hall Wing Rules',\n workRealm: 'Realm Rules',\n};\nexport const ARTISAN_AGREEMENTS_TITLE = 'Artisan Agreements';\n\n/**\n * The label for a rule or agreement point narrowed to one type (\"Tales only\", \"Educational wing\n * only\", \"Tales, Educational wing only\"); undefined when it applies to every type.\n */\nexport function appliesToTypeLabel(entry: {\n workProjectType?: WorkProjectType;\n hallWingType?: HallWingType;\n}): string | undefined {\n const parts = [\n entry.workProjectType && WORK_PROJECT_TYPES[entry.workProjectType].label,\n entry.hallWingType && `${HALL_WING_TYPES[entry.hallWingType].label} wing`,\n ].filter((part): part is string => Boolean(part));\n return parts.length > 0 ? `${parts.join(', ')} only` : undefined;\n}\n\n// --- Stored-enum display labels ---\n// What a screen shows for a stored enum value, keyed by the canonical union so a new member fails\n// the build until it has its text. A screen renders the label, never the raw value.\n\n/** Guild invite statuses (a Work's invited-users list and an invite conversation's status line). */\nexport const GUILD_INVITE_STATUS_LABELS: Record<GuildInviteConversationStatus, string> = {\n pending: 'Pending',\n accepted: 'Accepted',\n declined: 'Declined',\n cancelled: 'Cancelled',\n finalized: 'Finalized',\n};\n\n/** Commission proposal statuses (a Work's proposals table). */\nexport const COMMISSION_PROPOSAL_STATUS_LABELS: Record<CommissionProposalStatus, string> = {\n open: 'Open',\n invited: 'Invited',\n accepted: 'Accepted',\n rejected: 'Rejected',\n};\n\n/** Chapter, track, and episode review statuses (the Tale, Tune, and Television section tables). */\nexport const HALL_SUB_ITEM_STATUS_LABELS: Record<HallSubItemStatus, string> = {\n unpublished: 'unpublished',\n pending_approval: 'Pending approval',\n published: 'published',\n};\n\n/** Audition statuses (the audition page). */\nexport const AUDITION_STATUS_LABELS: Record<Audition['status'], string> = {\n open: 'Open',\n closed: 'Closed',\n pendingReview: 'PendingReview',\n};\n\n/** Work types (Work lists, the Work overview, the Works popover). */\nexport const WORK_PROJECT_TYPE_LABELS: Record<WorkProjectType, string> = {\n Tales: 'Tales',\n Tunes: 'Tunes',\n Television: 'Television',\n};\n\n/** Invite source types, as the invite dialog names the context an invite was sent from. */\nexport const INVITE_SOURCE_TYPE_LABELS: Record<InviteSourceType, string> = {\n standalone: 'standalone',\n craftSkill: 'craftSkill',\n commission: 'commission',\n audition: 'audition',\n};\n\n/** Short-link target types (the admin Short Links list), in the target-type enum's order. */\nexport const SHORT_LINK_TARGET_TYPE_LABELS: Record<ShortLinkTargetType, string> = {\n audition: 'Audition',\n 'audition-entry': 'Audition entry',\n commission: 'Commission',\n 'hall-library-item': 'Hall entry',\n};\n\n/** Support-thread statuses (a member's Messages, a Work's Correspondence, the admin browse list). */\nexport const ADMIN_DISPATCH_STATUS_LABELS: Record<AdminDispatch['status'], string> = {\n open: 'Open',\n user_reply: 'User replied',\n admin_reply: 'Admin replied',\n closed_resolved: 'Resolved',\n closed_unresolved: 'Closed',\n};\n\n/**\n * Why a full admin cannot remove their own Admin standing: the server refuses it, and the roster\n * panel withholds the control with the same words. Another full admin removes it.\n */\nexport const ADMIN_SELF_REVOKE_REFUSAL = \"You can't revoke your own admin standing. Ask another full admin to do it.\";\n\n/** The marker beside the sender of a support-thread message sent with admin authority. */\nexport const ADMIN_SENDER_MARKER_LABEL = 'Admin';\n\n/** The sender label on a support thread's system line (a close notice) in the admin preview. */\nexport const ADMIN_DISPATCH_SYSTEM_SENDER_LABEL = 'System';\n\n/** Child-safety case work statuses (the safety case console's case rows and closed-case lookup). */\nexport const CHILD_SAFETY_CASE_WORK_STATUS_LABELS: Record<ChildSafetyWorkStatus, string> = {\n new: 'new',\n triaged: 'triaged',\n reporting: 'reporting',\n actioning: 'actioning',\n processing: 'processing',\n failed: 'failed',\n operationallyResolved: 'operationallyResolved',\n};\n\n/** NCII case statuses (the safety case console's case rows and closed-case lookup). */\nexport const NCII_CASE_STATUS_LABELS: Record<NciiInternalStatus, string> = {\n open: 'open',\n removalInProgress: 'removalInProgress',\n processing: 'processing',\n failed: 'failed',\n removed: 'removed',\n rejected: 'rejected',\n closed: 'closed',\n};\n"]}
@@ -47,6 +47,16 @@ export declare const SWEEP_STATE_MAX_DEFERRED = 50;
47
47
  * so a publish stranded behind an abandoned claim always recovers inside it.
48
48
  */
49
49
  export declare const HALL_MEDIA_REAPER_CLAIM_LEASE_MS: number;
50
+ /** Accounts per `listUsers()` page in a full scan — the most Firebase Auth returns in one call. */
51
+ export declare const AUTH_LIST_USERS_PAGE_SIZE = 1000;
52
+ /**
53
+ * Most Auth accounts one run of a full `listUsers()` scan examines (`reconcileAccountStatus`,
54
+ * `orphanRegistrationCleanup`) before it saves its place and stops: two `listUsers()` pages. The scan
55
+ * ends a run at a page boundary once it has examined this many, so a userbase of any size never
56
+ * outruns one run's timeout; the lap resumes from the saved page on the next run, and the scan's
57
+ * cadence stamp is written only when a lap reaches the last page.
58
+ */
59
+ export declare const AUTH_FULL_SCAN_EXAMINED_PER_RUN: number;
50
60
  /**
51
61
  * The `sweepState/{sweepName}` doc ids: one per scheduled pass that keeps durable cadence or
52
62
  * rolling-cursor state (`SweepStateSchema`), so each pass owns exactly one doc and its cursor is
@@ -1 +1 @@
1
- {"version":3,"file":"scheduled-jobs.d.ts","sourceRoot":"","sources":["../../src/constants/scheduled-jobs.ts"],"names":[],"mappings":"AAIA,oEAAoE;AACpE,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C,wEAAwE;AACxE,eAAO,MAAM,6BAA6B,MAAM,CAAC;AAEjD,uEAAuE;AACvE,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAIpD;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,QAA2B,CAAC;AAEnE;6BAC6B;AAC7B,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C;;;;;;;GAOG;AACH,eAAO,MAAM,iCAAiC,QAAsB,CAAC;AACrE,eAAO,MAAM,gCAAgC,QAA2B,CAAC;AAEzE;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C;;;;;;;;GAQG;AACH,eAAO,MAAM,gCAAgC,QAAiB,CAAC;AAI/D;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,qhBAmBpB,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC"}
1
+ {"version":3,"file":"scheduled-jobs.d.ts","sourceRoot":"","sources":["../../src/constants/scheduled-jobs.ts"],"names":[],"mappings":"AAIA,oEAAoE;AACpE,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C,wEAAwE;AACxE,eAAO,MAAM,6BAA6B,MAAM,CAAC;AAEjD,uEAAuE;AACvE,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAIpD;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,QAA2B,CAAC;AAEnE;6BAC6B;AAC7B,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C;;;;;;;GAOG;AACH,eAAO,MAAM,iCAAiC,QAAsB,CAAC;AACrE,eAAO,MAAM,gCAAgC,QAA2B,CAAC;AAEzE;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C;;;;;;;;GAQG;AACH,eAAO,MAAM,gCAAgC,QAAiB,CAAC;AAI/D,mGAAmG;AACnG,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,QAAgC,CAAC;AAI7E;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,qhBAmBpB,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC"}
@@ -50,6 +50,17 @@ export const SWEEP_STATE_MAX_DEFERRED = 50;
50
50
  * so a publish stranded behind an abandoned claim always recovers inside it.
51
51
  */
52
52
  export const HALL_MEDIA_REAPER_CLAIM_LEASE_MS = 60 * 60 * 1000;
53
+ // --- Auth full scans ---
54
+ /** Accounts per `listUsers()` page in a full scan — the most Firebase Auth returns in one call. */
55
+ export const AUTH_LIST_USERS_PAGE_SIZE = 1000;
56
+ /**
57
+ * Most Auth accounts one run of a full `listUsers()` scan examines (`reconcileAccountStatus`,
58
+ * `orphanRegistrationCleanup`) before it saves its place and stops: two `listUsers()` pages. The scan
59
+ * ends a run at a page boundary once it has examined this many, so a userbase of any size never
60
+ * outruns one run's timeout; the lap resumes from the saved page on the next run, and the scan's
61
+ * cadence stamp is written only when a lap reaches the last page.
62
+ */
63
+ export const AUTH_FULL_SCAN_EXAMINED_PER_RUN = 2 * AUTH_LIST_USERS_PAGE_SIZE;
53
64
  // --- Sweep state ---
54
65
  /**
55
66
  * The `sweepState/{sweepName}` doc ids: one per scheduled pass that keeps durable cadence or
@@ -1 +1 @@
1
- {"version":3,"file":"scheduled-jobs.js","sourceRoot":"","sources":["../../src/constants/scheduled-jobs.ts"],"names":[],"mappings":"AAAA,yDAAyD;AAEzD,6BAA6B;AAE7B,oEAAoE;AACpE,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAE9C,wEAAwE;AACxE,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AAEjD,uEAAuE;AACvE,MAAM,CAAC,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAEpD,mCAAmC;AAEnC;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEnE;6BAC6B;AAC7B,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACrE,MAAM,CAAC,MAAM,gCAAgC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEzE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAE3C;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE/D,sBAAsB;AAEtB;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,2BAA2B;IAC3B,wBAAwB;IACxB,sBAAsB;IACtB,2BAA2B;IAC3B,2BAA2B;IAC3B,sBAAsB;IACtB,2BAA2B;IAC3B,wBAAwB;IACxB,wBAAwB;IACxB,qBAAqB;IACrB,sBAAsB;IACtB,iCAAiC;IACjC,uCAAuC;IACvC,2BAA2B;IAC3B,6BAA6B;IAC7B,gCAAgC;IAChC,+BAA+B;IAC/B,4BAA4B;CACpB,CAAC","sourcesContent":["// Scheduled-job intervals, batch sizes, and source URLs.\n\n// --- Admin task cleanup ---\n\n/** Interval in minutes for the admin-task cleanup scheduled job. */\nexport const ADMIN_TASK_CLEANUP_INTERVAL = 15;\n\n/** Batch size of expired task checkouts processed per scheduled run. */\nexport const ADMIN_TASK_EXPIRED_BATCH_SIZE = 500;\n\n/** Safety cap on iterations within a single admin-task cleanup run. */\nexport const ADMIN_TASK_CLEANUP_MAX_ITERATIONS = 10;\n\n// --- Hall-media orphan reaper ---\n\n/**\n * How long Hall-media copies are left alone before the orphan reaper may reclaim them — both a\n * Hall-owned asset (asset phase, measured from its `createdAt`) and the objects a write intent\n * names (copy-intent phase: recording a copy's or a first ingest's intent sets its `reapAfter`\n * this far out, far beyond any ingest's processing and retry time). It must exceed\n * the longest retry window of the publish trigger `onThresholdItemReviewed` (about a day), since\n * a pending retry re-derives the same deterministic copy and attaches its bytes. It also leaves a\n * publish that parked partway time to be unwound, resubmitted, and re-driven onto the same\n * destination while its earlier objects are still there to reuse.\n */\nexport const HALL_MEDIA_ORPHAN_GRACE_MS = 14 * 24 * 60 * 60 * 1000;\n\n/** Most candidates each reaper phase takes up per pass: assets in the asset phase, due intents in\n * the copy-intent phase. */\nexport const HALL_MEDIA_REAPER_PAGE_SIZE = 100;\n\n/**\n * Deferral backoff for both reaper phases — a copy intent the sweep cannot finish yet\n * (`reapAttemptCount`) and an asset-phase candidate it cannot positively clear (a cursor\n * `deferred` entry's `attemptCount`). With `n` the count after the deferral that is being\n * scheduled, the item is next due `min(BASE × 2^(n−1), MAX)` from now: 1, 2, 4, 8, then 14 days\n * from there on. The base is the reaper's daily cadence, since any shorter delay is simply the\n * next pass; the cap bounds how long a released hold or a recovered read waits to be noticed.\n */\nexport const HALL_MEDIA_REAPER_BACKOFF_BASE_MS = 24 * 60 * 60 * 1000;\nexport const HALL_MEDIA_REAPER_BACKOFF_MAX_MS = 14 * 24 * 60 * 60 * 1000;\n\n/**\n * Most rows one pass holds in its sweep-state `deferred` set — the rows its rolling cursor moved past\n * without positively clearing. Every pass retries the entries that are due, each retry re-reading the\n * row, so for the Hall-media orphan reaper the bound keeps that work to half of a\n * HALL_MEDIA_REAPER_PAGE_SIZE page on top of the pass's own page, and it keeps the sweep-state doc a\n * few kilobytes. A row a pass cannot positively clear is rare, so a full set means something\n * systemic is failing — the signal to stop and report, not to keep growing the list.\n */\nexport const SWEEP_STATE_MAX_DEFERRED = 50;\n\n/**\n * How long the copy-intent sweep holds a claim on an intent. Claiming moves the intent to\n * `reaping` and stamps `reapClaimedAt`, and that stamp is the claim's fencing token. Once the claim\n * is this old it has expired: a later sweep may take the intent over and re-stamp it, and the copy\n * path may take the `reaping` intent back to `copying`. It must far exceed the timeout of the\n * reaper function `reapOrphanedHallMediaCopies` (300 s), so a live pass never loses its claim, and\n * stay far below the retry budget of the publish trigger `onThresholdItemReviewed` (about a day),\n * so a publish stranded behind an abandoned claim always recovers inside it.\n */\nexport const HALL_MEDIA_REAPER_CLAIM_LEASE_MS = 60 * 60 * 1000;\n\n// --- Sweep state ---\n\n/**\n * The `sweepState/{sweepName}` doc ids: one per scheduled pass that keeps durable cadence or\n * rolling-cursor state (`SweepStateSchema`), so each pass owns exactly one doc and its cursor is\n * never shared with another pass.\n */\nexport const SWEEP_STATE_NAMES = [\n 'orphanRegistrationCleanup',\n 'reconcileAccountStatus',\n 'reconcilePublicUsers',\n 'hallMediaReaperAssetPhase',\n 'quarantineEnqueueBackstop',\n 'ncmecEnqueueBackstop',\n 'staleSafetyCaseAlertSweep',\n 'csamStrandedProcessing',\n 'nciiStrandedProcessing',\n 'expireRejectedMedia',\n 'cleanupOrphanUploads',\n 'mediaReconcileUnpublishedAssets',\n 'mediaReconcileUnreferencedBroadAssets',\n 'nciiEvidenceOrphanSweeper',\n 'nciiRetentionClosedRequests',\n 'reconcilePledgeRefundApprovals',\n 'crossoverServingDenyReconcile',\n 'crossoverPhotoDnaReconcile',\n] as const;\nexport type SweepStateName = (typeof SWEEP_STATE_NAMES)[number];\n"]}
1
+ {"version":3,"file":"scheduled-jobs.js","sourceRoot":"","sources":["../../src/constants/scheduled-jobs.ts"],"names":[],"mappings":"AAAA,yDAAyD;AAEzD,6BAA6B;AAE7B,oEAAoE;AACpE,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAE9C,wEAAwE;AACxE,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AAEjD,uEAAuE;AACvE,MAAM,CAAC,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAEpD,mCAAmC;AAEnC;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEnE;6BAC6B;AAC7B,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACrE,MAAM,CAAC,MAAM,gCAAgC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEzE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAE3C;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE/D,0BAA0B;AAE1B,mGAAmG;AACnG,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,GAAG,yBAAyB,CAAC;AAE7E,sBAAsB;AAEtB;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,2BAA2B;IAC3B,wBAAwB;IACxB,sBAAsB;IACtB,2BAA2B;IAC3B,2BAA2B;IAC3B,sBAAsB;IACtB,2BAA2B;IAC3B,wBAAwB;IACxB,wBAAwB;IACxB,qBAAqB;IACrB,sBAAsB;IACtB,iCAAiC;IACjC,uCAAuC;IACvC,2BAA2B;IAC3B,6BAA6B;IAC7B,gCAAgC;IAChC,+BAA+B;IAC/B,4BAA4B;CACpB,CAAC","sourcesContent":["// Scheduled-job intervals, batch sizes, and source URLs.\n\n// --- Admin task cleanup ---\n\n/** Interval in minutes for the admin-task cleanup scheduled job. */\nexport const ADMIN_TASK_CLEANUP_INTERVAL = 15;\n\n/** Batch size of expired task checkouts processed per scheduled run. */\nexport const ADMIN_TASK_EXPIRED_BATCH_SIZE = 500;\n\n/** Safety cap on iterations within a single admin-task cleanup run. */\nexport const ADMIN_TASK_CLEANUP_MAX_ITERATIONS = 10;\n\n// --- Hall-media orphan reaper ---\n\n/**\n * How long Hall-media copies are left alone before the orphan reaper may reclaim them — both a\n * Hall-owned asset (asset phase, measured from its `createdAt`) and the objects a write intent\n * names (copy-intent phase: recording a copy's or a first ingest's intent sets its `reapAfter`\n * this far out, far beyond any ingest's processing and retry time). It must exceed\n * the longest retry window of the publish trigger `onThresholdItemReviewed` (about a day), since\n * a pending retry re-derives the same deterministic copy and attaches its bytes. It also leaves a\n * publish that parked partway time to be unwound, resubmitted, and re-driven onto the same\n * destination while its earlier objects are still there to reuse.\n */\nexport const HALL_MEDIA_ORPHAN_GRACE_MS = 14 * 24 * 60 * 60 * 1000;\n\n/** Most candidates each reaper phase takes up per pass: assets in the asset phase, due intents in\n * the copy-intent phase. */\nexport const HALL_MEDIA_REAPER_PAGE_SIZE = 100;\n\n/**\n * Deferral backoff for both reaper phases — a copy intent the sweep cannot finish yet\n * (`reapAttemptCount`) and an asset-phase candidate it cannot positively clear (a cursor\n * `deferred` entry's `attemptCount`). With `n` the count after the deferral that is being\n * scheduled, the item is next due `min(BASE × 2^(n−1), MAX)` from now: 1, 2, 4, 8, then 14 days\n * from there on. The base is the reaper's daily cadence, since any shorter delay is simply the\n * next pass; the cap bounds how long a released hold or a recovered read waits to be noticed.\n */\nexport const HALL_MEDIA_REAPER_BACKOFF_BASE_MS = 24 * 60 * 60 * 1000;\nexport const HALL_MEDIA_REAPER_BACKOFF_MAX_MS = 14 * 24 * 60 * 60 * 1000;\n\n/**\n * Most rows one pass holds in its sweep-state `deferred` set — the rows its rolling cursor moved past\n * without positively clearing. Every pass retries the entries that are due, each retry re-reading the\n * row, so for the Hall-media orphan reaper the bound keeps that work to half of a\n * HALL_MEDIA_REAPER_PAGE_SIZE page on top of the pass's own page, and it keeps the sweep-state doc a\n * few kilobytes. A row a pass cannot positively clear is rare, so a full set means something\n * systemic is failing — the signal to stop and report, not to keep growing the list.\n */\nexport const SWEEP_STATE_MAX_DEFERRED = 50;\n\n/**\n * How long the copy-intent sweep holds a claim on an intent. Claiming moves the intent to\n * `reaping` and stamps `reapClaimedAt`, and that stamp is the claim's fencing token. Once the claim\n * is this old it has expired: a later sweep may take the intent over and re-stamp it, and the copy\n * path may take the `reaping` intent back to `copying`. It must far exceed the timeout of the\n * reaper function `reapOrphanedHallMediaCopies` (300 s), so a live pass never loses its claim, and\n * stay far below the retry budget of the publish trigger `onThresholdItemReviewed` (about a day),\n * so a publish stranded behind an abandoned claim always recovers inside it.\n */\nexport const HALL_MEDIA_REAPER_CLAIM_LEASE_MS = 60 * 60 * 1000;\n\n// --- Auth full scans ---\n\n/** Accounts per `listUsers()` page in a full scan — the most Firebase Auth returns in one call. */\nexport const AUTH_LIST_USERS_PAGE_SIZE = 1000;\n\n/**\n * Most Auth accounts one run of a full `listUsers()` scan examines (`reconcileAccountStatus`,\n * `orphanRegistrationCleanup`) before it saves its place and stops: two `listUsers()` pages. The scan\n * ends a run at a page boundary once it has examined this many, so a userbase of any size never\n * outruns one run's timeout; the lap resumes from the saved page on the next run, and the scan's\n * cadence stamp is written only when a lap reaches the last page.\n */\nexport const AUTH_FULL_SCAN_EXAMINED_PER_RUN = 2 * AUTH_LIST_USERS_PAGE_SIZE;\n\n// --- Sweep state ---\n\n/**\n * The `sweepState/{sweepName}` doc ids: one per scheduled pass that keeps durable cadence or\n * rolling-cursor state (`SweepStateSchema`), so each pass owns exactly one doc and its cursor is\n * never shared with another pass.\n */\nexport const SWEEP_STATE_NAMES = [\n 'orphanRegistrationCleanup',\n 'reconcileAccountStatus',\n 'reconcilePublicUsers',\n 'hallMediaReaperAssetPhase',\n 'quarantineEnqueueBackstop',\n 'ncmecEnqueueBackstop',\n 'staleSafetyCaseAlertSweep',\n 'csamStrandedProcessing',\n 'nciiStrandedProcessing',\n 'expireRejectedMedia',\n 'cleanupOrphanUploads',\n 'mediaReconcileUnpublishedAssets',\n 'mediaReconcileUnreferencedBroadAssets',\n 'nciiEvidenceOrphanSweeper',\n 'nciiRetentionClosedRequests',\n 'reconcilePledgeRefundApprovals',\n 'crossoverServingDenyReconcile',\n 'crossoverPhotoDnaReconcile',\n] as const;\nexport type SweepStateName = (typeof SWEEP_STATE_NAMES)[number];\n"]}
@@ -41,8 +41,17 @@ export declare const SweepDeferredRowSchema: z.ZodObject<{
41
41
  attemptCount: z.ZodNumber;
42
42
  }, z.core.$strict>;
43
43
  export type SweepDeferredRow = z.infer<typeof SweepDeferredRowSchema>;
44
+ export declare const SweepAuthScanLapSchema: z.ZodObject<{
45
+ pageToken: z.ZodString;
46
+ lapStartedAt: z.ZodNumber;
47
+ }, z.core.$strict>;
48
+ export type SweepAuthScanLap = z.infer<typeof SweepAuthScanLapSchema>;
44
49
  export declare const SweepStateSchema: z.ZodObject<{
45
50
  fullScanLastRunAt: z.ZodOptional<z.ZodNumber>;
51
+ fullScanLap: z.ZodOptional<z.ZodObject<{
52
+ pageToken: z.ZodString;
53
+ lapStartedAt: z.ZodNumber;
54
+ }, z.core.$strict>>;
46
55
  rollingCursor: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
47
56
  state: z.ZodLiteral<"inLap">;
48
57
  afterKey: z.ZodString;
@@ -1 +1 @@
1
- {"version":3,"file":"backend-state.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/backend-state.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAexB,eAAO,MAAM,oBAAoB;;;;;;;;;;;;iBAa/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,oEAAoE;AACpE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;EAA4B,CAAC;AAO9D,eAAO,MAAM,sBAAsB;;;;;kBAUxB,CAAC;AACZ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAUtE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;iBAgBzB,CAAC;AACL,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAU1D,eAAO,MAAM,kCAAkC;;;;;;;;;;;;;;iBAc7C,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAS9F,eAAO,MAAM,kCAAkC;;;;;;;;;;;;iBAgB7C,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAQ9F,eAAO,MAAM,mCAAmC;;;;;;;;iBAU9C,CAAC;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mCAAmC,CAAC,CAAC"}
1
+ {"version":3,"file":"backend-state.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/backend-state.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAexB,eAAO,MAAM,oBAAoB;;;;;;;;;;;;iBAa/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,oEAAoE;AACpE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;EAA4B,CAAC;AAO9D,eAAO,MAAM,sBAAsB;;;;;kBAUxB,CAAC;AACZ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAKtE,eAAO,MAAM,sBAAsB;;;kBAOxB,CAAC;AACZ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAYtE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;iBAkBzB,CAAC;AACL,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAU1D,eAAO,MAAM,kCAAkC;;;;;;;;;;;;;;iBAc7C,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAS9F,eAAO,MAAM,kCAAkC;;;;;;;;;;;;iBAgB7C,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAQ9F,eAAO,MAAM,mCAAmC;;;;;;;;iBAU9C,CAAC;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mCAAmC,CAAC,CAAC"}
@@ -45,18 +45,33 @@ export const SweepDeferredRowSchema = z
45
45
  attemptCount: z.number().int().positive(),
46
46
  })
47
47
  .strict();
48
+ // The place a full `listUsers()` scan stopped partway through a lap. Firebase Auth lists accounts
49
+ // only by page token — there is no key to resume after — so, unlike a rolling cursor, this keeps the
50
+ // token itself. A token Auth no longer accepts restarts the lap from the first page.
51
+ export const SweepAuthScanLapSchema = z
52
+ .object({
53
+ /** The `listUsers()` page token the next run starts from. */
54
+ pageToken: z.string().min(1),
55
+ /** Wall-clock ms the lap began. */
56
+ lapStartedAt: z.number().int().nonnegative(),
57
+ })
58
+ .strict();
48
59
  // sweepState/{sweepName} — durable cadence + cursor state for one scheduled pass: every persisted
49
60
  // sweep position in the app lives here, one doc per SWEEP_STATE_NAMES name. The fields a pass uses
50
61
  // are the ones it declares, so every field except `updatedAt` is optional. A pass over a bounded,
51
62
  // ordered source keeps its position in `rollingCursor` (a finished lap is `done`, stamped with
52
63
  // `lapCompletedAt` — the cadence stamp of a pass that runs a lap at a time), and the rows it moved
53
64
  // past without clearing in `deferred`. `fullScanLastRunAt` is the cadence of the ~weekly
54
- // `listUsers()` scan `orphanRegistrationCleanup` and `reconcileAccountStatus` run. The stamps are
55
- // wall-clock so a container recycle cannot reset the cadence.
65
+ // `listUsers()` scan `orphanRegistrationCleanup` and `reconcileAccountStatus` run, stamped when a lap
66
+ // reaches the last page; `fullScanLap` is that scan's place while a lap is still in progress
67
+ // (AUTH_FULL_SCAN_EXAMINED_PER_RUN bounds each run). The stamps are wall-clock so a container
68
+ // recycle cannot reset the cadence.
56
69
  export const SweepStateSchema = z
57
70
  .object({
58
- /** Wall-clock ms the last full `listUsers()` scan ran (absent = never). */
71
+ /** Wall-clock ms the last full `listUsers()` scan lap completed (absent = never). */
59
72
  fullScanLastRunAt: z.number().optional(),
73
+ /** The full scan's place while a lap is in progress; absent when no lap is running. */
74
+ fullScanLap: SweepAuthScanLapSchema.optional(),
60
75
  rollingCursor: SweepRollingCursorSchema.optional(),
61
76
  deferred: z.array(SweepDeferredRowSchema).max(SWEEP_STATE_MAX_DEFERRED).optional(),
62
77
  updatedAt: z.number(),
@@ -1 +1 @@
1
- {"version":3,"file":"backend-state.js","sourceRoot":"","sources":["../../src/doc-schemas/backend-state.ts"],"names":[],"mappings":"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","sourcesContent":["// Backend-only state document SCHEMAS — Cloud-Functions-owned bookkeeping docs that no\n// client ever reads or writes (firestore.rules denies client access to every path below).\n// They are registered here for the same reason every other stored shape is: ARCH-104 gives\n// the path ONE owner, and COLLECTION_SCHEMAS is the inventory the schema-doc generator and\n// the drift-check read. Types are inferred via z.infer.\n\nimport { z } from 'zod';\nimport { SWEEP_STATE_MAX_DEFERRED, SWEEP_STATE_NAMES } from '../constants/scheduled-jobs.js';\nimport { SweepRollingCursorSchema } from './sweep-cursor.js';\nimport {\n ChildSafetyNcmecCompletionChannelSchema,\n ChildSafetyNcmecCompletionProofTypeSchema,\n} from './safety/case.js';\n\n// operatorStepUp/{uid} — [H-08] per-operator TOTP step-up state: the authenticator secret\n// plus the currently open grant window. The secret is returned to the client EXACTLY ONCE by\n// enrollOperatorStepUp and is never readable through Firestore afterwards; `status` goes\n// 'pending' → 'active' on confirm, and each successful verify pushes `grantExpiresAt` out by\n// the step-up window. `failedAttempts` and `lockedUntil` make the server-side TOTP brute-force\n// lockout durable; omitted fields are the compatible no-lockout state for rows written before\n// that protection shipped. (functions/src/safety/operatorStepUp.ts)\nexport const OperatorStepUpSchema = z.object({\n secret: z.string(),\n status: z.enum(['pending', 'active']),\n /** Absent until the first successful verify; a grant is live while now < grantExpiresAt. */\n grantExpiresAt: z.number().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n /** Set when enrollment is confirmed (status flips to 'active'). */\n confirmedAt: z.number().optional(),\n /** Consecutive failed TOTP checks since the most recent success or lockout. */\n failedAttempts: z.number().int().nonnegative().optional(),\n /** Epoch ms until which TOTP verification is refused; absent or 0 means not locked. */\n lockedUntil: z.number().nonnegative().optional(),\n});\nexport type OperatorStepUp = z.infer<typeof OperatorStepUpSchema>;\n\n/** A `sweepState/{sweepName}` doc id — one of SWEEP_STATE_NAMES. */\nexport const SweepStateNameSchema = z.enum(SWEEP_STATE_NAMES);\n\n\n// One row a pass's rolling cursor moved past without positively clearing it (a read error, a\n// missing owner, a hold). The pass retries it from here once due instead of stopping at it, so one\n// uncertain row never pins the window. `key` / `value` are the row's position in the cursor's\n// terms (SweepRollingCursorSchema): its key, and its ordered value when the source has one.\nexport const SweepDeferredRowSchema = z\n .object({\n key: z.string().min(1),\n value: z.number().optional(),\n /** Epoch ms from which a pass may retry it. */\n nextAttemptAt: z.number().int().nonnegative(),\n /** Passes that examined it without clearing it; the pass that deferred it is the first. A pass's\n * own backoff (e.g. HALL_MEDIA_REAPER_BACKOFF_BASE_MS / _MAX_MS) grows from it. */\n attemptCount: z.number().int().positive(),\n })\n .strict();\nexport type SweepDeferredRow = z.infer<typeof SweepDeferredRowSchema>;\n\n// sweepState/{sweepName} — durable cadence + cursor state for one scheduled pass: every persisted\n// sweep position in the app lives here, one doc per SWEEP_STATE_NAMES name. The fields a pass uses\n// are the ones it declares, so every field except `updatedAt` is optional. A pass over a bounded,\n// ordered source keeps its position in `rollingCursor` (a finished lap is `done`, stamped with\n// `lapCompletedAt` — the cadence stamp of a pass that runs a lap at a time), and the rows it moved\n// past without clearing in `deferred`. `fullScanLastRunAt` is the cadence of the ~weekly\n// `listUsers()` scan `orphanRegistrationCleanup` and `reconcileAccountStatus` run. The stamps are\n// wall-clock so a container recycle cannot reset the cadence.\nexport const SweepStateSchema = z\n .object({\n /** Wall-clock ms the last full `listUsers()` scan ran (absent = never). */\n fullScanLastRunAt: z.number().optional(),\n rollingCursor: SweepRollingCursorSchema.optional(),\n deferred: z.array(SweepDeferredRowSchema).max(SWEEP_STATE_MAX_DEFERRED).optional(),\n updatedAt: z.number(),\n })\n .superRefine((val, ctx) => {\n const seen = new Set<string>();\n (val.deferred ?? []).forEach((row, index) => {\n if (seen.has(row.key)) {\n ctx.addIssue({ code: 'custom', path: ['deferred', index, 'key'], message: 'a row is deferred at most once' });\n }\n seen.add(row.key);\n });\n });\nexport type SweepState = z.infer<typeof SweepStateSchema>;\n\n// childSafetyCases/{caseId}/ncmecSubmissions/{submissionId}/ncmecCompletionProof/record —\n// [H-05] the IMMUTABLE record of a verified manual-portal completion. Written create-if-absent in\n// the SAME transaction as the submission's `completed` transition, so a submission can never reach\n// `completed` without a bound proof record; a re-completion keeps the original. The submission's\n// `completionProofRef` stores this doc's path, never an operator free-text string.\n// [Q13/H-06] `portalReceiptArtifactId` binds the proof to a verified NcmecPortalReceiptArtifactV1\n// on the same (caseId, submissionId) — an arbitrary operator string is never accepted.\n// (functions/src/safety/ncmecReporting.ts)\nexport const NcmecCompletionProofRecordV1Schema = z.object({\n caseId: z.string(),\n submissionId: z.string(),\n channel: ChildSafetyNcmecCompletionChannelSchema,\n proofType: ChildSafetyNcmecCompletionProofTypeSchema,\n /** Id of the verified portal-receipt artifact this completion is bound to. */\n portalReceiptArtifactId: z.string(),\n /** Optional operator free-text note about the confirmation. */\n proofText: z.string().optional(),\n /** The NCMEC-assigned report id — REQUIRED. The number IS the proof; there is no\n * \"filed, number pending\" grace. */\n ncmecReportId: z.string(),\n recordedByUid: z.string(),\n recordedAt: z.number(),\n});\nexport type NcmecCompletionProofRecordV1 = z.infer<typeof NcmecCompletionProofRecordV1Schema>;\n\n// childSafetyCases/{caseId}/portalReceiptArtifacts/{artifactId} — [Q13/H-06] the immutable\n// record of a PDF/screenshot of the completed NCMEC CyberTipline submission, stored in the\n// restricted evidence vault. `markNcmecPortalComplete` accepts only the id of a verified\n// artifact bound to the SAME (caseId, submissionId); an arbitrary operator string is never\n// proof. Written create-only, never overwritten, and carries the server-verified storage facts\n// (generation + sha256) so a later transactional verification can confirm the same object\n// still exists unchanged. (functions/src/safety/ncmecOperatorCommands.ts)\nexport const NcmecPortalReceiptArtifactV1Schema = z.object({\n schemaVersion: z.literal(1),\n caseId: z.string(),\n submissionId: z.string(),\n /** Key of the object in the restricted evidence vault bucket. */\n evidenceVaultKey: z.string(),\n /** GCS object generation at capture time (immutable identity peg). */\n objectGeneration: z.string(),\n /** SHA-256 hex digest of the object bytes at capture time. */\n sha256: z.string(),\n contentType: z.string(),\n sizeBytes: z.number(),\n capturedAt: z.number(),\n registeredByUid: z.string(),\n /** Optional operator free-text describing what the artifact is. */\n description: z.string().optional(),\n});\nexport type NcmecPortalReceiptArtifactV1 = z.infer<typeof NcmecPortalReceiptArtifactV1Schema>;\n\n// childSafetyCases/{caseId}/ncmecPortalCorrections/{correctionId} — the immutable record that\n// the operator filed a CORRECTION on the NCMEC manual portal. There is no in-app correction\n// submission pipeline; this records the portal filing. Its EXISTENCE is the gate on the\n// corrected-no-apparent-violation disposition, so it is never a bare-discretion close. Each\n// correction is a fresh id — a later correction is an additional record, never a mutation of\n// the prior one. (functions/src/safety/recordNcmecPortalCorrection.ts)\nexport const NcmecPortalCorrectionRecordV1Schema = z.object({\n schemaVersion: z.literal(1),\n caseId: z.string(),\n /** The portal-assigned NCMEC report id being corrected. */\n ncmecReportId: z.string(),\n /** When the operator filed the correction on the portal (operator-supplied). */\n correctionFiledAt: z.number(),\n reason: z.string(),\n recordedByUid: z.string(),\n recordedAt: z.number(),\n});\nexport type NcmecPortalCorrectionRecordV1 = z.infer<typeof NcmecPortalCorrectionRecordV1Schema>;\n"]}
1
+ {"version":3,"file":"backend-state.js","sourceRoot":"","sources":["../../src/doc-schemas/backend-state.ts"],"names":[],"mappings":"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","sourcesContent":["// Backend-only state document SCHEMAS — Cloud-Functions-owned bookkeeping docs that no\n// client ever reads or writes (firestore.rules denies client access to every path below).\n// They are registered here for the same reason every other stored shape is: ARCH-104 gives\n// the path ONE owner, and COLLECTION_SCHEMAS is the inventory the schema-doc generator and\n// the drift-check read. Types are inferred via z.infer.\n\nimport { z } from 'zod';\nimport { SWEEP_STATE_MAX_DEFERRED, SWEEP_STATE_NAMES } from '../constants/scheduled-jobs.js';\nimport { SweepRollingCursorSchema } from './sweep-cursor.js';\nimport {\n ChildSafetyNcmecCompletionChannelSchema,\n ChildSafetyNcmecCompletionProofTypeSchema,\n} from './safety/case.js';\n\n// operatorStepUp/{uid} — [H-08] per-operator TOTP step-up state: the authenticator secret\n// plus the currently open grant window. The secret is returned to the client EXACTLY ONCE by\n// enrollOperatorStepUp and is never readable through Firestore afterwards; `status` goes\n// 'pending' → 'active' on confirm, and each successful verify pushes `grantExpiresAt` out by\n// the step-up window. `failedAttempts` and `lockedUntil` make the server-side TOTP brute-force\n// lockout durable; omitted fields are the compatible no-lockout state for rows written before\n// that protection shipped. (functions/src/safety/operatorStepUp.ts)\nexport const OperatorStepUpSchema = z.object({\n secret: z.string(),\n status: z.enum(['pending', 'active']),\n /** Absent until the first successful verify; a grant is live while now < grantExpiresAt. */\n grantExpiresAt: z.number().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n /** Set when enrollment is confirmed (status flips to 'active'). */\n confirmedAt: z.number().optional(),\n /** Consecutive failed TOTP checks since the most recent success or lockout. */\n failedAttempts: z.number().int().nonnegative().optional(),\n /** Epoch ms until which TOTP verification is refused; absent or 0 means not locked. */\n lockedUntil: z.number().nonnegative().optional(),\n});\nexport type OperatorStepUp = z.infer<typeof OperatorStepUpSchema>;\n\n/** A `sweepState/{sweepName}` doc id — one of SWEEP_STATE_NAMES. */\nexport const SweepStateNameSchema = z.enum(SWEEP_STATE_NAMES);\n\n\n// One row a pass's rolling cursor moved past without positively clearing it (a read error, a\n// missing owner, a hold). The pass retries it from here once due instead of stopping at it, so one\n// uncertain row never pins the window. `key` / `value` are the row's position in the cursor's\n// terms (SweepRollingCursorSchema): its key, and its ordered value when the source has one.\nexport const SweepDeferredRowSchema = z\n .object({\n key: z.string().min(1),\n value: z.number().optional(),\n /** Epoch ms from which a pass may retry it. */\n nextAttemptAt: z.number().int().nonnegative(),\n /** Passes that examined it without clearing it; the pass that deferred it is the first. A pass's\n * own backoff (e.g. HALL_MEDIA_REAPER_BACKOFF_BASE_MS / _MAX_MS) grows from it. */\n attemptCount: z.number().int().positive(),\n })\n .strict();\nexport type SweepDeferredRow = z.infer<typeof SweepDeferredRowSchema>;\n\n// The place a full `listUsers()` scan stopped partway through a lap. Firebase Auth lists accounts\n// only by page token — there is no key to resume after — so, unlike a rolling cursor, this keeps the\n// token itself. A token Auth no longer accepts restarts the lap from the first page.\nexport const SweepAuthScanLapSchema = z\n .object({\n /** The `listUsers()` page token the next run starts from. */\n pageToken: z.string().min(1),\n /** Wall-clock ms the lap began. */\n lapStartedAt: z.number().int().nonnegative(),\n })\n .strict();\nexport type SweepAuthScanLap = z.infer<typeof SweepAuthScanLapSchema>;\n\n// sweepState/{sweepName} — durable cadence + cursor state for one scheduled pass: every persisted\n// sweep position in the app lives here, one doc per SWEEP_STATE_NAMES name. The fields a pass uses\n// are the ones it declares, so every field except `updatedAt` is optional. A pass over a bounded,\n// ordered source keeps its position in `rollingCursor` (a finished lap is `done`, stamped with\n// `lapCompletedAt` — the cadence stamp of a pass that runs a lap at a time), and the rows it moved\n// past without clearing in `deferred`. `fullScanLastRunAt` is the cadence of the ~weekly\n// `listUsers()` scan `orphanRegistrationCleanup` and `reconcileAccountStatus` run, stamped when a lap\n// reaches the last page; `fullScanLap` is that scan's place while a lap is still in progress\n// (AUTH_FULL_SCAN_EXAMINED_PER_RUN bounds each run). The stamps are wall-clock so a container\n// recycle cannot reset the cadence.\nexport const SweepStateSchema = z\n .object({\n /** Wall-clock ms the last full `listUsers()` scan lap completed (absent = never). */\n fullScanLastRunAt: z.number().optional(),\n /** The full scan's place while a lap is in progress; absent when no lap is running. */\n fullScanLap: SweepAuthScanLapSchema.optional(),\n rollingCursor: SweepRollingCursorSchema.optional(),\n deferred: z.array(SweepDeferredRowSchema).max(SWEEP_STATE_MAX_DEFERRED).optional(),\n updatedAt: z.number(),\n })\n .superRefine((val, ctx) => {\n const seen = new Set<string>();\n (val.deferred ?? []).forEach((row, index) => {\n if (seen.has(row.key)) {\n ctx.addIssue({ code: 'custom', path: ['deferred', index, 'key'], message: 'a row is deferred at most once' });\n }\n seen.add(row.key);\n });\n });\nexport type SweepState = z.infer<typeof SweepStateSchema>;\n\n// childSafetyCases/{caseId}/ncmecSubmissions/{submissionId}/ncmecCompletionProof/record —\n// [H-05] the IMMUTABLE record of a verified manual-portal completion. Written create-if-absent in\n// the SAME transaction as the submission's `completed` transition, so a submission can never reach\n// `completed` without a bound proof record; a re-completion keeps the original. The submission's\n// `completionProofRef` stores this doc's path, never an operator free-text string.\n// [Q13/H-06] `portalReceiptArtifactId` binds the proof to a verified NcmecPortalReceiptArtifactV1\n// on the same (caseId, submissionId) — an arbitrary operator string is never accepted.\n// (functions/src/safety/ncmecReporting.ts)\nexport const NcmecCompletionProofRecordV1Schema = z.object({\n caseId: z.string(),\n submissionId: z.string(),\n channel: ChildSafetyNcmecCompletionChannelSchema,\n proofType: ChildSafetyNcmecCompletionProofTypeSchema,\n /** Id of the verified portal-receipt artifact this completion is bound to. */\n portalReceiptArtifactId: z.string(),\n /** Optional operator free-text note about the confirmation. */\n proofText: z.string().optional(),\n /** The NCMEC-assigned report id — REQUIRED. The number IS the proof; there is no\n * \"filed, number pending\" grace. */\n ncmecReportId: z.string(),\n recordedByUid: z.string(),\n recordedAt: z.number(),\n});\nexport type NcmecCompletionProofRecordV1 = z.infer<typeof NcmecCompletionProofRecordV1Schema>;\n\n// childSafetyCases/{caseId}/portalReceiptArtifacts/{artifactId} — [Q13/H-06] the immutable\n// record of a PDF/screenshot of the completed NCMEC CyberTipline submission, stored in the\n// restricted evidence vault. `markNcmecPortalComplete` accepts only the id of a verified\n// artifact bound to the SAME (caseId, submissionId); an arbitrary operator string is never\n// proof. Written create-only, never overwritten, and carries the server-verified storage facts\n// (generation + sha256) so a later transactional verification can confirm the same object\n// still exists unchanged. (functions/src/safety/ncmecOperatorCommands.ts)\nexport const NcmecPortalReceiptArtifactV1Schema = z.object({\n schemaVersion: z.literal(1),\n caseId: z.string(),\n submissionId: z.string(),\n /** Key of the object in the restricted evidence vault bucket. */\n evidenceVaultKey: z.string(),\n /** GCS object generation at capture time (immutable identity peg). */\n objectGeneration: z.string(),\n /** SHA-256 hex digest of the object bytes at capture time. */\n sha256: z.string(),\n contentType: z.string(),\n sizeBytes: z.number(),\n capturedAt: z.number(),\n registeredByUid: z.string(),\n /** Optional operator free-text describing what the artifact is. */\n description: z.string().optional(),\n});\nexport type NcmecPortalReceiptArtifactV1 = z.infer<typeof NcmecPortalReceiptArtifactV1Schema>;\n\n// childSafetyCases/{caseId}/ncmecPortalCorrections/{correctionId} — the immutable record that\n// the operator filed a CORRECTION on the NCMEC manual portal. There is no in-app correction\n// submission pipeline; this records the portal filing. Its EXISTENCE is the gate on the\n// corrected-no-apparent-violation disposition, so it is never a bare-discretion close. Each\n// correction is a fresh id — a later correction is an additional record, never a mutation of\n// the prior one. (functions/src/safety/recordNcmecPortalCorrection.ts)\nexport const NcmecPortalCorrectionRecordV1Schema = z.object({\n schemaVersion: z.literal(1),\n caseId: z.string(),\n /** The portal-assigned NCMEC report id being corrected. */\n ncmecReportId: z.string(),\n /** When the operator filed the correction on the portal (operator-supplied). */\n correctionFiledAt: z.number(),\n reason: z.string(),\n recordedByUid: z.string(),\n recordedAt: z.number(),\n});\nexport type NcmecPortalCorrectionRecordV1 = z.infer<typeof NcmecPortalCorrectionRecordV1Schema>;\n"]}
@@ -6343,6 +6343,10 @@ export declare const COLLECTION_SCHEMAS: {
6343
6343
  }, z.core.$strip>;
6344
6344
  readonly 'sweepState/{sweepName}': z.ZodObject<{
6345
6345
  fullScanLastRunAt: z.ZodOptional<z.ZodNumber>;
6346
+ fullScanLap: z.ZodOptional<z.ZodObject<{
6347
+ pageToken: z.ZodString;
6348
+ lapStartedAt: z.ZodNumber;
6349
+ }, z.core.$strict>>;
6346
6350
  rollingCursor: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
6347
6351
  state: z.ZodLiteral<"inLap">;
6348
6352
  afterKey: z.ZodString;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/registry.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiN7B,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqQkB,CAAC;AAElD,MAAM,MAAM,wBAAwB,GAAG,MAAM,OAAO,kBAAkB,CAAC;AAEvE;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;CAmBiC,CAAC;AAEvE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAOvC,CAAC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/registry.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiN7B,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqQkB,CAAC;AAElD,MAAM,MAAM,wBAAwB,GAAG,MAAM,OAAO,kBAAkB,CAAC;AAEvE;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;CAmBiC,CAAC;AAEvE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAOvC,CAAC"}
@@ -1,15 +1,19 @@
1
1
  import type { FileOrigin } from './file-origin.js';
2
2
  export declare const fileOriginRowLabel: Record<FileOrigin, string>;
3
- export declare const DEFAULT_RELATIVE_TIME_CUTOFF_MS: number;
4
- /**
5
- * Format an upload's createdAt timestamp for display in a tray row.
6
- *
7
- * - Within `cutoffMs` of now: relative ("just now", "Xm ago", "Xh ago").
8
- * - Older: absolute local time. Today: "3:42 PM". Yesterday: "Yesterday 3:42 PM".
9
- * Older: short date + time, e.g. "May 21, 3:42 PM".
10
- *
11
- * Both `createdAtMs` and `nowMs` are millisecond epochs.
12
- */
13
- export declare function formatUploadTimestamp(createdAtMs: number, nowMs: number, cutoffMs?: number): string;
14
- export declare function getFileTypeLabel(contentType: string | undefined): string | null;
3
+ /** How long an upload row's time reads as relative ("5m ago") before it reads as a clock time. */
4
+ export declare const UPLOAD_ROW_RELATIVE_TIME_CUTOFF_MS: number;
5
+ /** An upload row's words for its time and its file's kind. */
6
+ export declare const UPLOAD_ROW_DISPLAY_COPY: {
7
+ readonly justNow: "just now";
8
+ readonly minutesAgo: (minutes: number) => string;
9
+ readonly hoursAgo: (hours: number) => string;
10
+ readonly yesterdayAt: (time: string) => string;
11
+ readonly dateAt: (date: string, time: string) => string;
12
+ readonly fileType: {
13
+ readonly image: "Image";
14
+ readonly video: "Video";
15
+ readonly audio: "Audio";
16
+ readonly other: "File";
17
+ };
18
+ };
15
19
  //# sourceMappingURL=upload-tray-display.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"upload-tray-display.d.ts","sourceRoot":"","sources":["../../src/media/upload-tray-display.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAUnD,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAyBzD,CAAC;AAUF,eAAO,MAAM,+BAA+B,QAAqB,CAAC;AAElE;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,MAAwC,GACjD,MAAM,CAuCR;AAUD,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAY/E"}
1
+ {"version":3,"file":"upload-tray-display.d.ts","sourceRoot":"","sources":["../../src/media/upload-tray-display.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AASnD,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAyBzD,CAAC;AAQF,kGAAkG;AAClG,eAAO,MAAM,kCAAkC,QAAqB,CAAC;AAErE,8DAA8D;AAC9D,eAAO,MAAM,uBAAuB;;mCAEZ,MAAM;+BACV,MAAM;iCACJ,MAAM;4BACX,MAAM,QAAQ,MAAM;;;;;;;CAE3B,CAAC"}
@@ -1,4 +1,3 @@
1
- import { getSimplifiedMediaType } from '@ttt-productions/media-schemas';
2
1
  // =============================================================================
3
2
  // User-facing labels per fileOrigin
4
3
  // =============================================================================
@@ -32,77 +31,19 @@ export const fileOriginRowLabel = {
32
31
  'ncii-evidence': 'Take-it-down evidence',
33
32
  };
34
33
  // =============================================================================
35
- // Timestamp formatting
34
+ // Row time and file kind
36
35
  // =============================================================================
37
36
  //
38
- // Cutoff after which the upload row stops showing relative time
39
- // ("2 minutes ago") and switches to absolute local time ("3:42 PM").
40
- // Default: 6 hours.
41
- export const DEFAULT_RELATIVE_TIME_CUTOFF_MS = 6 * 60 * 60 * 1000;
42
- /**
43
- * Format an upload's createdAt timestamp for display in a tray row.
44
- *
45
- * - Within `cutoffMs` of now: relative ("just now", "Xm ago", "Xh ago").
46
- * - Older: absolute local time. Today: "3:42 PM". Yesterday: "Yesterday 3:42 PM".
47
- * Older: short date + time, e.g. "May 21, 3:42 PM".
48
- *
49
- * Both `createdAtMs` and `nowMs` are millisecond epochs.
50
- */
51
- export function formatUploadTimestamp(createdAtMs, nowMs, cutoffMs = DEFAULT_RELATIVE_TIME_CUTOFF_MS) {
52
- const diffMs = Math.max(0, nowMs - createdAtMs);
53
- if (diffMs < cutoffMs) {
54
- // Relative path.
55
- if (diffMs < 60_000)
56
- return 'just now';
57
- const minutes = Math.floor(diffMs / 60_000);
58
- if (minutes < 60)
59
- return `${minutes}m ago`;
60
- const hours = Math.floor(diffMs / 3_600_000);
61
- return `${hours}h ago`;
62
- }
63
- // Absolute path.
64
- const created = new Date(createdAtMs);
65
- const now = new Date(nowMs);
66
- const timeStr = created.toLocaleTimeString(undefined, {
67
- hour: 'numeric',
68
- minute: '2-digit',
69
- });
70
- const sameDay = created.getFullYear() === now.getFullYear() &&
71
- created.getMonth() === now.getMonth() &&
72
- created.getDate() === now.getDate();
73
- if (sameDay)
74
- return timeStr;
75
- const yesterday = new Date(nowMs - 24 * 60 * 60 * 1000);
76
- const isYesterday = created.getFullYear() === yesterday.getFullYear() &&
77
- created.getMonth() === yesterday.getMonth() &&
78
- created.getDate() === yesterday.getDate();
79
- if (isYesterday)
80
- return `Yesterday ${timeStr}`;
81
- const dateStr = created.toLocaleDateString(undefined, {
82
- month: 'short',
83
- day: 'numeric',
84
- });
85
- return `${dateStr}, ${timeStr}`;
86
- }
87
- // =============================================================================
88
- // File type labeling
89
- // =============================================================================
90
- //
91
- // Friendly label for a content type string. Wraps getSimplifiedMediaType
92
- // from media-schemas with capitalization for tray display.
93
- // Returns null when contentType is undefined/empty.
94
- export function getFileTypeLabel(contentType) {
95
- if (!contentType)
96
- return null;
97
- switch (getSimplifiedMediaType(contentType)) {
98
- case 'image':
99
- return 'Image';
100
- case 'video':
101
- return 'Video';
102
- case 'audio':
103
- return 'Audio';
104
- default:
105
- return 'File';
106
- }
107
- }
37
+ // The words and cutoff the app hands upload-ui's `createUploadDisplayFormatters`.
38
+ /** How long an upload row's time reads as relative ("5m ago") before it reads as a clock time. */
39
+ export const UPLOAD_ROW_RELATIVE_TIME_CUTOFF_MS = 6 * 60 * 60 * 1000;
40
+ /** An upload row's words for its time and its file's kind. */
41
+ export const UPLOAD_ROW_DISPLAY_COPY = {
42
+ justNow: 'just now',
43
+ minutesAgo: (minutes) => `${minutes}m ago`,
44
+ hoursAgo: (hours) => `${hours}h ago`,
45
+ yesterdayAt: (time) => `Yesterday ${time}`,
46
+ dateAt: (date, time) => `${date}, ${time}`,
47
+ fileType: { image: 'Image', video: 'Video', audio: 'Audio', other: 'File' },
48
+ };
108
49
  //# sourceMappingURL=upload-tray-display.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"upload-tray-display.js","sourceRoot":"","sources":["../../src/media/upload-tray-display.ts"],"names":[],"mappings":"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","sourcesContent":["import type { FileOrigin } from './file-origin.js';\nimport { getSimplifiedMediaType } from '@ttt-productions/media-schemas';\n\n// =============================================================================\n// User-facing labels per fileOrigin\n// =============================================================================\n//\n// Generic labels for upload tray rows. Same regardless of platform (web,\n// iOS, Android, smart TV) since they describe TTT domain entities.\n\nexport const fileOriginRowLabel: Record<FileOrigin, string> = {\n 'profile-picture': 'Profile picture',\n 'craft-skill-media': 'Craft',\n 'squareStreetz': 'Square post',\n 'commission-posting': 'Commission posting',\n 'commission-proposal': 'Commission proposal',\n 'audition-prompt': 'Audition',\n // Neutral on purpose: this origin creates BOTH Platform and Sponsored auditions\n // (AdminAuditionPromptTargetInfoSchema.type), so it cannot borrow either settled\n // AUDITION_TYPES label without lying about half its uploads.\n 'admin-audition-prompt': 'Admin audition',\n 'audition-entry': 'Audition entry',\n 'hallLibrary-cover-square': 'Work cover',\n 'hallLibrary-cover-poster': 'Work cover',\n 'hallLibrary-cover-cinematic': 'Work cover',\n 'realm-cover': 'Realm cover',\n 'chapter-photo': 'Chapter photo',\n 'tune-track-photo': 'Track photo',\n 'tune-track-audio': 'Track audio',\n 'television-episode-photo': 'Episode photo',\n 'television-episode-video': 'Episode video',\n 'work-asset': 'Work asset',\n 'conversation-file': 'Conversation file',\n 'system-content': 'System video',\n 'ncii-evidence': 'Take-it-down evidence',\n};\n\n// =============================================================================\n// Timestamp formatting\n// =============================================================================\n//\n// Cutoff after which the upload row stops showing relative time\n// (\"2 minutes ago\") and switches to absolute local time (\"3:42 PM\").\n// Default: 6 hours.\n\nexport const DEFAULT_RELATIVE_TIME_CUTOFF_MS = 6 * 60 * 60 * 1000;\n\n/**\n * Format an upload's createdAt timestamp for display in a tray row.\n *\n * - Within `cutoffMs` of now: relative (\"just now\", \"Xm ago\", \"Xh ago\").\n * - Older: absolute local time. Today: \"3:42 PM\". Yesterday: \"Yesterday 3:42 PM\".\n * Older: short date + time, e.g. \"May 21, 3:42 PM\".\n *\n * Both `createdAtMs` and `nowMs` are millisecond epochs.\n */\nexport function formatUploadTimestamp(\n createdAtMs: number,\n nowMs: number,\n cutoffMs: number = DEFAULT_RELATIVE_TIME_CUTOFF_MS,\n): string {\n const diffMs = Math.max(0, nowMs - createdAtMs);\n\n if (diffMs < cutoffMs) {\n // Relative path.\n if (diffMs < 60_000) return 'just now';\n const minutes = Math.floor(diffMs / 60_000);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(diffMs / 3_600_000);\n return `${hours}h ago`;\n }\n\n // Absolute path.\n const created = new Date(createdAtMs);\n const now = new Date(nowMs);\n\n const timeStr = created.toLocaleTimeString(undefined, {\n hour: 'numeric',\n minute: '2-digit',\n });\n\n const sameDay =\n created.getFullYear() === now.getFullYear() &&\n created.getMonth() === now.getMonth() &&\n created.getDate() === now.getDate();\n if (sameDay) return timeStr;\n\n const yesterday = new Date(nowMs - 24 * 60 * 60 * 1000);\n const isYesterday =\n created.getFullYear() === yesterday.getFullYear() &&\n created.getMonth() === yesterday.getMonth() &&\n created.getDate() === yesterday.getDate();\n if (isYesterday) return `Yesterday ${timeStr}`;\n\n const dateStr = created.toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n });\n return `${dateStr}, ${timeStr}`;\n}\n\n// =============================================================================\n// File type labeling\n// =============================================================================\n//\n// Friendly label for a content type string. Wraps getSimplifiedMediaType\n// from media-schemas with capitalization for tray display.\n// Returns null when contentType is undefined/empty.\n\nexport function getFileTypeLabel(contentType: string | undefined): string | null {\n if (!contentType) return null;\n switch (getSimplifiedMediaType(contentType)) {\n case 'image':\n return 'Image';\n case 'video':\n return 'Video';\n case 'audio':\n return 'Audio';\n default:\n return 'File';\n }\n}\n\n"]}
1
+ {"version":3,"file":"upload-tray-display.js","sourceRoot":"","sources":["../../src/media/upload-tray-display.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,oCAAoC;AACpC,gFAAgF;AAChF,EAAE;AACF,yEAAyE;AACzE,mEAAmE;AAEnE,MAAM,CAAC,MAAM,kBAAkB,GAA+B;IAC5D,iBAAiB,EAAE,iBAAiB;IACpC,mBAAmB,EAAE,OAAO;IAC5B,eAAe,EAAE,aAAa;IAC9B,oBAAoB,EAAE,oBAAoB;IAC1C,qBAAqB,EAAE,qBAAqB;IAC5C,iBAAiB,EAAE,UAAU;IAC7B,gFAAgF;IAChF,iFAAiF;IACjF,6DAA6D;IAC7D,uBAAuB,EAAE,gBAAgB;IACzC,gBAAgB,EAAE,gBAAgB;IAClC,0BAA0B,EAAE,YAAY;IACxC,0BAA0B,EAAE,YAAY;IACxC,6BAA6B,EAAE,YAAY;IAC3C,aAAa,EAAE,aAAa;IAC5B,eAAe,EAAE,eAAe;IAChC,kBAAkB,EAAE,aAAa;IACjC,kBAAkB,EAAE,aAAa;IACjC,0BAA0B,EAAE,eAAe;IAC3C,0BAA0B,EAAE,eAAe;IAC3C,YAAY,EAAE,YAAY;IAC1B,mBAAmB,EAAE,mBAAmB;IACxC,gBAAgB,EAAE,cAAc;IAChC,eAAe,EAAE,uBAAuB;CACzC,CAAC;AAEF,gFAAgF;AAChF,yBAAyB;AACzB,gFAAgF;AAChF,EAAE;AACF,kFAAkF;AAElF,kGAAkG;AAClG,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAErE,8DAA8D;AAC9D,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,OAAO,EAAE,UAAU;IACnB,UAAU,EAAE,CAAC,OAAe,EAAE,EAAE,CAAC,GAAG,OAAO,OAAO;IAClD,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,GAAG,KAAK,OAAO;IAC5C,WAAW,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,aAAa,IAAI,EAAE;IAClD,MAAM,EAAE,CAAC,IAAY,EAAE,IAAY,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,EAAE;IAC1D,QAAQ,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE;CACnE,CAAC","sourcesContent":["import type { FileOrigin } from './file-origin.js';\n\n// =============================================================================\n// User-facing labels per fileOrigin\n// =============================================================================\n//\n// Generic labels for upload tray rows. Same regardless of platform (web,\n// iOS, Android, smart TV) since they describe TTT domain entities.\n\nexport const fileOriginRowLabel: Record<FileOrigin, string> = {\n 'profile-picture': 'Profile picture',\n 'craft-skill-media': 'Craft',\n 'squareStreetz': 'Square post',\n 'commission-posting': 'Commission posting',\n 'commission-proposal': 'Commission proposal',\n 'audition-prompt': 'Audition',\n // Neutral on purpose: this origin creates BOTH Platform and Sponsored auditions\n // (AdminAuditionPromptTargetInfoSchema.type), so it cannot borrow either settled\n // AUDITION_TYPES label without lying about half its uploads.\n 'admin-audition-prompt': 'Admin audition',\n 'audition-entry': 'Audition entry',\n 'hallLibrary-cover-square': 'Work cover',\n 'hallLibrary-cover-poster': 'Work cover',\n 'hallLibrary-cover-cinematic': 'Work cover',\n 'realm-cover': 'Realm cover',\n 'chapter-photo': 'Chapter photo',\n 'tune-track-photo': 'Track photo',\n 'tune-track-audio': 'Track audio',\n 'television-episode-photo': 'Episode photo',\n 'television-episode-video': 'Episode video',\n 'work-asset': 'Work asset',\n 'conversation-file': 'Conversation file',\n 'system-content': 'System video',\n 'ncii-evidence': 'Take-it-down evidence',\n};\n\n// =============================================================================\n// Row time and file kind\n// =============================================================================\n//\n// The words and cutoff the app hands upload-ui's `createUploadDisplayFormatters`.\n\n/** How long an upload row's time reads as relative (\"5m ago\") before it reads as a clock time. */\nexport const UPLOAD_ROW_RELATIVE_TIME_CUTOFF_MS = 6 * 60 * 60 * 1000;\n\n/** An upload row's words for its time and its file's kind. */\nexport const UPLOAD_ROW_DISPLAY_COPY = {\n justNow: 'just now',\n minutesAgo: (minutes: number) => `${minutes}m ago`,\n hoursAgo: (hours: number) => `${hours}h ago`,\n yesterdayAt: (time: string) => `Yesterday ${time}`,\n dateAt: (date: string, time: string) => `${date}, ${time}`,\n fileType: { image: 'Image', video: 'Video', audio: 'Audio', other: 'File' },\n} as const;\n"]}
@@ -1,6 +1,109 @@
1
1
  import { HALL_CONTENT_TEXT_FIELDS } from '../constants/business-content.js';
2
2
  import type { WorkProjectType } from '../types/content.js';
3
- import type { FullChapter, FullTelevisionEpisode, FullTuneTrack } from '../doc-schemas/content.js';
3
+ import { type FullChapter, type FullTelevisionEpisode, type FullTuneTrack } from '../doc-schemas/content.js';
4
+ /**
5
+ * Each work type's published Hall sub-item: the subcollection it is stored in and the schema it is
6
+ * stored as, declared together. A writer looks its work type up once and takes both from the one
7
+ * entry, so one kind's shape can never be stored in another kind's subcollection.
8
+ */
9
+ export declare const PUBLISHED_HALL_SUB_ITEM_BY_WORK_TYPE: {
10
+ readonly Tales: {
11
+ readonly subcollection: import("../paths/collections.js").HallItemSubcollection;
12
+ readonly schema: import("zod").ZodObject<{
13
+ uid: import("zod").ZodString;
14
+ title: import("zod").ZodString;
15
+ order: import("zod").ZodNumber;
16
+ description: import("zod").ZodOptional<import("zod").ZodString>;
17
+ content: import("zod").ZodString;
18
+ photoAssetId: import("zod").ZodString;
19
+ hidden: import("zod").ZodBoolean;
20
+ hiddenBy: import("zod").ZodOptional<import("zod").ZodEnum<{
21
+ direct: "direct";
22
+ cascade: "cascade";
23
+ }>>;
24
+ moderationClearedFields: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
25
+ moderationClearedReason: import("zod").ZodOptional<import("zod").ZodString>;
26
+ moderatedAt: import("zod").ZodOptional<import("zod").ZodNumber>;
27
+ edgeSyncState: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodEnum<{
28
+ failed: "failed";
29
+ processing: "processing";
30
+ }>>>;
31
+ edgeSyncProcessingAt: import("zod").ZodOptional<import("zod").ZodNumber>;
32
+ edgeSyncOp: import("zod").ZodOptional<import("zod").ZodEnum<{
33
+ block: "block";
34
+ blockClear: "blockClear";
35
+ }>>;
36
+ edgeSyncDecision: import("zod").ZodOptional<import("zod").ZodNumber>;
37
+ edgeSyncAssetIds: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
38
+ edgeSyncError: import("zod").ZodOptional<import("zod").ZodString>;
39
+ edgeSyncFailedAt: import("zod").ZodOptional<import("zod").ZodNumber>;
40
+ }, import("zod/v4/core").$strip>;
41
+ };
42
+ readonly Tunes: {
43
+ readonly subcollection: import("../paths/collections.js").HallItemSubcollection;
44
+ readonly schema: import("zod").ZodObject<{
45
+ uid: import("zod").ZodString;
46
+ title: import("zod").ZodString;
47
+ order: import("zod").ZodNumber;
48
+ description: import("zod").ZodOptional<import("zod").ZodString>;
49
+ audioAssetId: import("zod").ZodString;
50
+ photoAssetId: import("zod").ZodString;
51
+ hidden: import("zod").ZodBoolean;
52
+ hiddenBy: import("zod").ZodOptional<import("zod").ZodEnum<{
53
+ direct: "direct";
54
+ cascade: "cascade";
55
+ }>>;
56
+ moderationClearedFields: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
57
+ moderationClearedReason: import("zod").ZodOptional<import("zod").ZodString>;
58
+ moderatedAt: import("zod").ZodOptional<import("zod").ZodNumber>;
59
+ edgeSyncState: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodEnum<{
60
+ failed: "failed";
61
+ processing: "processing";
62
+ }>>>;
63
+ edgeSyncProcessingAt: import("zod").ZodOptional<import("zod").ZodNumber>;
64
+ edgeSyncOp: import("zod").ZodOptional<import("zod").ZodEnum<{
65
+ block: "block";
66
+ blockClear: "blockClear";
67
+ }>>;
68
+ edgeSyncDecision: import("zod").ZodOptional<import("zod").ZodNumber>;
69
+ edgeSyncAssetIds: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
70
+ edgeSyncError: import("zod").ZodOptional<import("zod").ZodString>;
71
+ edgeSyncFailedAt: import("zod").ZodOptional<import("zod").ZodNumber>;
72
+ }, import("zod/v4/core").$strip>;
73
+ };
74
+ readonly Television: {
75
+ readonly subcollection: import("../paths/collections.js").HallItemSubcollection;
76
+ readonly schema: import("zod").ZodObject<{
77
+ uid: import("zod").ZodString;
78
+ title: import("zod").ZodString;
79
+ order: import("zod").ZodNumber;
80
+ description: import("zod").ZodOptional<import("zod").ZodString>;
81
+ videoAssetId: import("zod").ZodString;
82
+ photoAssetId: import("zod").ZodString;
83
+ hidden: import("zod").ZodBoolean;
84
+ hiddenBy: import("zod").ZodOptional<import("zod").ZodEnum<{
85
+ direct: "direct";
86
+ cascade: "cascade";
87
+ }>>;
88
+ moderationClearedFields: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
89
+ moderationClearedReason: import("zod").ZodOptional<import("zod").ZodString>;
90
+ moderatedAt: import("zod").ZodOptional<import("zod").ZodNumber>;
91
+ edgeSyncState: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodEnum<{
92
+ failed: "failed";
93
+ processing: "processing";
94
+ }>>>;
95
+ edgeSyncProcessingAt: import("zod").ZodOptional<import("zod").ZodNumber>;
96
+ edgeSyncOp: import("zod").ZodOptional<import("zod").ZodEnum<{
97
+ block: "block";
98
+ blockClear: "blockClear";
99
+ }>>;
100
+ edgeSyncDecision: import("zod").ZodOptional<import("zod").ZodNumber>;
101
+ edgeSyncAssetIds: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
102
+ edgeSyncError: import("zod").ZodOptional<import("zod").ZodString>;
103
+ edgeSyncFailedAt: import("zod").ZodOptional<import("zod").ZodNumber>;
104
+ }, import("zod/v4/core").$strip>;
105
+ };
106
+ };
4
107
  /** A published hall-content surface whose text a change request or the moderation
5
108
  * text-clear remedy can target. */
6
109
  export type HallContentTextFieldSurface = keyof typeof HALL_CONTENT_TEXT_FIELDS;
@@ -1 +1 @@
1
- {"version":3,"file":"hall-content.d.ts","sourceRoot":"","sources":["../../src/utils/hall-content.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,wBAAwB,EAGzB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAEnG;oCACoC;AACpC,MAAM,MAAM,2BAA2B,GAAG,MAAM,OAAO,wBAAwB,CAAC;AAEhF,0FAA0F;AAC1F,MAAM,MAAM,wBAAwB,CAAC,CAAC,SAAS,2BAA2B,IACxE,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAExE,MAAM,MAAM,+BAA+B,CAAC,CAAC,SAAS,2BAA2B,IAC7E;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAC,CAAA;CAAE,GACnE;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,CAAC,CAAC,SAAS,2BAA2B,EACjF,OAAO,EAAE,CAAC,EACV,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAChD,+BAA+B,CAAC,CAAC,CAAC,CAkBpC;AAED;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAC1C,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACzC,MAAM,EAAE,CAQV;AAED,4EAA4E;AAC5E,wBAAgB,wBAAwB,CACtC,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACzC,OAAO,CAET;AAED,+FAA+F;AAC/F,MAAM,MAAM,iBAAiB,GAAG,CAAC,WAAW,GAAG,aAAa,GAAG,qBAAqB,CAAC,CAAC,QAAQ,CAAC,CAAC;AAEhG;;uBAEuB;AACvB,eAAO,MAAM,6BAA6B,4CAAoF,CAAC;AAE/H;kBACkB;AAClB,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED;0CAC0C;AAC1C,wBAAgB,iCAAiC,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAEvG;AAED;aACa;AACb,wBAAgB,+BAA+B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3E"}
1
+ {"version":3,"file":"hall-content.d.ts","sourceRoot":"","sources":["../../src/utils/hall-content.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,wBAAwB,EAGzB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EACnB,MAAM,2BAA2B,CAAC;AAGnC;;;;GAIG;AACH,eAAO,MAAM,oCAAoC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAIuC,CAAC;AAEzF;oCACoC;AACpC,MAAM,MAAM,2BAA2B,GAAG,MAAM,OAAO,wBAAwB,CAAC;AAEhF,0FAA0F;AAC1F,MAAM,MAAM,wBAAwB,CAAC,CAAC,SAAS,2BAA2B,IACxE,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAExE,MAAM,MAAM,+BAA+B,CAAC,CAAC,SAAS,2BAA2B,IAC7E;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAC,CAAA;CAAE,GACnE;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,CAAC,CAAC,SAAS,2BAA2B,EACjF,OAAO,EAAE,CAAC,EACV,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAChD,+BAA+B,CAAC,CAAC,CAAC,CAkBpC;AAED;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAC1C,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACzC,MAAM,EAAE,CAQV;AAED,4EAA4E;AAC5E,wBAAgB,wBAAwB,CACtC,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACzC,OAAO,CAET;AAED,+FAA+F;AAC/F,MAAM,MAAM,iBAAiB,GAAG,CAAC,WAAW,GAAG,aAAa,GAAG,qBAAqB,CAAC,CAAC,QAAQ,CAAC,CAAC;AAEhG;;uBAEuB;AACvB,eAAO,MAAM,6BAA6B,4CAAoF,CAAC;AAE/H;kBACkB;AAClB,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED;0CAC0C;AAC1C,wBAAgB,iCAAiC,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAEvG;AAED;aACa;AACb,wBAAgB,+BAA+B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3E"}
@@ -1,8 +1,20 @@
1
1
  // Pure hall-content rules shared by the member surfaces and the backend cores that own the
2
- // writes. Server-safe: constants and plain functions only.
2
+ // writes. Server-safe: constants, canonical schemas, and plain functions only.
3
3
  import { checkTextField } from './text-field.js';
4
4
  import { HALL_CONTENT_TEXT_FIELDS, HALL_SUB_ITEM_REQUIRED_FIELDS_BY_WORK_TYPE, HALL_SUB_ITEM_REQUIREMENT_LABELS, } from '../constants/business-content.js';
5
5
  import { HALL_CONTENT_CHANGE_REQUEST_INPUTS } from '../constants/text-fields.js';
6
+ import { PublishedChapterSchema, PublishedTelevisionEpisodeSchema, PublishedTuneTrackSchema, } from '../doc-schemas/content.js';
7
+ import { HALL_ITEM_SUBCOLLECTION_BY_WORK_TYPE } from '../paths/collections.js';
8
+ /**
9
+ * Each work type's published Hall sub-item: the subcollection it is stored in and the schema it is
10
+ * stored as, declared together. A writer looks its work type up once and takes both from the one
11
+ * entry, so one kind's shape can never be stored in another kind's subcollection.
12
+ */
13
+ export const PUBLISHED_HALL_SUB_ITEM_BY_WORK_TYPE = {
14
+ Tales: { subcollection: HALL_ITEM_SUBCOLLECTION_BY_WORK_TYPE.Tales, schema: PublishedChapterSchema },
15
+ Tunes: { subcollection: HALL_ITEM_SUBCOLLECTION_BY_WORK_TYPE.Tunes, schema: PublishedTuneTrackSchema },
16
+ Television: { subcollection: HALL_ITEM_SUBCOLLECTION_BY_WORK_TYPE.Television, schema: PublishedTelevisionEpisodeSchema },
17
+ };
6
18
  /**
7
19
  * The ONE strict per-surface check for a proposed hall-content text map: every key must be a
8
20
  * field that surface owns, and every value must meet that field's change-request declaration
@@ -1 +1 @@
1
- {"version":3,"file":"hall-content.js","sourceRoot":"","sources":["../../src/utils/hall-content.ts"],"names":[],"mappings":"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","sourcesContent":["// Pure hall-content rules shared by the member surfaces and the backend cores that own the\n// writes. Server-safe: constants and plain functions only.\n\nimport { checkTextField, type TextFieldDeclaration } from './text-field.js';\nimport {\n HALL_CONTENT_TEXT_FIELDS,\n HALL_SUB_ITEM_REQUIRED_FIELDS_BY_WORK_TYPE,\n HALL_SUB_ITEM_REQUIREMENT_LABELS,\n} from '../constants/business-content.js';\nimport { HALL_CONTENT_CHANGE_REQUEST_INPUTS } from '../constants/text-fields.js';\nimport type { WorkProjectType } from '../types/content.js';\nimport type { FullChapter, FullTelevisionEpisode, FullTuneTrack } from '../doc-schemas/content.js';\n\n/** A published hall-content surface whose text a change request or the moderation\n * text-clear remedy can target. */\nexport type HallContentTextFieldSurface = keyof typeof HALL_CONTENT_TEXT_FIELDS;\n\n/** The proposed-text map a given surface accepts: only that surface's own field names. */\nexport type HallContentTextFieldsFor<S extends HallContentTextFieldSurface> =\n Partial<Record<(typeof HALL_CONTENT_TEXT_FIELDS)[S][number], string>>;\n\nexport type HallContentTextFieldsValidation<S extends HallContentTextFieldSurface> =\n | { readonly ok: true; readonly fields: HallContentTextFieldsFor<S> }\n | { readonly ok: false; readonly reason: string };\n\n/**\n * The ONE strict per-surface check for a proposed hall-content text map: every key must be a\n * field that surface owns, and every value must meet that field's change-request declaration\n * (`HALL_CONTENT_CHANGE_REQUEST_INPUTS` — trimmed, never empty, within its max, in its format). The\n * stored `hallContentChangeRequests` shape is a flat field map with the request's own `surface`\n * as the single authoritative discriminator, so this is where the allowlist is enforced — at the\n * backend boundary, before the map is persisted or written onto a published doc.\n *\n * The allowlist comes from the canonical Hall text-field map and each rule from the field's\n * declaration; there is no second allowlist anywhere.\n *\n * @returns the normalized (trimmed) field map, or the user-facing reason it was rejected.\n */\nexport function validateHallContentTextFields<S extends HallContentTextFieldSurface>(\n surface: S,\n proposedFields: Readonly<Record<string, unknown>>,\n): HallContentTextFieldsValidation<S> {\n const allowed = HALL_CONTENT_TEXT_FIELDS[surface] as readonly string[];\n const normalized: Record<string, string> = {};\n\n for (const [field, rawValue] of Object.entries(proposedFields)) {\n if (!allowed.includes(field)) {\n return { ok: false, reason: `Field \"${field}\" is not editable on this item.` };\n }\n const declaration = (HALL_CONTENT_CHANGE_REQUEST_INPUTS[surface] as Record<string, TextFieldDeclaration>)[field];\n const result = checkTextField(declaration, typeof rawValue === 'string' ? rawValue : '');\n if (!result.ok) return { ok: false, reason: result.reason };\n normalized[field] = result.value;\n }\n\n if (Object.keys(normalized).length === 0) {\n return { ok: false, reason: 'No valid text fields proposed.' };\n }\n return { ok: true, fields: normalized as HallContentTextFieldsFor<S> };\n}\n\n/**\n * The requirements a Tale chapter / Tune track / Television episode has not met yet, as the\n * wording a surface can show, in the canonical field order. Empty means the sub-item may be\n * submitted, approved, and published.\n *\n * ONE owner for the member-side eligibility filter and all three backend cores — none of them\n * restates the per-type branch.\n */\nexport function unmetHallSubItemRequirements(\n workProjectType: WorkProjectType,\n subItem: Readonly<Record<string, unknown>>,\n): string[] {\n const required = HALL_SUB_ITEM_REQUIRED_FIELDS_BY_WORK_TYPE[workProjectType];\n return required\n .filter((field) => {\n const value = subItem[field];\n return typeof value !== 'string' || value.trim().length === 0;\n })\n .map((field) => HALL_SUB_ITEM_REQUIREMENT_LABELS[field]);\n}\n\n/** True when the sub-item meets every requirement its work type imposes. */\nexport function isHallSubItemPublishable(\n workProjectType: WorkProjectType,\n subItem: Readonly<Record<string, unknown>>,\n): boolean {\n return unmetHallSubItemRequirements(workProjectType, subItem).length === 0;\n}\n\n/** The review status every chapter / track / episode carries (one set for the three kinds). */\nexport type HallSubItemStatus = (FullChapter | FullTuneTrack | FullTelevisionEpisode)['status'];\n\n/** The statuses that lock a sub-item against edits and media attaches: submitted for library\n * review, or published to the Hall. Text changes to a published item go through a change\n * request instead. */\nexport const HALL_SUB_ITEM_LOCKED_STATUSES = ['pending_approval', 'published'] as const satisfies readonly HallSubItemStatus[];\n\n/** True when a sub-item in this status is locked; any other value, a missing status included, is\n * not a lock. */\nexport function isHallSubItemLocked(status: unknown): boolean {\n return (HALL_SUB_ITEM_LOCKED_STATUSES as readonly unknown[]).includes(status);\n}\n\n/** The `targetKey` a Hall-grain change request is stored and queried under: one open request per\n * Hall item's detail or per sub-item. */\nexport function hallContentChangeRequestTargetKey(hallItemId: string, subItemId?: string | null): string {\n return `${hallItemId}_${subItemId ?? 'detail'}`;\n}\n\n/** The `targetKey` a Realm-grain change request is stored and queried under: one open request per\n * Realm. */\nexport function workRealmChangeRequestTargetKey(workRealmId: string): string {\n return `realm_${workRealmId}`;\n}\n"]}
1
+ {"version":3,"file":"hall-content.js","sourceRoot":"","sources":["../../src/utils/hall-content.ts"],"names":[],"mappings":"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","sourcesContent":["// Pure hall-content rules shared by the member surfaces and the backend cores that own the\n// writes. Server-safe: constants, canonical schemas, and plain functions only.\n\nimport { checkTextField, type TextFieldDeclaration } from './text-field.js';\nimport {\n HALL_CONTENT_TEXT_FIELDS,\n HALL_SUB_ITEM_REQUIRED_FIELDS_BY_WORK_TYPE,\n HALL_SUB_ITEM_REQUIREMENT_LABELS,\n} from '../constants/business-content.js';\nimport { HALL_CONTENT_CHANGE_REQUEST_INPUTS } from '../constants/text-fields.js';\nimport type { WorkProjectType } from '../types/content.js';\nimport {\n PublishedChapterSchema,\n PublishedTelevisionEpisodeSchema,\n PublishedTuneTrackSchema,\n type FullChapter,\n type FullTelevisionEpisode,\n type FullTuneTrack,\n} from '../doc-schemas/content.js';\nimport { HALL_ITEM_SUBCOLLECTION_BY_WORK_TYPE } from '../paths/collections.js';\n\n/**\n * Each work type's published Hall sub-item: the subcollection it is stored in and the schema it is\n * stored as, declared together. A writer looks its work type up once and takes both from the one\n * entry, so one kind's shape can never be stored in another kind's subcollection.\n */\nexport const PUBLISHED_HALL_SUB_ITEM_BY_WORK_TYPE = {\n Tales: { subcollection: HALL_ITEM_SUBCOLLECTION_BY_WORK_TYPE.Tales, schema: PublishedChapterSchema },\n Tunes: { subcollection: HALL_ITEM_SUBCOLLECTION_BY_WORK_TYPE.Tunes, schema: PublishedTuneTrackSchema },\n Television: { subcollection: HALL_ITEM_SUBCOLLECTION_BY_WORK_TYPE.Television, schema: PublishedTelevisionEpisodeSchema },\n} as const satisfies Record<WorkProjectType, { subcollection: string; schema: unknown }>;\n\n/** A published hall-content surface whose text a change request or the moderation\n * text-clear remedy can target. */\nexport type HallContentTextFieldSurface = keyof typeof HALL_CONTENT_TEXT_FIELDS;\n\n/** The proposed-text map a given surface accepts: only that surface's own field names. */\nexport type HallContentTextFieldsFor<S extends HallContentTextFieldSurface> =\n Partial<Record<(typeof HALL_CONTENT_TEXT_FIELDS)[S][number], string>>;\n\nexport type HallContentTextFieldsValidation<S extends HallContentTextFieldSurface> =\n | { readonly ok: true; readonly fields: HallContentTextFieldsFor<S> }\n | { readonly ok: false; readonly reason: string };\n\n/**\n * The ONE strict per-surface check for a proposed hall-content text map: every key must be a\n * field that surface owns, and every value must meet that field's change-request declaration\n * (`HALL_CONTENT_CHANGE_REQUEST_INPUTS` — trimmed, never empty, within its max, in its format). The\n * stored `hallContentChangeRequests` shape is a flat field map with the request's own `surface`\n * as the single authoritative discriminator, so this is where the allowlist is enforced — at the\n * backend boundary, before the map is persisted or written onto a published doc.\n *\n * The allowlist comes from the canonical Hall text-field map and each rule from the field's\n * declaration; there is no second allowlist anywhere.\n *\n * @returns the normalized (trimmed) field map, or the user-facing reason it was rejected.\n */\nexport function validateHallContentTextFields<S extends HallContentTextFieldSurface>(\n surface: S,\n proposedFields: Readonly<Record<string, unknown>>,\n): HallContentTextFieldsValidation<S> {\n const allowed = HALL_CONTENT_TEXT_FIELDS[surface] as readonly string[];\n const normalized: Record<string, string> = {};\n\n for (const [field, rawValue] of Object.entries(proposedFields)) {\n if (!allowed.includes(field)) {\n return { ok: false, reason: `Field \"${field}\" is not editable on this item.` };\n }\n const declaration = (HALL_CONTENT_CHANGE_REQUEST_INPUTS[surface] as Record<string, TextFieldDeclaration>)[field];\n const result = checkTextField(declaration, typeof rawValue === 'string' ? rawValue : '');\n if (!result.ok) return { ok: false, reason: result.reason };\n normalized[field] = result.value;\n }\n\n if (Object.keys(normalized).length === 0) {\n return { ok: false, reason: 'No valid text fields proposed.' };\n }\n return { ok: true, fields: normalized as HallContentTextFieldsFor<S> };\n}\n\n/**\n * The requirements a Tale chapter / Tune track / Television episode has not met yet, as the\n * wording a surface can show, in the canonical field order. Empty means the sub-item may be\n * submitted, approved, and published.\n *\n * ONE owner for the member-side eligibility filter and all three backend cores — none of them\n * restates the per-type branch.\n */\nexport function unmetHallSubItemRequirements(\n workProjectType: WorkProjectType,\n subItem: Readonly<Record<string, unknown>>,\n): string[] {\n const required = HALL_SUB_ITEM_REQUIRED_FIELDS_BY_WORK_TYPE[workProjectType];\n return required\n .filter((field) => {\n const value = subItem[field];\n return typeof value !== 'string' || value.trim().length === 0;\n })\n .map((field) => HALL_SUB_ITEM_REQUIREMENT_LABELS[field]);\n}\n\n/** True when the sub-item meets every requirement its work type imposes. */\nexport function isHallSubItemPublishable(\n workProjectType: WorkProjectType,\n subItem: Readonly<Record<string, unknown>>,\n): boolean {\n return unmetHallSubItemRequirements(workProjectType, subItem).length === 0;\n}\n\n/** The review status every chapter / track / episode carries (one set for the three kinds). */\nexport type HallSubItemStatus = (FullChapter | FullTuneTrack | FullTelevisionEpisode)['status'];\n\n/** The statuses that lock a sub-item against edits and media attaches: submitted for library\n * review, or published to the Hall. Text changes to a published item go through a change\n * request instead. */\nexport const HALL_SUB_ITEM_LOCKED_STATUSES = ['pending_approval', 'published'] as const satisfies readonly HallSubItemStatus[];\n\n/** True when a sub-item in this status is locked; any other value, a missing status included, is\n * not a lock. */\nexport function isHallSubItemLocked(status: unknown): boolean {\n return (HALL_SUB_ITEM_LOCKED_STATUSES as readonly unknown[]).includes(status);\n}\n\n/** The `targetKey` a Hall-grain change request is stored and queried under: one open request per\n * Hall item's detail or per sub-item. */\nexport function hallContentChangeRequestTargetKey(hallItemId: string, subItemId?: string | null): string {\n return `${hallItemId}_${subItemId ?? 'detail'}`;\n}\n\n/** The `targetKey` a Realm-grain change request is stored and queried under: one open request per\n * Realm. */\nexport function workRealmChangeRequestTargetKey(workRealmId: string): string {\n return `realm_${workRealmId}`;\n}\n"]}
@@ -1,7 +1,6 @@
1
1
  export * from './admin-standing.js';
2
2
  export * from './age-derivation.js';
3
3
  export * from './app-config.js';
4
- export * from './format.js';
5
4
  export * from './guild-chat-access.js';
6
5
  export * from './guild-invite-system-message.js';
7
6
  export * from './hall-content.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,kCAAkC,CAAC;AACjD,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kCAAkC,CAAC;AACjD,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC"}
@@ -1,7 +1,6 @@
1
1
  export * from './admin-standing.js';
2
2
  export * from './age-derivation.js';
3
3
  export * from './app-config.js';
4
- export * from './format.js';
5
4
  export * from './guild-chat-access.js';
6
5
  export * from './guild-invite-system-message.js';
7
6
  export * from './hall-content.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,kCAAkC,CAAC;AACjD,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC","sourcesContent":["export * from './admin-standing.js';\nexport * from './age-derivation.js';\nexport * from './app-config.js';\nexport * from './format.js';\nexport * from './guild-chat-access.js';\nexport * from './guild-invite-system-message.js';\nexport * from './hall-content.js';\nexport * from './password.js';\nexport * from './text-field.js';\nexport * from './public-documents.js';\nexport * from './pledge-totals.js';\nexport * from './pledge-refund-eligibility.js';\nexport * from './work-file-folder.js';\nexport * from './work-realm-eligibility.js';\nexport * from './work-realm-name.js';\nexport * from './display-name-reservation.js';\nexport * from './work-publish-readiness.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kCAAkC,CAAC;AACjD,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC","sourcesContent":["export * from './admin-standing.js';\nexport * from './age-derivation.js';\nexport * from './app-config.js';\nexport * from './guild-chat-access.js';\nexport * from './guild-invite-system-message.js';\nexport * from './hall-content.js';\nexport * from './password.js';\nexport * from './text-field.js';\nexport * from './public-documents.js';\nexport * from './pledge-totals.js';\nexport * from './pledge-refund-eligibility.js';\nexport * from './work-file-folder.js';\nexport * from './work-realm-eligibility.js';\nexport * from './work-realm-name.js';\nexport * from './display-name-reservation.js';\nexport * from './work-publish-readiness.js';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/ttt-core",
3
- "version": "0.79.87",
3
+ "version": "0.79.89",
4
4
  "description": "Core types, Firestore path constants, and shared constants for TTT Productions",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,6 +0,0 @@
1
- /**
2
- * Format a byte count as a human-readable string: "24 KB", "1.2 MB", "850 B".
3
- * Returns null when bytes is undefined / non-finite / negative.
4
- */
5
- export declare function formatFileSize(bytes: number | undefined): string | null;
6
- //# sourceMappingURL=format.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAUvE"}
@@ -1,19 +0,0 @@
1
- /**
2
- * Format a byte count as a human-readable string: "24 KB", "1.2 MB", "850 B".
3
- * Returns null when bytes is undefined / non-finite / negative.
4
- */
5
- export function formatFileSize(bytes) {
6
- if (bytes === undefined || !Number.isFinite(bytes) || bytes < 0)
7
- return null;
8
- if (bytes < 1024)
9
- return `${bytes} B`;
10
- if (bytes < 1024 * 1024)
11
- return `${Math.round(bytes / 1024)} KB`;
12
- if (bytes < 1024 * 1024 * 1024) {
13
- const mb = bytes / (1024 * 1024);
14
- return `${mb < 10 ? mb.toFixed(1) : Math.round(mb)} MB`;
15
- }
16
- const gb = bytes / (1024 * 1024 * 1024);
17
- return `${gb < 10 ? gb.toFixed(1) : Math.round(gb)} GB`;
18
- }
19
- //# sourceMappingURL=format.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,KAAyB;IACtD,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7E,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,GAAG,KAAK,IAAI,CAAC;IACtC,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACjE,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;QAC/B,MAAM,EAAE,GAAG,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QACjC,OAAO,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC;IAC1D,CAAC;IACD,MAAM,EAAE,GAAG,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;IACxC,OAAO,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC;AAC1D,CAAC","sourcesContent":["/**\n * Format a byte count as a human-readable string: \"24 KB\", \"1.2 MB\", \"850 B\".\n * Returns null when bytes is undefined / non-finite / negative.\n */\nexport function formatFileSize(bytes: number | undefined): string | null {\n if (bytes === undefined || !Number.isFinite(bytes) || bytes < 0) return null;\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;\n if (bytes < 1024 * 1024 * 1024) {\n const mb = bytes / (1024 * 1024);\n return `${mb < 10 ? mb.toFixed(1) : Math.round(mb)} MB`;\n }\n const gb = bytes / (1024 * 1024 * 1024);\n return `${gb < 10 ? gb.toFixed(1) : Math.round(gb)} GB`;\n}\n"]}