@ttt-productions/ttt-core 0.78.23 → 0.78.24
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.
- package/dist/constants/business-user.d.ts +1 -1
- package/dist/constants/business-user.js +1 -1
- package/dist/constants/business-user.js.map +1 -1
- package/dist/constants/release-flags.d.ts +2 -2
- package/dist/constants/release-flags.d.ts.map +1 -1
- package/dist/constants/release-flags.js +2 -2
- package/dist/constants/release-flags.js.map +1 -1
- package/dist/doc-schemas/chat-sync.d.ts.map +1 -1
- package/dist/doc-schemas/chat-sync.js +3 -2
- package/dist/doc-schemas/chat-sync.js.map +1 -1
- package/dist/doc-schemas/commissions.d.ts +14 -11
- package/dist/doc-schemas/commissions.d.ts.map +1 -1
- package/dist/doc-schemas/commissions.js +14 -4
- package/dist/doc-schemas/commissions.js.map +1 -1
- package/dist/doc-schemas/media-activation-jobs.d.ts.map +1 -1
- package/dist/doc-schemas/media-activation-jobs.js +4 -3
- package/dist/doc-schemas/media-activation-jobs.js.map +1 -1
- package/dist/doc-schemas/media-assets.d.ts +2 -2
- package/dist/doc-schemas/media-assets.d.ts.map +1 -1
- package/dist/doc-schemas/media-assets.js +5 -4
- package/dist/doc-schemas/media-assets.js.map +1 -1
- package/dist/doc-schemas/messaging.d.ts.map +1 -1
- package/dist/doc-schemas/messaging.js +3 -2
- package/dist/doc-schemas/messaging.js.map +1 -1
- package/dist/doc-schemas/ncii/allegations.js +3 -3
- package/dist/doc-schemas/ncii/allegations.js.map +1 -1
- package/dist/doc-schemas/ncii/cases.d.ts.map +1 -1
- package/dist/doc-schemas/ncii/cases.js +4 -3
- package/dist/doc-schemas/ncii/cases.js.map +1 -1
- package/dist/doc-schemas/ncii/config.d.ts.map +1 -1
- package/dist/doc-schemas/ncii/config.js +4 -3
- package/dist/doc-schemas/ncii/config.js.map +1 -1
- package/dist/doc-schemas/ncii/holds.js +3 -3
- package/dist/doc-schemas/ncii/holds.js.map +1 -1
- package/dist/doc-schemas/ncii/removal.js +3 -3
- package/dist/doc-schemas/ncii/removal.js.map +1 -1
- package/dist/doc-schemas/ncii/requests.d.ts.map +1 -1
- package/dist/doc-schemas/ncii/requests.js +4 -3
- package/dist/doc-schemas/ncii/requests.js.map +1 -1
- package/dist/doc-schemas/notification-ledger.d.ts.map +1 -1
- package/dist/doc-schemas/notification-ledger.js +3 -1
- package/dist/doc-schemas/notification-ledger.js.map +1 -1
- package/dist/doc-schemas/payments.d.ts.map +1 -1
- package/dist/doc-schemas/payments.js +2 -1
- package/dist/doc-schemas/payments.js.map +1 -1
- package/dist/doc-schemas/registry.d.ts +108 -35
- package/dist/doc-schemas/registry.d.ts.map +1 -1
- package/dist/doc-schemas/registry.js +4 -0
- package/dist/doc-schemas/registry.js.map +1 -1
- package/dist/doc-schemas/safety/age.d.ts.map +1 -1
- package/dist/doc-schemas/safety/age.js +4 -3
- package/dist/doc-schemas/safety/age.js.map +1 -1
- package/dist/doc-schemas/safety/case-aliases.d.ts.map +1 -1
- package/dist/doc-schemas/safety/case-aliases.js +5 -4
- package/dist/doc-schemas/safety/case-aliases.js.map +1 -1
- package/dist/doc-schemas/safety/case.d.ts.map +1 -1
- package/dist/doc-schemas/safety/case.js +4 -3
- package/dist/doc-schemas/safety/case.js.map +1 -1
- package/dist/doc-schemas/safety/evidence.js +3 -3
- package/dist/doc-schemas/safety/evidence.js.map +1 -1
- package/dist/doc-schemas/safety/foundation.d.ts.map +1 -1
- package/dist/doc-schemas/safety/foundation.js +5 -3
- package/dist/doc-schemas/safety/foundation.js.map +1 -1
- package/dist/doc-schemas/safety/holds.d.ts.map +1 -1
- package/dist/doc-schemas/safety/holds.js +4 -3
- package/dist/doc-schemas/safety/holds.js.map +1 -1
- package/dist/doc-schemas/safety/monitors.d.ts.map +1 -1
- package/dist/doc-schemas/safety/monitors.js +5 -3
- package/dist/doc-schemas/safety/monitors.js.map +1 -1
- package/dist/doc-schemas/safety/provenance.js +3 -3
- package/dist/doc-schemas/safety/provenance.js.map +1 -1
- package/dist/doc-schemas/safety/report.d.ts.map +1 -1
- package/dist/doc-schemas/safety/report.js +4 -3
- package/dist/doc-schemas/safety/report.js.map +1 -1
- package/dist/doc-schemas/safety/sagas.js +3 -3
- package/dist/doc-schemas/safety/sagas.js.map +1 -1
- package/dist/doc-schemas/social.d.ts +130 -0
- package/dist/doc-schemas/social.d.ts.map +1 -1
- package/dist/doc-schemas/social.js +91 -2
- package/dist/doc-schemas/social.js.map +1 -1
- package/dist/doc-schemas/square-announcement-jobs.d.ts +306 -14
- package/dist/doc-schemas/square-announcement-jobs.d.ts.map +1 -1
- package/dist/doc-schemas/square-announcement-jobs.js +15 -2
- package/dist/doc-schemas/square-announcement-jobs.js.map +1 -1
- package/dist/doc-schemas/user.js +3 -3
- package/dist/doc-schemas/user.js.map +1 -1
- package/dist/doc-schemas/work-project.js +2 -2
- package/dist/doc-schemas/work-project.js.map +1 -1
- package/dist/ids/chat-ids.d.ts +3 -2
- package/dist/ids/chat-ids.d.ts.map +1 -1
- package/dist/ids/chat-ids.js +3 -2
- package/dist/ids/chat-ids.js.map +1 -1
- package/dist/media/domain-events-admin.d.ts +0 -7
- package/dist/media/domain-events-admin.d.ts.map +1 -1
- package/dist/media/domain-events-admin.js +0 -11
- package/dist/media/domain-events-admin.js.map +1 -1
- package/dist/media/domain-events.d.ts +0 -6
- package/dist/media/domain-events.d.ts.map +1 -1
- package/dist/media/domain-events.js +1 -2
- package/dist/media/domain-events.js.map +1 -1
- package/dist/media/hall-library-target-fields.js +1 -1
- package/dist/media/hall-library-target-fields.js.map +1 -1
- package/dist/media/pending-media.d.ts +0 -54
- package/dist/media/pending-media.d.ts.map +1 -1
- package/dist/media/photodna-coverage.d.ts.map +1 -1
- package/dist/media/photodna-coverage.js +5 -3
- package/dist/media/photodna-coverage.js.map +1 -1
- package/dist/paths/related-ids.js +1 -1
- package/dist/paths/related-ids.js.map +1 -1
- package/dist/safety/ncii-intake-derivations.d.ts.map +1 -1
- package/dist/safety/ncii-intake-derivations.js +4 -2
- package/dist/safety/ncii-intake-derivations.js.map +1 -1
- package/dist/safety/resource-keys.d.ts.map +1 -1
- package/dist/safety/resource-keys.js +2 -1
- package/dist/safety/resource-keys.js.map +1 -1
- package/dist/schemas/chat.js +3 -3
- package/dist/schemas/chat.js.map +1 -1
- package/dist/schemas/ncii.js +1 -1
- package/dist/schemas/ncii.js.map +1 -1
- package/dist/schemas/notification.d.ts +8 -8
- package/dist/schemas/notification.d.ts.map +1 -1
- package/dist/schemas/notification.js +17 -11
- package/dist/schemas/notification.js.map +1 -1
- package/dist/types/social.d.ts +1 -45
- package/dist/types/social.d.ts.map +1 -1
- package/dist/types/social.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provenance.js","sourceRoot":"","sources":["../../../src/doc-schemas/safety/provenance.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,sBAAsB;AACtB,EAAE;AACF,8EAA8E;AAC9E,mFAAmF;AACnF,oFAAoF;AACpF,+EAA+E;AAC/E,EAAE;AACF
|
|
1
|
+
{"version":3,"file":"provenance.js","sourceRoot":"","sources":["../../../src/doc-schemas/safety/provenance.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,sBAAsB;AACtB,EAAE;AACF,8EAA8E;AAC9E,mFAAmF;AACnF,oFAAoF;AACpF,+EAA+E;AAC/E,EAAE;AACF,+EAA+E;AAC/E,mFAAmF;AACnF,8DAA8D;AAC9D,EAAE;AACF,gFAAgF;AAChF,kFAAkF;AAClF,gFAAgF;AAChF,2CAA2C;AAE3C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,8EAA8E;AAC9E,kCAAkC;AAClC,6EAA6E;AAC7E,+EAA+E;AAC/E,yEAAyE;AACzE,8EAA8E;AAE9E,6BAA6B;AAC7B,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,IAAI,CAAC;IAC9C,YAAY;IACZ,gBAAgB;IAChB,aAAa;IACb,OAAO;IACP,cAAc;CACf,CAAC,CAAC;AAGH;;;;;;8EAM8E;AAC9E,MAAM,CAAC,MAAM,mCAAmC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC;AAGvF;8CAC8C;AAC9C,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,CAAC,MAAM,CAAC;IACvD,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACjC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACrC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACvC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CACvC,CAAC,CAAC,MAAM,EAAE,CAAC;AAGZ;;;8EAG8E;AAC9E,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3B,IAAI,EAAE,yBAAyB;IAC/B,WAAW,EAAE,gCAAgC,EAAE,kCAAkC;IACjF,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,qEAAqE;IAClG,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,qCAAqC;IACrE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,cAAc,EAAE,mCAAmC;CACpD,CAAC,CAAC,MAAM,EAAE,CAAC;AAGZ,8EAA8E;AAC9E,mFAAmF;AACnF,+EAA+E;AAC/E,8CAA8C;AAC9C,8EAA8E;AAE9E,4EAA4E;AAC5E,MAAM,CAAC,MAAM,iCAAiC,GAAG;IAC/C,UAAU,EAAE,EAAE;IACd,cAAc,EAAE,EAAE;IAClB,WAAW,EAAE,EAAE;IACf,KAAK,EAAE,EAAE;IACT,yDAAyD;CACjD,CAAC","sourcesContent":["// Trust & Safety — the provenance cluster (Appendix A §A6, Finding-M2 concrete\n// retention profile).\n//\n// `eventProvenance/{eventId}` — server-only, restricted provenance for source\n// events (upload/message/login/report). On a hold the record is copied/linked into\n// the evidence manifest (retentionClass='casePromoted', hold-governed). A scheduled\n// deletion worker enforces the routine TTLs with a hold-aware no-delete guard.\n//\n// Every shape here is transcribed verbatim from the frozen Trust & Safety spec\n// (Appendix A §A6) — no invented values, no placeholders; the durable design owner\n// is ttt-prod docs/design/safety-evidence-control-profile.md.\n//\n// Collection note: this cluster introduces ONE NEW Firestore collection. Wiring\n// collections.ts / path-builders.ts / registry.ts is deferred to the app leg (the\n// orchestrator binds the schema + path builder there); the deterministic doc-id\n// shape is documented on the schema below.\n\nimport { z } from 'zod';\n\n// ===========================================================================\n// §A6 — eventProvenance/{eventId}\n// `eventId` is DETERMINISTIC per source event (e.g. the upload event id, the\n// message-send seq key, the login session key, or the report id) so a replayed\n// source event converges on the same provenance doc — never a duplicate.\n// ===========================================================================\n\n/** §A6 source-event kind. */\nexport const EventProvenanceKindSchema = z.enum([\n 'uploadInit',\n 'uploadFinalize',\n 'messageSend',\n 'login',\n 'reportSubmit',\n]);\nexport type EventProvenanceKind = z.infer<typeof EventProvenanceKindSchema>;\n\n/** §A6 retention class. Build defaults (counsel may only SHORTEN):\n * routine uploadInit/uploadFinalize/messageSend = 90 days;\n * login = 30 days;\n * reportSubmit = retained with the case.\n * On a hold the record is copied/linked into the evidence manifest as\n * 'casePromoted' (hold-governed); the scheduled deletion worker's hold-aware\n * no-delete guard never deletes a casePromoted record while a hold exists. */\nexport const EventProvenanceRetentionClassSchema = z.enum(['routine', 'casePromoted']);\nexport type EventProvenanceRetentionClass = z.infer<typeof EventProvenanceRetentionClassSchema>;\n\n/** §A6 `subjectRefs` — a TYPED object, NOT an open array. Each field is the\n * relevant id for the source event's kind. */\nexport const EventProvenanceSubjectRefsSchema = z.object({\n uid: z.string().min(1).optional(),\n assetId: z.string().min(1).optional(),\n channelId: z.string().min(1).optional(),\n messageSeq: z.number().optional(),\n reportId: z.string().min(1).optional(),\n}).strict();\nexport type EventProvenanceSubjectRefs = z.infer<typeof EventProvenanceSubjectRefsSchema>;\n\n/** `eventProvenance/{eventId}` — server-only, restricted provenance row. Doc id is\n * deterministic per source event. `trustedClientIp` is derived behind Firebase/\n * Cloudflare and is a restricted, INDEX-EXEMPT field; `userAgent` is truncated to\n * 256 chars + normalized. NO reuse for ads/engagement-profiling/analytics. */\nexport const EventProvenanceV1Schema = z.object({\n schemaVersion: z.literal(1),\n kind: EventProvenanceKindSchema,\n subjectRefs: EventProvenanceSubjectRefsSchema, // typed object, not an open array\n trustedClientIp: z.string(), // derived behind Firebase/Cloudflare; restricted, INDEX-EXEMPT field\n userAgent: z.string().max(256), // truncated to 256 chars, normalized\n at: z.number(),\n sessionId: z.string(),\n retentionClass: EventProvenanceRetentionClassSchema,\n}).strict();\nexport type EventProvenanceV1 = z.infer<typeof EventProvenanceV1Schema>;\n\n// ===========================================================================\n// §A6 — routine-retention build defaults (counsel may only SHORTEN). These are the\n// hold-aware deletion worker's TTL inputs; days, not ms. `reportSubmit` has NO\n// routine TTL — it is retained with the case.\n// ===========================================================================\n\n/** Routine TTL (days) by kind — build default; counsel may only shorten. */\nexport const EVENT_PROVENANCE_ROUTINE_TTL_DAYS = {\n uploadInit: 90,\n uploadFinalize: 90,\n messageSend: 90,\n login: 30,\n // reportSubmit: retained with the case — no routine TTL.\n} as const;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/doc-schemas/safety/report.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/doc-schemas/safety/report.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAmBxB;;;;;;;;;;;wDAWwD;AACxD,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAMjD;sFACsF;AACtF,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAe9B,CAAC;AACZ,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAMlF;wBACwB;AACxB,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAc9B,CAAC;AACZ,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAMlF;;;;4EAI4E;AAC5E,eAAO,MAAM,uBAAuB;;;;;;kBAMzB,CAAC;AACZ,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAMxE,mEAAmE;AACnE,eAAO,MAAM,+BAA+B;;;;;EAK1C,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF;;;wBAGwB;AACxB,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAa7B,CAAC;AACZ,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAMhF,4DAA4D;AAC5D,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAOhC,CAAC;AACZ,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAMtF;;;;yFAIyF;AACzF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAerB,CAAC;AACZ,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// Trust & Safety — the report spine (Appendix A §A1, Finding-H1).
|
|
2
2
|
//
|
|
3
3
|
// The canonical report-target contract produced by the server `resolveReportTarget()`
|
|
4
|
-
// resolver
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// resolver plus the protected report root + public projection. Every shape here is
|
|
5
|
+
// transcribed verbatim from the frozen Trust & Safety spec (Appendix A §A1) — no
|
|
6
|
+
// invented values, no placeholders; the durable design owner is ttt-prod
|
|
7
|
+
// docs/design/content-moderation-and-reporting.md.
|
|
7
8
|
//
|
|
8
9
|
// SHARED enums + the target locator come from ./foundation.js (the single source for
|
|
9
10
|
// every cross-cluster enum); they are NEVER redefined here.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/doc-schemas/safety/report.ts"],"names":[],"mappings":"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","sourcesContent":["// Trust & Safety — the report spine (Appendix A §A1, Finding-H1).\n//\n// The canonical report-target contract produced by the server `resolveReportTarget()`\n// resolver (Phase 1 step 2) plus the protected report root + public projection. Every\n// shape here is transcribed verbatim from docs/code_changes_needed/trust-and-safety/\n// IMPLEMENTATION_PLAN.md Appendix A §A1 — no invented values, no placeholders.\n//\n// SHARED enums + the target locator come from ./foundation.js (the single source for\n// every cross-cluster enum); they are NEVER redefined here.\n//\n// Collection bindings (../registry.ts): `contentReports/{reportId}` →\n// `ProtectedReportRootV1Schema`, `contentReports/{reportId}/publicProjection/{reportId}`\n// → `ReportPublicProjectionV1Schema`, and `activeReportGroups/{groupKey}` →\n// `ReportGroupV1Schema`. These REPLACED the legacy loose-string report/group schemas; the\n// report DOC shape now lives entirely in this file.\n\nimport { z } from 'zod';\nimport {\n ReportReasonSchema,\n ReportableItemTypeSchema,\n ProtectedForkSchema,\n TargetLocatorV1Schema,\n} from './foundation.js';\nimport {\n MAX_REPORT_NARRATIVE_LENGTH,\n MAX_REPORT_SNAPSHOT_TEXT_LENGTH,\n} from '../../constants/business.js';\n\n// ===========================================================================\n// Canonical-key version token. The key formulas are DEFINED below as comments\n// (deterministic IDs are documented, never computed in this schema layer); the\n// server resolver/writer computes them. Bump this token if any formula input\n// set or hashing scheme changes.\n// ===========================================================================\n\n/** Version token folded into every canonical key formula below.\n *\n * v2 (pre-launch, no migration): two coordinated formula-input changes ride this\n * single bump —\n * (1) `canonicalTargetKey` composition is now INJECTIVE: each field is\n * length-prefixed (`len:value`) before joining, so no pair of adjacent\n * attacker-controlled fields (chat `canonicalParentPath` + `canonicalItemId`)\n * can produce a colliding preimage across different (channel, message) pairs.\n * (2) `revision` is derived from CONTENT-relevant state only (the content-edit\n * stamp `updatedAt`, falling back to `createdAt`) — NEVER the Firestore write\n * clock (`updateTime`). Counter writes (likes/votes/follows) no longer fragment\n * report groups or defeat per-reporter dedupe. */\nexport const REPORT_CANONICAL_KEY_VERSION = 'v2';\n\n// ===========================================================================\n// A1 — Server resolver result (ResolvedReportTargetV1)\n// ===========================================================================\n\n/** The server resolver result. The client `reportedItemId`/`reportedUserId` are\n * hints only; every field here is re-derived server-side at the reported revision. */\nexport const ResolvedReportTargetV1Schema = z.object({\n schemaVersion: z.literal(1),\n itemType: ReportableItemTypeSchema,\n canonicalParentPath: z.string(), // server-derived parent doc path\n canonicalItemId: z.string(), // server-derived item id (NEVER the client hint)\n revision: z.number(), // reported content revision — derived from the content-edit stamp\n // (updatedAt, falling back to createdAt), NEVER the write clock (updateTime)\n ownerUid: z.string(), // server-derived owner/sender uid (NOT client-supplied)\n ownerBlockKey: z.string(), // derived owner block key\n mediaAssetId: z.string().optional(), // typed per surface\n channelId: z.string().optional(),\n messageId: z.string().optional(),\n attachmentId: z.string().optional(),\n locator: TargetLocatorV1Schema, // the §A11 discriminated locator for downstream hold/removal\n resolvedAt: z.number(),\n}).strict();\nexport type ResolvedReportTargetV1 = z.infer<typeof ResolvedReportTargetV1Schema>;\n\n// ===========================================================================\n// A1 — Sanitized frozen snapshot (ReportTargetSnapshotV1)\n// ===========================================================================\n\n/** The sanitized frozen snapshot captured at resolve time — no reporter identity,\n * no PII; immutable. */\nexport const ReportTargetSnapshotV1Schema = z.object({\n schemaVersion: z.literal(1),\n itemType: ReportableItemTypeSchema,\n canonicalItemId: z.string(),\n revision: z.number(),\n contentSummaryRef: z.string(), // pointer to a sanitized content snapshot (restricted)\n contentHashes: z.array(z.string()).max(16).optional(),\n // [H-11/EUAS-013/R14] The sanitized REPORTED CONTENT captured at report time, so an author can't edit\n // the violating text/media out before review and have the operator see only the cleaned live doc\n // (edit-to-evade). `capturedText` = the bounded reported text/title/description (NO PII);\n // `capturedMediaAssetIds` = the reported media asset ids. Frozen + immutable like the rest.\n capturedText: z.string().max(MAX_REPORT_SNAPSHOT_TEXT_LENGTH).optional(),\n capturedMediaAssetIds: z.array(z.string()).max(32).optional(),\n capturedAt: z.number(),\n}).strict();\nexport type ReportTargetSnapshotV1 = z.infer<typeof ReportTargetSnapshotV1Schema>;\n\n// ===========================================================================\n// A1 — Segregated reporter narrative (NarrativeRecordV1)\n// ===========================================================================\n\n/** The reporter's free-text narrative — segregated reporter PII. Lives in a\n * restricted subcollection (`contentReports/{reportId}/private/narrative`),\n * admin-read-only, client-write-forbidden; NEVER inlined on the report root or\n * the public projection. The frozen target snapshot is stored alongside it at\n * `contentReports/{reportId}/private/snapshot` (ReportTargetSnapshotV1). */\nexport const NarrativeRecordV1Schema = z.object({\n schemaVersion: z.literal(1),\n reportId: z.string().min(1),\n reporterUid: z.string().min(1), // stored so account-erasure can scrub the narrative\n text: z.string().min(1).max(MAX_REPORT_NARRATIVE_LENGTH),\n createdAt: z.number(),\n}).strict();\nexport type NarrativeRecordV1 = z.infer<typeof NarrativeRecordV1Schema>;\n\n// ===========================================================================\n// A1 — Protected report root (ProtectedReportRootV1)\n// ===========================================================================\n\n/** Cluster-local lifecycle status of the protected report root. */\nexport const ProtectedReportRootStatusSchema = z.enum([\n 'pending_review',\n 'grouped',\n 'actioned',\n 'dismissed',\n]);\nexport type ProtectedReportRootStatus = z.infer<typeof ProtectedReportRootStatusSchema>;\n\n/** The protected report root (restricted — reporter identity + narrative segregated).\n * FUTURE shape of the EXISTING `contentReports/{reportId}` collection (rebinding\n * deferred to the app leg). `protectedFork` is set ONLY by the protected branch\n * (Phase 1 step 2a). */\nexport const ProtectedReportRootV1Schema = z.object({\n schemaVersion: z.literal(1),\n reportId: z.string().min(1), // = sha256(lp(version) + lp(reporterUid) + lp(canonicalTargetKey)) — see injective formulas below\n reporterUid: z.string().min(1),\n reason: ReportReasonSchema,\n resolvedTarget: ResolvedReportTargetV1Schema,\n snapshotRef: z.string(), // → ReportTargetSnapshotV1\n canonicalTargetKey: z.string(), // see formula below\n narrativeRef: z.string().optional(),\n protectedFork: ProtectedForkSchema.optional(), // set ONLY by the protected branch (Phase 1 step 2a)\n status: ProtectedReportRootStatusSchema,\n createdAt: z.number(),\n updatedAt: z.number(),\n}).strict();\nexport type ProtectedReportRootV1 = z.infer<typeof ProtectedReportRootV1Schema>;\n\n// ===========================================================================\n// A1 — Public projection (ReportPublicProjectionV1)\n// ===========================================================================\n\n/** Public projection — no reporter identity / narrative. */\nexport const ReportPublicProjectionV1Schema = z.object({\n schemaVersion: z.literal(1),\n reportId: z.string().min(1),\n itemType: ReportableItemTypeSchema,\n reason: ReportReasonSchema,\n status: ProtectedReportRootStatusSchema,\n createdAt: z.number(),\n}).strict();\nexport type ReportPublicProjectionV1 = z.infer<typeof ReportPublicProjectionV1Schema>;\n\n// ===========================================================================\n// A1 — Report group (ReportGroupV1) — dedup/count group keyed by canonicalTargetKey\n// ===========================================================================\n\n/** The dedup/count group for all reports of the same target+revision. The group key\n * IS the `canonicalTargetKey` (== reportGroupKey). NO trusted client owner field — the\n * owner lives on the restricted ProtectedReportRootV1 (resolvedTarget.ownerUid), never\n * here. An app-side trigger maintains the counts; this is the public-ish group surface\n * the admin browse + admin-task queue read. Bound to `activeReportGroups/{groupKey}`. */\nexport const ReportGroupV1Schema = z.object({\n schemaVersion: z.literal(1),\n groupKey: z.string().min(1), // = canonicalTargetKey\n itemType: ReportableItemTypeSchema,\n totalReports: z.number(),\n highestReasonScore: z.number(),\n lastReportAt: z.number(),\n latestReason: ReportReasonSchema,\n // [R12] close-lifecycle states. An ordinary report's admin close (hide/remove/restore) moves the\n // group to `processing` and STAYS in the queue — it is NEVER cleared on the click. The group flips\n // to the terminal `resolved` ONLY when the edge serving-deny (hide/remove) or block-clear (restore)\n // VERIFIES. If the content-serving-sync saga exhausts its retries (dead-letter), the group moves to\n // `failed` — kept in the queue, surfaced in the Safety Console failed-jobs view, with a Restart that\n // re-arms the job. Both are explicit values (never derived).\n status: z.enum(['pending', 'reviewing', 'processing', 'failed', 'resolved']),\n}).strict();\nexport type ReportGroupV1 = z.infer<typeof ReportGroupV1Schema>;\n\n// ===========================================================================\n// Canonical keys (DEFINED — not inline prose). Computed server-side by the\n// resolver/writer; documented here, NOT computed in this schema layer. The\n// `version` token in each formula is `REPORT_CANONICAL_KEY_VERSION` above (now v2).\n//\n// canonicalTargetKey = sha256( lp(version) + lp(itemType) + lp(canonicalParentPath)\n// + lp(canonicalItemId) + lp(String(revision)) )\n// // v2: INJECTIVE composition. lp(s) length-prefixes each field\n// // as `<byteLength>:<s>` (or hash each field separately) so no ':'\n// // ambiguity can straddle two adjacent attacker-controlled fields.\n// // The old `[...].join(':')` was collision-prone for chat targets,\n// // whose canonicalParentPath + canonicalItemId are BOTH client-\n// // derived strings (synthetic:chat:<channelRef> / <messageId>).\n// // `revision` is the CONTENT revision (content-edit stamp, never\n// // the Firestore write clock) — see REPORT_CANONICAL_KEY_VERSION.\n//\n// reportId = sha256( lp(version) + lp(reporterUid) + lp(canonicalTargetKey) )\n// // deterministic dup-key: one report per reporter+target+revision.\n// // This determinism is what makes the protected branch's retry a\n// // no-op on the already-committed hold (Phase 1 step 2a).\n//\n// reportGroupKey = canonicalTargetKey\n// // grouping key: all reporters of the same target+revision.\n// ===========================================================================\n"]}
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/doc-schemas/safety/report.ts"],"names":[],"mappings":"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","sourcesContent":["// Trust & Safety — the report spine (Appendix A §A1, Finding-H1).\n//\n// The canonical report-target contract produced by the server `resolveReportTarget()`\n// resolver plus the protected report root + public projection. Every shape here is\n// transcribed verbatim from the frozen Trust & Safety spec (Appendix A §A1) — no\n// invented values, no placeholders; the durable design owner is ttt-prod\n// docs/design/content-moderation-and-reporting.md.\n//\n// SHARED enums + the target locator come from ./foundation.js (the single source for\n// every cross-cluster enum); they are NEVER redefined here.\n//\n// Collection bindings (../registry.ts): `contentReports/{reportId}` →\n// `ProtectedReportRootV1Schema`, `contentReports/{reportId}/publicProjection/{reportId}`\n// → `ReportPublicProjectionV1Schema`, and `activeReportGroups/{groupKey}` →\n// `ReportGroupV1Schema`. These REPLACED the legacy loose-string report/group schemas; the\n// report DOC shape now lives entirely in this file.\n\nimport { z } from 'zod';\nimport {\n ReportReasonSchema,\n ReportableItemTypeSchema,\n ProtectedForkSchema,\n TargetLocatorV1Schema,\n} from './foundation.js';\nimport {\n MAX_REPORT_NARRATIVE_LENGTH,\n MAX_REPORT_SNAPSHOT_TEXT_LENGTH,\n} from '../../constants/business.js';\n\n// ===========================================================================\n// Canonical-key version token. The key formulas are DEFINED below as comments\n// (deterministic IDs are documented, never computed in this schema layer); the\n// server resolver/writer computes them. Bump this token if any formula input\n// set or hashing scheme changes.\n// ===========================================================================\n\n/** Version token folded into every canonical key formula below.\n *\n * v2 (pre-launch, no migration): two coordinated formula-input changes ride this\n * single bump —\n * (1) `canonicalTargetKey` composition is now INJECTIVE: each field is\n * length-prefixed (`len:value`) before joining, so no pair of adjacent\n * attacker-controlled fields (chat `canonicalParentPath` + `canonicalItemId`)\n * can produce a colliding preimage across different (channel, message) pairs.\n * (2) `revision` is derived from CONTENT-relevant state only (the content-edit\n * stamp `updatedAt`, falling back to `createdAt`) — NEVER the Firestore write\n * clock (`updateTime`). Counter writes (likes/votes/follows) no longer fragment\n * report groups or defeat per-reporter dedupe. */\nexport const REPORT_CANONICAL_KEY_VERSION = 'v2';\n\n// ===========================================================================\n// A1 — Server resolver result (ResolvedReportTargetV1)\n// ===========================================================================\n\n/** The server resolver result. The client `reportedItemId`/`reportedUserId` are\n * hints only; every field here is re-derived server-side at the reported revision. */\nexport const ResolvedReportTargetV1Schema = z.object({\n schemaVersion: z.literal(1),\n itemType: ReportableItemTypeSchema,\n canonicalParentPath: z.string(), // server-derived parent doc path\n canonicalItemId: z.string(), // server-derived item id (NEVER the client hint)\n revision: z.number(), // reported content revision — derived from the content-edit stamp\n // (updatedAt, falling back to createdAt), NEVER the write clock (updateTime)\n ownerUid: z.string(), // server-derived owner/sender uid (NOT client-supplied)\n ownerBlockKey: z.string(), // derived owner block key\n mediaAssetId: z.string().optional(), // typed per surface\n channelId: z.string().optional(),\n messageId: z.string().optional(),\n attachmentId: z.string().optional(),\n locator: TargetLocatorV1Schema, // the §A11 discriminated locator for downstream hold/removal\n resolvedAt: z.number(),\n}).strict();\nexport type ResolvedReportTargetV1 = z.infer<typeof ResolvedReportTargetV1Schema>;\n\n// ===========================================================================\n// A1 — Sanitized frozen snapshot (ReportTargetSnapshotV1)\n// ===========================================================================\n\n/** The sanitized frozen snapshot captured at resolve time — no reporter identity,\n * no PII; immutable. */\nexport const ReportTargetSnapshotV1Schema = z.object({\n schemaVersion: z.literal(1),\n itemType: ReportableItemTypeSchema,\n canonicalItemId: z.string(),\n revision: z.number(),\n contentSummaryRef: z.string(), // pointer to a sanitized content snapshot (restricted)\n contentHashes: z.array(z.string()).max(16).optional(),\n // [H-11/EUAS-013/R14] The sanitized REPORTED CONTENT captured at report time, so an author can't edit\n // the violating text/media out before review and have the operator see only the cleaned live doc\n // (edit-to-evade). `capturedText` = the bounded reported text/title/description (NO PII);\n // `capturedMediaAssetIds` = the reported media asset ids. Frozen + immutable like the rest.\n capturedText: z.string().max(MAX_REPORT_SNAPSHOT_TEXT_LENGTH).optional(),\n capturedMediaAssetIds: z.array(z.string()).max(32).optional(),\n capturedAt: z.number(),\n}).strict();\nexport type ReportTargetSnapshotV1 = z.infer<typeof ReportTargetSnapshotV1Schema>;\n\n// ===========================================================================\n// A1 — Segregated reporter narrative (NarrativeRecordV1)\n// ===========================================================================\n\n/** The reporter's free-text narrative — segregated reporter PII. Lives in a\n * restricted subcollection (`contentReports/{reportId}/private/narrative`),\n * admin-read-only, client-write-forbidden; NEVER inlined on the report root or\n * the public projection. The frozen target snapshot is stored alongside it at\n * `contentReports/{reportId}/private/snapshot` (ReportTargetSnapshotV1). */\nexport const NarrativeRecordV1Schema = z.object({\n schemaVersion: z.literal(1),\n reportId: z.string().min(1),\n reporterUid: z.string().min(1), // stored so account-erasure can scrub the narrative\n text: z.string().min(1).max(MAX_REPORT_NARRATIVE_LENGTH),\n createdAt: z.number(),\n}).strict();\nexport type NarrativeRecordV1 = z.infer<typeof NarrativeRecordV1Schema>;\n\n// ===========================================================================\n// A1 — Protected report root (ProtectedReportRootV1)\n// ===========================================================================\n\n/** Cluster-local lifecycle status of the protected report root. */\nexport const ProtectedReportRootStatusSchema = z.enum([\n 'pending_review',\n 'grouped',\n 'actioned',\n 'dismissed',\n]);\nexport type ProtectedReportRootStatus = z.infer<typeof ProtectedReportRootStatusSchema>;\n\n/** The protected report root (restricted — reporter identity + narrative segregated).\n * FUTURE shape of the EXISTING `contentReports/{reportId}` collection (rebinding\n * deferred to the app leg). `protectedFork` is set ONLY by the protected branch\n * (Phase 1 step 2a). */\nexport const ProtectedReportRootV1Schema = z.object({\n schemaVersion: z.literal(1),\n reportId: z.string().min(1), // = sha256(lp(version) + lp(reporterUid) + lp(canonicalTargetKey)) — see injective formulas below\n reporterUid: z.string().min(1),\n reason: ReportReasonSchema,\n resolvedTarget: ResolvedReportTargetV1Schema,\n snapshotRef: z.string(), // → ReportTargetSnapshotV1\n canonicalTargetKey: z.string(), // see formula below\n narrativeRef: z.string().optional(),\n protectedFork: ProtectedForkSchema.optional(), // set ONLY by the protected branch (Phase 1 step 2a)\n status: ProtectedReportRootStatusSchema,\n createdAt: z.number(),\n updatedAt: z.number(),\n}).strict();\nexport type ProtectedReportRootV1 = z.infer<typeof ProtectedReportRootV1Schema>;\n\n// ===========================================================================\n// A1 — Public projection (ReportPublicProjectionV1)\n// ===========================================================================\n\n/** Public projection — no reporter identity / narrative. */\nexport const ReportPublicProjectionV1Schema = z.object({\n schemaVersion: z.literal(1),\n reportId: z.string().min(1),\n itemType: ReportableItemTypeSchema,\n reason: ReportReasonSchema,\n status: ProtectedReportRootStatusSchema,\n createdAt: z.number(),\n}).strict();\nexport type ReportPublicProjectionV1 = z.infer<typeof ReportPublicProjectionV1Schema>;\n\n// ===========================================================================\n// A1 — Report group (ReportGroupV1) — dedup/count group keyed by canonicalTargetKey\n// ===========================================================================\n\n/** The dedup/count group for all reports of the same target+revision. The group key\n * IS the `canonicalTargetKey` (== reportGroupKey). NO trusted client owner field — the\n * owner lives on the restricted ProtectedReportRootV1 (resolvedTarget.ownerUid), never\n * here. An app-side trigger maintains the counts; this is the public-ish group surface\n * the admin browse + admin-task queue read. Bound to `activeReportGroups/{groupKey}`. */\nexport const ReportGroupV1Schema = z.object({\n schemaVersion: z.literal(1),\n groupKey: z.string().min(1), // = canonicalTargetKey\n itemType: ReportableItemTypeSchema,\n totalReports: z.number(),\n highestReasonScore: z.number(),\n lastReportAt: z.number(),\n latestReason: ReportReasonSchema,\n // [R12] close-lifecycle states. An ordinary report's admin close (hide/remove/restore) moves the\n // group to `processing` and STAYS in the queue — it is NEVER cleared on the click. The group flips\n // to the terminal `resolved` ONLY when the edge serving-deny (hide/remove) or block-clear (restore)\n // VERIFIES. If the content-serving-sync saga exhausts its retries (dead-letter), the group moves to\n // `failed` — kept in the queue, surfaced in the Safety Console failed-jobs view, with a Restart that\n // re-arms the job. Both are explicit values (never derived).\n status: z.enum(['pending', 'reviewing', 'processing', 'failed', 'resolved']),\n}).strict();\nexport type ReportGroupV1 = z.infer<typeof ReportGroupV1Schema>;\n\n// ===========================================================================\n// Canonical keys (DEFINED — not inline prose). Computed server-side by the\n// resolver/writer; documented here, NOT computed in this schema layer. The\n// `version` token in each formula is `REPORT_CANONICAL_KEY_VERSION` above (now v2).\n//\n// canonicalTargetKey = sha256( lp(version) + lp(itemType) + lp(canonicalParentPath)\n// + lp(canonicalItemId) + lp(String(revision)) )\n// // v2: INJECTIVE composition. lp(s) length-prefixes each field\n// // as `<byteLength>:<s>` (or hash each field separately) so no ':'\n// // ambiguity can straddle two adjacent attacker-controlled fields.\n// // The old `[...].join(':')` was collision-prone for chat targets,\n// // whose canonicalParentPath + canonicalItemId are BOTH client-\n// // derived strings (synthetic:chat:<channelRef> / <messageId>).\n// // `revision` is the CONTENT revision (content-edit stamp, never\n// // the Firestore write clock) — see REPORT_CANONICAL_KEY_VERSION.\n//\n// reportId = sha256( lp(version) + lp(reporterUid) + lp(canonicalTargetKey) )\n// // deterministic dup-key: one report per reporter+target+revision.\n// // This determinism is what makes the protected branch's retry a\n// // no-op on the already-committed hold (Phase 1 step 2a).\n//\n// reportGroupKey = canonicalTargetKey\n// // grouping key: all reporters of the same target+revision.\n// ===========================================================================\n"]}
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
// no doc ever lies about quarantine completion. Each saga is idempotent by
|
|
8
8
|
// `commandId`; the reconciler verifies external state, never a prior response.
|
|
9
9
|
//
|
|
10
|
-
// Every shape here is transcribed verbatim from
|
|
11
|
-
//
|
|
12
|
-
//
|
|
10
|
+
// Every shape here is transcribed verbatim from the frozen Trust & Safety spec
|
|
11
|
+
// (Appendix A §A5) — no invented values, no placeholders; the durable design owner
|
|
12
|
+
// is ttt-prod docs/design/csam-detection-and-response.md.
|
|
13
13
|
//
|
|
14
14
|
// SHARED enums come from ./foundation.js (the single source for every
|
|
15
15
|
// cross-cluster enum); they are NEVER redefined here. This cluster IMPORTS
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sagas.js","sourceRoot":"","sources":["../../../src/doc-schemas/safety/sagas.ts"],"names":[],"mappings":"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","sourcesContent":["// Trust & Safety — three independent durable sagas + the closure coordinator\n// (Appendix A §A5, Finding-H5 / Finding-H6).\n//\n// Quarantine, NCMEC reporting, and account action are THREE independent durable\n// sagas. The quarantine job NEVER folds reporting or account action into its\n// phases — it can be `quarantineComplete` while the other two are still open, so\n// no doc ever lies about quarantine completion. Each saga is idempotent by\n// `commandId`; the reconciler verifies external state, never a prior response.\n//\n// Every shape here is transcribed verbatim from docs/code_changes_needed/\n// trust-and-safety/IMPLEMENTATION_PLAN.md Appendix A §A5 — no invented values, no\n// placeholders.\n//\n// SHARED enums come from ./foundation.js (the single source for every\n// cross-cluster enum); they are NEVER redefined here. This cluster IMPORTS\n// NcmecSubmissionStateSchema (the §A9 reporting lifecycle) from foundation.\n//\n// Collection note: this cluster introduces THREE NEW Firestore collections plus\n// one overflow subcollection under `quarantineSagaJobs`. Wiring collections.ts /\n// path-builders.ts / registry.ts is deferred to the app leg (the orchestrator\n// binds the schemas + path builders there); the composite doc-id shapes are\n// documented on each schema below.\n//\n// CLOSURE COORDINATOR — there is NO doc for it. The case is \"operationally\n// resolvable\" via a PROJECTION computed on the case root from the three\n// independent statuses (see the closure predicate comment at the bottom of this\n// file). Do NOT create a closure-coordinator document.\n\nimport { z } from 'zod';\nimport { NcmecSubmissionStateSchema } from './foundation.js';\n\n// ===========================================================================\n// A5 — quarantineSagaJobs/{caseId}\n// Quarantine ONLY (hold → serving-deny → evidence → active-byte removal). Never\n// folds reporting / account action. Doc id is the `caseId`.\n// ===========================================================================\n\n/** Top-level run status of the quarantine saga. */\nexport const QuarantineStatusSchema = z.enum([\n 'running',\n 'quarantineComplete',\n 'deadLetter',\n]);\nexport type QuarantineStatus = z.infer<typeof QuarantineStatusSchema>;\n\n/** The FULL quarantine phase enum. `servingDeny*` precede `evidenceCopying`;\n * `activeCopiesRemoval*` require `evidenceVerified`. */\nexport const QuarantinePhaseSchema = z.enum([\n 'holdCommitted',\n 'sourceManifestCaptured',\n 'servingDenyRequested',\n 'servingDenyVerified',\n 'evidenceCopying',\n 'evidenceVerified',\n 'activeCopiesRemovalRequested',\n 'activeCopiesRemovalVerified',\n 'quarantineComplete',\n]);\nexport type QuarantinePhase = z.infer<typeof QuarantinePhaseSchema>;\n\n/** `quarantineSagaJobs/{caseId}` — quarantine-only durable saga. Doc id is the\n * `caseId`. `commandId` is deterministic per `(caseId, phase)`. `relatedAssetIds`\n * is bounded at MAX 256; overflow → `…/relatedAssets/{assetId}`. */\nexport const QuarantineSagaJobV1Schema = z.object({\n schemaVersion: z.literal(1),\n caseId: z.string().min(1),\n quarantineStatus: QuarantineStatusSchema,\n phase: QuarantinePhaseSchema,\n phaseVersion: z.number(),\n commandId: z.string().min(1), // deterministic per (caseId, phase)\n relatedAssetIds: z.array(z.string().min(1)).max(256), // bounded; overflow → …/relatedAssets/{assetId}\n attemptCount: z.number(),\n nextAttemptAt: z.number(),\n leaseOwner: z.string().min(1).optional(),\n leaseExpiresAt: z.number().optional(),\n lastError: z.string().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n completedAt: z.number().optional(),\n}).strict();\nexport type QuarantineSagaJobV1 = z.infer<typeof QuarantineSagaJobV1Schema>;\n\n/** `quarantineSagaJobs/{caseId}/relatedAssets/{assetId}` — overflow beyond\n * `relatedAssetIds` MAX 256. Doc id is the `assetId`. */\nexport const QuarantineSagaRelatedAssetV1Schema = z.object({\n rootIngestId: z.string().min(1),\n addedAt: z.number(),\n}).strict();\nexport type QuarantineSagaRelatedAssetV1 = z.infer<typeof QuarantineSagaRelatedAssetV1Schema>;\n\n// ===========================================================================\n// A5 — ncmecSubmissionJobs/{caseId}__{submissionId}\n// The API/manual reporting lifecycle. Drives the …/ncmecSubmissions/{submissionId}\n// records + …/attempts; owns retries, ambiguity reconciliation, manual-portal\n// fallback, supplemental/correction. Doc id is the deterministic composite\n// `{caseId}__{submissionId}`.\n// ===========================================================================\n\n/** `ncmecSubmissionJobs/{caseId}__{submissionId}` — reporting-lifecycle durable\n * saga. Doc id is the deterministic composite `{caseId}__{submissionId}`.\n * `commandId` is the saga's idempotency key. `state` is the §A9\n * NcmecSubmissionState (NO `notRequired` — that lives on ReportDisposition). */\nexport const NcmecSubmissionJobV1Schema = z.object({\n schemaVersion: z.literal(1),\n caseId: z.string().min(1),\n submissionId: z.string().min(1),\n state: NcmecSubmissionStateSchema, // §A9 NcmecSubmissionState\n commandId: z.string().min(1),\n attemptCount: z.number(),\n nextAttemptAt: z.number(),\n leaseOwner: z.string().min(1).optional(),\n leaseExpiresAt: z.number().optional(),\n lastErrorCode: z.string().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n completedAt: z.number().optional(),\n}).strict();\nexport type NcmecSubmissionJobV1 = z.infer<typeof NcmecSubmissionJobV1Schema>;\n\n// ===========================================================================\n// A5 — accountActionCommands/{caseId}__{uid}\n// The per-account safety-action lifecycle. Doc id is the deterministic composite\n// `{caseId}__{uid}`. A dead-letter replay never re-issues a second ban\n// (idempotent `commandId` + merge-generation check).\n// ===========================================================================\n\n/** The per-account safety action this command applies. `reinstate` is the inverse of a\n * restriction: it restores `status:'active'`, clears `privateData.safetyLocked`, and\n * re-enables a disabled Auth account (used to lift a safety-lock / ban when a case is\n * closed as a false positive). The case's safety HOLDS are released separately by the\n * case-close path, not by this action. */\nexport const AccountActionSchema = z.enum([\n 'ban',\n 'suspend',\n 'watch',\n 'safetyLocked',\n 'reinstate',\n 'none',\n]);\nexport type AccountAction = z.infer<typeof AccountActionSchema>;\n\n/** Where the action originated. */\nexport const AccountActionSourceSchema = z.enum(['autoHash', 'operator']);\nexport type AccountActionSource = z.infer<typeof AccountActionSourceSchema>;\n\n/** Command lifecycle status. `deadLetter` parks a command whose attempt budget is\n * exhausted (the drain query stops matching it); an operator replays it via\n * `adminReplayDeadLetter` (generic reset to `pending`). */\nexport const AccountActionStatusSchema = z.enum(['pending', 'applied', 'reverted', 'deadLetter']);\nexport type AccountActionStatus = z.infer<typeof AccountActionStatusSchema>;\n\n/** `accountActionCommands/{caseId}__{uid}` — per-account safety-action durable\n * saga. Doc id is the deterministic composite `{caseId}__{uid}`. `commandId` is\n * the saga's idempotency key. */\nexport const AccountActionCommandV1Schema = z.object({\n schemaVersion: z.literal(1),\n caseId: z.string().min(1),\n targetUid: z.string().min(1),\n action: AccountActionSchema,\n source: AccountActionSourceSchema,\n status: AccountActionStatusSchema,\n commandId: z.string().min(1),\n attemptCount: z.number(),\n nextAttemptAt: z.number(),\n leaseOwner: z.string().min(1).optional(),\n leaseExpiresAt: z.number().optional(),\n lastError: z.string().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n completedAt: z.number().optional(),\n}).strict();\nexport type AccountActionCommandV1 = z.infer<typeof AccountActionCommandV1Schema>;\n\n// ===========================================================================\n// A5 — CLOSURE COORDINATOR (a PROJECTION on the case root, NOT a doc).\n//\n// There is NO closure-coordinator document. The case is OPERATIONALLY RESOLVABLE\n// only when ALL THREE independent statuses agree:\n//\n// 1. quarantine : QuarantineSagaJobV1.quarantineStatus === 'quarantineComplete'\n// 2. account action : case-root accountActionStatus ∈ { 'noAccountActionRequired', 'resolved' }\n// 3. reporting predicate (Finding-H6):\n// ( case-root reportDisposition === 'notRequired' AND no NCMEC submission was ever opened )\n// OR ( the submission's state === 'completed' )\n//\n// Reporting closure keys on the case-root `reportDisposition` (§A9\n// ReportDisposition), NOT on a `notRequired` submission state (that value no\n// longer exists on NcmecSubmissionState). A `notRequired`-disposed case is never\n// blocked on a non-existent report; a human-review case stays\n// `operatorDecisionPending` until the operator commands the action.\n// ===========================================================================\n"]}
|
|
1
|
+
{"version":3,"file":"sagas.js","sourceRoot":"","sources":["../../../src/doc-schemas/safety/sagas.ts"],"names":[],"mappings":"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","sourcesContent":["// Trust & Safety — three independent durable sagas + the closure coordinator\n// (Appendix A §A5, Finding-H5 / Finding-H6).\n//\n// Quarantine, NCMEC reporting, and account action are THREE independent durable\n// sagas. The quarantine job NEVER folds reporting or account action into its\n// phases — it can be `quarantineComplete` while the other two are still open, so\n// no doc ever lies about quarantine completion. Each saga is idempotent by\n// `commandId`; the reconciler verifies external state, never a prior response.\n//\n// Every shape here is transcribed verbatim from the frozen Trust & Safety spec\n// (Appendix A §A5) — no invented values, no placeholders; the durable design owner\n// is ttt-prod docs/design/csam-detection-and-response.md.\n//\n// SHARED enums come from ./foundation.js (the single source for every\n// cross-cluster enum); they are NEVER redefined here. This cluster IMPORTS\n// NcmecSubmissionStateSchema (the §A9 reporting lifecycle) from foundation.\n//\n// Collection note: this cluster introduces THREE NEW Firestore collections plus\n// one overflow subcollection under `quarantineSagaJobs`. Wiring collections.ts /\n// path-builders.ts / registry.ts is deferred to the app leg (the orchestrator\n// binds the schemas + path builders there); the composite doc-id shapes are\n// documented on each schema below.\n//\n// CLOSURE COORDINATOR — there is NO doc for it. The case is \"operationally\n// resolvable\" via a PROJECTION computed on the case root from the three\n// independent statuses (see the closure predicate comment at the bottom of this\n// file). Do NOT create a closure-coordinator document.\n\nimport { z } from 'zod';\nimport { NcmecSubmissionStateSchema } from './foundation.js';\n\n// ===========================================================================\n// A5 — quarantineSagaJobs/{caseId}\n// Quarantine ONLY (hold → serving-deny → evidence → active-byte removal). Never\n// folds reporting / account action. Doc id is the `caseId`.\n// ===========================================================================\n\n/** Top-level run status of the quarantine saga. */\nexport const QuarantineStatusSchema = z.enum([\n 'running',\n 'quarantineComplete',\n 'deadLetter',\n]);\nexport type QuarantineStatus = z.infer<typeof QuarantineStatusSchema>;\n\n/** The FULL quarantine phase enum. `servingDeny*` precede `evidenceCopying`;\n * `activeCopiesRemoval*` require `evidenceVerified`. */\nexport const QuarantinePhaseSchema = z.enum([\n 'holdCommitted',\n 'sourceManifestCaptured',\n 'servingDenyRequested',\n 'servingDenyVerified',\n 'evidenceCopying',\n 'evidenceVerified',\n 'activeCopiesRemovalRequested',\n 'activeCopiesRemovalVerified',\n 'quarantineComplete',\n]);\nexport type QuarantinePhase = z.infer<typeof QuarantinePhaseSchema>;\n\n/** `quarantineSagaJobs/{caseId}` — quarantine-only durable saga. Doc id is the\n * `caseId`. `commandId` is deterministic per `(caseId, phase)`. `relatedAssetIds`\n * is bounded at MAX 256; overflow → `…/relatedAssets/{assetId}`. */\nexport const QuarantineSagaJobV1Schema = z.object({\n schemaVersion: z.literal(1),\n caseId: z.string().min(1),\n quarantineStatus: QuarantineStatusSchema,\n phase: QuarantinePhaseSchema,\n phaseVersion: z.number(),\n commandId: z.string().min(1), // deterministic per (caseId, phase)\n relatedAssetIds: z.array(z.string().min(1)).max(256), // bounded; overflow → …/relatedAssets/{assetId}\n attemptCount: z.number(),\n nextAttemptAt: z.number(),\n leaseOwner: z.string().min(1).optional(),\n leaseExpiresAt: z.number().optional(),\n lastError: z.string().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n completedAt: z.number().optional(),\n}).strict();\nexport type QuarantineSagaJobV1 = z.infer<typeof QuarantineSagaJobV1Schema>;\n\n/** `quarantineSagaJobs/{caseId}/relatedAssets/{assetId}` — overflow beyond\n * `relatedAssetIds` MAX 256. Doc id is the `assetId`. */\nexport const QuarantineSagaRelatedAssetV1Schema = z.object({\n rootIngestId: z.string().min(1),\n addedAt: z.number(),\n}).strict();\nexport type QuarantineSagaRelatedAssetV1 = z.infer<typeof QuarantineSagaRelatedAssetV1Schema>;\n\n// ===========================================================================\n// A5 — ncmecSubmissionJobs/{caseId}__{submissionId}\n// The API/manual reporting lifecycle. Drives the …/ncmecSubmissions/{submissionId}\n// records + …/attempts; owns retries, ambiguity reconciliation, manual-portal\n// fallback, supplemental/correction. Doc id is the deterministic composite\n// `{caseId}__{submissionId}`.\n// ===========================================================================\n\n/** `ncmecSubmissionJobs/{caseId}__{submissionId}` — reporting-lifecycle durable\n * saga. Doc id is the deterministic composite `{caseId}__{submissionId}`.\n * `commandId` is the saga's idempotency key. `state` is the §A9\n * NcmecSubmissionState (NO `notRequired` — that lives on ReportDisposition). */\nexport const NcmecSubmissionJobV1Schema = z.object({\n schemaVersion: z.literal(1),\n caseId: z.string().min(1),\n submissionId: z.string().min(1),\n state: NcmecSubmissionStateSchema, // §A9 NcmecSubmissionState\n commandId: z.string().min(1),\n attemptCount: z.number(),\n nextAttemptAt: z.number(),\n leaseOwner: z.string().min(1).optional(),\n leaseExpiresAt: z.number().optional(),\n lastErrorCode: z.string().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n completedAt: z.number().optional(),\n}).strict();\nexport type NcmecSubmissionJobV1 = z.infer<typeof NcmecSubmissionJobV1Schema>;\n\n// ===========================================================================\n// A5 — accountActionCommands/{caseId}__{uid}\n// The per-account safety-action lifecycle. Doc id is the deterministic composite\n// `{caseId}__{uid}`. A dead-letter replay never re-issues a second ban\n// (idempotent `commandId` + merge-generation check).\n// ===========================================================================\n\n/** The per-account safety action this command applies. `reinstate` is the inverse of a\n * restriction: it restores `status:'active'`, clears `privateData.safetyLocked`, and\n * re-enables a disabled Auth account (used to lift a safety-lock / ban when a case is\n * closed as a false positive). The case's safety HOLDS are released separately by the\n * case-close path, not by this action. */\nexport const AccountActionSchema = z.enum([\n 'ban',\n 'suspend',\n 'watch',\n 'safetyLocked',\n 'reinstate',\n 'none',\n]);\nexport type AccountAction = z.infer<typeof AccountActionSchema>;\n\n/** Where the action originated. */\nexport const AccountActionSourceSchema = z.enum(['autoHash', 'operator']);\nexport type AccountActionSource = z.infer<typeof AccountActionSourceSchema>;\n\n/** Command lifecycle status. `deadLetter` parks a command whose attempt budget is\n * exhausted (the drain query stops matching it); an operator replays it via\n * `adminReplayDeadLetter` (generic reset to `pending`). */\nexport const AccountActionStatusSchema = z.enum(['pending', 'applied', 'reverted', 'deadLetter']);\nexport type AccountActionStatus = z.infer<typeof AccountActionStatusSchema>;\n\n/** `accountActionCommands/{caseId}__{uid}` — per-account safety-action durable\n * saga. Doc id is the deterministic composite `{caseId}__{uid}`. `commandId` is\n * the saga's idempotency key. */\nexport const AccountActionCommandV1Schema = z.object({\n schemaVersion: z.literal(1),\n caseId: z.string().min(1),\n targetUid: z.string().min(1),\n action: AccountActionSchema,\n source: AccountActionSourceSchema,\n status: AccountActionStatusSchema,\n commandId: z.string().min(1),\n attemptCount: z.number(),\n nextAttemptAt: z.number(),\n leaseOwner: z.string().min(1).optional(),\n leaseExpiresAt: z.number().optional(),\n lastError: z.string().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n completedAt: z.number().optional(),\n}).strict();\nexport type AccountActionCommandV1 = z.infer<typeof AccountActionCommandV1Schema>;\n\n// ===========================================================================\n// A5 — CLOSURE COORDINATOR (a PROJECTION on the case root, NOT a doc).\n//\n// There is NO closure-coordinator document. The case is OPERATIONALLY RESOLVABLE\n// only when ALL THREE independent statuses agree:\n//\n// 1. quarantine : QuarantineSagaJobV1.quarantineStatus === 'quarantineComplete'\n// 2. account action : case-root accountActionStatus ∈ { 'noAccountActionRequired', 'resolved' }\n// 3. reporting predicate (Finding-H6):\n// ( case-root reportDisposition === 'notRequired' AND no NCMEC submission was ever opened )\n// OR ( the submission's state === 'completed' )\n//\n// Reporting closure keys on the case-root `reportDisposition` (§A9\n// ReportDisposition), NOT on a `notRequired` submission state (that value no\n// longer exists on NcmecSubmissionState). A `notRequired`-disposed case is never\n// blocked on a non-existent report; a human-review case stays\n// `operatorDecisionPending` until the operator commands the action.\n// ===========================================================================\n"]}
|
|
@@ -31,6 +31,23 @@ export declare const SquareStreetzPostTypeSchema: z.ZodEnum<{
|
|
|
31
31
|
NEW_COMMISSION: "NEW_COMMISSION";
|
|
32
32
|
}>;
|
|
33
33
|
export type SquareStreetzPostType = z.infer<typeof SquareStreetzPostTypeSchema>;
|
|
34
|
+
/**
|
|
35
|
+
* squareStreetzFeed/activePosts/socialPosts/{postId}.
|
|
36
|
+
*
|
|
37
|
+
* **Media-pair invariant (MEDIA-101) — enforced by the superRefine below:**
|
|
38
|
+
* `mediaAssetId` and `mediaType` are ONE unit — both present, or both absent.
|
|
39
|
+
* Gateway URLs are extensionless, so nothing downstream can recover the kind from
|
|
40
|
+
* the id: a post persisted with an asset and NO kind classifies as `'other'` and
|
|
41
|
+
* `AppMediaPreview` renders a Download link INSTEAD of the image/video. The mirror
|
|
42
|
+
* case (a kind with no asset) is an orphan that describes media the post does not
|
|
43
|
+
* carry. Declared HERE, on the canonical doc schema (ARCH-102), so every writer
|
|
44
|
+
* inherits it from the one post-create chokepoint's parse rather than each write
|
|
45
|
+
* site re-deciding — and so no reader ever needs a `?? 'other'` guess.
|
|
46
|
+
*
|
|
47
|
+
* Announcement posts REFERENCE a source asset (audition `videoAssetId`, commission
|
|
48
|
+
* `commissionAttachment.mediaAssetId`), so the kind always comes from that source —
|
|
49
|
+
* it is never invented or defaulted at the post write.
|
|
50
|
+
*/
|
|
34
51
|
export declare const SquareStreetzPostSchema: z.ZodObject<{
|
|
35
52
|
postId: z.ZodString;
|
|
36
53
|
createdBy: z.ZodObject<{
|
|
@@ -75,6 +92,119 @@ export declare const SquareStreetzPostSchema: z.ZodObject<{
|
|
|
75
92
|
hidden: z.ZodBoolean;
|
|
76
93
|
}, z.core.$strip>;
|
|
77
94
|
export type SquareStreetzPost = z.infer<typeof SquareStreetzPostSchema>;
|
|
95
|
+
/**
|
|
96
|
+
* The create/announcement transport payload for a SquareStreetz post — the exact input
|
|
97
|
+
* `runManageSquareStreetzPost` accepts. Every enqueue site (the durable
|
|
98
|
+
* `squareAnnouncementJobs` outbox) and the manual post callables build one of these; the
|
|
99
|
+
* different post types populate different subsets, so every field beyond `userId` is
|
|
100
|
+
* optional. The TYPE is inferred from this schema (ARCH-102 — one declaration; the
|
|
101
|
+
* frontend extends it with a local `mediaFile?: File`).
|
|
102
|
+
*
|
|
103
|
+
* NON-strict and carrying NO cross-field pairing refinement ON PURPOSE: it is parsed at
|
|
104
|
+
* ENQUEUE inside the announcing transaction (ttt-prod square-announcement-outbox.ts), so a
|
|
105
|
+
* throw here would fail the user's primary action over an announcement defect (ENG-009). It
|
|
106
|
+
* validates that each present field has its canonical type and strips unknowns; the
|
|
107
|
+
* `mediaAssetId`↔`mediaType` pairing (MEDIA-101) is enforced at DELIVERY, where the post
|
|
108
|
+
* core parses `SquareStreetzPostSchema` and a failure dead-letters the job instead.
|
|
109
|
+
*/
|
|
110
|
+
export declare const SquareStreetzPostPayloadSchema: z.ZodObject<{
|
|
111
|
+
userId: z.ZodString;
|
|
112
|
+
mentions: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
113
|
+
placeholder: z.ZodString;
|
|
114
|
+
type: z.ZodEnum<{
|
|
115
|
+
workRealm: "workRealm";
|
|
116
|
+
workProject: "workProject";
|
|
117
|
+
audition: "audition";
|
|
118
|
+
user: "user";
|
|
119
|
+
commission: "commission";
|
|
120
|
+
}>;
|
|
121
|
+
id: z.ZodString;
|
|
122
|
+
}, z.core.$strict>>>;
|
|
123
|
+
newMediaAssetId: z.ZodOptional<z.ZodString>;
|
|
124
|
+
craftSkill: z.ZodOptional<z.ZodObject<{
|
|
125
|
+
id: z.ZodString;
|
|
126
|
+
name: z.ZodString;
|
|
127
|
+
mediaAssetId: z.ZodString;
|
|
128
|
+
type: z.ZodEnum<{
|
|
129
|
+
video: "video";
|
|
130
|
+
image: "image";
|
|
131
|
+
audio: "audio";
|
|
132
|
+
}>;
|
|
133
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
134
|
+
acting: "acting";
|
|
135
|
+
"voice-over": "voice-over";
|
|
136
|
+
singing: "singing";
|
|
137
|
+
rapping: "rapping";
|
|
138
|
+
"instrumental-performance": "instrumental-performance";
|
|
139
|
+
"dance-choreography": "dance-choreography";
|
|
140
|
+
"stand-up-sketch-comedy": "stand-up-sketch-comedy";
|
|
141
|
+
"writing-storytelling": "writing-storytelling";
|
|
142
|
+
songwriting: "songwriting";
|
|
143
|
+
"poetry-spoken-word": "poetry-spoken-word";
|
|
144
|
+
"drawing-illustration": "drawing-illustration";
|
|
145
|
+
painting: "painting";
|
|
146
|
+
animation: "animation";
|
|
147
|
+
"graphic-design": "graphic-design";
|
|
148
|
+
photography: "photography";
|
|
149
|
+
"cinematography-camera": "cinematography-camera";
|
|
150
|
+
"video-editing": "video-editing";
|
|
151
|
+
"visual-effects": "visual-effects";
|
|
152
|
+
directing: "directing";
|
|
153
|
+
"costume-makeup": "costume-makeup";
|
|
154
|
+
"set-prop-design": "set-prop-design";
|
|
155
|
+
"music-production": "music-production";
|
|
156
|
+
"composing-scoring": "composing-scoring";
|
|
157
|
+
"sound-design": "sound-design";
|
|
158
|
+
}>>>;
|
|
159
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
160
|
+
original: "original";
|
|
161
|
+
mimicOnTtt: "mimicOnTtt";
|
|
162
|
+
mimicOffTtt: "mimicOffTtt";
|
|
163
|
+
}>>;
|
|
164
|
+
source: z.ZodOptional<z.ZodObject<{
|
|
165
|
+
type: z.ZodEnum<{
|
|
166
|
+
workProject: "workProject";
|
|
167
|
+
user: "user";
|
|
168
|
+
}>;
|
|
169
|
+
id: z.ZodString;
|
|
170
|
+
}, z.core.$strict>>;
|
|
171
|
+
}, z.core.$strip>>;
|
|
172
|
+
craftSkillId: z.ZodOptional<z.ZodString>;
|
|
173
|
+
workProjectTitle: z.ZodOptional<z.ZodString>;
|
|
174
|
+
workProjectId: z.ZodOptional<z.ZodString>;
|
|
175
|
+
workProjectType: z.ZodOptional<z.ZodEnum<{
|
|
176
|
+
Tales: "Tales";
|
|
177
|
+
Tunes: "Tunes";
|
|
178
|
+
Television: "Television";
|
|
179
|
+
}>>;
|
|
180
|
+
workProjectDescription: z.ZodOptional<z.ZodString>;
|
|
181
|
+
workRealmId: z.ZodOptional<z.ZodString>;
|
|
182
|
+
workRealmTitle: z.ZodOptional<z.ZodString>;
|
|
183
|
+
hallItemId: z.ZodOptional<z.ZodString>;
|
|
184
|
+
hallItemTitle: z.ZodOptional<z.ZodString>;
|
|
185
|
+
hallSubItemType: z.ZodOptional<z.ZodEnum<{
|
|
186
|
+
chapter: "chapter";
|
|
187
|
+
track: "track";
|
|
188
|
+
episode: "episode";
|
|
189
|
+
}>>;
|
|
190
|
+
content: z.ZodOptional<z.ZodString>;
|
|
191
|
+
mediaAssetId: z.ZodOptional<z.ZodString>;
|
|
192
|
+
mediaType: z.ZodOptional<z.ZodEnum<{
|
|
193
|
+
video: "video";
|
|
194
|
+
image: "image";
|
|
195
|
+
audio: "audio";
|
|
196
|
+
other: "other";
|
|
197
|
+
}>>;
|
|
198
|
+
createdAt: z.ZodOptional<z.ZodNumber>;
|
|
199
|
+
auditionId: z.ZodOptional<z.ZodString>;
|
|
200
|
+
commissionListingId: z.ZodOptional<z.ZodString>;
|
|
201
|
+
auditionAnnounceKind: z.ZodOptional<z.ZodEnum<{
|
|
202
|
+
work: "work";
|
|
203
|
+
platform: "platform";
|
|
204
|
+
sponsored: "sponsored";
|
|
205
|
+
}>>;
|
|
206
|
+
}, z.core.$strip>;
|
|
207
|
+
export type SquareStreetzPostPayload = z.infer<typeof SquareStreetzPostPayloadSchema>;
|
|
78
208
|
export declare const MentionHistoryItemSchema: z.ZodObject<{
|
|
79
209
|
placeholder: z.ZodString;
|
|
80
210
|
type: z.ZodEnum<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"social.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/social.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"social.d.ts","sourceRoot":"","sources":["../../src/doc-schemas/social.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB;;+EAE+E;AAC/E,eAAO,MAAM,eAAe;;;;;EAA+C,CAAC;AAC5E,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAGxD;;+EAE+E;AAC/E,eAAO,MAAM,0BAA0B;;;;EAA4C,CAAC;AACpF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,eAAO,MAAM,2BAA2B;;;;;;;;;;;EAWtC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEhF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsClC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkCzC,CAAC;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAEtF,eAAO,MAAM,wBAAwB;;;;;;;;;;;kBAAiD,CAAC;AACvF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;iBAGvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAQlF,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;iBAK1C,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF,eAAO,MAAM,gBAAgB;;;;;;;;;iBAK3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAK1D,eAAO,MAAM,mBAAmB;;;iBAG9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAKhE,eAAO,MAAM,uBAAuB;;iBAElC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAIxE,eAAO,MAAM,mBAAmB;;;iBAG9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { MentionSchema } from '../media/atoms.js';
|
|
7
7
|
import { FollowableTargetTypeSchema } from '../schemas/social.js';
|
|
8
|
+
import { ContentMediaKindSchema } from './media-assets.js';
|
|
9
|
+
import { CraftSkillKindSchema, CraftSkillSourceReferenceSchema } from './user.js';
|
|
10
|
+
import { HallSubItemTypeSchema } from './content.js';
|
|
11
|
+
import { CRAFT_SKILL_TAG_VALUES } from '../constants/options.js';
|
|
12
|
+
import { WORK_PROJECT_TYPE_KEYS } from '../types/content.js';
|
|
8
13
|
/** The ONE canonical display/transport media-type family (the 'other' bucket covers
|
|
9
14
|
* non-previewable kinds). Distinct from the 3-value stored ContentMediaKindSchema
|
|
10
15
|
* (doc-schemas/media-assets.ts) and media-schemas' generic MediaKindSchema. */
|
|
@@ -26,6 +31,23 @@ export const SquareStreetzPostTypeSchema = z.enum([
|
|
|
26
31
|
'NEW_AUDITION',
|
|
27
32
|
'NEW_COMMISSION',
|
|
28
33
|
]);
|
|
34
|
+
/**
|
|
35
|
+
* squareStreetzFeed/activePosts/socialPosts/{postId}.
|
|
36
|
+
*
|
|
37
|
+
* **Media-pair invariant (MEDIA-101) — enforced by the superRefine below:**
|
|
38
|
+
* `mediaAssetId` and `mediaType` are ONE unit — both present, or both absent.
|
|
39
|
+
* Gateway URLs are extensionless, so nothing downstream can recover the kind from
|
|
40
|
+
* the id: a post persisted with an asset and NO kind classifies as `'other'` and
|
|
41
|
+
* `AppMediaPreview` renders a Download link INSTEAD of the image/video. The mirror
|
|
42
|
+
* case (a kind with no asset) is an orphan that describes media the post does not
|
|
43
|
+
* carry. Declared HERE, on the canonical doc schema (ARCH-102), so every writer
|
|
44
|
+
* inherits it from the one post-create chokepoint's parse rather than each write
|
|
45
|
+
* site re-deciding — and so no reader ever needs a `?? 'other'` guess.
|
|
46
|
+
*
|
|
47
|
+
* Announcement posts REFERENCE a source asset (audition `videoAssetId`, commission
|
|
48
|
+
* `commissionAttachment.mediaAssetId`), so the kind always comes from that source —
|
|
49
|
+
* it is never invented or defaulted at the post write.
|
|
50
|
+
*/
|
|
29
51
|
export const SquareStreetzPostSchema = z.object({
|
|
30
52
|
postId: z.string(),
|
|
31
53
|
createdBy: z.object({ uid: z.string() }),
|
|
@@ -33,7 +55,9 @@ export const SquareStreetzPostSchema = z.object({
|
|
|
33
55
|
content: z.string(),
|
|
34
56
|
mentions: z.array(MentionSchema).optional(),
|
|
35
57
|
relatedIds: z.array(z.string()),
|
|
58
|
+
/** Paired with `mediaType` — see the media-pair invariant above. */
|
|
36
59
|
mediaAssetId: z.string().optional(),
|
|
60
|
+
/** Paired with `mediaAssetId` — see the media-pair invariant above. */
|
|
37
61
|
mediaType: mediaTypeSchema.optional(),
|
|
38
62
|
createdAt: z.number(),
|
|
39
63
|
likes: z.number(),
|
|
@@ -44,9 +68,74 @@ export const SquareStreetzPostSchema = z.object({
|
|
|
44
68
|
// flag on a post — the legacy pre-finalize upload-gating (`visible`) and automated
|
|
45
69
|
// text-moderation outcome (`moderationStatus`/`moderationReason`/`moderationLayer`)
|
|
46
70
|
// fields were removed 2026-07-03: nothing wrote them (posts are created only AFTER
|
|
47
|
-
// media finalize under pattern C), so they were dead.
|
|
48
|
-
//
|
|
71
|
+
// media finalize under pattern C), so they were dead. Durable owner:
|
|
72
|
+
// ttt-prod docs/design/streetz-social-feed.md.
|
|
49
73
|
hidden: z.boolean(),
|
|
74
|
+
}).superRefine((val, ctx) => {
|
|
75
|
+
if (val.mediaAssetId !== undefined && val.mediaType === undefined) {
|
|
76
|
+
ctx.addIssue({
|
|
77
|
+
code: 'custom',
|
|
78
|
+
path: ['mediaType'],
|
|
79
|
+
message: 'mediaType is required whenever a post carries mediaAssetId (MEDIA-101: an extensionless gateway URL with no kind renders a Download link instead of the media)',
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (val.mediaType !== undefined && val.mediaAssetId === undefined) {
|
|
83
|
+
ctx.addIssue({
|
|
84
|
+
code: 'custom',
|
|
85
|
+
path: ['mediaAssetId'],
|
|
86
|
+
message: 'mediaType is only allowed on a post that carries mediaAssetId — an orphan kind describes media the post does not have',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
/**
|
|
91
|
+
* The create/announcement transport payload for a SquareStreetz post — the exact input
|
|
92
|
+
* `runManageSquareStreetzPost` accepts. Every enqueue site (the durable
|
|
93
|
+
* `squareAnnouncementJobs` outbox) and the manual post callables build one of these; the
|
|
94
|
+
* different post types populate different subsets, so every field beyond `userId` is
|
|
95
|
+
* optional. The TYPE is inferred from this schema (ARCH-102 — one declaration; the
|
|
96
|
+
* frontend extends it with a local `mediaFile?: File`).
|
|
97
|
+
*
|
|
98
|
+
* NON-strict and carrying NO cross-field pairing refinement ON PURPOSE: it is parsed at
|
|
99
|
+
* ENQUEUE inside the announcing transaction (ttt-prod square-announcement-outbox.ts), so a
|
|
100
|
+
* throw here would fail the user's primary action over an announcement defect (ENG-009). It
|
|
101
|
+
* validates that each present field has its canonical type and strips unknowns; the
|
|
102
|
+
* `mediaAssetId`↔`mediaType` pairing (MEDIA-101) is enforced at DELIVERY, where the post
|
|
103
|
+
* core parses `SquareStreetzPostSchema` and a failure dead-letters the job instead.
|
|
104
|
+
*/
|
|
105
|
+
export const SquareStreetzPostPayloadSchema = z.object({
|
|
106
|
+
userId: z.string(),
|
|
107
|
+
mentions: z.array(MentionSchema).optional(),
|
|
108
|
+
newMediaAssetId: z.string().optional(),
|
|
109
|
+
// NEW_CRAFT_SKILL announcement descriptor — id/kind/tags/source let the post describe the
|
|
110
|
+
// craft WITHOUT leaking the client-supplied original file name.
|
|
111
|
+
craftSkill: z
|
|
112
|
+
.object({
|
|
113
|
+
id: z.string(),
|
|
114
|
+
name: z.string(),
|
|
115
|
+
mediaAssetId: z.string(),
|
|
116
|
+
type: ContentMediaKindSchema,
|
|
117
|
+
tags: z.array(z.enum(CRAFT_SKILL_TAG_VALUES)).optional(),
|
|
118
|
+
kind: CraftSkillKindSchema.optional(),
|
|
119
|
+
source: CraftSkillSourceReferenceSchema.optional(),
|
|
120
|
+
})
|
|
121
|
+
.optional(),
|
|
122
|
+
craftSkillId: z.string().optional(),
|
|
123
|
+
workProjectTitle: z.string().optional(),
|
|
124
|
+
workProjectId: z.string().optional(),
|
|
125
|
+
workProjectType: z.enum(WORK_PROJECT_TYPE_KEYS).optional(),
|
|
126
|
+
workProjectDescription: z.string().optional(),
|
|
127
|
+
workRealmId: z.string().optional(),
|
|
128
|
+
workRealmTitle: z.string().optional(),
|
|
129
|
+
hallItemId: z.string().optional(),
|
|
130
|
+
hallItemTitle: z.string().optional(),
|
|
131
|
+
hallSubItemType: HallSubItemTypeSchema.optional(),
|
|
132
|
+
content: z.string().optional(),
|
|
133
|
+
mediaAssetId: z.string().optional(),
|
|
134
|
+
mediaType: mediaTypeSchema.optional(),
|
|
135
|
+
createdAt: z.number().optional(),
|
|
136
|
+
auditionId: z.string().optional(),
|
|
137
|
+
commissionListingId: z.string().optional(),
|
|
138
|
+
auditionAnnounceKind: AuditionAnnounceKindSchema.optional(),
|
|
50
139
|
});
|
|
51
140
|
export const MentionHistoryItemSchema = MentionSchema.extend({ viewedAt: z.number() });
|
|
52
141
|
export const MentionHistoryDocumentSchema = z.object({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"social.js","sourceRoot":"","sources":["../../src/doc-schemas/social.ts"],"names":[],"mappings":"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","sourcesContent":["// Social Firestore document SCHEMAS — SquareStreetz posts, mention history, and\n// follow edges. Types inferred via z.infer. (Pledge-payment ledger docs moved to\n// ../doc-schemas/payments.ts. SquareStreetzPostPayload is a transport shape, not a\n// stored doc — it stays in ../types/social.ts.)\n\nimport { z } from 'zod';\nimport { MentionSchema } from '../media/atoms.js';\nimport { FollowableTargetTypeSchema } from '../schemas/social.js';\n\n/** The ONE canonical display/transport media-type family (the 'other' bucket covers\n * non-previewable kinds). Distinct from the 3-value stored ContentMediaKindSchema\n * (doc-schemas/media-assets.ts) and media-schemas' generic MediaKindSchema. */\nexport const MediaTypeSchema = z.enum(['image', 'video', 'audio', 'other']);\nexport type MediaType = z.infer<typeof MediaTypeSchema>;\nconst mediaTypeSchema = MediaTypeSchema;\n\n/** Discriminates the announcement voice/mention shape of a NEW_AUDITION Streetz post:\n * 'work' names the creating work inline; 'platform' | 'sponsored' name TTT Productions\n * the company instead (no work mention). The ONE declaration of this union. */\nexport const AuditionAnnounceKindSchema = z.enum(['work', 'platform', 'sponsored']);\nexport type AuditionAnnounceKind = z.infer<typeof AuditionAnnounceKindSchema>;\n\nexport const SquareStreetzPostTypeSchema = z.enum([\n 'PROFILE_PICTURE_UPDATE',\n 'NEW_CRAFT_SKILL',\n 'NEW_ARTISAN_CREATOR',\n 'COMMISSION_ACCEPTED',\n 'DELETE_CRAFT_SKILL',\n 'USER_POST',\n 'NEW_WORK_PROJECT',\n 'LIBRARY_PUBLISHED',\n 'NEW_AUDITION',\n 'NEW_COMMISSION',\n]);\nexport type SquareStreetzPostType = z.infer<typeof SquareStreetzPostTypeSchema>;\n\nexport const SquareStreetzPostSchema = z.object({\n postId: z.string(),\n createdBy: z.object({ uid: z.string() }),\n authorId: z.string(),\n content: z.string(),\n mentions: z.array(MentionSchema).optional(),\n relatedIds: z.array(z.string()),\n mediaAssetId: z.string().optional(),\n mediaType: mediaTypeSchema.optional(),\n createdAt: z.number(),\n likes: z.number(),\n postType: SquareStreetzPostTypeSchema.optional(),\n relatedAssetId: z.string().optional(),\n // Admin moderation hide (reversible). When true the post is suppressed from all\n // feeds/lists; restored by clearing it. `hidden` is the ONLY operative moderation\n // flag on a post — the legacy pre-finalize upload-gating (`visible`) and automated\n // text-moderation outcome (`moderationStatus`/`moderationReason`/`moderationLayer`)\n // fields were removed 2026-07-03: nothing wrote them (posts are created only AFTER\n // media finalize under pattern C), so they were dead. See\n // CODE_CHANGE_feed_slice_hygiene_misc.md item 1.\n hidden: z.boolean(),\n});\nexport type SquareStreetzPost = z.infer<typeof SquareStreetzPostSchema>;\n\nexport const MentionHistoryItemSchema = MentionSchema.extend({ viewedAt: z.number() });\nexport type MentionHistoryItem = z.infer<typeof MentionHistoryItemSchema>;\n\nexport const MentionHistoryDocumentSchema = z.object({\n userId: z.string(),\n items: z.array(MentionHistoryItemSchema),\n});\nexport type MentionHistoryDocument = z.infer<typeof MentionHistoryDocumentSchema>;\n\n// Authoritative result of the addToMentionHistory callable — the transaction computes\n// the deduped, capped list in memory before committing, so returning it costs no read.\n// Declared HERE (not schemas/social.ts) because it composes MentionHistoryItemSchema\n// and this module already imports schemas/social.js (FollowableTargetTypeSchema) — the\n// reverse runtime import would be a module cycle. Re-exported on the ./schemas subpath\n// via schemas/index.ts. Non-strict (server → client result posture).\nexport const AddToMentionHistoryResultSchema = z.object({\n success: z.literal(true),\n itemCount: z.number().int().nonnegative(),\n /** The full, deduped, capped history AFTER this write — a drop-in cache replacement. */\n items: z.array(MentionHistoryItemSchema),\n});\nexport type AddToMentionHistoryResult = z.infer<typeof AddToMentionHistoryResultSchema>;\n\nexport const FollowEdgeSchema = z.object({\n followerUid: z.string(),\n targetType: FollowableTargetTypeSchema,\n targetId: z.string(),\n followedOn: z.number(),\n});\nexport type FollowEdge = z.infer<typeof FollowEdgeSchema>;\n\n// followCounters/{targetType__targetId} — denormalized follower count for a followable target,\n// maintained by the follow/unfollow callables via FieldValue.increment (never written by clients).\n// The Hall Library \"Most Followed\" sort mirrors the workProject count onto hallItems.followerCount.\nexport const FollowCounterSchema = z.object({\n followerCount: z.number(),\n updatedOn: z.number(),\n});\nexport type FollowCounter = z.infer<typeof FollowCounterSchema>;\n\n// userProfiles/{uid}/userLikes/likeHistory/squareStreetzLikes/{postId} — a user's like on a\n// SquareStreetz post; existence == liked. The path nests three of the registry segments\n// (userLikes / likeHistory / squareStreetzLikes). (functions/src/social/likeSquareStreetzPost.ts)\nexport const SquareStreetzLikeSchema = z.object({\n likedOn: z.number(),\n});\nexport type SquareStreetzLike = z.infer<typeof SquareStreetzLikeSchema>;\n\n// squareStreetzFeed/trendingPosts — singleton holding the ordered top-N trending post IDs that the\n// client infinite-scrolls through. (functions/src/social/runUpdateTrendingFeed.ts)\nexport const TrendingPostsSchema = z.object({\n postIds: z.array(z.string()),\n lastUpdated: z.number(),\n});\nexport type TrendingPosts = z.infer<typeof TrendingPostsSchema>;\n"]}
|
|
1
|
+
{"version":3,"file":"social.js","sourceRoot":"","sources":["../../src/doc-schemas/social.ts"],"names":[],"mappings":"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","sourcesContent":["// Social Firestore document SCHEMAS — SquareStreetz posts, mention history, and\n// follow edges. Types inferred via z.infer. (Pledge-payment ledger docs moved to\n// ../doc-schemas/payments.ts. SquareStreetzPostPayload is a transport shape, not a\n// stored doc — it stays in ../types/social.ts.)\n\nimport { z } from 'zod';\nimport { MentionSchema } from '../media/atoms.js';\nimport { FollowableTargetTypeSchema } from '../schemas/social.js';\nimport { ContentMediaKindSchema } from './media-assets.js';\nimport { CraftSkillKindSchema, CraftSkillSourceReferenceSchema } from './user.js';\nimport { HallSubItemTypeSchema } from './content.js';\nimport { CRAFT_SKILL_TAG_VALUES } from '../constants/options.js';\nimport { WORK_PROJECT_TYPE_KEYS } from '../types/content.js';\n\n/** The ONE canonical display/transport media-type family (the 'other' bucket covers\n * non-previewable kinds). Distinct from the 3-value stored ContentMediaKindSchema\n * (doc-schemas/media-assets.ts) and media-schemas' generic MediaKindSchema. */\nexport const MediaTypeSchema = z.enum(['image', 'video', 'audio', 'other']);\nexport type MediaType = z.infer<typeof MediaTypeSchema>;\nconst mediaTypeSchema = MediaTypeSchema;\n\n/** Discriminates the announcement voice/mention shape of a NEW_AUDITION Streetz post:\n * 'work' names the creating work inline; 'platform' | 'sponsored' name TTT Productions\n * the company instead (no work mention). The ONE declaration of this union. */\nexport const AuditionAnnounceKindSchema = z.enum(['work', 'platform', 'sponsored']);\nexport type AuditionAnnounceKind = z.infer<typeof AuditionAnnounceKindSchema>;\n\nexport const SquareStreetzPostTypeSchema = z.enum([\n 'PROFILE_PICTURE_UPDATE',\n 'NEW_CRAFT_SKILL',\n 'NEW_ARTISAN_CREATOR',\n 'COMMISSION_ACCEPTED',\n 'DELETE_CRAFT_SKILL',\n 'USER_POST',\n 'NEW_WORK_PROJECT',\n 'LIBRARY_PUBLISHED',\n 'NEW_AUDITION',\n 'NEW_COMMISSION',\n]);\nexport type SquareStreetzPostType = z.infer<typeof SquareStreetzPostTypeSchema>;\n\n/**\n * squareStreetzFeed/activePosts/socialPosts/{postId}.\n *\n * **Media-pair invariant (MEDIA-101) — enforced by the superRefine below:**\n * `mediaAssetId` and `mediaType` are ONE unit — both present, or both absent.\n * Gateway URLs are extensionless, so nothing downstream can recover the kind from\n * the id: a post persisted with an asset and NO kind classifies as `'other'` and\n * `AppMediaPreview` renders a Download link INSTEAD of the image/video. The mirror\n * case (a kind with no asset) is an orphan that describes media the post does not\n * carry. Declared HERE, on the canonical doc schema (ARCH-102), so every writer\n * inherits it from the one post-create chokepoint's parse rather than each write\n * site re-deciding — and so no reader ever needs a `?? 'other'` guess.\n *\n * Announcement posts REFERENCE a source asset (audition `videoAssetId`, commission\n * `commissionAttachment.mediaAssetId`), so the kind always comes from that source —\n * it is never invented or defaulted at the post write.\n */\nexport const SquareStreetzPostSchema = z.object({\n postId: z.string(),\n createdBy: z.object({ uid: z.string() }),\n authorId: z.string(),\n content: z.string(),\n mentions: z.array(MentionSchema).optional(),\n relatedIds: z.array(z.string()),\n /** Paired with `mediaType` — see the media-pair invariant above. */\n mediaAssetId: z.string().optional(),\n /** Paired with `mediaAssetId` — see the media-pair invariant above. */\n mediaType: mediaTypeSchema.optional(),\n createdAt: z.number(),\n likes: z.number(),\n postType: SquareStreetzPostTypeSchema.optional(),\n relatedAssetId: z.string().optional(),\n // Admin moderation hide (reversible). When true the post is suppressed from all\n // feeds/lists; restored by clearing it. `hidden` is the ONLY operative moderation\n // flag on a post — the legacy pre-finalize upload-gating (`visible`) and automated\n // text-moderation outcome (`moderationStatus`/`moderationReason`/`moderationLayer`)\n // fields were removed 2026-07-03: nothing wrote them (posts are created only AFTER\n // media finalize under pattern C), so they were dead. Durable owner:\n // ttt-prod docs/design/streetz-social-feed.md.\n hidden: z.boolean(),\n}).superRefine((val, ctx) => {\n if (val.mediaAssetId !== undefined && val.mediaType === undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['mediaType'],\n message: 'mediaType is required whenever a post carries mediaAssetId (MEDIA-101: an extensionless gateway URL with no kind renders a Download link instead of the media)',\n });\n }\n if (val.mediaType !== undefined && val.mediaAssetId === undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['mediaAssetId'],\n message: 'mediaType is only allowed on a post that carries mediaAssetId — an orphan kind describes media the post does not have',\n });\n }\n});\nexport type SquareStreetzPost = z.infer<typeof SquareStreetzPostSchema>;\n\n/**\n * The create/announcement transport payload for a SquareStreetz post — the exact input\n * `runManageSquareStreetzPost` accepts. Every enqueue site (the durable\n * `squareAnnouncementJobs` outbox) and the manual post callables build one of these; the\n * different post types populate different subsets, so every field beyond `userId` is\n * optional. The TYPE is inferred from this schema (ARCH-102 — one declaration; the\n * frontend extends it with a local `mediaFile?: File`).\n *\n * NON-strict and carrying NO cross-field pairing refinement ON PURPOSE: it is parsed at\n * ENQUEUE inside the announcing transaction (ttt-prod square-announcement-outbox.ts), so a\n * throw here would fail the user's primary action over an announcement defect (ENG-009). It\n * validates that each present field has its canonical type and strips unknowns; the\n * `mediaAssetId`↔`mediaType` pairing (MEDIA-101) is enforced at DELIVERY, where the post\n * core parses `SquareStreetzPostSchema` and a failure dead-letters the job instead.\n */\nexport const SquareStreetzPostPayloadSchema = z.object({\n userId: z.string(),\n mentions: z.array(MentionSchema).optional(),\n newMediaAssetId: z.string().optional(),\n // NEW_CRAFT_SKILL announcement descriptor — id/kind/tags/source let the post describe the\n // craft WITHOUT leaking the client-supplied original file name.\n craftSkill: z\n .object({\n id: z.string(),\n name: z.string(),\n mediaAssetId: z.string(),\n type: ContentMediaKindSchema,\n tags: z.array(z.enum(CRAFT_SKILL_TAG_VALUES)).optional(),\n kind: CraftSkillKindSchema.optional(),\n source: CraftSkillSourceReferenceSchema.optional(),\n })\n .optional(),\n craftSkillId: z.string().optional(),\n workProjectTitle: z.string().optional(),\n workProjectId: z.string().optional(),\n workProjectType: z.enum(WORK_PROJECT_TYPE_KEYS).optional(),\n workProjectDescription: z.string().optional(),\n workRealmId: z.string().optional(),\n workRealmTitle: z.string().optional(),\n hallItemId: z.string().optional(),\n hallItemTitle: z.string().optional(),\n hallSubItemType: HallSubItemTypeSchema.optional(),\n content: z.string().optional(),\n mediaAssetId: z.string().optional(),\n mediaType: mediaTypeSchema.optional(),\n createdAt: z.number().optional(),\n auditionId: z.string().optional(),\n commissionListingId: z.string().optional(),\n auditionAnnounceKind: AuditionAnnounceKindSchema.optional(),\n});\nexport type SquareStreetzPostPayload = z.infer<typeof SquareStreetzPostPayloadSchema>;\n\nexport const MentionHistoryItemSchema = MentionSchema.extend({ viewedAt: z.number() });\nexport type MentionHistoryItem = z.infer<typeof MentionHistoryItemSchema>;\n\nexport const MentionHistoryDocumentSchema = z.object({\n userId: z.string(),\n items: z.array(MentionHistoryItemSchema),\n});\nexport type MentionHistoryDocument = z.infer<typeof MentionHistoryDocumentSchema>;\n\n// Authoritative result of the addToMentionHistory callable — the transaction computes\n// the deduped, capped list in memory before committing, so returning it costs no read.\n// Declared HERE (not schemas/social.ts) because it composes MentionHistoryItemSchema\n// and this module already imports schemas/social.js (FollowableTargetTypeSchema) — the\n// reverse runtime import would be a module cycle. Re-exported on the ./schemas subpath\n// via schemas/index.ts. Non-strict (server → client result posture).\nexport const AddToMentionHistoryResultSchema = z.object({\n success: z.literal(true),\n itemCount: z.number().int().nonnegative(),\n /** The full, deduped, capped history AFTER this write — a drop-in cache replacement. */\n items: z.array(MentionHistoryItemSchema),\n});\nexport type AddToMentionHistoryResult = z.infer<typeof AddToMentionHistoryResultSchema>;\n\nexport const FollowEdgeSchema = z.object({\n followerUid: z.string(),\n targetType: FollowableTargetTypeSchema,\n targetId: z.string(),\n followedOn: z.number(),\n});\nexport type FollowEdge = z.infer<typeof FollowEdgeSchema>;\n\n// followCounters/{targetType__targetId} — denormalized follower count for a followable target,\n// maintained by the follow/unfollow callables via FieldValue.increment (never written by clients).\n// The Hall Library \"Most Followed\" sort mirrors the workProject count onto hallItems.followerCount.\nexport const FollowCounterSchema = z.object({\n followerCount: z.number(),\n updatedOn: z.number(),\n});\nexport type FollowCounter = z.infer<typeof FollowCounterSchema>;\n\n// userProfiles/{uid}/userLikes/likeHistory/squareStreetzLikes/{postId} — a user's like on a\n// SquareStreetz post; existence == liked. The path nests three of the registry segments\n// (userLikes / likeHistory / squareStreetzLikes). (functions/src/social/likeSquareStreetzPost.ts)\nexport const SquareStreetzLikeSchema = z.object({\n likedOn: z.number(),\n});\nexport type SquareStreetzLike = z.infer<typeof SquareStreetzLikeSchema>;\n\n// squareStreetzFeed/trendingPosts — singleton holding the ordered top-N trending post IDs that the\n// client infinite-scrolls through. (functions/src/social/runUpdateTrendingFeed.ts)\nexport const TrendingPostsSchema = z.object({\n postIds: z.array(z.string()),\n lastUpdated: z.number(),\n});\nexport type TrendingPosts = z.infer<typeof TrendingPostsSchema>;\n"]}
|