@ttt-productions/ttt-core 0.79.43 → 0.79.45

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.
@@ -1,4 +1,5 @@
1
1
  import type { WorkProjectType } from '../types/content.js';
2
+ export { HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE, type HallContentDetailSurface, type HallContentSubItemSurface, } from './hall-content-routing.js';
2
3
  /** The maximum character length for a SquareStreetz post created on behalf of a workProject. */
3
4
  export declare const MAX_SQUARE_STREETZ_DESCRIPTION_LENGTH = 150;
4
5
  /** Maximum number of open commissions a workProject can have. Mode-varied. */
@@ -115,17 +116,17 @@ export interface HallContentSurfaceRouting {
115
116
  subItemFields: readonly ClearableTextFieldName[];
116
117
  }
117
118
  /**
118
- * CANONICAL WorkProjectType → hall text-surface routing. The ONE owner of "a Tale's sub-item is
119
- * a chapter, a Tune's is a tuneTrack, a Television's is a televisionEpisode" and of which fields
120
- * each of those surfaces exposes. Cross-boundary: the backend text-clear and change-request
119
+ * WorkProjectType → hall text-surface routing. Surface identities project from the lower-level
120
+ * `HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE` owner; this map adds the clearable field tuples each
121
+ * surface exposes. Cross-boundary: the backend text-clear and change-request
121
122
  * runners resolve `surface` from (workProjectType, is-sub-item?) through this map, and the admin
122
123
  * field pickers project their checkbox options from the same entry — three sites that each
123
124
  * hand-rolled the routing before (a ternary that fell through to the tuneTrack tuple for
124
125
  * Television, silently correct only because the two tuples happen to match today).
125
126
  *
126
- * Every field tuple is PROJECTED from MODERATION_CLEARABLE_TEXT_FIELDS above — no field literal
127
- * is restated here — and the `satisfies Record<WorkProjectType, …>` makes adding a work-project
128
- * type a compile error until its surfaces are routed.
127
+ * Every identity and field tuple is PROJECTED from its canonical owner — no surface or field
128
+ * literal is restated here — and the `satisfies Record<WorkProjectType, …>` makes adding a
129
+ * work-project type a compile error until its surfaces are routed.
129
130
  */
130
131
  export declare const HALL_CONTENT_SURFACES_BY_WORK_TYPE: {
131
132
  readonly Tales: {
@@ -1 +1 @@
1
- {"version":3,"file":"business-content.d.ts","sourceRoot":"","sources":["../../src/constants/business-content.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"business-content.d.ts","sourceRoot":"","sources":["../../src/constants/business-content.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,8 @@
3
3
  import { MAX_WORK_PROJECT_TITLE_LENGTH, MAX_WORK_PROJECT_DESCRIPTION_LENGTH, MAX_CHAPTER_CONTENT_LENGTH, MAX_WORK_REALM_TITLE_LENGTH, MAX_WORK_REALM_DESCRIPTION_LENGTH, } from "./business-work-project.js";
4
4
  import { ACTIVE_LIMITS } from './app-mode.js';
5
5
  import { HALL_LIBRARY_TARGET_FIELDS } from '../media/hall-library-target-fields.js';
6
+ import { HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE } from './hall-content-routing.js';
7
+ export { HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE, } from './hall-content-routing.js';
6
8
  /** The maximum character length for a SquareStreetz post created on behalf of a workProject. */
7
9
  export const MAX_SQUARE_STREETZ_DESCRIPTION_LENGTH = 150;
8
10
  // --- Commission Board & Proposals ---
@@ -98,35 +100,35 @@ export const HALL_CONTENT_TEXT_FIELD_MAX = {
98
100
  workingDescription: MAX_WORK_REALM_DESCRIPTION_LENGTH,
99
101
  };
100
102
  /**
101
- * CANONICAL WorkProjectType → hall text-surface routing. The ONE owner of "a Tale's sub-item is
102
- * a chapter, a Tune's is a tuneTrack, a Television's is a televisionEpisode" and of which fields
103
- * each of those surfaces exposes. Cross-boundary: the backend text-clear and change-request
103
+ * WorkProjectType → hall text-surface routing. Surface identities project from the lower-level
104
+ * `HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE` owner; this map adds the clearable field tuples each
105
+ * surface exposes. Cross-boundary: the backend text-clear and change-request
104
106
  * runners resolve `surface` from (workProjectType, is-sub-item?) through this map, and the admin
105
107
  * field pickers project their checkbox options from the same entry — three sites that each
106
108
  * hand-rolled the routing before (a ternary that fell through to the tuneTrack tuple for
107
109
  * Television, silently correct only because the two tuples happen to match today).
108
110
  *
109
- * Every field tuple is PROJECTED from MODERATION_CLEARABLE_TEXT_FIELDS above — no field literal
110
- * is restated here — and the `satisfies Record<WorkProjectType, …>` makes adding a work-project
111
- * type a compile error until its surfaces are routed.
111
+ * Every identity and field tuple is PROJECTED from its canonical owner — no surface or field
112
+ * literal is restated here — and the `satisfies Record<WorkProjectType, …>` makes adding a
113
+ * work-project type a compile error until its surfaces are routed.
112
114
  */
113
115
  export const HALL_CONTENT_SURFACES_BY_WORK_TYPE = {
114
116
  Tales: {
115
- detailSurface: 'tale',
117
+ detailSurface: HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Tales.detailSurface,
116
118
  detailFields: MODERATION_CLEARABLE_TEXT_FIELDS.tale,
117
- subItemSurface: 'chapter',
119
+ subItemSurface: HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Tales.subItemSurface,
118
120
  subItemFields: MODERATION_CLEARABLE_TEXT_FIELDS.chapter,
119
121
  },
120
122
  Tunes: {
121
- detailSurface: 'tune',
123
+ detailSurface: HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Tunes.detailSurface,
122
124
  detailFields: MODERATION_CLEARABLE_TEXT_FIELDS.tune,
123
- subItemSurface: 'tuneTrack',
125
+ subItemSurface: HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Tunes.subItemSurface,
124
126
  subItemFields: MODERATION_CLEARABLE_TEXT_FIELDS.tuneTrack,
125
127
  },
126
128
  Television: {
127
- detailSurface: 'television',
129
+ detailSurface: HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Television.detailSurface,
128
130
  detailFields: MODERATION_CLEARABLE_TEXT_FIELDS.television,
129
- subItemSurface: 'televisionEpisode',
131
+ subItemSurface: HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Television.subItemSurface,
130
132
  subItemFields: MODERATION_CLEARABLE_TEXT_FIELDS.televisionEpisode,
131
133
  },
132
134
  };
@@ -1 +1 @@
1
- {"version":3,"file":"business-content.js","sourceRoot":"","sources":["../../src/constants/business-content.ts"],"names":[],"mappings":"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","sourcesContent":["// Content-surface business-rule constants — commissions, auditions, the social\n// feed, and messaging.\nimport {\n MAX_WORK_PROJECT_TITLE_LENGTH,\n MAX_WORK_PROJECT_DESCRIPTION_LENGTH,\n MAX_CHAPTER_CONTENT_LENGTH,\n MAX_WORK_REALM_TITLE_LENGTH,\n MAX_WORK_REALM_DESCRIPTION_LENGTH,\n} from \"./business-work-project.js\";\nimport { ACTIVE_LIMITS } from './app-mode.js';\nimport { HALL_LIBRARY_TARGET_FIELDS } from '../media/hall-library-target-fields.js';\nimport type { WorkProjectType } from '../types/content.js';\n\n/** The maximum character length for a SquareStreetz post created on behalf of a workProject. */\nexport const MAX_SQUARE_STREETZ_DESCRIPTION_LENGTH = 150;\n\n// --- Commission Board & Proposals ---\n\n/** Maximum number of open commissions a workProject can have. Mode-varied. */\nexport const MAX_COMMISSION_LISTINGS = ACTIVE_LIMITS.workProject.maxCommissionListings;\n\n/** Maximum number of proposal artisans saved to a commission. */\nexport const MAX_SAVED_PROPOSAL_ARTISANS = 5;\n\n/** Maximum length for a commission title. */\nexport const MAX_COMMISSION_TITLE_LENGTH = MAX_WORK_PROJECT_TITLE_LENGTH;\n\n/** Maximum length for a commission description / cover letter. */\nexport const MAX_COMMISSION_DESCRIPTION_LENGTH = 400;\n\n// --- Moderation text-clear + published hall-content text fields ---\n\n/**\n * Neutral placeholder written into a single cleared text field by the moderation\n * text-clear remedy. Canonical wording — the backend `runModerationClearText`\n * core writes exactly this string; a wording change is a single edit here.\n */\nexport const MODERATION_CLEAR_PLACEHOLDER =\n 'Removed by moderation — awaiting an update by the steward.';\n\n/**\n * Default REASON strings recorded on a moderation action when the admin supplies no\n * user-facing reason. Distinct from MODERATION_CLEAR_PLACEHOLDER (which is written INTO\n * cleared content fields) — these are the recorded justification text. Canonical wording;\n * consumers (resolveAdminTask's forceRetitle / chat-delete actions) never inline them.\n */\nexport const MODERATION_DEFAULT_REMOVAL_REASON = 'Removed by moderation.';\nexport const MODERATION_DEFAULT_RETITLE_REASON = 'Title/description removed by moderation.';\n\n/**\n * CANONICAL clearable text-field map for the per-field moderation TEXT-CLEAR remedy.\n * Cross-boundary single owner: the admin field-picker offers this set per surface and\n * the backend clear runner validates + normalizes caller-supplied fields against it.\n * Keys are surfaces; values are the exact document field names (NOT display labels)\n * recorded verbatim in `moderationClearedFields`. Covers every clearable surface —\n * workProject/workRealm shell metadata, hall parent details (tale/tune/television),\n * and hall sub-items (chapter/track/episode).\n */\nexport const MODERATION_CLEARABLE_TEXT_FIELDS = {\n // workProject + workRealm shell metadata\n workProject: ['workingTitle', 'workingDescription'],\n workRealm: ['workingTitle', 'workingDescription'],\n // hall content parent details\n tale: ['title', 'description'],\n tune: ['title', 'description'],\n television: ['title', 'description'],\n // hall content sub-items\n chapter: ['title', 'content'],\n tuneTrack: ['title', 'description'],\n televisionEpisode: ['title', 'description'],\n} as const satisfies Record<string, readonly string[]>;\n\n/** A surface whose text fields the moderation clear remedy can neutralize. */\nexport type ModerationClearableSurface = keyof typeof MODERATION_CLEARABLE_TEXT_FIELDS;\n\n/**\n * Every DISTINCT clearable/changeable text-field NAME, DERIVED from the canonical\n * MODERATION_CLEARABLE_TEXT_FIELDS map (the union of every surface's tuple). Label maps and\n * per-field cap maps key on this instead of `string`, so adding a field to a surface is a\n * compile error until its label and cap exist.\n */\nexport type ClearableTextFieldName =\n (typeof MODERATION_CLEARABLE_TEXT_FIELDS)[ModerationClearableSurface][number];\n\n/**\n * The bounded computed-field portion of a moderation text-clear write. Writers\n * may only set one of the canonical clearable field names, never an arbitrary\n * document key. Sentinel-bearing moderation metadata stays runtime-specific.\n */\nexport type ModerationClearTextFieldPatch = {\n [Field in ClearableTextFieldName]?: string;\n};\n\n/**\n * The DISTINCT clearable text-field NAMES across the whole hall content family (tale/tune/\n * television detail → title/description; chapter → title/content; track/episode →\n * title/description). DERIVED from the canonical MODERATION_CLEARABLE_TEXT_FIELDS map — the\n * union of every hall surface's fields — so the `clearHallContentText` callable's field-enum\n * (`HallClearFieldSchema`) never restates the `title`/`description`/`content` literals. Index\n * access on the map's readonly tuples preserves the literal types, so this stays a typed\n * `readonly ['title','description','content']` a `z.enum(...)` can consume directly.\n */\nexport const HALL_CLEARABLE_TEXT_FIELD_NAMES = [\n MODERATION_CLEARABLE_TEXT_FIELDS.tale[0], // title\n MODERATION_CLEARABLE_TEXT_FIELDS.tale[1], // description\n MODERATION_CLEARABLE_TEXT_FIELDS.chapter[1], // content\n] as const;\n\n/**\n * The raw doc field names each published hall-content surface exposes as changeable TEXT.\n * Cross-boundary: the member \"Update details\" field picker offers this set per surface and\n * the change-request submit runner validates against it. Detail surfaces (tale/tune/\n * television) live on the hall parent doc; sub-item surfaces on the published chapter/\n * track/episode doc. This is the HALL-SURFACE SUBSET of the canonical\n * MODERATION_CLEARABLE_TEXT_FIELDS above (it excludes the `workProject` shell) — every\n * tuple is projected from that one owner, so the two can never drift.\n */\nexport const HALL_CONTENT_TEXT_FIELDS = {\n tale: MODERATION_CLEARABLE_TEXT_FIELDS.tale,\n tune: MODERATION_CLEARABLE_TEXT_FIELDS.tune,\n television: MODERATION_CLEARABLE_TEXT_FIELDS.television,\n chapter: MODERATION_CLEARABLE_TEXT_FIELDS.chapter,\n tuneTrack: MODERATION_CLEARABLE_TEXT_FIELDS.tuneTrack,\n televisionEpisode: MODERATION_CLEARABLE_TEXT_FIELDS.televisionEpisode,\n // Realm grain (R1, 2026-07-12): targets the `workRealms/{id}` doc directly.\n workRealm: MODERATION_CLEARABLE_TEXT_FIELDS.workRealm,\n} as const satisfies Record<string, readonly string[]>;\n\n/** Per-field max lengths for proposed hall-content text. DERIVED from the owning\n * constants — never independent numbers — so the change-request pipeline can never\n * drift from the authoring limits again (the pre-2026-07-13 copy carried its own\n * 200/5000/100000 set while authoring enforced 150/300/2500). Keyed on the derived\n * ClearableTextFieldName union, so a new clearable field fails the build here until\n * it has a cap. */\nexport const HALL_CONTENT_TEXT_FIELD_MAX = {\n title: MAX_WORK_PROJECT_TITLE_LENGTH,\n description: MAX_WORK_PROJECT_DESCRIPTION_LENGTH,\n content: MAX_CHAPTER_CONTENT_LENGTH,\n workingTitle: MAX_WORK_REALM_TITLE_LENGTH,\n workingDescription: MAX_WORK_REALM_DESCRIPTION_LENGTH,\n} as const satisfies Record<ClearableTextFieldName, number>;\n\n/** One work-project type's two hall text surfaces — the published DETAIL on the hall parent\n * and its chapter/track/episode SUB-ITEM — each with the clearable/changeable field tuple\n * that surface exposes. */\nexport interface HallContentSurfaceRouting {\n /** Surface identity of the hall parent DETAIL (tale / tune / television). */\n detailSurface: ModerationClearableSurface;\n /** The DETAIL surface's clearable text fields. */\n detailFields: readonly ClearableTextFieldName[];\n /** Surface identity of the SUB-ITEM (chapter / tuneTrack / televisionEpisode). */\n subItemSurface: ModerationClearableSurface;\n /** The SUB-ITEM surface's clearable text fields — a Tale chapter carries title/content, a\n * Tune track and a Television episode title/description. */\n subItemFields: readonly ClearableTextFieldName[];\n}\n\n/**\n * CANONICAL WorkProjectType → hall text-surface routing. The ONE owner of \"a Tale's sub-item is\n * a chapter, a Tune's is a tuneTrack, a Television's is a televisionEpisode\" and of which fields\n * each of those surfaces exposes. Cross-boundary: the backend text-clear and change-request\n * runners resolve `surface` from (workProjectType, is-sub-item?) through this map, and the admin\n * field pickers project their checkbox options from the same entry — three sites that each\n * hand-rolled the routing before (a ternary that fell through to the tuneTrack tuple for\n * Television, silently correct only because the two tuples happen to match today).\n *\n * Every field tuple is PROJECTED from MODERATION_CLEARABLE_TEXT_FIELDS above — no field literal\n * is restated here — and the `satisfies Record<WorkProjectType, …>` makes adding a work-project\n * type a compile error until its surfaces are routed.\n */\nexport const HALL_CONTENT_SURFACES_BY_WORK_TYPE = {\n Tales: {\n detailSurface: 'tale',\n detailFields: MODERATION_CLEARABLE_TEXT_FIELDS.tale,\n subItemSurface: 'chapter',\n subItemFields: MODERATION_CLEARABLE_TEXT_FIELDS.chapter,\n },\n Tunes: {\n detailSurface: 'tune',\n detailFields: MODERATION_CLEARABLE_TEXT_FIELDS.tune,\n subItemSurface: 'tuneTrack',\n subItemFields: MODERATION_CLEARABLE_TEXT_FIELDS.tuneTrack,\n },\n Television: {\n detailSurface: 'television',\n detailFields: MODERATION_CLEARABLE_TEXT_FIELDS.television,\n subItemSurface: 'televisionEpisode',\n subItemFields: MODERATION_CLEARABLE_TEXT_FIELDS.televisionEpisode,\n },\n} as const satisfies Record<WorkProjectType, HallContentSurfaceRouting>;\n\n/**\n * Work-shell text field → the field it appears as on the PUBLISHED hall parent\n * (`PublishedHallItem`): a Work's `workingTitle` is the hall item's `title`, its\n * `workingDescription` the hall item's `description`. The moderation placeholder writers\n * dual-write the shell and its published projection through a COMPUTED key, so the mapping\n * has to be a closed const map whose values are declared `PublishedHallItem` keys rather than\n * an inline ternary over two string literals. Both sides are projected from the canonical\n * clearable-field map, so neither name is restated here.\n */\nexport const WORK_SHELL_TEXT_FIELD_TO_HALL_ITEM_FIELD = {\n [MODERATION_CLEARABLE_TEXT_FIELDS.workProject[0]]: MODERATION_CLEARABLE_TEXT_FIELDS.tale[0], // workingTitle → title\n [MODERATION_CLEARABLE_TEXT_FIELDS.workProject[1]]: MODERATION_CLEARABLE_TEXT_FIELDS.tale[1], // workingDescription → description\n} as const satisfies Record<\n (typeof MODERATION_CLEARABLE_TEXT_FIELDS)['workProject'][number],\n (typeof MODERATION_CLEARABLE_TEXT_FIELDS)['tale'][number]\n>;\n\n// --- Hall sub-item publish requirements ---\n\n/**\n * CANONICAL per-work-type rule for what a Tale chapter / Tune track / Television episode must\n * carry before it may be submitted, approved, or published. ONE owner for the member-side\n * eligibility filter, the submit core, the approve core, and the publish core — each of which\n * otherwise restates the same three-way branch (ENG-002 / ENG-005).\n *\n * Every type requires a non-empty `title` and `photoAssetId`; a Tale chapter additionally\n * requires its `content`, a Tune track its audio, a Television episode its video. The media\n * field names are PROJECTED from the canonical upload target-field map and the text field names\n * from the clearable-field map, so no field literal is restated. These are exactly the fields\n * the published sub-item schemas (`PublishedChapter` / `PublishedTuneTrack` /\n * `PublishedTelevisionEpisode`) declare as REQUIRED.\n */\nexport const HALL_SUB_ITEM_REQUIRED_FIELDS_BY_WORK_TYPE = {\n Tales: [\n MODERATION_CLEARABLE_TEXT_FIELDS.chapter[0], // title\n HALL_LIBRARY_TARGET_FIELDS['chapter-photo'], // photoAssetId\n MODERATION_CLEARABLE_TEXT_FIELDS.chapter[1], // content\n ],\n Tunes: [\n MODERATION_CLEARABLE_TEXT_FIELDS.tuneTrack[0], // title\n HALL_LIBRARY_TARGET_FIELDS['tune-track-photo'], // photoAssetId\n HALL_LIBRARY_TARGET_FIELDS['tune-track-audio'], // audioAssetId\n ],\n Television: [\n MODERATION_CLEARABLE_TEXT_FIELDS.televisionEpisode[0], // title\n HALL_LIBRARY_TARGET_FIELDS['television-episode-photo'], // photoAssetId\n HALL_LIBRARY_TARGET_FIELDS['television-episode-video'], // videoAssetId\n ],\n} as const satisfies Record<WorkProjectType, readonly string[]>;\n\n/** A field name some work type requires on a publishable hall sub-item. */\nexport type HallSubItemRequiredField =\n (typeof HALL_SUB_ITEM_REQUIRED_FIELDS_BY_WORK_TYPE)[WorkProjectType][number];\n\n/**\n * User-facing wording for each required sub-item field, used when a surface has to say what is\n * still missing. Keyed on the derived union, so a new requirement fails the build until it has\n * wording.\n */\nexport const HALL_SUB_ITEM_REQUIREMENT_LABELS = {\n title: 'a title',\n content: 'chapter text',\n photoAssetId: 'a picture',\n audioAssetId: 'an audio file',\n videoAssetId: 'a video file',\n} as const satisfies Record<HallSubItemRequiredField, string>;\n\n/** Admin decision reason on a published change request (required on a deny; shown to\n * the member). One owner for the review callable schema AND the admin work-view input. */\nexport const MAX_HALL_CHANGE_REQUEST_REASON_LENGTH = 2000;\n\n// --- Real-people / parody disclaimer (R3, 2026-07-12) ---\n\n/**\n * The ONE standard platform disclaimer rendered on every published work whose\n * author attested it depicts real people (parody / satire / commentary). Two\n * layers, always shown together (DJ, 2026-07-12): the themed playbill HEADER\n * on top, the plain legal MESSAGE underneath. There is no free-text disclaimer\n * anywhere — these constants are the only wording, so a wording change is a\n * single edit here (display surfaces and any backend consumer read these; the\n * text is never persisted on docs). The MESSAGE deliberately never ADMITS the\n * work depicts real people — it hedges (\"even those based on real people\",\n * the standard film-boilerplate move) so the disclaimer never hands over the\n * depiction element of a claim. Legal wording pending counsel confirmation\n * (tracked in docs/ttt-prod-legal/).\n */\nexport const REAL_PEOPLE_DISCLAIMER_HEADER =\n 'A Notice from the Management: the faces may be familiar, but the players are pure ' +\n 'invention — parody and satire, in the grand old tradition of the stage.';\n\nexport const REAL_PEOPLE_DISCLAIMER_MESSAGE =\n 'All characters, statements, and events in this work, even those based on real people, ' +\n 'are entirely fictional and are presented as parody, satire, or commentary. Any voices, ' +\n 'likenesses, or portrayals of real people are impersonated or fictionalized, and nothing ' +\n 'in this work should be understood as a statement of fact about, or as speech by, any ' +\n 'real person. This work is not created by, affiliated with, sponsored by, or endorsed by ' +\n 'any real person portrayed or referenced in it.';\n\n// --- Hall library per-user viewing-state doc (hall-viewing-experience Area 2, ruled 2026-07-04) ---\n\n/**\n * Caps for the per-user Hall viewing-state doc (`HallLibraryPreferencesSchema`,\n * `userProfiles/{uid}/privateData/hallLibraryPreferences`). Every list on that doc is\n * capped for launch — no unbounded per-user lists. `hiddenWorkIds`/`inkedWorkIds` are\n * oldest-pruned (or the write is rejected with a friendly message once full);\n * `inProgress` is LRU-pruned by `updatedAt`; `recentlyViewed` is capped, newest-kept.\n * Enforcement lives in the backend core (`runX`) that owns this doc — this is the\n * shared source of truth for the cap values themselves.\n */\nexport const HALL_LIBRARY_PREFS_CAPS = {\n hiddenWorkIds: 200,\n inkedWorkIds: 200,\n inProgress: 50,\n recentlyViewed: 20,\n} as const satisfies Record<string, number>;\n\n// --- Auditions ---\n\n/** Maximum length for an audition title. */\nexport const MAX_AUDITION_TITLE_LENGTH = 150;\n\n/** Curated-audition option-video batch bounds (the fixed 2..8 option count carried\n * from the Create click through targetInfo/upload-variables to the create core's\n * re-check). ONE declaration — every `.min()/.max()` derives from these. */\nexport const MIN_CURATED_AUDITION_OPTIONS = 2;\nexport const MAX_CURATED_AUDITION_OPTIONS = 8;\n\n/** Maximum length for an audition description. */\nexport const MAX_AUDITION_DESCRIPTION_LENGTH = 1000;\n\n/**\n * Maximum sponsored-audition payout amount in whole USD accepted at the upload\n * trust boundary. Mirrors MAX_PLEDGE_PAYMENT_AMOUNT_CENTS ($500,000) expressed\n * in dollars — a finite ceiling that rejects absurd/garbage amounts before any\n * downstream processing.\n */\nexport const MAX_SPONSORED_AUDITION_AMOUNT_USD = 500_000;\n\n/**\n * The one user-facing rejection sentence when a Work's own active guildmate tries to enter\n * that Work's open audition (entries exist to recruit OUTSIDERS; a member can't be\n * recruited). Shared by both backend enforcement points (upload-accept and publish) — it is\n * the `failed-precondition` answer text the entry UI renders verbatim.\n */\nexport const GUILDMATE_AUDITION_ENTRY_REJECTION =\n 'You are already a guildmate of this Work — its audition is for recruiting new collaborators.';\n\n// --- SquareStreetz (Social Feed) ---\n\n/** Maximum length for a SquareStreetz post. */\nexport const MAX_POST_LENGTH = 500;\n\n/** Maximum number of mentions allowed in a single SquareStreetz post. */\nexport const MAX_MENTIONS = 3;\n\n/** Maximum characters of a mention's display name shown before truncation. */\nexport const MAX_MENTION_DISPLAY_LENGTH = 30;\n\n// --- Messages & Invites ---\n\n/** Maximum length for a guild-invite message. */\nexport const MAX_GUILD_INVITE_MESSAGE_LENGTH = 500;\n\n/** Maximum length for a pledgePayment message. */\nexport const MAX_PLEDGE_PAYMENT_MESSAGE_LENGTH = 500;\n"]}
1
+ {"version":3,"file":"business-content.js","sourceRoot":"","sources":["../../src/constants/business-content.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,uBAAuB;AACvB,OAAO,EACL,6BAA6B,EAC7B,mCAAmC,EACnC,0BAA0B,EAC1B,2BAA2B,EAC3B,iCAAiC,GAClC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,0BAA0B,EAAE,MAAM,wCAAwC,CAAC;AAEpF,OAAO,EAAE,uCAAuC,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EACL,uCAAuC,GAGxC,MAAM,2BAA2B,CAAC;AAEnC,gGAAgG;AAChG,MAAM,CAAC,MAAM,qCAAqC,GAAG,GAAG,CAAC;AAEzD,uCAAuC;AAEvC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,uBAAuB,GAAG,aAAa,CAAC,WAAW,CAAC,qBAAqB,CAAC;AAEvF,iEAAiE;AACjE,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAE7C,6CAA6C;AAC7C,MAAM,CAAC,MAAM,2BAA2B,GAAG,6BAA6B,CAAC;AAEzE,kEAAkE;AAClE,MAAM,CAAC,MAAM,iCAAiC,GAAG,GAAG,CAAC;AAErD,qEAAqE;AAErE;;;;GAIG;AACH,MAAM,CAAC,MAAM,4BAA4B,GACvC,4DAA4D,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,wBAAwB,CAAC;AAC1E,MAAM,CAAC,MAAM,iCAAiC,GAAG,0CAA0C,CAAC;AAE5F;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,yCAAyC;IACzC,WAAW,EAAE,CAAC,cAAc,EAAE,oBAAoB,CAAC;IACnD,SAAS,EAAE,CAAC,cAAc,EAAE,oBAAoB,CAAC;IACjD,8BAA8B;IAC9B,IAAI,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC;IAC9B,IAAI,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC;IAC9B,UAAU,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC;IACpC,yBAAyB;IACzB,OAAO,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC;IAC7B,SAAS,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC;IACnC,iBAAiB,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC;CACS,CAAC;AAuBvD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG;IAC7C,gCAAgC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ;IAClD,gCAAgC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,cAAc;IACxD,gCAAgC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU;CAC/C,CAAC;AAEX;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,IAAI,EAAE,gCAAgC,CAAC,IAAI;IAC3C,IAAI,EAAE,gCAAgC,CAAC,IAAI;IAC3C,UAAU,EAAE,gCAAgC,CAAC,UAAU;IACvD,OAAO,EAAE,gCAAgC,CAAC,OAAO;IACjD,SAAS,EAAE,gCAAgC,CAAC,SAAS;IACrD,iBAAiB,EAAE,gCAAgC,CAAC,iBAAiB;IACrE,4EAA4E;IAC5E,SAAS,EAAE,gCAAgC,CAAC,SAAS;CACD,CAAC;AAEvD;;;;;oBAKoB;AACpB,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,KAAK,EAAE,6BAA6B;IACpC,WAAW,EAAE,mCAAmC;IAChD,OAAO,EAAE,0BAA0B;IACnC,YAAY,EAAE,2BAA2B;IACzC,kBAAkB,EAAE,iCAAiC;CACI,CAAC;AAiB5D;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAG;IAChD,KAAK,EAAE;QACL,aAAa,EAAE,uCAAuC,CAAC,KAAK,CAAC,aAAa;QAC1E,YAAY,EAAE,gCAAgC,CAAC,IAAI;QACnD,cAAc,EAAE,uCAAuC,CAAC,KAAK,CAAC,cAAc;QAC5E,aAAa,EAAE,gCAAgC,CAAC,OAAO;KACxD;IACD,KAAK,EAAE;QACL,aAAa,EAAE,uCAAuC,CAAC,KAAK,CAAC,aAAa;QAC1E,YAAY,EAAE,gCAAgC,CAAC,IAAI;QACnD,cAAc,EAAE,uCAAuC,CAAC,KAAK,CAAC,cAAc;QAC5E,aAAa,EAAE,gCAAgC,CAAC,SAAS;KAC1D;IACD,UAAU,EAAE;QACV,aAAa,EAAE,uCAAuC,CAAC,UAAU,CAAC,aAAa;QAC/E,YAAY,EAAE,gCAAgC,CAAC,UAAU;QACzD,cAAc,EAAE,uCAAuC,CAAC,UAAU,CAAC,cAAc;QACjF,aAAa,EAAE,gCAAgC,CAAC,iBAAiB;KAClE;CACoE,CAAC;AAExE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,wCAAwC,GAAG;IACtD,CAAC,gCAAgC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,gCAAgC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,uBAAuB;IACpH,CAAC,gCAAgC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,gCAAgC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,mCAAmC;CAIjI,CAAC;AAEF,6CAA6C;AAE7C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,0CAA0C,GAAG;IACxD,KAAK,EAAE;QACL,gCAAgC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ;QACrD,0BAA0B,CAAC,eAAe,CAAC,EAAE,eAAe;QAC5D,gCAAgC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU;KACxD;IACD,KAAK,EAAE;QACL,gCAAgC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ;QACvD,0BAA0B,CAAC,kBAAkB,CAAC,EAAE,eAAe;QAC/D,0BAA0B,CAAC,kBAAkB,CAAC,EAAE,eAAe;KAChE;IACD,UAAU,EAAE;QACV,gCAAgC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,QAAQ;QAC/D,0BAA0B,CAAC,0BAA0B,CAAC,EAAE,eAAe;QACvE,0BAA0B,CAAC,0BAA0B,CAAC,EAAE,eAAe;KACxE;CAC4D,CAAC;AAMhE;;;;GAIG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,cAAc;IACvB,YAAY,EAAE,WAAW;IACzB,YAAY,EAAE,eAAe;IAC7B,YAAY,EAAE,cAAc;CAC+B,CAAC;AAE9D;2FAC2F;AAC3F,MAAM,CAAC,MAAM,qCAAqC,GAAG,IAAI,CAAC;AAE1D,2DAA2D;AAE3D;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,6BAA6B,GACxC,oFAAoF;IACpF,yEAAyE,CAAC;AAE5E,MAAM,CAAC,MAAM,8BAA8B,GACzC,wFAAwF;IACxF,yFAAyF;IACzF,0FAA0F;IAC1F,uFAAuF;IACvF,0FAA0F;IAC1F,gDAAgD,CAAC;AAEnD,qGAAqG;AAErG;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,aAAa,EAAE,GAAG;IAClB,YAAY,EAAE,GAAG;IACjB,UAAU,EAAE,EAAE;IACd,cAAc,EAAE,EAAE;CACuB,CAAC;AAE5C,oBAAoB;AAEpB,4CAA4C;AAC5C,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAE7C;;4EAE4E;AAC5E,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC;AAC9C,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC;AAE9C,kDAAkD;AAClD,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,OAAO,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAC7C,8FAA8F,CAAC;AAEjG,sCAAsC;AAEtC,+CAA+C;AAC/C,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAC;AAEnC,yEAAyE;AACzE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC;AAE9B,8EAA8E;AAC9E,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,CAAC;AAE7C,6BAA6B;AAE7B,iDAAiD;AACjD,MAAM,CAAC,MAAM,+BAA+B,GAAG,GAAG,CAAC;AAEnD,kDAAkD;AAClD,MAAM,CAAC,MAAM,iCAAiC,GAAG,GAAG,CAAC","sourcesContent":["// Content-surface business-rule constants — commissions, auditions, the social\n// feed, and messaging.\nimport {\n MAX_WORK_PROJECT_TITLE_LENGTH,\n MAX_WORK_PROJECT_DESCRIPTION_LENGTH,\n MAX_CHAPTER_CONTENT_LENGTH,\n MAX_WORK_REALM_TITLE_LENGTH,\n MAX_WORK_REALM_DESCRIPTION_LENGTH,\n} from \"./business-work-project.js\";\nimport { ACTIVE_LIMITS } from './app-mode.js';\nimport { HALL_LIBRARY_TARGET_FIELDS } from '../media/hall-library-target-fields.js';\nimport type { WorkProjectType } from '../types/content.js';\nimport { HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE } from './hall-content-routing.js';\nexport {\n HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE,\n type HallContentDetailSurface,\n type HallContentSubItemSurface,\n} from './hall-content-routing.js';\n\n/** The maximum character length for a SquareStreetz post created on behalf of a workProject. */\nexport const MAX_SQUARE_STREETZ_DESCRIPTION_LENGTH = 150;\n\n// --- Commission Board & Proposals ---\n\n/** Maximum number of open commissions a workProject can have. Mode-varied. */\nexport const MAX_COMMISSION_LISTINGS = ACTIVE_LIMITS.workProject.maxCommissionListings;\n\n/** Maximum number of proposal artisans saved to a commission. */\nexport const MAX_SAVED_PROPOSAL_ARTISANS = 5;\n\n/** Maximum length for a commission title. */\nexport const MAX_COMMISSION_TITLE_LENGTH = MAX_WORK_PROJECT_TITLE_LENGTH;\n\n/** Maximum length for a commission description / cover letter. */\nexport const MAX_COMMISSION_DESCRIPTION_LENGTH = 400;\n\n// --- Moderation text-clear + published hall-content text fields ---\n\n/**\n * Neutral placeholder written into a single cleared text field by the moderation\n * text-clear remedy. Canonical wording — the backend `runModerationClearText`\n * core writes exactly this string; a wording change is a single edit here.\n */\nexport const MODERATION_CLEAR_PLACEHOLDER =\n 'Removed by moderation — awaiting an update by the steward.';\n\n/**\n * Default REASON strings recorded on a moderation action when the admin supplies no\n * user-facing reason. Distinct from MODERATION_CLEAR_PLACEHOLDER (which is written INTO\n * cleared content fields) — these are the recorded justification text. Canonical wording;\n * consumers (resolveAdminTask's forceRetitle / chat-delete actions) never inline them.\n */\nexport const MODERATION_DEFAULT_REMOVAL_REASON = 'Removed by moderation.';\nexport const MODERATION_DEFAULT_RETITLE_REASON = 'Title/description removed by moderation.';\n\n/**\n * CANONICAL clearable text-field map for the per-field moderation TEXT-CLEAR remedy.\n * Cross-boundary single owner: the admin field-picker offers this set per surface and\n * the backend clear runner validates + normalizes caller-supplied fields against it.\n * Keys are surfaces; values are the exact document field names (NOT display labels)\n * recorded verbatim in `moderationClearedFields`. Covers every clearable surface —\n * workProject/workRealm shell metadata, hall parent details (tale/tune/television),\n * and hall sub-items (chapter/track/episode).\n */\nexport const MODERATION_CLEARABLE_TEXT_FIELDS = {\n // workProject + workRealm shell metadata\n workProject: ['workingTitle', 'workingDescription'],\n workRealm: ['workingTitle', 'workingDescription'],\n // hall content parent details\n tale: ['title', 'description'],\n tune: ['title', 'description'],\n television: ['title', 'description'],\n // hall content sub-items\n chapter: ['title', 'content'],\n tuneTrack: ['title', 'description'],\n televisionEpisode: ['title', 'description'],\n} as const satisfies Record<string, readonly string[]>;\n\n/** A surface whose text fields the moderation clear remedy can neutralize. */\nexport type ModerationClearableSurface = keyof typeof MODERATION_CLEARABLE_TEXT_FIELDS;\n\n/**\n * Every DISTINCT clearable/changeable text-field NAME, DERIVED from the canonical\n * MODERATION_CLEARABLE_TEXT_FIELDS map (the union of every surface's tuple). Label maps and\n * per-field cap maps key on this instead of `string`, so adding a field to a surface is a\n * compile error until its label and cap exist.\n */\nexport type ClearableTextFieldName =\n (typeof MODERATION_CLEARABLE_TEXT_FIELDS)[ModerationClearableSurface][number];\n\n/**\n * The bounded computed-field portion of a moderation text-clear write. Writers\n * may only set one of the canonical clearable field names, never an arbitrary\n * document key. Sentinel-bearing moderation metadata stays runtime-specific.\n */\nexport type ModerationClearTextFieldPatch = {\n [Field in ClearableTextFieldName]?: string;\n};\n\n/**\n * The DISTINCT clearable text-field NAMES across the whole hall content family (tale/tune/\n * television detail → title/description; chapter → title/content; track/episode →\n * title/description). DERIVED from the canonical MODERATION_CLEARABLE_TEXT_FIELDS map — the\n * union of every hall surface's fields — so the `clearHallContentText` callable's field-enum\n * (`HallClearFieldSchema`) never restates the `title`/`description`/`content` literals. Index\n * access on the map's readonly tuples preserves the literal types, so this stays a typed\n * `readonly ['title','description','content']` a `z.enum(...)` can consume directly.\n */\nexport const HALL_CLEARABLE_TEXT_FIELD_NAMES = [\n MODERATION_CLEARABLE_TEXT_FIELDS.tale[0], // title\n MODERATION_CLEARABLE_TEXT_FIELDS.tale[1], // description\n MODERATION_CLEARABLE_TEXT_FIELDS.chapter[1], // content\n] as const;\n\n/**\n * The raw doc field names each published hall-content surface exposes as changeable TEXT.\n * Cross-boundary: the member \"Update details\" field picker offers this set per surface and\n * the change-request submit runner validates against it. Detail surfaces (tale/tune/\n * television) live on the hall parent doc; sub-item surfaces on the published chapter/\n * track/episode doc. This is the HALL-SURFACE SUBSET of the canonical\n * MODERATION_CLEARABLE_TEXT_FIELDS above (it excludes the `workProject` shell) — every\n * tuple is projected from that one owner, so the two can never drift.\n */\nexport const HALL_CONTENT_TEXT_FIELDS = {\n tale: MODERATION_CLEARABLE_TEXT_FIELDS.tale,\n tune: MODERATION_CLEARABLE_TEXT_FIELDS.tune,\n television: MODERATION_CLEARABLE_TEXT_FIELDS.television,\n chapter: MODERATION_CLEARABLE_TEXT_FIELDS.chapter,\n tuneTrack: MODERATION_CLEARABLE_TEXT_FIELDS.tuneTrack,\n televisionEpisode: MODERATION_CLEARABLE_TEXT_FIELDS.televisionEpisode,\n // Realm grain (R1, 2026-07-12): targets the `workRealms/{id}` doc directly.\n workRealm: MODERATION_CLEARABLE_TEXT_FIELDS.workRealm,\n} as const satisfies Record<string, readonly string[]>;\n\n/** Per-field max lengths for proposed hall-content text. DERIVED from the owning\n * constants — never independent numbers — so the change-request pipeline can never\n * drift from the authoring limits again (the pre-2026-07-13 copy carried its own\n * 200/5000/100000 set while authoring enforced 150/300/2500). Keyed on the derived\n * ClearableTextFieldName union, so a new clearable field fails the build here until\n * it has a cap. */\nexport const HALL_CONTENT_TEXT_FIELD_MAX = {\n title: MAX_WORK_PROJECT_TITLE_LENGTH,\n description: MAX_WORK_PROJECT_DESCRIPTION_LENGTH,\n content: MAX_CHAPTER_CONTENT_LENGTH,\n workingTitle: MAX_WORK_REALM_TITLE_LENGTH,\n workingDescription: MAX_WORK_REALM_DESCRIPTION_LENGTH,\n} as const satisfies Record<ClearableTextFieldName, number>;\n\n/** One work-project type's two hall text surfaces — the published DETAIL on the hall parent\n * and its chapter/track/episode SUB-ITEM — each with the clearable/changeable field tuple\n * that surface exposes. */\nexport interface HallContentSurfaceRouting {\n /** Surface identity of the hall parent DETAIL (tale / tune / television). */\n detailSurface: ModerationClearableSurface;\n /** The DETAIL surface's clearable text fields. */\n detailFields: readonly ClearableTextFieldName[];\n /** Surface identity of the SUB-ITEM (chapter / tuneTrack / televisionEpisode). */\n subItemSurface: ModerationClearableSurface;\n /** The SUB-ITEM surface's clearable text fields — a Tale chapter carries title/content, a\n * Tune track and a Television episode title/description. */\n subItemFields: readonly ClearableTextFieldName[];\n}\n\n/**\n * WorkProjectType → hall text-surface routing. Surface identities project from the lower-level\n * `HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE` owner; this map adds the clearable field tuples each\n * surface exposes. Cross-boundary: the backend text-clear and change-request\n * runners resolve `surface` from (workProjectType, is-sub-item?) through this map, and the admin\n * field pickers project their checkbox options from the same entry — three sites that each\n * hand-rolled the routing before (a ternary that fell through to the tuneTrack tuple for\n * Television, silently correct only because the two tuples happen to match today).\n *\n * Every identity and field tuple is PROJECTED from its canonical owner — no surface or field\n * literal is restated here — and the `satisfies Record<WorkProjectType, …>` makes adding a\n * work-project type a compile error until its surfaces are routed.\n */\nexport const HALL_CONTENT_SURFACES_BY_WORK_TYPE = {\n Tales: {\n detailSurface: HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Tales.detailSurface,\n detailFields: MODERATION_CLEARABLE_TEXT_FIELDS.tale,\n subItemSurface: HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Tales.subItemSurface,\n subItemFields: MODERATION_CLEARABLE_TEXT_FIELDS.chapter,\n },\n Tunes: {\n detailSurface: HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Tunes.detailSurface,\n detailFields: MODERATION_CLEARABLE_TEXT_FIELDS.tune,\n subItemSurface: HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Tunes.subItemSurface,\n subItemFields: MODERATION_CLEARABLE_TEXT_FIELDS.tuneTrack,\n },\n Television: {\n detailSurface: HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Television.detailSurface,\n detailFields: MODERATION_CLEARABLE_TEXT_FIELDS.television,\n subItemSurface: HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Television.subItemSurface,\n subItemFields: MODERATION_CLEARABLE_TEXT_FIELDS.televisionEpisode,\n },\n} as const satisfies Record<WorkProjectType, HallContentSurfaceRouting>;\n\n/**\n * Work-shell text field → the field it appears as on the PUBLISHED hall parent\n * (`PublishedHallItem`): a Work's `workingTitle` is the hall item's `title`, its\n * `workingDescription` the hall item's `description`. The moderation placeholder writers\n * dual-write the shell and its published projection through a COMPUTED key, so the mapping\n * has to be a closed const map whose values are declared `PublishedHallItem` keys rather than\n * an inline ternary over two string literals. Both sides are projected from the canonical\n * clearable-field map, so neither name is restated here.\n */\nexport const WORK_SHELL_TEXT_FIELD_TO_HALL_ITEM_FIELD = {\n [MODERATION_CLEARABLE_TEXT_FIELDS.workProject[0]]: MODERATION_CLEARABLE_TEXT_FIELDS.tale[0], // workingTitle → title\n [MODERATION_CLEARABLE_TEXT_FIELDS.workProject[1]]: MODERATION_CLEARABLE_TEXT_FIELDS.tale[1], // workingDescription → description\n} as const satisfies Record<\n (typeof MODERATION_CLEARABLE_TEXT_FIELDS)['workProject'][number],\n (typeof MODERATION_CLEARABLE_TEXT_FIELDS)['tale'][number]\n>;\n\n// --- Hall sub-item publish requirements ---\n\n/**\n * CANONICAL per-work-type rule for what a Tale chapter / Tune track / Television episode must\n * carry before it may be submitted, approved, or published. ONE owner for the member-side\n * eligibility filter, the submit core, the approve core, and the publish core — each of which\n * otherwise restates the same three-way branch (ENG-002 / ENG-005).\n *\n * Every type requires a non-empty `title` and `photoAssetId`; a Tale chapter additionally\n * requires its `content`, a Tune track its audio, a Television episode its video. The media\n * field names are PROJECTED from the canonical upload target-field map and the text field names\n * from the clearable-field map, so no field literal is restated. These are exactly the fields\n * the published sub-item schemas (`PublishedChapter` / `PublishedTuneTrack` /\n * `PublishedTelevisionEpisode`) declare as REQUIRED.\n */\nexport const HALL_SUB_ITEM_REQUIRED_FIELDS_BY_WORK_TYPE = {\n Tales: [\n MODERATION_CLEARABLE_TEXT_FIELDS.chapter[0], // title\n HALL_LIBRARY_TARGET_FIELDS['chapter-photo'], // photoAssetId\n MODERATION_CLEARABLE_TEXT_FIELDS.chapter[1], // content\n ],\n Tunes: [\n MODERATION_CLEARABLE_TEXT_FIELDS.tuneTrack[0], // title\n HALL_LIBRARY_TARGET_FIELDS['tune-track-photo'], // photoAssetId\n HALL_LIBRARY_TARGET_FIELDS['tune-track-audio'], // audioAssetId\n ],\n Television: [\n MODERATION_CLEARABLE_TEXT_FIELDS.televisionEpisode[0], // title\n HALL_LIBRARY_TARGET_FIELDS['television-episode-photo'], // photoAssetId\n HALL_LIBRARY_TARGET_FIELDS['television-episode-video'], // videoAssetId\n ],\n} as const satisfies Record<WorkProjectType, readonly string[]>;\n\n/** A field name some work type requires on a publishable hall sub-item. */\nexport type HallSubItemRequiredField =\n (typeof HALL_SUB_ITEM_REQUIRED_FIELDS_BY_WORK_TYPE)[WorkProjectType][number];\n\n/**\n * User-facing wording for each required sub-item field, used when a surface has to say what is\n * still missing. Keyed on the derived union, so a new requirement fails the build until it has\n * wording.\n */\nexport const HALL_SUB_ITEM_REQUIREMENT_LABELS = {\n title: 'a title',\n content: 'chapter text',\n photoAssetId: 'a picture',\n audioAssetId: 'an audio file',\n videoAssetId: 'a video file',\n} as const satisfies Record<HallSubItemRequiredField, string>;\n\n/** Admin decision reason on a published change request (required on a deny; shown to\n * the member). One owner for the review callable schema AND the admin work-view input. */\nexport const MAX_HALL_CHANGE_REQUEST_REASON_LENGTH = 2000;\n\n// --- Real-people / parody disclaimer (R3, 2026-07-12) ---\n\n/**\n * The ONE standard platform disclaimer rendered on every published work whose\n * author attested it depicts real people (parody / satire / commentary). Two\n * layers, always shown together (DJ, 2026-07-12): the themed playbill HEADER\n * on top, the plain legal MESSAGE underneath. There is no free-text disclaimer\n * anywhere — these constants are the only wording, so a wording change is a\n * single edit here (display surfaces and any backend consumer read these; the\n * text is never persisted on docs). The MESSAGE deliberately never ADMITS the\n * work depicts real people — it hedges (\"even those based on real people\",\n * the standard film-boilerplate move) so the disclaimer never hands over the\n * depiction element of a claim. Legal wording pending counsel confirmation\n * (tracked in docs/ttt-prod-legal/).\n */\nexport const REAL_PEOPLE_DISCLAIMER_HEADER =\n 'A Notice from the Management: the faces may be familiar, but the players are pure ' +\n 'invention — parody and satire, in the grand old tradition of the stage.';\n\nexport const REAL_PEOPLE_DISCLAIMER_MESSAGE =\n 'All characters, statements, and events in this work, even those based on real people, ' +\n 'are entirely fictional and are presented as parody, satire, or commentary. Any voices, ' +\n 'likenesses, or portrayals of real people are impersonated or fictionalized, and nothing ' +\n 'in this work should be understood as a statement of fact about, or as speech by, any ' +\n 'real person. This work is not created by, affiliated with, sponsored by, or endorsed by ' +\n 'any real person portrayed or referenced in it.';\n\n// --- Hall library per-user viewing-state doc (hall-viewing-experience Area 2, ruled 2026-07-04) ---\n\n/**\n * Caps for the per-user Hall viewing-state doc (`HallLibraryPreferencesSchema`,\n * `userProfiles/{uid}/privateData/hallLibraryPreferences`). Every list on that doc is\n * capped for launch — no unbounded per-user lists. `hiddenWorkIds`/`inkedWorkIds` are\n * oldest-pruned (or the write is rejected with a friendly message once full);\n * `inProgress` is LRU-pruned by `updatedAt`; `recentlyViewed` is capped, newest-kept.\n * Enforcement lives in the backend core (`runX`) that owns this doc — this is the\n * shared source of truth for the cap values themselves.\n */\nexport const HALL_LIBRARY_PREFS_CAPS = {\n hiddenWorkIds: 200,\n inkedWorkIds: 200,\n inProgress: 50,\n recentlyViewed: 20,\n} as const satisfies Record<string, number>;\n\n// --- Auditions ---\n\n/** Maximum length for an audition title. */\nexport const MAX_AUDITION_TITLE_LENGTH = 150;\n\n/** Curated-audition option-video batch bounds (the fixed 2..8 option count carried\n * from the Create click through targetInfo/upload-variables to the create core's\n * re-check). ONE declaration — every `.min()/.max()` derives from these. */\nexport const MIN_CURATED_AUDITION_OPTIONS = 2;\nexport const MAX_CURATED_AUDITION_OPTIONS = 8;\n\n/** Maximum length for an audition description. */\nexport const MAX_AUDITION_DESCRIPTION_LENGTH = 1000;\n\n/**\n * Maximum sponsored-audition payout amount in whole USD accepted at the upload\n * trust boundary. Mirrors MAX_PLEDGE_PAYMENT_AMOUNT_CENTS ($500,000) expressed\n * in dollars — a finite ceiling that rejects absurd/garbage amounts before any\n * downstream processing.\n */\nexport const MAX_SPONSORED_AUDITION_AMOUNT_USD = 500_000;\n\n/**\n * The one user-facing rejection sentence when a Work's own active guildmate tries to enter\n * that Work's open audition (entries exist to recruit OUTSIDERS; a member can't be\n * recruited). Shared by both backend enforcement points (upload-accept and publish) — it is\n * the `failed-precondition` answer text the entry UI renders verbatim.\n */\nexport const GUILDMATE_AUDITION_ENTRY_REJECTION =\n 'You are already a guildmate of this Work — its audition is for recruiting new collaborators.';\n\n// --- SquareStreetz (Social Feed) ---\n\n/** Maximum length for a SquareStreetz post. */\nexport const MAX_POST_LENGTH = 500;\n\n/** Maximum number of mentions allowed in a single SquareStreetz post. */\nexport const MAX_MENTIONS = 3;\n\n/** Maximum characters of a mention's display name shown before truncation. */\nexport const MAX_MENTION_DISPLAY_LENGTH = 30;\n\n// --- Messages & Invites ---\n\n/** Maximum length for a guild-invite message. */\nexport const MAX_GUILD_INVITE_MESSAGE_LENGTH = 500;\n\n/** Maximum length for a pledgePayment message. */\nexport const MAX_PLEDGE_PAYMENT_MESSAGE_LENGTH = 500;\n"]}
@@ -0,0 +1,23 @@
1
+ import type { WorkProjectType } from '../types/content.js';
2
+ /**
3
+ * Canonical identities for the Hall detail and sub-item surfaces owned by each Work type.
4
+ * Higher-level routing maps project fields and upload origins from this table so the surface
5
+ * literals are declared once without creating a constants <-> media dependency cycle.
6
+ */
7
+ export declare const HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE: {
8
+ readonly Tales: {
9
+ readonly detailSurface: "tale";
10
+ readonly subItemSurface: "chapter";
11
+ };
12
+ readonly Tunes: {
13
+ readonly detailSurface: "tune";
14
+ readonly subItemSurface: "tuneTrack";
15
+ };
16
+ readonly Television: {
17
+ readonly detailSurface: "television";
18
+ readonly subItemSurface: "televisionEpisode";
19
+ };
20
+ };
21
+ export type HallContentDetailSurface = (typeof HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE)[WorkProjectType]['detailSurface'];
22
+ export type HallContentSubItemSurface = (typeof HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE)[WorkProjectType]['subItemSurface'];
23
+ //# sourceMappingURL=hall-content-routing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hall-content-routing.d.ts","sourceRoot":"","sources":["../../src/constants/hall-content-routing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D;;;;GAIG;AACH,eAAO,MAAM,uCAAuC;;;;;;;;;;;;;CAgBnD,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAClC,CAAC,OAAO,uCAAuC,CAAC,CAAC,eAAe,CAAC,CAAC,eAAe,CAAC,CAAC;AAErF,MAAM,MAAM,yBAAyB,GACnC,CAAC,OAAO,uCAAuC,CAAC,CAAC,eAAe,CAAC,CAAC,gBAAgB,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Canonical identities for the Hall detail and sub-item surfaces owned by each Work type.
3
+ * Higher-level routing maps project fields and upload origins from this table so the surface
4
+ * literals are declared once without creating a constants <-> media dependency cycle.
5
+ */
6
+ export const HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE = {
7
+ Tales: {
8
+ detailSurface: 'tale',
9
+ subItemSurface: 'chapter',
10
+ },
11
+ Tunes: {
12
+ detailSurface: 'tune',
13
+ subItemSurface: 'tuneTrack',
14
+ },
15
+ Television: {
16
+ detailSurface: 'television',
17
+ subItemSurface: 'televisionEpisode',
18
+ },
19
+ };
20
+ //# sourceMappingURL=hall-content-routing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hall-content-routing.js","sourceRoot":"","sources":["../../src/constants/hall-content-routing.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,MAAM,uCAAuC,GAAG;IACrD,KAAK,EAAE;QACL,aAAa,EAAE,MAAM;QACrB,cAAc,EAAE,SAAS;KAC1B;IACD,KAAK,EAAE;QACL,aAAa,EAAE,MAAM;QACrB,cAAc,EAAE,WAAW;KAC5B;IACD,UAAU,EAAE;QACV,aAAa,EAAE,YAAY;QAC3B,cAAc,EAAE,mBAAmB;KACpC;CAIF,CAAC","sourcesContent":["import type { WorkProjectType } from '../types/content.js';\n\n/**\n * Canonical identities for the Hall detail and sub-item surfaces owned by each Work type.\n * Higher-level routing maps project fields and upload origins from this table so the surface\n * literals are declared once without creating a constants <-> media dependency cycle.\n */\nexport const HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE = {\n Tales: {\n detailSurface: 'tale',\n subItemSurface: 'chapter',\n },\n Tunes: {\n detailSurface: 'tune',\n subItemSurface: 'tuneTrack',\n },\n Television: {\n detailSurface: 'television',\n subItemSurface: 'televisionEpisode',\n },\n} as const satisfies Record<\n WorkProjectType,\n { readonly detailSurface: string; readonly subItemSurface: string }\n>;\n\nexport type HallContentDetailSurface =\n (typeof HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE)[WorkProjectType]['detailSurface'];\n\nexport type HallContentSubItemSurface =\n (typeof HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE)[WorkProjectType]['subItemSurface'];\n"]}
@@ -1,3 +1,4 @@
1
+ import { type HallContentSubItemSurface } from '../constants/hall-content-routing.js';
1
2
  export declare const HALL_LIBRARY_TARGET_FIELDS: {
2
3
  readonly 'hallLibrary-cover-square': "coverSquareAssetId";
3
4
  readonly 'hallLibrary-cover-poster': "coverPosterAssetId";
@@ -26,6 +27,22 @@ export declare const HALL_LIBRARY_SUB_ITEM_TARGET_FIELDS: {
26
27
  };
27
28
  export type HallLibraryCoverTargetField = typeof HALL_LIBRARY_COVER_TARGET_FIELDS[HallLibraryCoverFileOrigin];
28
29
  export type HallLibrarySubItemTargetField = typeof HALL_LIBRARY_SUB_ITEM_TARGET_FIELDS[HallLibrarySubItemFileOrigin];
30
+ /** The working sub-item surface addressed by a Hall-library sub-item upload. */
31
+ export type HallLibrarySubItemSurface = HallContentSubItemSurface;
32
+ /**
33
+ * Canonical upload-origin → working-sub-item routing. The processor uses this to derive
34
+ * publicationArgs, and the publication adapter uses the same declaration to reject a
35
+ * malformed persisted job before it can attach a media field to the wrong document shape.
36
+ */
37
+ export declare const HALL_LIBRARY_SUB_ITEM_SURFACE_BY_ORIGIN: {
38
+ readonly 'chapter-photo': "chapter";
39
+ readonly 'tune-track-photo': "tuneTrack";
40
+ readonly 'tune-track-audio': "tuneTrack";
41
+ readonly 'television-episode-photo': "televisionEpisode";
42
+ readonly 'television-episode-video': "televisionEpisode";
43
+ };
44
+ /** True when this origin is valid for the addressed working sub-item surface. */
45
+ export declare function isHallLibrarySubItemOriginForSurface(origin: HallLibrarySubItemFileOrigin, surface: HallLibrarySubItemSurface): boolean;
29
46
  /** True when the origin writes a cover field on the hall parent / Work section doc. */
30
47
  export declare function isHallLibraryCoverFileOrigin(origin: string): origin is HallLibraryCoverFileOrigin;
31
48
  /** True when the origin writes a media field on a chapter / track / episode doc. */
@@ -1 +1 @@
1
- {"version":3,"file":"hall-library-target-fields.d.ts","sourceRoot":"","sources":["../../src/media/hall-library-target-fields.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,0BAA0B;;;;;;;;;CASiB,CAAC;AAEzD,MAAM,MAAM,qBAAqB,GAAG,MAAM,OAAO,0BAA0B,CAAC;AAC5E,MAAM,MAAM,sBAAsB,GAAG,OAAO,0BAA0B,CAAC,qBAAqB,CAAC,CAAC;AAU9F,MAAM,MAAM,0BAA0B,GACpC,OAAO,CAAC,qBAAqB,EAAE,qBAAqB,MAAM,EAAE,CAAC,CAAC;AAChE,MAAM,MAAM,4BAA4B,GACtC,OAAO,CAAC,qBAAqB,EAAE,0BAA0B,CAAC,CAAC;AAE7D,eAAO,MAAM,gCAAgC;;;;CAI2C,CAAC;AAEzF,eAAO,MAAM,mCAAmC;;;;;;CAM0C,CAAC;AAE3F,MAAM,MAAM,2BAA2B,GACrC,OAAO,gCAAgC,CAAC,0BAA0B,CAAC,CAAC;AACtE,MAAM,MAAM,6BAA6B,GACvC,OAAO,mCAAmC,CAAC,4BAA4B,CAAC,CAAC;AAE3E,uFAAuF;AACvF,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,GACb,MAAM,IAAI,0BAA0B,CAEtC;AAED,oFAAoF;AACpF,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,MAAM,GACb,MAAM,IAAI,4BAA4B,CAExC"}
1
+ {"version":3,"file":"hall-library-target-fields.d.ts","sourceRoot":"","sources":["../../src/media/hall-library-target-fields.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,sCAAsC,CAAC;AAa9C,eAAO,MAAM,0BAA0B;;;;;;;;;CASiB,CAAC;AAEzD,MAAM,MAAM,qBAAqB,GAAG,MAAM,OAAO,0BAA0B,CAAC;AAC5E,MAAM,MAAM,sBAAsB,GAAG,OAAO,0BAA0B,CAAC,qBAAqB,CAAC,CAAC;AAU9F,MAAM,MAAM,0BAA0B,GACpC,OAAO,CAAC,qBAAqB,EAAE,qBAAqB,MAAM,EAAE,CAAC,CAAC;AAChE,MAAM,MAAM,4BAA4B,GACtC,OAAO,CAAC,qBAAqB,EAAE,0BAA0B,CAAC,CAAC;AAE7D,eAAO,MAAM,gCAAgC;;;;CAI2C,CAAC;AAEzF,eAAO,MAAM,mCAAmC;;;;;;CAM0C,CAAC;AAE3F,MAAM,MAAM,2BAA2B,GACrC,OAAO,gCAAgC,CAAC,0BAA0B,CAAC,CAAC;AACtE,MAAM,MAAM,6BAA6B,GACvC,OAAO,mCAAmC,CAAC,4BAA4B,CAAC,CAAC;AAE3E,gFAAgF;AAChF,MAAM,MAAM,yBAAyB,GAAG,yBAAyB,CAAC;AAElE;;;;GAIG;AACH,eAAO,MAAM,uCAAuC;;;;;;CAMgC,CAAC;AAErF,iFAAiF;AACjF,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,4BAA4B,EACpC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAET;AAED,uFAAuF;AACvF,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,GACb,MAAM,IAAI,0BAA0B,CAEtC;AAED,oFAAoF;AACpF,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,MAAM,GACb,MAAM,IAAI,4BAA4B,CAExC"}
@@ -1,3 +1,4 @@
1
+ import { HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE, } from '../constants/hall-content-routing.js';
1
2
  // Server-side mapping from hallLibrary/sub-item fileOrigin to the doc field
2
3
  // that receives the processed media-asset reference (never a URL — see
3
4
  // ttt-prod docs/design/media-assets-and-protected-serving.md).
@@ -31,6 +32,22 @@ export const HALL_LIBRARY_SUB_ITEM_TARGET_FIELDS = {
31
32
  'television-episode-photo': HALL_LIBRARY_TARGET_FIELDS['television-episode-photo'],
32
33
  'television-episode-video': HALL_LIBRARY_TARGET_FIELDS['television-episode-video'],
33
34
  };
35
+ /**
36
+ * Canonical upload-origin → working-sub-item routing. The processor uses this to derive
37
+ * publicationArgs, and the publication adapter uses the same declaration to reject a
38
+ * malformed persisted job before it can attach a media field to the wrong document shape.
39
+ */
40
+ export const HALL_LIBRARY_SUB_ITEM_SURFACE_BY_ORIGIN = {
41
+ 'chapter-photo': HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Tales.subItemSurface,
42
+ 'tune-track-photo': HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Tunes.subItemSurface,
43
+ 'tune-track-audio': HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Tunes.subItemSurface,
44
+ 'television-episode-photo': HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Television.subItemSurface,
45
+ 'television-episode-video': HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Television.subItemSurface,
46
+ };
47
+ /** True when this origin is valid for the addressed working sub-item surface. */
48
+ export function isHallLibrarySubItemOriginForSurface(origin, surface) {
49
+ return HALL_LIBRARY_SUB_ITEM_SURFACE_BY_ORIGIN[origin] === surface;
50
+ }
34
51
  /** True when the origin writes a cover field on the hall parent / Work section doc. */
35
52
  export function isHallLibraryCoverFileOrigin(origin) {
36
53
  return Object.prototype.hasOwnProperty.call(HALL_LIBRARY_COVER_TARGET_FIELDS, origin);
@@ -1 +1 @@
1
- {"version":3,"file":"hall-library-target-fields.js","sourceRoot":"","sources":["../../src/media/hall-library-target-fields.ts"],"names":[],"mappings":"AAEA,4EAA4E;AAC5E,uEAAuE;AACvE,+DAA+D;AAC/D,EAAE;AACF,kEAAkE;AAClE,sEAAsE;AACtE,oEAAoE;AACpE,oDAAoD;AACpD,EAAE;AACF,qEAAqE;AACrE,qCAAqC;AACrC,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,0BAA0B,EAAK,oBAAoB;IACnD,0BAA0B,EAAK,oBAAoB;IACnD,6BAA6B,EAAE,uBAAuB;IACtD,eAAe,EAAY,cAAc;IACzC,kBAAkB,EAAe,cAAc;IAC/C,kBAAkB,EAAe,cAAc;IAC/C,0BAA0B,EAAe,cAAc;IACvD,0BAA0B,EAAe,cAAc;CACD,CAAC;AAkBzD,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,0BAA0B,EAAK,0BAA0B,CAAC,0BAA0B,CAAC;IACrF,0BAA0B,EAAK,0BAA0B,CAAC,0BAA0B,CAAC;IACrF,6BAA6B,EAAE,0BAA0B,CAAC,6BAA6B,CAAC;CACF,CAAC;AAEzF,MAAM,CAAC,MAAM,mCAAmC,GAAG;IACjD,eAAe,EAAa,0BAA0B,CAAC,eAAe,CAAC;IACvE,kBAAkB,EAAU,0BAA0B,CAAC,kBAAkB,CAAC;IAC1E,kBAAkB,EAAU,0BAA0B,CAAC,kBAAkB,CAAC;IAC1E,0BAA0B,EAAE,0BAA0B,CAAC,0BAA0B,CAAC;IAClF,0BAA0B,EAAE,0BAA0B,CAAC,0BAA0B,CAAC;CACM,CAAC;AAO3F,uFAAuF;AACvF,MAAM,UAAU,4BAA4B,CAC1C,MAAc;IAEd,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,gCAAgC,EAAE,MAAM,CAAC,CAAC;AACxF,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,8BAA8B,CAC5C,MAAc;IAEd,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,mCAAmC,EAAE,MAAM,CAAC,CAAC;AAC3F,CAAC","sourcesContent":["import type { FileOrigin } from './file-origin.js';\n\n// Server-side mapping from hallLibrary/sub-item fileOrigin to the doc field\n// that receives the processed media-asset reference (never a URL — see\n// ttt-prod docs/design/media-assets-and-protected-serving.md).\n//\n// Owned in ttt-core because it's a contract fact about the upload\n// pipeline — same tier as TTT_MEDIA_SPECS and PATH_BUILDERS. Frontend\n// never reads this. Processors look the field up after deriving the\n// target doc path from the typed IDs in targetInfo.\n//\n// The Partial<Record<FileOrigin, string>> constraint guarantees only\n// valid FileOrigin strings are keys.\nexport const HALL_LIBRARY_TARGET_FIELDS = {\n 'hallLibrary-cover-square': 'coverSquareAssetId',\n 'hallLibrary-cover-poster': 'coverPosterAssetId',\n 'hallLibrary-cover-cinematic': 'coverCinematicAssetId',\n 'chapter-photo': 'photoAssetId',\n 'tune-track-photo': 'photoAssetId',\n 'tune-track-audio': 'audioAssetId',\n 'television-episode-photo': 'photoAssetId',\n 'television-episode-video': 'videoAssetId',\n} as const satisfies Partial<Record<FileOrigin, string>>;\n\nexport type HallLibraryFileOrigin = keyof typeof HALL_LIBRARY_TARGET_FIELDS;\nexport type HallLibraryTargetField = typeof HALL_LIBRARY_TARGET_FIELDS[HallLibraryFileOrigin];\n\n// The same map split by WRITE LEVEL. A cover origin attaches its field on the hall\n// PARENT (the Work's tale/tune/television section doc); a sub-item origin attaches on a\n// chapter / track / episode doc. A processor owns exactly one level, so indexing the whole\n// map there types the write against fields the document it writes never declares.\n//\n// Both subsets are derived from the one map above — no field name is restated — and the\n// `satisfies Pick<...>` constraints close the partition at compile time: an origin added to\n// the parent map lands in one of the two key unions and fails to build until it is listed.\nexport type HallLibraryCoverFileOrigin =\n Extract<HallLibraryFileOrigin, `hallLibrary-cover-${string}`>;\nexport type HallLibrarySubItemFileOrigin =\n Exclude<HallLibraryFileOrigin, HallLibraryCoverFileOrigin>;\n\nexport const HALL_LIBRARY_COVER_TARGET_FIELDS = {\n 'hallLibrary-cover-square': HALL_LIBRARY_TARGET_FIELDS['hallLibrary-cover-square'],\n 'hallLibrary-cover-poster': HALL_LIBRARY_TARGET_FIELDS['hallLibrary-cover-poster'],\n 'hallLibrary-cover-cinematic': HALL_LIBRARY_TARGET_FIELDS['hallLibrary-cover-cinematic'],\n} as const satisfies Pick<typeof HALL_LIBRARY_TARGET_FIELDS, HallLibraryCoverFileOrigin>;\n\nexport const HALL_LIBRARY_SUB_ITEM_TARGET_FIELDS = {\n 'chapter-photo': HALL_LIBRARY_TARGET_FIELDS['chapter-photo'],\n 'tune-track-photo': HALL_LIBRARY_TARGET_FIELDS['tune-track-photo'],\n 'tune-track-audio': HALL_LIBRARY_TARGET_FIELDS['tune-track-audio'],\n 'television-episode-photo': HALL_LIBRARY_TARGET_FIELDS['television-episode-photo'],\n 'television-episode-video': HALL_LIBRARY_TARGET_FIELDS['television-episode-video'],\n} as const satisfies Pick<typeof HALL_LIBRARY_TARGET_FIELDS, HallLibrarySubItemFileOrigin>;\n\nexport type HallLibraryCoverTargetField =\n typeof HALL_LIBRARY_COVER_TARGET_FIELDS[HallLibraryCoverFileOrigin];\nexport type HallLibrarySubItemTargetField =\n typeof HALL_LIBRARY_SUB_ITEM_TARGET_FIELDS[HallLibrarySubItemFileOrigin];\n\n/** True when the origin writes a cover field on the hall parent / Work section doc. */\nexport function isHallLibraryCoverFileOrigin(\n origin: string,\n): origin is HallLibraryCoverFileOrigin {\n return Object.prototype.hasOwnProperty.call(HALL_LIBRARY_COVER_TARGET_FIELDS, origin);\n}\n\n/** True when the origin writes a media field on a chapter / track / episode doc. */\nexport function isHallLibrarySubItemFileOrigin(\n origin: string,\n): origin is HallLibrarySubItemFileOrigin {\n return Object.prototype.hasOwnProperty.call(HALL_LIBRARY_SUB_ITEM_TARGET_FIELDS, origin);\n}\n\n"]}
1
+ {"version":3,"file":"hall-library-target-fields.js","sourceRoot":"","sources":["../../src/media/hall-library-target-fields.ts"],"names":[],"mappings":"AACA,OAAO,EACL,uCAAuC,GAExC,MAAM,sCAAsC,CAAC;AAE9C,4EAA4E;AAC5E,uEAAuE;AACvE,+DAA+D;AAC/D,EAAE;AACF,kEAAkE;AAClE,sEAAsE;AACtE,oEAAoE;AACpE,oDAAoD;AACpD,EAAE;AACF,qEAAqE;AACrE,qCAAqC;AACrC,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,0BAA0B,EAAK,oBAAoB;IACnD,0BAA0B,EAAK,oBAAoB;IACnD,6BAA6B,EAAE,uBAAuB;IACtD,eAAe,EAAY,cAAc;IACzC,kBAAkB,EAAe,cAAc;IAC/C,kBAAkB,EAAe,cAAc;IAC/C,0BAA0B,EAAe,cAAc;IACvD,0BAA0B,EAAe,cAAc;CACD,CAAC;AAkBzD,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,0BAA0B,EAAK,0BAA0B,CAAC,0BAA0B,CAAC;IACrF,0BAA0B,EAAK,0BAA0B,CAAC,0BAA0B,CAAC;IACrF,6BAA6B,EAAE,0BAA0B,CAAC,6BAA6B,CAAC;CACF,CAAC;AAEzF,MAAM,CAAC,MAAM,mCAAmC,GAAG;IACjD,eAAe,EAAa,0BAA0B,CAAC,eAAe,CAAC;IACvE,kBAAkB,EAAU,0BAA0B,CAAC,kBAAkB,CAAC;IAC1E,kBAAkB,EAAU,0BAA0B,CAAC,kBAAkB,CAAC;IAC1E,0BAA0B,EAAE,0BAA0B,CAAC,0BAA0B,CAAC;IAClF,0BAA0B,EAAE,0BAA0B,CAAC,0BAA0B,CAAC;CACM,CAAC;AAU3F;;;;GAIG;AACH,MAAM,CAAC,MAAM,uCAAuC,GAAG;IACrD,eAAe,EAAE,uCAAuC,CAAC,KAAK,CAAC,cAAc;IAC7E,kBAAkB,EAAE,uCAAuC,CAAC,KAAK,CAAC,cAAc;IAChF,kBAAkB,EAAE,uCAAuC,CAAC,KAAK,CAAC,cAAc;IAChF,0BAA0B,EAAE,uCAAuC,CAAC,UAAU,CAAC,cAAc;IAC7F,0BAA0B,EAAE,uCAAuC,CAAC,UAAU,CAAC,cAAc;CACX,CAAC;AAErF,iFAAiF;AACjF,MAAM,UAAU,oCAAoC,CAClD,MAAoC,EACpC,OAAkC;IAElC,OAAO,uCAAuC,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC;AACrE,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,4BAA4B,CAC1C,MAAc;IAEd,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,gCAAgC,EAAE,MAAM,CAAC,CAAC;AACxF,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,8BAA8B,CAC5C,MAAc;IAEd,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,mCAAmC,EAAE,MAAM,CAAC,CAAC;AAC3F,CAAC","sourcesContent":["import type { FileOrigin } from './file-origin.js';\nimport {\n HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE,\n type HallContentSubItemSurface,\n} from '../constants/hall-content-routing.js';\n\n// Server-side mapping from hallLibrary/sub-item fileOrigin to the doc field\n// that receives the processed media-asset reference (never a URL — see\n// ttt-prod docs/design/media-assets-and-protected-serving.md).\n//\n// Owned in ttt-core because it's a contract fact about the upload\n// pipeline — same tier as TTT_MEDIA_SPECS and PATH_BUILDERS. Frontend\n// never reads this. Processors look the field up after deriving the\n// target doc path from the typed IDs in targetInfo.\n//\n// The Partial<Record<FileOrigin, string>> constraint guarantees only\n// valid FileOrigin strings are keys.\nexport const HALL_LIBRARY_TARGET_FIELDS = {\n 'hallLibrary-cover-square': 'coverSquareAssetId',\n 'hallLibrary-cover-poster': 'coverPosterAssetId',\n 'hallLibrary-cover-cinematic': 'coverCinematicAssetId',\n 'chapter-photo': 'photoAssetId',\n 'tune-track-photo': 'photoAssetId',\n 'tune-track-audio': 'audioAssetId',\n 'television-episode-photo': 'photoAssetId',\n 'television-episode-video': 'videoAssetId',\n} as const satisfies Partial<Record<FileOrigin, string>>;\n\nexport type HallLibraryFileOrigin = keyof typeof HALL_LIBRARY_TARGET_FIELDS;\nexport type HallLibraryTargetField = typeof HALL_LIBRARY_TARGET_FIELDS[HallLibraryFileOrigin];\n\n// The same map split by WRITE LEVEL. A cover origin attaches its field on the hall\n// PARENT (the Work's tale/tune/television section doc); a sub-item origin attaches on a\n// chapter / track / episode doc. A processor owns exactly one level, so indexing the whole\n// map there types the write against fields the document it writes never declares.\n//\n// Both subsets are derived from the one map above — no field name is restated — and the\n// `satisfies Pick<...>` constraints close the partition at compile time: an origin added to\n// the parent map lands in one of the two key unions and fails to build until it is listed.\nexport type HallLibraryCoverFileOrigin =\n Extract<HallLibraryFileOrigin, `hallLibrary-cover-${string}`>;\nexport type HallLibrarySubItemFileOrigin =\n Exclude<HallLibraryFileOrigin, HallLibraryCoverFileOrigin>;\n\nexport const HALL_LIBRARY_COVER_TARGET_FIELDS = {\n 'hallLibrary-cover-square': HALL_LIBRARY_TARGET_FIELDS['hallLibrary-cover-square'],\n 'hallLibrary-cover-poster': HALL_LIBRARY_TARGET_FIELDS['hallLibrary-cover-poster'],\n 'hallLibrary-cover-cinematic': HALL_LIBRARY_TARGET_FIELDS['hallLibrary-cover-cinematic'],\n} as const satisfies Pick<typeof HALL_LIBRARY_TARGET_FIELDS, HallLibraryCoverFileOrigin>;\n\nexport const HALL_LIBRARY_SUB_ITEM_TARGET_FIELDS = {\n 'chapter-photo': HALL_LIBRARY_TARGET_FIELDS['chapter-photo'],\n 'tune-track-photo': HALL_LIBRARY_TARGET_FIELDS['tune-track-photo'],\n 'tune-track-audio': HALL_LIBRARY_TARGET_FIELDS['tune-track-audio'],\n 'television-episode-photo': HALL_LIBRARY_TARGET_FIELDS['television-episode-photo'],\n 'television-episode-video': HALL_LIBRARY_TARGET_FIELDS['television-episode-video'],\n} as const satisfies Pick<typeof HALL_LIBRARY_TARGET_FIELDS, HallLibrarySubItemFileOrigin>;\n\nexport type HallLibraryCoverTargetField =\n typeof HALL_LIBRARY_COVER_TARGET_FIELDS[HallLibraryCoverFileOrigin];\nexport type HallLibrarySubItemTargetField =\n typeof HALL_LIBRARY_SUB_ITEM_TARGET_FIELDS[HallLibrarySubItemFileOrigin];\n\n/** The working sub-item surface addressed by a Hall-library sub-item upload. */\nexport type HallLibrarySubItemSurface = HallContentSubItemSurface;\n\n/**\n * Canonical upload-origin → working-sub-item routing. The processor uses this to derive\n * publicationArgs, and the publication adapter uses the same declaration to reject a\n * malformed persisted job before it can attach a media field to the wrong document shape.\n */\nexport const HALL_LIBRARY_SUB_ITEM_SURFACE_BY_ORIGIN = {\n 'chapter-photo': HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Tales.subItemSurface,\n 'tune-track-photo': HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Tunes.subItemSurface,\n 'tune-track-audio': HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Tunes.subItemSurface,\n 'television-episode-photo': HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Television.subItemSurface,\n 'television-episode-video': HALL_CONTENT_SURFACE_NAMES_BY_WORK_TYPE.Television.subItemSurface,\n} as const satisfies Record<HallLibrarySubItemFileOrigin, HallLibrarySubItemSurface>;\n\n/** True when this origin is valid for the addressed working sub-item surface. */\nexport function isHallLibrarySubItemOriginForSurface(\n origin: HallLibrarySubItemFileOrigin,\n surface: HallLibrarySubItemSurface,\n): boolean {\n return HALL_LIBRARY_SUB_ITEM_SURFACE_BY_ORIGIN[origin] === surface;\n}\n\n/** True when the origin writes a cover field on the hall parent / Work section doc. */\nexport function isHallLibraryCoverFileOrigin(\n origin: string,\n): origin is HallLibraryCoverFileOrigin {\n return Object.prototype.hasOwnProperty.call(HALL_LIBRARY_COVER_TARGET_FIELDS, origin);\n}\n\n/** True when the origin writes a media field on a chapter / track / episode doc. */\nexport function isHallLibrarySubItemFileOrigin(\n origin: string,\n): origin is HallLibrarySubItemFileOrigin {\n return Object.prototype.hasOwnProperty.call(HALL_LIBRARY_SUB_ITEM_TARGET_FIELDS, origin);\n}\n\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/ttt-core",
3
- "version": "0.79.43",
3
+ "version": "0.79.45",
4
4
  "description": "Core types, Firestore path constants, and shared constants for TTT Productions",
5
5
  "repository": {
6
6
  "type": "git",