@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":"ncii-intake-derivations.js","sourceRoot":"","sources":["../../src/safety/ncii-intake-derivations.ts"],"names":[],"mappings":"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","sourcesContent":["// Shared NCII / TAKE IT DOWN intake derivations (Trust & Safety IMPLEMENTATION_PLAN\n// Phase NCII-2/3/4, Appendix A §A11). Pure, deterministic id/key/summary derivations\n// used by BOTH the public Next.js intake route and the Cloud Functions deadline/scan\n// workers. These used to be hand-mirrored in two app TS projects (the Cloud Functions\n// graph AND the App Hosting route graph, which cannot cross-import). This is now the ONE\n// canonical declaration (ARCH-102 / Cross-Boundary Type Invariant): both module graphs\n// import these derivations from ttt-core so the deterministic id/key contracts can never\n// drift between the two runtimes.\n//\n// These are LOAD-BEARING KEYSPACE CONTRACTS. A formula (or version-tag) change re-keys\n// EVERY existing intake request doc / receipt / snapshot — treat any such change as a\n// breaking migration of the intake keyspace, never a routine edit. The per-function\n// version-bump warnings below are not decorative.\n//\n// node:crypto sha256 keeps this pure + server-safe: this module lives behind the\n// `./safety` entry (never on the main barrel), so the frontend bundle never loads it.\n// The completeness predicate + intake-facts shape are NOT here — they are canonical on\n// `./doc-schemas` (beside `computeCompleteness`); import those from there.\n\nimport { resourceKeyHash } from './resource-keys.js';\nimport type {\n TakeItDownFieldCode,\n TakeItDownRequesterRole,\n TargetLocatorV1,\n TargetLocatorSummaryV1,\n} from '../doc-schemas/index.js';\n\n/**\n * sha256-hex chokepoint. Aliased to `resourceKeyHash` (the single canonical hashing\n * helper in `./resource-keys`) so the whole `./safety` surface has ONE sha256\n * implementation, never two. Named `sha256Hex` here because these intake derivations\n * hash arbitrary keyspace strings, not resource keys.\n */\nexport const sha256Hex = resourceKeyHash;\n\n/** Unit separator — illegal in any id/url/field we join, so a flat separator-joined\n * string is stable by construction (no key-ordering ambiguity, no JSON). */\nconst SEP = '\\x1f';\n\n// ===========================================================================\n// Deterministic ids ([H-07] idempotent intake)\n// ===========================================================================\n\n/** Version tag for the deterministic intake requestId. A bump re-keys EVERY\n * request doc id — treat as a breaking migration of the intake keyspace. */\nconst REQUEST_ID_VERSION = 'takeItDownRequest:v1';\n\n/** Deterministic `nciiTemporaryHolds/{holdId}` doc id for a request. ONE definition shared\n * by intake (which writes the row) and the removal saga (which reads its `target` locator as\n * the fallback when a public-form request has no case-linked allegation). */\nexport function tempHoldId(requestId: string): string {\n return sha256Hex(`nciiTemporaryHold:v1:${requestId}`);\n}\n\n/**\n * Canonical, collision-resistant key for a request target. For a resolvable\n * TTT-hosted locator the key is the locator discriminant + its identity fields;\n * for an external/unresolvable locator (a bare URL or free text) it is the\n * `url`/`additionalText` payload. The SAME logical target ALWAYS normalizes to the\n * SAME key so two submissions about the same content with the same idempotencyKey\n * resolve to ONE request doc, and two DIFFERENT targets never collide.\n *\n * No JSON — a flat unit-separator-joined string (the separator is illegal in any\n * id/url field) is stable by construction (no key-ordering ambiguity).\n */\nexport function normalizedTargetKey(locator: TargetLocatorV1): string {\n const k = locator.kind;\n switch (locator.kind) {\n case 'mediaAsset':\n return [k, locator.mediaAssetId].join(SEP);\n case 'hallItem':\n return [k, locator.hallItemId, locator.subItemId ?? ''].join(SEP);\n case 'squarePost':\n return [k, locator.postId].join(SEP);\n case 'profileImage':\n return [k, locator.profileUid].join(SEP);\n case 'username':\n return [k, locator.profileUid].join(SEP);\n case 'craftSkill':\n return [k, locator.profileUid, locator.craftSkillId].join(SEP);\n case 'commissionListing':\n return [k, locator.commissionListingId].join(SEP);\n case 'audition':\n return [k, locator.auditionId].join(SEP);\n case 'auditionEntry':\n return [k, locator.auditionId, locator.auditionEntryId].join(SEP);\n case 'guildInviteMessage':\n return [k, locator.channelId, locator.messageId].join(SEP);\n case 'chatAttachment':\n return [k, locator.channelId, locator.messageId, locator.attachmentId].join(SEP);\n case 'guildChatMessage':\n return [k, locator.channelId, locator.messageId].join(SEP);\n case 'adminWorkMessage':\n return [k, locator.adminDispatchId, locator.messageId].join(SEP);\n case 'commissionProposal':\n return [k, locator.commissionListingId, locator.commissionProposalId].join(SEP);\n case 'workProject':\n return [k, locator.workProjectId].join(SEP);\n case 'workAsset':\n return [k, locator.workProjectId, locator.workAssetId].join(SEP);\n case 'workRealm':\n return [k, locator.workRealmId].join(SEP);\n case 'url':\n // Normalize the URL (trim + lowercase scheme/host) so trivially-different\n // spellings of the same external link collapse to one request.\n return [k, normalizeExternalUrl(locator.url)].join(SEP);\n case 'additionalText':\n return [k, locator.textRef].join(SEP);\n }\n}\n\n/** Best-effort URL normalization for the external-locator key. A non-parseable\n * string is lowercased + trimmed (still deterministic). NEVER throws. */\nexport function normalizeExternalUrl(raw: string): string {\n const trimmed = raw.trim();\n try {\n const u = new URL(trimmed);\n u.protocol = u.protocol.toLowerCase();\n u.hostname = u.hostname.toLowerCase();\n return u.toString();\n } catch {\n return trimmed.toLowerCase();\n }\n}\n\n/**\n * [H-07] The deterministic intake requestId:\n * sha256('takeItDownRequest:v1:' + idempotencyKey + ':' + normalizedTargetKey).\n * A retry with the SAME client idempotencyKey + the SAME target re-derives the SAME\n * id, so the create-if-absent transaction is a no-op and re-returns the same receipt\n * (never double-arms a clock / double-commits a hold).\n */\nexport function deterministicRequestId(idempotencyKey: string, targetKey: string): string {\n return sha256Hex(`${REQUEST_ID_VERSION}:${idempotencyKey}:${targetKey}`);\n}\n\n/**\n * The opaque public `requestReference` printed on the one-time receipt and stored on the\n * public-status projection. Since the status token was removed, this is a deterministic\n * derivation of the internal `requestId` — a separate sha256 so the raw `requestId` is\n * never recoverable from the reference (which appears on the receipt / projection).\n * 32 hex chars is ample entropy for an opaque, non-enumerable capability.\n */\nexport function deriveRequestReference(requestId: string): string {\n return sha256Hex(`takeItDownRequestRef:v1:${requestId}`).slice(0, 32);\n}\n\n// ===========================================================================\n// Cumulative submission snapshot hash (the immutable receipt fingerprint)\n// ===========================================================================\n\n/**\n * Deterministic hash of the cumulative request state as of a submission — recorded\n * on `TakeItDownSubmissionV1.cumulativeRequestSnapshotHash`. Built from the SORTED\n * supplied field codes + the normalized target key + the requester role so the same\n * cumulative state always fingerprints identically (append-order independent).\n */\nexport function cumulativeSnapshotHash(args: {\n requesterRole: TakeItDownRequesterRole;\n targetKey: string;\n suppliedFieldCodes: readonly TakeItDownFieldCode[];\n}): string {\n const sorted = [...args.suppliedFieldCodes].sort();\n return sha256Hex(\n ['takeItDownSnapshot:v1', args.requesterRole, args.targetKey, sorted.join(',')].join(SEP),\n );\n}\n\n// ===========================================================================\n// Privacy-safe target summary (root + public projection — NO narrative / PII)\n// ===========================================================================\n\n/** A short, non-sensitive surface label per locator kind (no ids, no narrative). */\nexport function surfaceLabelFor(locator: TargetLocatorV1): string {\n switch (locator.kind) {\n case 'mediaAsset':\n return 'Media item';\n case 'hallItem':\n return 'Hall library item';\n case 'squarePost':\n return 'Square Streetz post';\n case 'profileImage':\n return 'Profile image';\n case 'username':\n return 'Username';\n case 'craftSkill':\n return 'Craft skill';\n case 'commissionListing':\n return 'Commission listing';\n case 'audition':\n return 'Audition';\n case 'auditionEntry':\n return 'Audition entry';\n case 'guildInviteMessage':\n return 'Guild invite message';\n case 'chatAttachment':\n return 'Chat attachment';\n case 'guildChatMessage':\n return 'Guild chat message';\n case 'adminWorkMessage':\n return 'Work correspondence message';\n case 'commissionProposal':\n return 'Commission proposal';\n case 'workProject':\n return 'Work project';\n case 'workAsset':\n return 'Work file';\n case 'workRealm':\n return 'Work realm';\n case 'url':\n return 'External link';\n case 'additionalText':\n return 'Described content';\n }\n}\n\n/** Build the privacy-safe `TargetLocatorSummaryV1` (kind + surface label +\n * whether a TTT-hosted target was server-resolved). NEVER the narrative or ids. */\nexport function targetLocatorSummary(\n locator: TargetLocatorV1,\n hasResolvedTarget: boolean,\n): TargetLocatorSummaryV1 {\n return {\n kind: locator.kind,\n surfaceLabel: surfaceLabelFor(locator),\n hasResolvedTarget,\n };\n}\n\n/** TTT-hosted locator kinds the intake will synchronously server-resolve +\n * snapshot + temp-hold. `url` / `additionalText` are external/unresolvable: store\n * the locator + submission, invent NO target. */\nconst TTT_HOSTED_KINDS: ReadonlySet<TargetLocatorV1['kind']> = new Set([\n 'mediaAsset',\n 'hallItem',\n 'squarePost',\n 'profileImage',\n 'username',\n 'craftSkill',\n 'commissionListing',\n 'audition',\n 'auditionEntry',\n 'guildInviteMessage',\n 'chatAttachment',\n]);\n\n/** True when the locator points at a TTT-hosted surface (server-resolvable). */\nexport function isTttHostedLocator(locator: TargetLocatorV1): boolean {\n return TTT_HOSTED_KINDS.has(locator.kind);\n}\n\n// The intake temp hold is placed on the canonical key(s) every destructive guard checks — the\n// content-doc key (`contentDocResourceKey(docPath, 'current')` in `./resource-keys`) + the\n// mediaAsset byte key when the target is media-backed. There is intentionally NO bespoke\n// `nciiTemporaryTarget` key builder here: that namespace was consulted by NO guard, so a hold\n// placed on it never blocked deletion ([H3] void). The route-side mirror's `contentDocCanonicalKey`\n// is likewise NOT ported — it duplicated `contentDocResourceKey(docPath, 'current')`, which is the\n// single key authority.\n"]}
|
|
1
|
+
{"version":3,"file":"ncii-intake-derivations.js","sourceRoot":"","sources":["../../src/safety/ncii-intake-derivations.ts"],"names":[],"mappings":"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","sourcesContent":["// Shared NCII / TAKE IT DOWN intake derivations (frozen Trust & Safety spec,\n// Appendix A §A11; durable design owner: ttt-prod\n// docs/design/nonconsensual-intimate-imagery-and-take-it-down.md).\n// Pure, deterministic id/key/summary derivations\n// used by BOTH the public Next.js intake route and the Cloud Functions deadline/scan\n// workers. These used to be hand-mirrored in two app TS projects (the Cloud Functions\n// graph AND the App Hosting route graph, which cannot cross-import). This is now the ONE\n// canonical declaration (ARCH-102 / Cross-Boundary Type Invariant): both module graphs\n// import these derivations from ttt-core so the deterministic id/key contracts can never\n// drift between the two runtimes.\n//\n// These are LOAD-BEARING KEYSPACE CONTRACTS. A formula (or version-tag) change re-keys\n// EVERY existing intake request doc / receipt / snapshot — treat any such change as a\n// breaking migration of the intake keyspace, never a routine edit. The per-function\n// version-bump warnings below are not decorative.\n//\n// node:crypto sha256 keeps this pure + server-safe: this module lives behind the\n// `./safety` entry (never on the main barrel), so the frontend bundle never loads it.\n// The completeness predicate + intake-facts shape are NOT here — they are canonical on\n// `./doc-schemas` (beside `computeCompleteness`); import those from there.\n\nimport { resourceKeyHash } from './resource-keys.js';\nimport type {\n TakeItDownFieldCode,\n TakeItDownRequesterRole,\n TargetLocatorV1,\n TargetLocatorSummaryV1,\n} from '../doc-schemas/index.js';\n\n/**\n * sha256-hex chokepoint. Aliased to `resourceKeyHash` (the single canonical hashing\n * helper in `./resource-keys`) so the whole `./safety` surface has ONE sha256\n * implementation, never two. Named `sha256Hex` here because these intake derivations\n * hash arbitrary keyspace strings, not resource keys.\n */\nexport const sha256Hex = resourceKeyHash;\n\n/** Unit separator — illegal in any id/url/field we join, so a flat separator-joined\n * string is stable by construction (no key-ordering ambiguity, no JSON). */\nconst SEP = '\\x1f';\n\n// ===========================================================================\n// Deterministic ids ([H-07] idempotent intake)\n// ===========================================================================\n\n/** Version tag for the deterministic intake requestId. A bump re-keys EVERY\n * request doc id — treat as a breaking migration of the intake keyspace. */\nconst REQUEST_ID_VERSION = 'takeItDownRequest:v1';\n\n/** Deterministic `nciiTemporaryHolds/{holdId}` doc id for a request. ONE definition shared\n * by intake (which writes the row) and the removal saga (which reads its `target` locator as\n * the fallback when a public-form request has no case-linked allegation). */\nexport function tempHoldId(requestId: string): string {\n return sha256Hex(`nciiTemporaryHold:v1:${requestId}`);\n}\n\n/**\n * Canonical, collision-resistant key for a request target. For a resolvable\n * TTT-hosted locator the key is the locator discriminant + its identity fields;\n * for an external/unresolvable locator (a bare URL or free text) it is the\n * `url`/`additionalText` payload. The SAME logical target ALWAYS normalizes to the\n * SAME key so two submissions about the same content with the same idempotencyKey\n * resolve to ONE request doc, and two DIFFERENT targets never collide.\n *\n * No JSON — a flat unit-separator-joined string (the separator is illegal in any\n * id/url field) is stable by construction (no key-ordering ambiguity).\n */\nexport function normalizedTargetKey(locator: TargetLocatorV1): string {\n const k = locator.kind;\n switch (locator.kind) {\n case 'mediaAsset':\n return [k, locator.mediaAssetId].join(SEP);\n case 'hallItem':\n return [k, locator.hallItemId, locator.subItemId ?? ''].join(SEP);\n case 'squarePost':\n return [k, locator.postId].join(SEP);\n case 'profileImage':\n return [k, locator.profileUid].join(SEP);\n case 'username':\n return [k, locator.profileUid].join(SEP);\n case 'craftSkill':\n return [k, locator.profileUid, locator.craftSkillId].join(SEP);\n case 'commissionListing':\n return [k, locator.commissionListingId].join(SEP);\n case 'audition':\n return [k, locator.auditionId].join(SEP);\n case 'auditionEntry':\n return [k, locator.auditionId, locator.auditionEntryId].join(SEP);\n case 'guildInviteMessage':\n return [k, locator.channelId, locator.messageId].join(SEP);\n case 'chatAttachment':\n return [k, locator.channelId, locator.messageId, locator.attachmentId].join(SEP);\n case 'guildChatMessage':\n return [k, locator.channelId, locator.messageId].join(SEP);\n case 'adminWorkMessage':\n return [k, locator.adminDispatchId, locator.messageId].join(SEP);\n case 'commissionProposal':\n return [k, locator.commissionListingId, locator.commissionProposalId].join(SEP);\n case 'workProject':\n return [k, locator.workProjectId].join(SEP);\n case 'workAsset':\n return [k, locator.workProjectId, locator.workAssetId].join(SEP);\n case 'workRealm':\n return [k, locator.workRealmId].join(SEP);\n case 'url':\n // Normalize the URL (trim + lowercase scheme/host) so trivially-different\n // spellings of the same external link collapse to one request.\n return [k, normalizeExternalUrl(locator.url)].join(SEP);\n case 'additionalText':\n return [k, locator.textRef].join(SEP);\n }\n}\n\n/** Best-effort URL normalization for the external-locator key. A non-parseable\n * string is lowercased + trimmed (still deterministic). NEVER throws. */\nexport function normalizeExternalUrl(raw: string): string {\n const trimmed = raw.trim();\n try {\n const u = new URL(trimmed);\n u.protocol = u.protocol.toLowerCase();\n u.hostname = u.hostname.toLowerCase();\n return u.toString();\n } catch {\n return trimmed.toLowerCase();\n }\n}\n\n/**\n * [H-07] The deterministic intake requestId:\n * sha256('takeItDownRequest:v1:' + idempotencyKey + ':' + normalizedTargetKey).\n * A retry with the SAME client idempotencyKey + the SAME target re-derives the SAME\n * id, so the create-if-absent transaction is a no-op and re-returns the same receipt\n * (never double-arms a clock / double-commits a hold).\n */\nexport function deterministicRequestId(idempotencyKey: string, targetKey: string): string {\n return sha256Hex(`${REQUEST_ID_VERSION}:${idempotencyKey}:${targetKey}`);\n}\n\n/**\n * The opaque public `requestReference` printed on the one-time receipt and stored on the\n * public-status projection. Since the status token was removed, this is a deterministic\n * derivation of the internal `requestId` — a separate sha256 so the raw `requestId` is\n * never recoverable from the reference (which appears on the receipt / projection).\n * 32 hex chars is ample entropy for an opaque, non-enumerable capability.\n */\nexport function deriveRequestReference(requestId: string): string {\n return sha256Hex(`takeItDownRequestRef:v1:${requestId}`).slice(0, 32);\n}\n\n// ===========================================================================\n// Cumulative submission snapshot hash (the immutable receipt fingerprint)\n// ===========================================================================\n\n/**\n * Deterministic hash of the cumulative request state as of a submission — recorded\n * on `TakeItDownSubmissionV1.cumulativeRequestSnapshotHash`. Built from the SORTED\n * supplied field codes + the normalized target key + the requester role so the same\n * cumulative state always fingerprints identically (append-order independent).\n */\nexport function cumulativeSnapshotHash(args: {\n requesterRole: TakeItDownRequesterRole;\n targetKey: string;\n suppliedFieldCodes: readonly TakeItDownFieldCode[];\n}): string {\n const sorted = [...args.suppliedFieldCodes].sort();\n return sha256Hex(\n ['takeItDownSnapshot:v1', args.requesterRole, args.targetKey, sorted.join(',')].join(SEP),\n );\n}\n\n// ===========================================================================\n// Privacy-safe target summary (root + public projection — NO narrative / PII)\n// ===========================================================================\n\n/** A short, non-sensitive surface label per locator kind (no ids, no narrative). */\nexport function surfaceLabelFor(locator: TargetLocatorV1): string {\n switch (locator.kind) {\n case 'mediaAsset':\n return 'Media item';\n case 'hallItem':\n return 'Hall library item';\n case 'squarePost':\n return 'Square Streetz post';\n case 'profileImage':\n return 'Profile image';\n case 'username':\n return 'Username';\n case 'craftSkill':\n return 'Craft skill';\n case 'commissionListing':\n return 'Commission listing';\n case 'audition':\n return 'Audition';\n case 'auditionEntry':\n return 'Audition entry';\n case 'guildInviteMessage':\n return 'Guild invite message';\n case 'chatAttachment':\n return 'Chat attachment';\n case 'guildChatMessage':\n return 'Guild chat message';\n case 'adminWorkMessage':\n return 'Work correspondence message';\n case 'commissionProposal':\n return 'Commission proposal';\n case 'workProject':\n return 'Work project';\n case 'workAsset':\n return 'Work file';\n case 'workRealm':\n return 'Work realm';\n case 'url':\n return 'External link';\n case 'additionalText':\n return 'Described content';\n }\n}\n\n/** Build the privacy-safe `TargetLocatorSummaryV1` (kind + surface label +\n * whether a TTT-hosted target was server-resolved). NEVER the narrative or ids. */\nexport function targetLocatorSummary(\n locator: TargetLocatorV1,\n hasResolvedTarget: boolean,\n): TargetLocatorSummaryV1 {\n return {\n kind: locator.kind,\n surfaceLabel: surfaceLabelFor(locator),\n hasResolvedTarget,\n };\n}\n\n/** TTT-hosted locator kinds the intake will synchronously server-resolve +\n * snapshot + temp-hold. `url` / `additionalText` are external/unresolvable: store\n * the locator + submission, invent NO target. */\nconst TTT_HOSTED_KINDS: ReadonlySet<TargetLocatorV1['kind']> = new Set([\n 'mediaAsset',\n 'hallItem',\n 'squarePost',\n 'profileImage',\n 'username',\n 'craftSkill',\n 'commissionListing',\n 'audition',\n 'auditionEntry',\n 'guildInviteMessage',\n 'chatAttachment',\n]);\n\n/** True when the locator points at a TTT-hosted surface (server-resolvable). */\nexport function isTttHostedLocator(locator: TargetLocatorV1): boolean {\n return TTT_HOSTED_KINDS.has(locator.kind);\n}\n\n// The intake temp hold is placed on the canonical key(s) every destructive guard checks — the\n// content-doc key (`contentDocResourceKey(docPath, 'current')` in `./resource-keys`) + the\n// mediaAsset byte key when the target is media-backed. There is intentionally NO bespoke\n// `nciiTemporaryTarget` key builder here: that namespace was consulted by NO guard, so a hold\n// placed on it never blocked deletion ([H3] void). The route-side mirror's `contentDocCanonicalKey`\n// is likewise NOT ported — it duplicated `contentDocResourceKey(docPath, 'current')`, which is the\n// single key authority.\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-keys.d.ts","sourceRoot":"","sources":["../../src/safety/resource-keys.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resource-keys.d.ts","sourceRoot":"","sources":["../../src/safety/resource-keys.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AASlF,mFAAmF;AACnF,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,QAAQ,CAAC,YAAY,EAAE,sBAAsB,CAAC;IAC9C;2EACuE;IACvE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,6EAA6E;IAC7E,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,oBAAoB,EAAE,MAAM,GAAG,MAAM,CAEpE;AAiBD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,CAE7F;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GAAG,MAAM,GAC1B,WAAW,CAEb;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,cAAc,GAAG,eAAe,CAAC,GAAG,WAAW,EAAE,CAS/G;AAED;wFACwF;AACxF,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,WAAW,CAEvE;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,GAAG,MAAM,GACjC,WAAW,CAOb;AAED,kCAAkC;AAClC,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,CAEjE;AAED,4BAA4B;AAC5B,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAE3D;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE7F"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
// Canonical safety-hold resource keys (Trust & Safety
|
|
1
|
+
// Canonical safety-hold resource keys (frozen Trust & Safety spec, Appendix A §A3;
|
|
2
|
+
// durable design owner: ttt-prod docs/design/csam-detection-and-response.md).
|
|
2
3
|
//
|
|
3
4
|
// A safety hold is keyed by the PHYSICAL resource it protects, not by the case or
|
|
4
5
|
// the request that created it. The `canonicalResourceKey` is a deterministic string
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-keys.js","sourceRoot":"","sources":["../../src/safety/resource-keys.ts"],"names":[],"mappings":"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","sourcesContent":["// Canonical safety-hold resource keys (Trust & Safety IMPLEMENTATION_PLAN Appendix A3).\n//\n// A safety hold is keyed by the PHYSICAL resource it protects, not by the case or\n// the request that created it. The `canonicalResourceKey` is a deterministic string\n// built from exactly the identity fields that pin a physical resource:\n//\n// - a Firestore content target = document path + immutable revision/generation\n// - a storage object = bucket + key + generation\n// - a media target = mediaAsset identity (and, incident-wide, its\n// MediaOriginLineageV1.rootIngestId)\n// - a chat message range = channelId + sequence/epoch boundary\n// - a channel = channelId\n// - an account = uid\n//\n// `resourceKeyHash = sha256(canonicalResourceKey)` is the Firestore doc id for both\n// `safetyHoldResources/{resourceKeyHash}` (the O(1) per-flag counter aggregate) and\n// the `{caseId}__{resourceKeyHash}` ref. THE SAME PHYSICAL RESOURCE MUST ALWAYS HASH\n// IDENTICALLY — that is the whole correctness story: every hold over the same bytes\n// joins the same aggregate, and every destructive guard consults the same doc.\n//\n// This is the ONE canonical declaration (ARCH-102). Both TTT app TS projects (the Cloud\n// Functions graph AND the App Hosting route graph) import these builders from ttt-core\n// rather than hand-mirroring the format — a re-key would otherwise drift the two runtimes\n// apart. node:crypto sha256 keeps it pure + server-safe (this module lives behind the\n// `./safety` entry, never on the main barrel, so the frontend bundle never loads it).\n//\n// Determinism rules (do not change without a key-version bump):\n// - Format is `v1` + a type tag + the identity fields, joined by the unit-separator\n// control char (\\x1f), which cannot appear in a Firestore path, bucket name, key,\n// uid, or channel id. No JSON (key ordering / whitespace is not load-bearing here;\n// a flat separator-joined string is stable by construction).\n// - The type tag prevents cross-type collisions (a contentDoc whose path happens to\n// equal a storage key never shares a hash with that storage object).\n// - A bump to the format string ('v1') is a hold-key migration and is NOT backward\n// compatible — every existing ref/aggregate would re-key. Treat it as a breaking\n// change to the entire hold authority.\n\nimport { createHash } from 'node:crypto';\nimport type { MediaAsset, SafetyHoldResourceType } from '../doc-schemas/index.js';\n\n/** Format/version tag for every canonical key. Bumping this re-keys ALL holds. */\nconst KEY_FORMAT = 'safetyHoldKey/v1';\n\n/** Unit separator — illegal in every identity field we join, so it is an unambiguous\n * field delimiter that never appears inside a path, key, uid, or channel id. */\nconst SEP = '\\x1f';\n\n/** A canonical resource key paired with its sha256 hash (the Firestore doc id). */\nexport interface ResourceKey {\n /** The hold `resourceType` this key belongs to (Appendix A3 enum). */\n readonly resourceType: SafetyHoldResourceType;\n /** A natural identifier for the resource, stored on the ref as `resourceId`\n * (human-debuggable; NOT used for hashing — the canonical key is). */\n readonly resourceId: string;\n /** The deterministic canonical key string (Appendix A3). */\n readonly canonicalResourceKey: string;\n /** `sha256(canonicalResourceKey)` hex — the `safetyHoldResources` doc id. */\n readonly resourceKeyHash: string;\n}\n\n/** sha256-hex of a canonical key string. The single hashing chokepoint. */\nexport function resourceKeyHash(canonicalResourceKey: string): string {\n return createHash('sha256').update(canonicalResourceKey, 'utf8').digest('hex');\n}\n\n/** Build a {canonicalResourceKey, resourceKeyHash} from a type tag + identity parts. */\nfunction buildKey(\n resourceType: SafetyHoldResourceType,\n resourceId: string,\n ...parts: string[]\n): ResourceKey {\n const canonicalResourceKey = [KEY_FORMAT, resourceType, ...parts].join(SEP);\n return {\n resourceType,\n resourceId,\n canonicalResourceKey,\n resourceKeyHash: resourceKeyHash(canonicalResourceKey),\n };\n}\n\n/**\n * Firestore content document target = document path + immutable revision.\n * `docPath` must be the canonical full path (e.g. via `toPath(PATH_BUILDERS.x(...))`),\n * NOT a client-supplied path. `revision` pins a mutable doc to a specific version so a\n * post-hold edit cannot slip the bytes out from under the hold; pass the revision/\n * version the report froze.\n */\nexport function contentDocResourceKey(docPath: string, revision: string | number): ResourceKey {\n return buildKey('contentDoc', docPath, docPath, String(revision));\n}\n\n/**\n * Storage object target = bucket + key + generation. `generation` pins the exact\n * object version (a re-upload to the same key gets a new generation and is a different\n * physical resource). Pass the generation captured when the hold was placed.\n */\nexport function storageObjectResourceKey(\n bucket: string,\n key: string,\n generation: string | number,\n): ResourceKey {\n return buildKey('storageObject', `${bucket}/${key}`, bucket, key, String(generation));\n}\n\n/**\n * Media target. Returns keys for BOTH:\n * 1. the specific `mediaAssetId` (this exact asset/copy), AND\n * 2. its `originLineage.rootIngestId` — when present — as a SEPARATE `mediaAsset`\n * key so a hold can block the ENTIRE incident lineage (every copy/variant that\n * inherited the same root ingest), per Appendix A0/A3 incident-wide blocking.\n *\n * The caller decides which key(s) to place a hold on: a single-asset takedown holds\n * key[0]; an incident-wide block over a CSAM/NCII lineage holds the rootIngest key so\n * every existing AND future copy of the same root is denied. Both keys are `mediaAsset`\n * resourceType (rootIngest is a media identity, not a distinct type — A3 has no\n * separate `rootIngest` type; it is modeled as a mediaAsset key over the root id).\n *\n * Order is stable: index 0 = the asset itself; index 1 (if any) = the rootIngest.\n */\nexport function mediaAssetResourceKeys(asset: Pick<MediaAsset, 'mediaAssetId' | 'originLineage'>): ResourceKey[] {\n const keys: ResourceKey[] = [\n buildKey('mediaAsset', asset.mediaAssetId, 'asset', asset.mediaAssetId),\n ];\n const rootIngestId = asset.originLineage?.rootIngestId;\n if (rootIngestId) {\n keys.push(buildKey('mediaAsset', rootIngestId, 'rootIngest', rootIngestId));\n }\n return keys;\n}\n\n/** The mediaAsset key for a bare rootIngestId (no asset doc in hand) — same canonical\n * shape as `mediaAssetResourceKeys(...)`'s index-1 entry, so it hashes identically. */\nexport function rootIngestResourceKey(rootIngestId: string): ResourceKey {\n return buildKey('mediaAsset', rootIngestId, 'rootIngest', rootIngestId);\n}\n\n/**\n * Chat message-range target = channelId + a sequence/epoch boundary identifying the\n * span of messages held. `messageSeqOrEpoch` is the caller's range identifier (e.g. a\n * sequence number or epoch marker); the SAME range over the same channel must always\n * be passed in the same canonical form by the caller.\n */\nexport function chatMessageRangeResourceKey(\n channelId: string,\n messageSeqOrEpoch: string | number,\n): ResourceKey {\n return buildKey(\n 'chatMessageRange',\n `${channelId}#${messageSeqOrEpoch}`,\n channelId,\n String(messageSeqOrEpoch),\n );\n}\n\n/** Channel target = channelId. */\nexport function channelResourceKey(channelId: string): ResourceKey {\n return buildKey('channel', channelId, channelId);\n}\n\n/** Account target = uid. */\nexport function accountResourceKey(uid: string): ResourceKey {\n return buildKey('account', uid, uid);\n}\n\n/**\n * The composite doc id for a `safetyResourceCommands` row: `resourceKeyHash__commandId`.\n * ONE canonical derivation across the whole safety runtime — both the NCII removal and the\n * CSAM quarantine sagas write and read this collection, and a drift between two hand-written\n * joiners would target different docs, breaking command idempotency on a safety-critical path.\n * `PATH_BUILDERS.safetyResourceCommand(commandDocId)` consumes the result of this function.\n */\nexport function safetyResourceCommandDocId(resourceKeyHash: string, commandId: string): string {\n return `${resourceKeyHash}__${commandId}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"resource-keys.js","sourceRoot":"","sources":["../../src/safety/resource-keys.ts"],"names":[],"mappings":"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","sourcesContent":["// Canonical safety-hold resource keys (frozen Trust & Safety spec, Appendix A §A3;\n// durable design owner: ttt-prod docs/design/csam-detection-and-response.md).\n//\n// A safety hold is keyed by the PHYSICAL resource it protects, not by the case or\n// the request that created it. The `canonicalResourceKey` is a deterministic string\n// built from exactly the identity fields that pin a physical resource:\n//\n// - a Firestore content target = document path + immutable revision/generation\n// - a storage object = bucket + key + generation\n// - a media target = mediaAsset identity (and, incident-wide, its\n// MediaOriginLineageV1.rootIngestId)\n// - a chat message range = channelId + sequence/epoch boundary\n// - a channel = channelId\n// - an account = uid\n//\n// `resourceKeyHash = sha256(canonicalResourceKey)` is the Firestore doc id for both\n// `safetyHoldResources/{resourceKeyHash}` (the O(1) per-flag counter aggregate) and\n// the `{caseId}__{resourceKeyHash}` ref. THE SAME PHYSICAL RESOURCE MUST ALWAYS HASH\n// IDENTICALLY — that is the whole correctness story: every hold over the same bytes\n// joins the same aggregate, and every destructive guard consults the same doc.\n//\n// This is the ONE canonical declaration (ARCH-102). Both TTT app TS projects (the Cloud\n// Functions graph AND the App Hosting route graph) import these builders from ttt-core\n// rather than hand-mirroring the format — a re-key would otherwise drift the two runtimes\n// apart. node:crypto sha256 keeps it pure + server-safe (this module lives behind the\n// `./safety` entry, never on the main barrel, so the frontend bundle never loads it).\n//\n// Determinism rules (do not change without a key-version bump):\n// - Format is `v1` + a type tag + the identity fields, joined by the unit-separator\n// control char (\\x1f), which cannot appear in a Firestore path, bucket name, key,\n// uid, or channel id. No JSON (key ordering / whitespace is not load-bearing here;\n// a flat separator-joined string is stable by construction).\n// - The type tag prevents cross-type collisions (a contentDoc whose path happens to\n// equal a storage key never shares a hash with that storage object).\n// - A bump to the format string ('v1') is a hold-key migration and is NOT backward\n// compatible — every existing ref/aggregate would re-key. Treat it as a breaking\n// change to the entire hold authority.\n\nimport { createHash } from 'node:crypto';\nimport type { MediaAsset, SafetyHoldResourceType } from '../doc-schemas/index.js';\n\n/** Format/version tag for every canonical key. Bumping this re-keys ALL holds. */\nconst KEY_FORMAT = 'safetyHoldKey/v1';\n\n/** Unit separator — illegal in every identity field we join, so it is an unambiguous\n * field delimiter that never appears inside a path, key, uid, or channel id. */\nconst SEP = '\\x1f';\n\n/** A canonical resource key paired with its sha256 hash (the Firestore doc id). */\nexport interface ResourceKey {\n /** The hold `resourceType` this key belongs to (Appendix A3 enum). */\n readonly resourceType: SafetyHoldResourceType;\n /** A natural identifier for the resource, stored on the ref as `resourceId`\n * (human-debuggable; NOT used for hashing — the canonical key is). */\n readonly resourceId: string;\n /** The deterministic canonical key string (Appendix A3). */\n readonly canonicalResourceKey: string;\n /** `sha256(canonicalResourceKey)` hex — the `safetyHoldResources` doc id. */\n readonly resourceKeyHash: string;\n}\n\n/** sha256-hex of a canonical key string. The single hashing chokepoint. */\nexport function resourceKeyHash(canonicalResourceKey: string): string {\n return createHash('sha256').update(canonicalResourceKey, 'utf8').digest('hex');\n}\n\n/** Build a {canonicalResourceKey, resourceKeyHash} from a type tag + identity parts. */\nfunction buildKey(\n resourceType: SafetyHoldResourceType,\n resourceId: string,\n ...parts: string[]\n): ResourceKey {\n const canonicalResourceKey = [KEY_FORMAT, resourceType, ...parts].join(SEP);\n return {\n resourceType,\n resourceId,\n canonicalResourceKey,\n resourceKeyHash: resourceKeyHash(canonicalResourceKey),\n };\n}\n\n/**\n * Firestore content document target = document path + immutable revision.\n * `docPath` must be the canonical full path (e.g. via `toPath(PATH_BUILDERS.x(...))`),\n * NOT a client-supplied path. `revision` pins a mutable doc to a specific version so a\n * post-hold edit cannot slip the bytes out from under the hold; pass the revision/\n * version the report froze.\n */\nexport function contentDocResourceKey(docPath: string, revision: string | number): ResourceKey {\n return buildKey('contentDoc', docPath, docPath, String(revision));\n}\n\n/**\n * Storage object target = bucket + key + generation. `generation` pins the exact\n * object version (a re-upload to the same key gets a new generation and is a different\n * physical resource). Pass the generation captured when the hold was placed.\n */\nexport function storageObjectResourceKey(\n bucket: string,\n key: string,\n generation: string | number,\n): ResourceKey {\n return buildKey('storageObject', `${bucket}/${key}`, bucket, key, String(generation));\n}\n\n/**\n * Media target. Returns keys for BOTH:\n * 1. the specific `mediaAssetId` (this exact asset/copy), AND\n * 2. its `originLineage.rootIngestId` — when present — as a SEPARATE `mediaAsset`\n * key so a hold can block the ENTIRE incident lineage (every copy/variant that\n * inherited the same root ingest), per Appendix A0/A3 incident-wide blocking.\n *\n * The caller decides which key(s) to place a hold on: a single-asset takedown holds\n * key[0]; an incident-wide block over a CSAM/NCII lineage holds the rootIngest key so\n * every existing AND future copy of the same root is denied. Both keys are `mediaAsset`\n * resourceType (rootIngest is a media identity, not a distinct type — A3 has no\n * separate `rootIngest` type; it is modeled as a mediaAsset key over the root id).\n *\n * Order is stable: index 0 = the asset itself; index 1 (if any) = the rootIngest.\n */\nexport function mediaAssetResourceKeys(asset: Pick<MediaAsset, 'mediaAssetId' | 'originLineage'>): ResourceKey[] {\n const keys: ResourceKey[] = [\n buildKey('mediaAsset', asset.mediaAssetId, 'asset', asset.mediaAssetId),\n ];\n const rootIngestId = asset.originLineage?.rootIngestId;\n if (rootIngestId) {\n keys.push(buildKey('mediaAsset', rootIngestId, 'rootIngest', rootIngestId));\n }\n return keys;\n}\n\n/** The mediaAsset key for a bare rootIngestId (no asset doc in hand) — same canonical\n * shape as `mediaAssetResourceKeys(...)`'s index-1 entry, so it hashes identically. */\nexport function rootIngestResourceKey(rootIngestId: string): ResourceKey {\n return buildKey('mediaAsset', rootIngestId, 'rootIngest', rootIngestId);\n}\n\n/**\n * Chat message-range target = channelId + a sequence/epoch boundary identifying the\n * span of messages held. `messageSeqOrEpoch` is the caller's range identifier (e.g. a\n * sequence number or epoch marker); the SAME range over the same channel must always\n * be passed in the same canonical form by the caller.\n */\nexport function chatMessageRangeResourceKey(\n channelId: string,\n messageSeqOrEpoch: string | number,\n): ResourceKey {\n return buildKey(\n 'chatMessageRange',\n `${channelId}#${messageSeqOrEpoch}`,\n channelId,\n String(messageSeqOrEpoch),\n );\n}\n\n/** Channel target = channelId. */\nexport function channelResourceKey(channelId: string): ResourceKey {\n return buildKey('channel', channelId, channelId);\n}\n\n/** Account target = uid. */\nexport function accountResourceKey(uid: string): ResourceKey {\n return buildKey('account', uid, uid);\n}\n\n/**\n * The composite doc id for a `safetyResourceCommands` row: `resourceKeyHash__commandId`.\n * ONE canonical derivation across the whole safety runtime — both the NCII removal and the\n * CSAM quarantine sagas write and read this collection, and a drift between two hand-written\n * joiners would target different docs, breaking command idempotency on a safety-critical path.\n * `PATH_BUILDERS.safetyResourceCommand(commandDocId)` consumes the result of this function.\n */\nexport function safetyResourceCommandDocId(resourceKeyHash: string, commandId: string): string {\n return `${resourceKeyHash}__${commandId}`;\n}\n"]}
|
package/dist/schemas/chat.js
CHANGED
|
@@ -10,7 +10,7 @@ export const ArchiveGuildChatChannelInputSchema = z.object({
|
|
|
10
10
|
}).strict();
|
|
11
11
|
// Tombstone (delete) a guild chat channel — same input shape as archive. The callable marks the
|
|
12
12
|
// channel `isDeleted`, bumps configVersion, revokes grants, and re-projects members to `removed`;
|
|
13
|
-
// storage is retained (never a physical purge). See docs/design/chat-realtime-system.md.
|
|
13
|
+
// storage is retained (never a physical purge). See ttt-prod docs/design/chat-realtime-system.md.
|
|
14
14
|
export const DeleteGuildChatChannelInputSchema = z.object({
|
|
15
15
|
workProjectId: workProjectIdSchema,
|
|
16
16
|
guildChatChannelId: guildChatChannelIdSchema,
|
|
@@ -24,7 +24,7 @@ export const CreateGuildChatChannelInputSchema = z.object({
|
|
|
24
24
|
}).strict();
|
|
25
25
|
// Guild channels + invite threads moved to the realtime chat (Cloudflare DOs), which
|
|
26
26
|
// sends through the DO client — NOT this callable. The Firestore send path is retained
|
|
27
|
-
// PERMANENTLY for admin-support threads only (docs/design/chat-realtime-system.md).
|
|
27
|
+
// PERMANENTLY for admin-support threads only (ttt-prod docs/design/chat-realtime-system.md).
|
|
28
28
|
export const SendGuildChatMessageInputSchema = z.object({
|
|
29
29
|
threadKind: z.literal('adminSupport'),
|
|
30
30
|
adminDispatchId: adminDispatchIdSchema,
|
|
@@ -92,7 +92,7 @@ export const UpdateGuildChatChannelInputSchema = z.object({
|
|
|
92
92
|
requiredGuildStandings: z.array(z.string().min(1).max(64)).max(20).optional(),
|
|
93
93
|
allowedUserIds: z.array(z.string().min(1).max(128)).max(500).optional(),
|
|
94
94
|
}).strict();
|
|
95
|
-
// --- Admin chat moderation callables (review-only; chat-realtime-system.md) ---
|
|
95
|
+
// --- Admin chat moderation callables (review-only; ttt-prod docs/design/chat-realtime-system.md) ---
|
|
96
96
|
// The CLIENT-facing channel-ref for the admin chat-moderation callables
|
|
97
97
|
// (`adminModerateChatMessage` / `adminReadChannelContext`). This is the WIRE shape, which
|
|
98
98
|
// is `kind`-discriminated — NOT the internal `scope`-keyed `ChannelRefTupleSchema`
|
package/dist/schemas/chat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/schemas/chat.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport {\n workProjectIdSchema,\n guildChatChannelIdSchema,\n guildInviteIdSchema,\n adminDispatchIdSchema,\n} from './atoms.js';\nimport {\n MAX_CHAT_MESSAGE_LENGTH,\n MAX_GUILD_CHAT_CHANNEL_NAME_LENGTH,\n MAX_GUILD_CHAT_CHANNEL_DESCRIPTION_LENGTH,\n} from '../constants/chat.js';\nimport {\n MAX_ADMIN_DISPATCH_SUBJECT_LENGTH,\n MAX_ADMIN_DISPATCH_INITIAL_TEXT_LENGTH,\n MAX_CHAT_MODERATION_REASON_LENGTH,\n} from '../constants/business.js';\nimport {\n ReplyToSchema,\n} from '@ttt-productions/chat-schemas';\nimport { AdminDispatchContextRefSchema } from '../doc-schemas/messaging.js';\n\nexport const ArchiveGuildChatChannelInputSchema = z.object({\n workProjectId: workProjectIdSchema,\n guildChatChannelId: guildChatChannelIdSchema,\n}).strict();\nexport type ArchiveGuildChatChannelInput = z.infer<typeof ArchiveGuildChatChannelInputSchema>;\n\n// Tombstone (delete) a guild chat channel — same input shape as archive. The callable marks the\n// channel `isDeleted`, bumps configVersion, revokes grants, and re-projects members to `removed`;\n// storage is retained (never a physical purge). See docs/design/chat-realtime-system.md.\nexport const DeleteGuildChatChannelInputSchema = z.object({\n workProjectId: workProjectIdSchema,\n guildChatChannelId: guildChatChannelIdSchema,\n}).strict();\nexport type DeleteGuildChatChannelInput = z.infer<typeof DeleteGuildChatChannelInputSchema>;\n\nexport const CreateGuildChatChannelInputSchema = z.object({\n workProjectId: workProjectIdSchema,\n channelName: z.string().min(1).max(MAX_GUILD_CHAT_CHANNEL_NAME_LENGTH),\n description: z.string().max(MAX_GUILD_CHAT_CHANNEL_DESCRIPTION_LENGTH).optional(),\n requiredGuildStandings: z.array(z.string().min(1).max(64)).max(20),\n allowedUserIds: z.array(z.string().min(1).max(128)).max(500),\n}).strict();\nexport type CreateGuildChatChannelInput = z.infer<typeof CreateGuildChatChannelInputSchema>;\n\n// Guild channels + invite threads moved to the realtime chat (Cloudflare DOs), which\n// sends through the DO client — NOT this callable. The Firestore send path is retained\n// PERMANENTLY for admin-support threads only (docs/design/chat-realtime-system.md).\nexport const SendGuildChatMessageInputSchema = z.object({\n threadKind: z.literal('adminSupport'),\n adminDispatchId: adminDispatchIdSchema,\n isUserReply: z.boolean(),\n text: z.string().max(MAX_CHAT_MESSAGE_LENGTH),\n replyTo: ReplyToSchema.optional(),\n}).strict();\nexport type SendGuildChatMessageInput = z.infer<typeof SendGuildChatMessageInputSchema>;\n\n// Wire result contract for the sendGuildChatMessage callable — what the CLIENT relies on.\n// The backend core's internal result type extends this with composition-only\n// inline-materialize fields (backend↔backend contract; see runSendGuildChatMessage) —\n// those extra fields may appear on the wire and are not part of the client contract,\n// so this schema is deliberately non-strict.\nexport const SendGuildChatMessageResultSchema = z.object({\n success: z.literal(true),\n messageId: z.string().min(1),\n messageDocPath: z.string().min(1),\n});\nexport type SendGuildChatMessageResult = z.infer<typeof SendGuildChatMessageResultSchema>;\n\n// Subject/initial-text share the admin-dispatch caps (the \"contact admin\" composer\n// enforces them) — the old MAX_CHAT_MESSAGE_LENGTH bound let a 4000-char SUBJECT through.\nexport const StartAdminSupportThreadInputSchema = z.object({\n subject: z.string().min(1).max(MAX_ADMIN_DISPATCH_SUBJECT_LENGTH),\n initialMessage: z.string().min(1).max(MAX_ADMIN_DISPATCH_INITIAL_TEXT_LENGTH),\n}).strict();\nexport type StartAdminSupportThreadInput = z.infer<typeof StartAdminSupportThreadInputSchema>;\n\n// Authoritative result of startAdminSupportThread — both ids are minted inside the\n// creating transaction. Non-strict (server → client result posture).\nexport const StartAdminSupportThreadResultSchema = z.object({\n success: z.literal(true),\n adminDispatchId: z.string().min(1),\n /** Doc id of the initial conversation message. */\n messageId: z.string().min(1),\n});\nexport type StartAdminSupportThreadResult = z.infer<typeof StartAdminSupportThreadResultSchema>;\n\n// Member-initiated workProject → admin correspondence thread (party-generic dispatch,\n// partyKind 'workProject'). Initiating is a Work ACTION (`adminDispatch.start`, BACKEND-006);\n// the callable enforces the one-open-work-initiated-thread spam guard. `contextRef` is\n// server-validated against the workProject before it is stored.\nexport const StartWorkProjectAdminSupportThreadInputSchema = z.object({\n workProjectId: workProjectIdSchema,\n subject: z.string().min(1).max(MAX_ADMIN_DISPATCH_SUBJECT_LENGTH),\n initialMessage: z.string().min(1).max(MAX_ADMIN_DISPATCH_INITIAL_TEXT_LENGTH),\n contextRef: AdminDispatchContextRefSchema.optional(),\n}).strict();\nexport type StartWorkProjectAdminSupportThreadInput = z.infer<typeof StartWorkProjectAdminSupportThreadInputSchema>;\n\n// Admin-initiated dispatch to a workProject party (mirrors the admin→user dispatch:\n// lands unread for the work's active guildmates, creates NO admin task, uncapped).\nexport const CreateAdminDispatchToWorkProjectInputSchema = z.object({\n workProjectId: workProjectIdSchema,\n subject: z.string().trim().min(1).max(MAX_ADMIN_DISPATCH_SUBJECT_LENGTH),\n message: z.string().trim().min(1).max(MAX_ADMIN_DISPATCH_INITIAL_TEXT_LENGTH),\n contextRef: AdminDispatchContextRefSchema.optional(),\n}).strict();\nexport type CreateAdminDispatchToWorkProjectInput = z.infer<typeof CreateAdminDispatchToWorkProjectInputSchema>;\n\n// CLIENT-CALLED wire contract for the `mintChatGrant` callable (Contract A; P3).\n// Crosses the backend↔frontend boundary — the web client (and any future mobile/TV\n// client) constructs this payload — so it lives here per the callable-validation\n// convention, not locally in the functions repo. The REAL Firestore authorization\n// check happens in the callable; this schema pins the scope shape.\nexport const ChatGrantInputSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('channel'), workProjectId: workProjectIdSchema, guildChatChannelId: guildChatChannelIdSchema }).strict(),\n z.object({ kind: z.literal('invite'), guildInviteId: guildInviteIdSchema }).strict(),\n z.object({ kind: z.literal('inbox') }).strict(),\n]);\nexport type ChatGrantInput = z.infer<typeof ChatGrantInputSchema>;\n\nexport const UpdateGuildChatChannelInputSchema = z.object({\n workProjectId: workProjectIdSchema,\n guildChatChannelId: guildChatChannelIdSchema,\n channelName: z.string().min(1).max(MAX_GUILD_CHAT_CHANNEL_NAME_LENGTH).optional(),\n description: z.string().max(MAX_GUILD_CHAT_CHANNEL_DESCRIPTION_LENGTH).optional(),\n requiredGuildStandings: z.array(z.string().min(1).max(64)).max(20).optional(),\n allowedUserIds: z.array(z.string().min(1).max(128)).max(500).optional(),\n}).strict();\nexport type UpdateGuildChatChannelInput = z.infer<typeof UpdateGuildChatChannelInputSchema>;\n\n// --- Admin chat moderation callables (review-only; chat-realtime-system.md) ---\n\n// The CLIENT-facing channel-ref for the admin chat-moderation callables\n// (`adminModerateChatMessage` / `adminReadChannelContext`). This is the WIRE shape, which\n// is `kind`-discriminated — NOT the internal `scope`-keyed `ChannelRefTupleSchema`\n// (@ttt-productions/chat-schemas), which the callables map to server-side AFTER parsing.\n// Mirrors the existing `TombstoneChatSchema.channel` union in schemas/admin.ts.\nexport const AdminChatModerationChannelSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('channel'), workProjectId: workProjectIdSchema, guildChatChannelId: guildChatChannelIdSchema }).strict(),\n z.object({ kind: z.literal('invite'), guildInviteId: guildInviteIdSchema }).strict(),\n]);\nexport type AdminChatModerationChannel = z.infer<typeof AdminChatModerationChannelSchema>;\n\n// `adminModerateChatMessage` — queue a DO-owned hide/delete command. The moderated message's\n// attachment asset id(s) are NEVER client-supplied (the drain derives them server-side), so\n// `.strict()` rejects any such field.\nexport const AdminModerateChatMessageInputSchema = z.object({\n requestId: z.string().min(1).max(200),\n action: z.enum(['hide', 'delete']),\n messageSeq: z.number().int().nonnegative(),\n expectedMessageRevision: z.number().int().nonnegative(),\n caseId: z.string().min(1).max(200),\n reason: z.string().min(1).max(MAX_CHAT_MODERATION_REASON_LENGTH),\n channel: AdminChatModerationChannelSchema,\n}).strict();\nexport type AdminModerateChatMessageInput = z.infer<typeof AdminModerateChatMessageInputSchema>;\n\n// `adminReadChannelContext` — case-bound admin read of a bounded (≤50 before / ≤50 after)\n// message window around a reported message. Requires a case id + reason; both request and\n// outcome are audited.\nexport const AdminReadChannelContextInputSchema = z.object({\n reportedSeq: z.number().int().nonnegative(),\n caseId: z.string().min(1).max(200),\n reason: z.string().min(1).max(MAX_CHAT_MODERATION_REASON_LENGTH),\n before: z.number().int().min(0).max(50).optional(),\n after: z.number().int().min(0).max(50).optional(),\n channel: AdminChatModerationChannelSchema,\n}).strict();\nexport type AdminReadChannelContextInput = z.infer<typeof AdminReadChannelContextInputSchema>;\n\n\n\n"]}
|
|
1
|
+
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/schemas/chat.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport {\n workProjectIdSchema,\n guildChatChannelIdSchema,\n guildInviteIdSchema,\n adminDispatchIdSchema,\n} from './atoms.js';\nimport {\n MAX_CHAT_MESSAGE_LENGTH,\n MAX_GUILD_CHAT_CHANNEL_NAME_LENGTH,\n MAX_GUILD_CHAT_CHANNEL_DESCRIPTION_LENGTH,\n} from '../constants/chat.js';\nimport {\n MAX_ADMIN_DISPATCH_SUBJECT_LENGTH,\n MAX_ADMIN_DISPATCH_INITIAL_TEXT_LENGTH,\n MAX_CHAT_MODERATION_REASON_LENGTH,\n} from '../constants/business.js';\nimport {\n ReplyToSchema,\n} from '@ttt-productions/chat-schemas';\nimport { AdminDispatchContextRefSchema } from '../doc-schemas/messaging.js';\n\nexport const ArchiveGuildChatChannelInputSchema = z.object({\n workProjectId: workProjectIdSchema,\n guildChatChannelId: guildChatChannelIdSchema,\n}).strict();\nexport type ArchiveGuildChatChannelInput = z.infer<typeof ArchiveGuildChatChannelInputSchema>;\n\n// Tombstone (delete) a guild chat channel — same input shape as archive. The callable marks the\n// channel `isDeleted`, bumps configVersion, revokes grants, and re-projects members to `removed`;\n// storage is retained (never a physical purge). See ttt-prod docs/design/chat-realtime-system.md.\nexport const DeleteGuildChatChannelInputSchema = z.object({\n workProjectId: workProjectIdSchema,\n guildChatChannelId: guildChatChannelIdSchema,\n}).strict();\nexport type DeleteGuildChatChannelInput = z.infer<typeof DeleteGuildChatChannelInputSchema>;\n\nexport const CreateGuildChatChannelInputSchema = z.object({\n workProjectId: workProjectIdSchema,\n channelName: z.string().min(1).max(MAX_GUILD_CHAT_CHANNEL_NAME_LENGTH),\n description: z.string().max(MAX_GUILD_CHAT_CHANNEL_DESCRIPTION_LENGTH).optional(),\n requiredGuildStandings: z.array(z.string().min(1).max(64)).max(20),\n allowedUserIds: z.array(z.string().min(1).max(128)).max(500),\n}).strict();\nexport type CreateGuildChatChannelInput = z.infer<typeof CreateGuildChatChannelInputSchema>;\n\n// Guild channels + invite threads moved to the realtime chat (Cloudflare DOs), which\n// sends through the DO client — NOT this callable. The Firestore send path is retained\n// PERMANENTLY for admin-support threads only (ttt-prod docs/design/chat-realtime-system.md).\nexport const SendGuildChatMessageInputSchema = z.object({\n threadKind: z.literal('adminSupport'),\n adminDispatchId: adminDispatchIdSchema,\n isUserReply: z.boolean(),\n text: z.string().max(MAX_CHAT_MESSAGE_LENGTH),\n replyTo: ReplyToSchema.optional(),\n}).strict();\nexport type SendGuildChatMessageInput = z.infer<typeof SendGuildChatMessageInputSchema>;\n\n// Wire result contract for the sendGuildChatMessage callable — what the CLIENT relies on.\n// The backend core's internal result type extends this with composition-only\n// inline-materialize fields (backend↔backend contract; see runSendGuildChatMessage) —\n// those extra fields may appear on the wire and are not part of the client contract,\n// so this schema is deliberately non-strict.\nexport const SendGuildChatMessageResultSchema = z.object({\n success: z.literal(true),\n messageId: z.string().min(1),\n messageDocPath: z.string().min(1),\n});\nexport type SendGuildChatMessageResult = z.infer<typeof SendGuildChatMessageResultSchema>;\n\n// Subject/initial-text share the admin-dispatch caps (the \"contact admin\" composer\n// enforces them) — the old MAX_CHAT_MESSAGE_LENGTH bound let a 4000-char SUBJECT through.\nexport const StartAdminSupportThreadInputSchema = z.object({\n subject: z.string().min(1).max(MAX_ADMIN_DISPATCH_SUBJECT_LENGTH),\n initialMessage: z.string().min(1).max(MAX_ADMIN_DISPATCH_INITIAL_TEXT_LENGTH),\n}).strict();\nexport type StartAdminSupportThreadInput = z.infer<typeof StartAdminSupportThreadInputSchema>;\n\n// Authoritative result of startAdminSupportThread — both ids are minted inside the\n// creating transaction. Non-strict (server → client result posture).\nexport const StartAdminSupportThreadResultSchema = z.object({\n success: z.literal(true),\n adminDispatchId: z.string().min(1),\n /** Doc id of the initial conversation message. */\n messageId: z.string().min(1),\n});\nexport type StartAdminSupportThreadResult = z.infer<typeof StartAdminSupportThreadResultSchema>;\n\n// Member-initiated workProject → admin correspondence thread (party-generic dispatch,\n// partyKind 'workProject'). Initiating is a Work ACTION (`adminDispatch.start`, BACKEND-006);\n// the callable enforces the one-open-work-initiated-thread spam guard. `contextRef` is\n// server-validated against the workProject before it is stored.\nexport const StartWorkProjectAdminSupportThreadInputSchema = z.object({\n workProjectId: workProjectIdSchema,\n subject: z.string().min(1).max(MAX_ADMIN_DISPATCH_SUBJECT_LENGTH),\n initialMessage: z.string().min(1).max(MAX_ADMIN_DISPATCH_INITIAL_TEXT_LENGTH),\n contextRef: AdminDispatchContextRefSchema.optional(),\n}).strict();\nexport type StartWorkProjectAdminSupportThreadInput = z.infer<typeof StartWorkProjectAdminSupportThreadInputSchema>;\n\n// Admin-initiated dispatch to a workProject party (mirrors the admin→user dispatch:\n// lands unread for the work's active guildmates, creates NO admin task, uncapped).\nexport const CreateAdminDispatchToWorkProjectInputSchema = z.object({\n workProjectId: workProjectIdSchema,\n subject: z.string().trim().min(1).max(MAX_ADMIN_DISPATCH_SUBJECT_LENGTH),\n message: z.string().trim().min(1).max(MAX_ADMIN_DISPATCH_INITIAL_TEXT_LENGTH),\n contextRef: AdminDispatchContextRefSchema.optional(),\n}).strict();\nexport type CreateAdminDispatchToWorkProjectInput = z.infer<typeof CreateAdminDispatchToWorkProjectInputSchema>;\n\n// CLIENT-CALLED wire contract for the `mintChatGrant` callable (Contract A; P3).\n// Crosses the backend↔frontend boundary — the web client (and any future mobile/TV\n// client) constructs this payload — so it lives here per the callable-validation\n// convention, not locally in the functions repo. The REAL Firestore authorization\n// check happens in the callable; this schema pins the scope shape.\nexport const ChatGrantInputSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('channel'), workProjectId: workProjectIdSchema, guildChatChannelId: guildChatChannelIdSchema }).strict(),\n z.object({ kind: z.literal('invite'), guildInviteId: guildInviteIdSchema }).strict(),\n z.object({ kind: z.literal('inbox') }).strict(),\n]);\nexport type ChatGrantInput = z.infer<typeof ChatGrantInputSchema>;\n\nexport const UpdateGuildChatChannelInputSchema = z.object({\n workProjectId: workProjectIdSchema,\n guildChatChannelId: guildChatChannelIdSchema,\n channelName: z.string().min(1).max(MAX_GUILD_CHAT_CHANNEL_NAME_LENGTH).optional(),\n description: z.string().max(MAX_GUILD_CHAT_CHANNEL_DESCRIPTION_LENGTH).optional(),\n requiredGuildStandings: z.array(z.string().min(1).max(64)).max(20).optional(),\n allowedUserIds: z.array(z.string().min(1).max(128)).max(500).optional(),\n}).strict();\nexport type UpdateGuildChatChannelInput = z.infer<typeof UpdateGuildChatChannelInputSchema>;\n\n// --- Admin chat moderation callables (review-only; ttt-prod docs/design/chat-realtime-system.md) ---\n\n// The CLIENT-facing channel-ref for the admin chat-moderation callables\n// (`adminModerateChatMessage` / `adminReadChannelContext`). This is the WIRE shape, which\n// is `kind`-discriminated — NOT the internal `scope`-keyed `ChannelRefTupleSchema`\n// (@ttt-productions/chat-schemas), which the callables map to server-side AFTER parsing.\n// Mirrors the existing `TombstoneChatSchema.channel` union in schemas/admin.ts.\nexport const AdminChatModerationChannelSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('channel'), workProjectId: workProjectIdSchema, guildChatChannelId: guildChatChannelIdSchema }).strict(),\n z.object({ kind: z.literal('invite'), guildInviteId: guildInviteIdSchema }).strict(),\n]);\nexport type AdminChatModerationChannel = z.infer<typeof AdminChatModerationChannelSchema>;\n\n// `adminModerateChatMessage` — queue a DO-owned hide/delete command. The moderated message's\n// attachment asset id(s) are NEVER client-supplied (the drain derives them server-side), so\n// `.strict()` rejects any such field.\nexport const AdminModerateChatMessageInputSchema = z.object({\n requestId: z.string().min(1).max(200),\n action: z.enum(['hide', 'delete']),\n messageSeq: z.number().int().nonnegative(),\n expectedMessageRevision: z.number().int().nonnegative(),\n caseId: z.string().min(1).max(200),\n reason: z.string().min(1).max(MAX_CHAT_MODERATION_REASON_LENGTH),\n channel: AdminChatModerationChannelSchema,\n}).strict();\nexport type AdminModerateChatMessageInput = z.infer<typeof AdminModerateChatMessageInputSchema>;\n\n// `adminReadChannelContext` — case-bound admin read of a bounded (≤50 before / ≤50 after)\n// message window around a reported message. Requires a case id + reason; both request and\n// outcome are audited.\nexport const AdminReadChannelContextInputSchema = z.object({\n reportedSeq: z.number().int().nonnegative(),\n caseId: z.string().min(1).max(200),\n reason: z.string().min(1).max(MAX_CHAT_MODERATION_REASON_LENGTH),\n before: z.number().int().min(0).max(50).optional(),\n after: z.number().int().min(0).max(50).optional(),\n channel: AdminChatModerationChannelSchema,\n}).strict();\nexport type AdminReadChannelContextInput = z.infer<typeof AdminReadChannelContextInputSchema>;\n\n\n\n"]}
|
package/dist/schemas/ncii.js
CHANGED
|
@@ -5,7 +5,7 @@ import { TAKE_IT_DOWN_VALIDITY_CONFIRMATION } from '../constants/safety-confirma
|
|
|
5
5
|
// NCII / TAKE IT DOWN callable-input schemas. Cross-boundary contracts for the
|
|
6
6
|
// `functions/src/ncii/` callables — moved here from local `functions/` definitions so the
|
|
7
7
|
// callable-validation standard (parse `request.data` with a `.strict()` ttt-core schema before any
|
|
8
|
-
// auth check) is met from one authoritative place. See docs/design/callable-validation.md.
|
|
8
|
+
// auth check) is met from one authoritative place. See ttt-prod docs/design/callable-validation.md.
|
|
9
9
|
// Operator sets the child-safety crossover minorAssessment on an NCII case (CSAM possible-minor
|
|
10
10
|
// crossover — DECISION 2026-07-01: operator-only, no automated age signal). A routing action: it
|
|
11
11
|
// opens/links a PARALLEL child-safety review + deny-serving + PhotoDNA, WITHOUT touching the NCII
|
package/dist/schemas/ncii.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ncii.js","sourceRoot":"","sources":["../../src/schemas/ncii.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport { ReportableItemTypeSchema, NciiMinorAssessmentSchema } from '../doc-schemas/safety/foundation.js';\nimport {\n MAX_NCII_EVIDENCE_REASON_LENGTH,\n MAX_NCII_RATIONALE_LENGTH,\n MAX_NCII_NONCONSENT_STATEMENT_LENGTH,\n MAX_NCII_SIGNED_NAME_LENGTH,\n MAX_NCII_SUPPORTING_FACTS_LENGTH,\n} from '../constants/business.js';\nimport { TAKE_IT_DOWN_VALIDITY_CONFIRMATION } from '../constants/safety-confirmation-phrases.js';\n\n// NCII / TAKE IT DOWN callable-input schemas. Cross-boundary contracts for the\n// `functions/src/ncii/` callables — moved here from local `functions/` definitions so the\n// callable-validation standard (parse `request.data` with a `.strict()` ttt-core schema before any\n// auth check) is met from one authoritative place. See docs/design/callable-validation.md.\n\n// Operator sets the child-safety crossover minorAssessment on an NCII case (CSAM possible-minor\n// crossover — DECISION 2026-07-01: operator-only, no automated age signal). A routing action: it\n// opens/links a PARALLEL child-safety review + deny-serving + PhotoDNA, WITHOUT touching the NCII\n// removal clock and WITHOUT auto-NCMEC (that still needs a validated hash or explicit human\n// confirmApparentViolation). Writes the `ncii.minorAssessmentSet` audit event.\nexport const SetNciiMinorAssessmentInputSchema = z.object({\n caseId: z.string().min(1),\n minorAssessment: NciiMinorAssessmentSchema,\n}).strict();\nexport type SetNciiMinorAssessmentInput = z.infer<typeof SetNciiMinorAssessmentInputSchema>;\n\n// ===========================================================================\n// listNciiEvidencePool — admin-only pool read for the find-&-link flow.\n// Optional caseId selects the currently-linked evidence to also return.\n// ===========================================================================\nexport const ListNciiEvidencePoolInputSchema = z.object({\n caseId: z.string().min(1).optional(),\n}).strict();\nexport type ListNciiEvidencePoolInput = z.infer<typeof ListNciiEvidencePoolInputSchema>;\n\n// ===========================================================================\n// linkNciiEvidenceToCase / unlinkNciiEvidenceFromCase — find-&-link step 2 (+ reverse).\n// ===========================================================================\nexport const LinkNciiEvidenceToCaseInputSchema = z.object({\n allegationId: z.string().min(1),\n caseId: z.string().min(1),\n}).strict();\nexport type LinkNciiEvidenceToCaseInput = z.infer<typeof LinkNciiEvidenceToCaseInputSchema>;\n\nexport const UnlinkNciiEvidenceFromCaseInputSchema = z.object({\n allegationId: z.string().min(1),\n caseId: z.string().min(1),\n}).strict();\nexport type UnlinkNciiEvidenceFromCaseInput = z.infer<typeof UnlinkNciiEvidenceFromCaseInputSchema>;\n\n// ===========================================================================\n// markNciiEvidence — admin-only \"NCII linked evidence\" intake (find-&-link step 1).\n// Client ids are HINTS; the target is re-resolved server-side.\n// ===========================================================================\nexport const MarkNciiEvidenceInputSchema = z.object({\n itemType: ReportableItemTypeSchema,\n reportedItemId: z.string().min(1),\n parentItemId: z.string().min(1).optional(),\n reportedUserId: z.string().min(1).optional(),\n reason: z.string().trim().max(MAX_NCII_EVIDENCE_REASON_LENGTH).optional(),\n}).strict();\nexport type MarkNciiEvidenceInput = z.infer<typeof MarkNciiEvidenceInputSchema>;\n\n// ===========================================================================\n// decideTakeItDownValidity — the nciiRequestReviewer-gated validity determination.\n// ===========================================================================\n\n/** Enumerated invalid-ruling reasons (never free-form). */\nexport const TakeItDownInvalidReasonCodeSchema = z.enum([\n 'missingSignature',\n 'missingLocator',\n 'missingNonconsentStatement',\n 'missingContactInformation',\n 'requesterNotDepictedPersonOrAuthorizedRepresentative',\n 'authorityNotEstablished',\n 'locatorNotResolvableAfterReasonableEffort',\n 'requestOutsideCoveredDepiction',\n 'duplicateSuperseded',\n 'fraudulentOrBadFaith',\n 'otherCounselApproved',\n]);\nexport type TakeItDownInvalidReasonCodeInput = z.infer<typeof TakeItDownInvalidReasonCodeSchema>;\n\nexport const DecideTakeItDownValidityInputSchema = z.object({\n requestId: z.string().min(1),\n result: z.enum(['valid', 'invalid', 'unableToLocate']),\n /** Required for an 'invalid' result; an enumerated reason (never free-form). */\n reasonCode: TakeItDownInvalidReasonCodeSchema.optional(),\n /** [EUAS-010] The operator's written rationale for this ruling. Persisted as an IMMUTABLE\n * restricted rationale row that `rationaleRef` then points at — the UI no longer fabricates a\n * `rationale:<requestId>` pointer. Required + substantive (non-placeholder). */\n rationaleText: z\n .string()\n .trim()\n .min(10, 'A substantive rationale is required for a validity decision.')\n .max(MAX_NCII_RATIONALE_LENGTH),\n /** Explicit typed confirmation (interim control until the passkey profile lands). */\n confirmation: z.literal(TAKE_IT_DOWN_VALIDITY_CONFIRMATION),\n}).strict();\nexport type DecideTakeItDownValidityInput = z.infer<typeof DecideTakeItDownValidityInputSchema>;\n\n// ===========================================================================\n// submitInAppNciiRequest — logged-in entry to the one NCII statutory intake core.\n// Client ids are HINTS; the target is re-resolved server-side.\n// ===========================================================================\nexport const SubmitInAppNciiRequestInputSchema = z\n .object({\n idempotencyKey: z.string().min(8).max(256),\n itemType: ReportableItemTypeSchema,\n reportedItemId: z.string().min(1).max(256),\n parentItemId: z.string().min(1).max(256).optional(),\n nonconsentStatement: z.string().min(1).max(MAX_NCII_NONCONSENT_STATEMENT_LENGTH),\n /** Typed-name electronic signature. */\n signedName: z.string().min(1).max(MAX_NCII_SIGNED_NAME_LENGTH),\n goodFaithCertification: z.literal(true),\n contactEmail: z.string().email().max(320).optional(),\n contactPhone: z.string().min(3).max(64).optional(),\n supportingFacts: z.string().max(MAX_NCII_SUPPORTING_FACTS_LENGTH).default(''),\n })\n .strict()\n // At least one contact method — the form prefills the account email but allows an override.\n .refine((b) => Boolean(b.contactEmail) || Boolean(b.contactPhone), {\n message: 'At least one contact method (email or phone) is required.',\n path: ['contactEmail'],\n });\nexport type SubmitInAppNciiRequestInput = z.infer<typeof SubmitInAppNciiRequestInputSchema>;\n\n// ===========================================================================\n// resolveNciiReportPreview — logged-in read resolving a reported item to a previewable asset.\n// Client ids are HINTS; the target is re-resolved server-side.\n// ===========================================================================\nexport const ResolveNciiReportPreviewInputSchema = z.object({\n itemType: ReportableItemTypeSchema,\n reportedItemId: z.string().min(1).max(256),\n parentItemId: z.string().min(1).max(256).optional(),\n}).strict();\nexport type ResolveNciiReportPreviewInput = z.infer<typeof ResolveNciiReportPreviewInputSchema>;\n"]}
|
|
1
|
+
{"version":3,"file":"ncii.js","sourceRoot":"","sources":["../../src/schemas/ncii.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport { ReportableItemTypeSchema, NciiMinorAssessmentSchema } from '../doc-schemas/safety/foundation.js';\nimport {\n MAX_NCII_EVIDENCE_REASON_LENGTH,\n MAX_NCII_RATIONALE_LENGTH,\n MAX_NCII_NONCONSENT_STATEMENT_LENGTH,\n MAX_NCII_SIGNED_NAME_LENGTH,\n MAX_NCII_SUPPORTING_FACTS_LENGTH,\n} from '../constants/business.js';\nimport { TAKE_IT_DOWN_VALIDITY_CONFIRMATION } from '../constants/safety-confirmation-phrases.js';\n\n// NCII / TAKE IT DOWN callable-input schemas. Cross-boundary contracts for the\n// `functions/src/ncii/` callables — moved here from local `functions/` definitions so the\n// callable-validation standard (parse `request.data` with a `.strict()` ttt-core schema before any\n// auth check) is met from one authoritative place. See ttt-prod docs/design/callable-validation.md.\n\n// Operator sets the child-safety crossover minorAssessment on an NCII case (CSAM possible-minor\n// crossover — DECISION 2026-07-01: operator-only, no automated age signal). A routing action: it\n// opens/links a PARALLEL child-safety review + deny-serving + PhotoDNA, WITHOUT touching the NCII\n// removal clock and WITHOUT auto-NCMEC (that still needs a validated hash or explicit human\n// confirmApparentViolation). Writes the `ncii.minorAssessmentSet` audit event.\nexport const SetNciiMinorAssessmentInputSchema = z.object({\n caseId: z.string().min(1),\n minorAssessment: NciiMinorAssessmentSchema,\n}).strict();\nexport type SetNciiMinorAssessmentInput = z.infer<typeof SetNciiMinorAssessmentInputSchema>;\n\n// ===========================================================================\n// listNciiEvidencePool — admin-only pool read for the find-&-link flow.\n// Optional caseId selects the currently-linked evidence to also return.\n// ===========================================================================\nexport const ListNciiEvidencePoolInputSchema = z.object({\n caseId: z.string().min(1).optional(),\n}).strict();\nexport type ListNciiEvidencePoolInput = z.infer<typeof ListNciiEvidencePoolInputSchema>;\n\n// ===========================================================================\n// linkNciiEvidenceToCase / unlinkNciiEvidenceFromCase — find-&-link step 2 (+ reverse).\n// ===========================================================================\nexport const LinkNciiEvidenceToCaseInputSchema = z.object({\n allegationId: z.string().min(1),\n caseId: z.string().min(1),\n}).strict();\nexport type LinkNciiEvidenceToCaseInput = z.infer<typeof LinkNciiEvidenceToCaseInputSchema>;\n\nexport const UnlinkNciiEvidenceFromCaseInputSchema = z.object({\n allegationId: z.string().min(1),\n caseId: z.string().min(1),\n}).strict();\nexport type UnlinkNciiEvidenceFromCaseInput = z.infer<typeof UnlinkNciiEvidenceFromCaseInputSchema>;\n\n// ===========================================================================\n// markNciiEvidence — admin-only \"NCII linked evidence\" intake (find-&-link step 1).\n// Client ids are HINTS; the target is re-resolved server-side.\n// ===========================================================================\nexport const MarkNciiEvidenceInputSchema = z.object({\n itemType: ReportableItemTypeSchema,\n reportedItemId: z.string().min(1),\n parentItemId: z.string().min(1).optional(),\n reportedUserId: z.string().min(1).optional(),\n reason: z.string().trim().max(MAX_NCII_EVIDENCE_REASON_LENGTH).optional(),\n}).strict();\nexport type MarkNciiEvidenceInput = z.infer<typeof MarkNciiEvidenceInputSchema>;\n\n// ===========================================================================\n// decideTakeItDownValidity — the nciiRequestReviewer-gated validity determination.\n// ===========================================================================\n\n/** Enumerated invalid-ruling reasons (never free-form). */\nexport const TakeItDownInvalidReasonCodeSchema = z.enum([\n 'missingSignature',\n 'missingLocator',\n 'missingNonconsentStatement',\n 'missingContactInformation',\n 'requesterNotDepictedPersonOrAuthorizedRepresentative',\n 'authorityNotEstablished',\n 'locatorNotResolvableAfterReasonableEffort',\n 'requestOutsideCoveredDepiction',\n 'duplicateSuperseded',\n 'fraudulentOrBadFaith',\n 'otherCounselApproved',\n]);\nexport type TakeItDownInvalidReasonCodeInput = z.infer<typeof TakeItDownInvalidReasonCodeSchema>;\n\nexport const DecideTakeItDownValidityInputSchema = z.object({\n requestId: z.string().min(1),\n result: z.enum(['valid', 'invalid', 'unableToLocate']),\n /** Required for an 'invalid' result; an enumerated reason (never free-form). */\n reasonCode: TakeItDownInvalidReasonCodeSchema.optional(),\n /** [EUAS-010] The operator's written rationale for this ruling. Persisted as an IMMUTABLE\n * restricted rationale row that `rationaleRef` then points at — the UI no longer fabricates a\n * `rationale:<requestId>` pointer. Required + substantive (non-placeholder). */\n rationaleText: z\n .string()\n .trim()\n .min(10, 'A substantive rationale is required for a validity decision.')\n .max(MAX_NCII_RATIONALE_LENGTH),\n /** Explicit typed confirmation (interim control until the passkey profile lands). */\n confirmation: z.literal(TAKE_IT_DOWN_VALIDITY_CONFIRMATION),\n}).strict();\nexport type DecideTakeItDownValidityInput = z.infer<typeof DecideTakeItDownValidityInputSchema>;\n\n// ===========================================================================\n// submitInAppNciiRequest — logged-in entry to the one NCII statutory intake core.\n// Client ids are HINTS; the target is re-resolved server-side.\n// ===========================================================================\nexport const SubmitInAppNciiRequestInputSchema = z\n .object({\n idempotencyKey: z.string().min(8).max(256),\n itemType: ReportableItemTypeSchema,\n reportedItemId: z.string().min(1).max(256),\n parentItemId: z.string().min(1).max(256).optional(),\n nonconsentStatement: z.string().min(1).max(MAX_NCII_NONCONSENT_STATEMENT_LENGTH),\n /** Typed-name electronic signature. */\n signedName: z.string().min(1).max(MAX_NCII_SIGNED_NAME_LENGTH),\n goodFaithCertification: z.literal(true),\n contactEmail: z.string().email().max(320).optional(),\n contactPhone: z.string().min(3).max(64).optional(),\n supportingFacts: z.string().max(MAX_NCII_SUPPORTING_FACTS_LENGTH).default(''),\n })\n .strict()\n // At least one contact method — the form prefills the account email but allows an override.\n .refine((b) => Boolean(b.contactEmail) || Boolean(b.contactPhone), {\n message: 'At least one contact method (email or phone) is required.',\n path: ['contactEmail'],\n });\nexport type SubmitInAppNciiRequestInput = z.infer<typeof SubmitInAppNciiRequestInputSchema>;\n\n// ===========================================================================\n// resolveNciiReportPreview — logged-in read resolving a reported item to a previewable asset.\n// Client ids are HINTS; the target is re-resolved server-side.\n// ===========================================================================\nexport const ResolveNciiReportPreviewInputSchema = z.object({\n itemType: ReportableItemTypeSchema,\n reportedItemId: z.string().min(1).max(256),\n parentItemId: z.string().min(1).max(256).optional(),\n}).strict();\nexport type ResolveNciiReportPreviewInput = z.infer<typeof ResolveNciiReportPreviewInputSchema>;\n"]}
|
|
@@ -15,9 +15,8 @@ export declare const NotificationTypeSchema: z.ZodEnum<{
|
|
|
15
15
|
content_appeal_reviewed: "content_appeal_reviewed";
|
|
16
16
|
}>;
|
|
17
17
|
export type NotificationType = z.infer<typeof NotificationTypeSchema>;
|
|
18
|
-
/** Delivery channels. Only `inApp` is implemented at launch; `email`/`push`
|
|
19
|
-
*
|
|
20
|
-
* chokepoint. */
|
|
18
|
+
/** Delivery channels. Only `inApp` is implemented at launch; `email`/`push` are
|
|
19
|
+
* reserved for future channel handlers. */
|
|
21
20
|
export declare const NOTIFICATION_CHANNEL_VALUES: readonly ["inApp", "email", "push"];
|
|
22
21
|
export declare const NotificationChannelSchema: z.ZodEnum<{
|
|
23
22
|
email: "email";
|
|
@@ -39,9 +38,9 @@ export interface NotificationTypeCatalogEntry {
|
|
|
39
38
|
defaultChannels: NotificationChannel[];
|
|
40
39
|
}
|
|
41
40
|
/**
|
|
42
|
-
* The canonical TTT notification type catalog
|
|
43
|
-
* type's category/delivery
|
|
44
|
-
*
|
|
41
|
+
* The canonical TTT notification type catalog — the source `TTT_NOTIFICATION_CONFIG`
|
|
42
|
+
* (../notifications) derives every type's category/delivery from. `defaultChannels`
|
|
43
|
+
* is `['inApp']` for every type at launch.
|
|
45
44
|
*/
|
|
46
45
|
export declare const NOTIFICATION_TYPE_CATALOG: Record<NotificationType, NotificationTypeCatalogEntry>;
|
|
47
46
|
/**
|
|
@@ -253,8 +252,9 @@ export type MarkNotificationsSeenObservedInput = z.infer<typeof MarkNotification
|
|
|
253
252
|
* single observed generation for the whole category. (Design choice: archive-all
|
|
254
253
|
* carries no observedActivityGeneration at the top level; the server reads each
|
|
255
254
|
* card's current generation as its own precondition. This is consistent with the
|
|
256
|
-
*
|
|
257
|
-
*
|
|
255
|
+
* frozen "mark-all/archive-all run bounded pages with stable cursors until
|
|
256
|
+
* exhausted" protocol (ttt-prod docs/design/notification-system.md) and the
|
|
257
|
+
* archive-history identity formula
|
|
258
258
|
* which keys each occurrence on requestId independently.)
|
|
259
259
|
*/
|
|
260
260
|
export declare const ArchiveNotificationObservedScopeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification.d.ts","sourceRoot":"","sources":["../../src/schemas/notification.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"notification.d.ts","sourceRoot":"","sources":["../../src/schemas/notification.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,8 +4,14 @@
|
|
|
4
4
|
// notification's `metadata` is `Record<string, unknown>`. All TTT notification
|
|
5
5
|
// policy lives here: the canonical type catalog (category / delivery / default
|
|
6
6
|
// channels), the per-type typed `metadata` discriminated union, the broadcast
|
|
7
|
-
// audience selector, and the callable input schemas.
|
|
8
|
-
//
|
|
7
|
+
// audience selector, and the callable input schemas. There is NO single send
|
|
8
|
+
// chokepoint: ttt-prod has two intake lanes — reliable single/shared occurrences
|
|
9
|
+
// (`writeReliableOccurrenceRow` / `enqueueReliableOccurrence`) and audience fanout
|
|
10
|
+
// (`notificationFanoutJobs` → `processNotificationFanoutJobs`) — which converge on
|
|
11
|
+
// the `notificationDeliveries` ledger, with `materializeDeliveries` as the sole
|
|
12
|
+
// materializer. The fanout lane validates `metadata` against
|
|
13
|
+
// `NotificationMetadataByTypeSchema` before a job is enqueued. See ttt-prod
|
|
14
|
+
// docs/design/notification-system.md.
|
|
9
15
|
//
|
|
10
16
|
// The two notification audit-event payload shapes live here too (the
|
|
11
17
|
// `AuditEventType` union members `notification.broadcastSent` /
|
|
@@ -39,7 +45,7 @@ export const NOTIFICATION_TYPE_VALUES = [
|
|
|
39
45
|
'hall_content_change_request_resolved',
|
|
40
46
|
'admin_announcement',
|
|
41
47
|
'followed_content_published',
|
|
42
|
-
//
|
|
48
|
+
// The two audience-fanout triggers (see ttt-prod docs/design/notification-system.md).
|
|
43
49
|
// `member_content_published`: the Hall-publish MEMBER job ("your work was published!") — same
|
|
44
50
|
// `thresholdItemId` eventId as the follower job, different type so `deliveryId` stays distinct.
|
|
45
51
|
// `followed_craft_skill_published`: craft-skill publish → the artisan's followers
|
|
@@ -61,16 +67,15 @@ export const NOTIFICATION_TYPE_VALUES = [
|
|
|
61
67
|
'content_appeal_reviewed',
|
|
62
68
|
];
|
|
63
69
|
export const NotificationTypeSchema = z.enum(NOTIFICATION_TYPE_VALUES);
|
|
64
|
-
/** Delivery channels. Only `inApp` is implemented at launch; `email`/`push`
|
|
65
|
-
*
|
|
66
|
-
* chokepoint. */
|
|
70
|
+
/** Delivery channels. Only `inApp` is implemented at launch; `email`/`push` are
|
|
71
|
+
* reserved for future channel handlers. */
|
|
67
72
|
export const NOTIFICATION_CHANNEL_VALUES = ['inApp', 'email', 'push'];
|
|
68
73
|
export const NotificationChannelSchema = z.enum(NOTIFICATION_CHANNEL_VALUES);
|
|
69
74
|
export const NotificationCategorySchema = z.enum(['user', 'admin']);
|
|
70
75
|
/**
|
|
71
|
-
* The canonical TTT notification type catalog
|
|
72
|
-
* type's category/delivery
|
|
73
|
-
*
|
|
76
|
+
* The canonical TTT notification type catalog — the source `TTT_NOTIFICATION_CONFIG`
|
|
77
|
+
* (../notifications) derives every type's category/delivery from. `defaultChannels`
|
|
78
|
+
* is `['inApp']` for every type at launch.
|
|
74
79
|
*/
|
|
75
80
|
export const NOTIFICATION_TYPE_CATALOG = {
|
|
76
81
|
content_report: { category: 'admin', delivery: 'realtime', defaultChannels: ['inApp'] },
|
|
@@ -298,8 +303,9 @@ export const MarkNotificationsSeenObservedInputSchema = z.object({
|
|
|
298
303
|
* single observed generation for the whole category. (Design choice: archive-all
|
|
299
304
|
* carries no observedActivityGeneration at the top level; the server reads each
|
|
300
305
|
* card's current generation as its own precondition. This is consistent with the
|
|
301
|
-
*
|
|
302
|
-
*
|
|
306
|
+
* frozen "mark-all/archive-all run bounded pages with stable cursors until
|
|
307
|
+
* exhausted" protocol (ttt-prod docs/design/notification-system.md) and the
|
|
308
|
+
* archive-history identity formula
|
|
303
309
|
* which keys each occurrence on requestId independently.)
|
|
304
310
|
*/
|
|
305
311
|
export const ArchiveNotificationObservedScopeSchema = z.discriminatedUnion('kind', [
|