@ttt-productions/chat-core 0.10.2 → 0.11.0

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/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { MAX_CHAT_MESSAGE_LENGTH } from "./constants.js";
2
2
  export { GROUP_GAP_SEC } from "./types.js";
3
3
  export { isContinuation } from "./grouping.js";
4
- export type { ChatAttachment, ChatId, ChatThreadV1, ChatMessageV1, ChatAccessMode, SendAttachmentInput, SendAttachmentFn, ModerationHandlers, ChatNameResolver, ChatPrewarmSenders, } from "./types.js";
4
+ export type { ChatId, ChatThreadV1, ChatMessageV1, ChatAccessMode, ModerationHandlers, ChatNameResolver, ChatPrewarmSenders, } from "./types.js";
5
5
  export type { MentionRef, ParsedSegment, MentionProvider, RecentMentionsAdapter, MentionAnchor, } from "./mentions/types.js";
6
6
  export { parseMentionTokens, formatMentionToken } from "./mentions/parser.js";
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,YAAY,EACR,cAAc,EACd,MAAM,EACN,YAAY,EACZ,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,GACrB,MAAM,YAAY,CAAC;AAEpB,YAAY,EACR,UAAU,EACV,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,aAAa,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,YAAY,EACR,MAAM,EACN,YAAY,EACZ,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,GACrB,MAAM,YAAY,CAAC;AAEpB,YAAY,EACR,UAAU,EACV,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,aAAa,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,8EAA8E;AAC9E,uDAAuD;AACvD,EAAE;AACF,8EAA8E;AAC9E,iEAAiE;AACjE,iCAAiC;AAEjC,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAqB/C,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC","sourcesContent":["// Pure chat-core barrel. No React, no Firebase (client or admin) reachable from\n// here — safe for Cloud Functions, scripts, and future native/TV clients that\n// only need the parser, contracts, and grouping logic.\n//\n// The chat React UI, hooks, Firestore-client adapter config, and render types\n// live in @ttt-productions/chat-react. Pure chat schemas live in\n// @ttt-productions/chat-schemas.\n\nexport { MAX_CHAT_MESSAGE_LENGTH } from \"./constants.js\";\n\nexport { GROUP_GAP_SEC } from \"./types.js\";\nexport { isContinuation } from \"./grouping.js\";\nexport type {\n ChatAttachment,\n ChatId,\n ChatThreadV1,\n ChatMessageV1,\n ChatAccessMode,\n SendAttachmentInput,\n SendAttachmentFn,\n ModerationHandlers,\n ChatNameResolver,\n ChatPrewarmSenders,\n} from \"./types.js\";\n\nexport type {\n MentionRef,\n ParsedSegment,\n MentionProvider,\n RecentMentionsAdapter,\n MentionAnchor,\n} from \"./mentions/types.js\";\nexport { parseMentionTokens, formatMentionToken } from \"./mentions/parser.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,8EAA8E;AAC9E,uDAAuD;AACvD,EAAE;AACF,8EAA8E;AAC9E,iEAAiE;AACjE,iCAAiC;AAEjC,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAkB/C,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC","sourcesContent":["// Pure chat-core barrel. No React, no Firebase (client or admin) reachable from\n// here — safe for Cloud Functions, scripts, and future native/TV clients that\n// only need the parser, contracts, and grouping logic.\n//\n// The chat React UI, hooks, Firestore-client adapter config, and render types\n// live in @ttt-productions/chat-react. Pure chat schemas live in\n// @ttt-productions/chat-schemas.\n\nexport { MAX_CHAT_MESSAGE_LENGTH } from \"./constants.js\";\n\nexport { GROUP_GAP_SEC } from \"./types.js\";\nexport { isContinuation } from \"./grouping.js\";\nexport type {\n ChatId,\n ChatThreadV1,\n ChatMessageV1,\n ChatAccessMode,\n ModerationHandlers,\n ChatNameResolver,\n ChatPrewarmSenders,\n} from \"./types.js\";\n\nexport type {\n MentionRef,\n ParsedSegment,\n MentionProvider,\n RecentMentionsAdapter,\n MentionAnchor,\n} from \"./mentions/types.js\";\nexport { parseMentionTokens, formatMentionToken } from \"./mentions/parser.js\";\n"]}
package/dist/types.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import type { ChatAttachment, ReplyTo } from "@ttt-productions/chat-schemas";
2
- export type { ChatAttachment };
1
+ import type { ReplyTo } from "@ttt-productions/chat-schemas";
3
2
  export type ChatId = string;
4
3
  export type ChatThreadV1 = {
5
4
  allowedUserIds: string[];
@@ -16,7 +15,6 @@ export type ChatMessageV1 = {
16
15
  senderId: string;
17
16
  text?: string;
18
17
  type?: string;
19
- attachment?: ChatAttachment;
20
18
  replyTo?: ReplyTo;
21
19
  isSystemMessage?: boolean;
22
20
  /** Moderation tombstone flag on the stored message (backend-written); consumers
@@ -40,29 +38,6 @@ export type ChatMessageV1 = {
40
38
  * Admins (`isAdmin: true`) bypass both modes.
41
39
  */
42
40
  export type ChatAccessMode = "firestore-rules" | "explicit-allowlist";
43
- /**
44
- * Called by the composer after the file has been uploaded to Storage. The
45
- * consumer wires this to a backend callable that writes the pendingMedia doc
46
- * (caption text + reply pointer) AND a placeholder chat message doc with
47
- * `attachment.status: 'pending'`. The media processor later flips that same
48
- * message doc to `ready` (sets `url`) or `failed`.
49
- *
50
- * The placeholder bubble therefore arrives through the normal Firestore
51
- * listener — the composer does not need a separate optimistic local state.
52
- * While pending/failed the doc is sender-only (rule-enforced).
53
- */
54
- export type SendAttachmentInput = {
55
- text: string;
56
- attachment: {
57
- attachmentId: string;
58
- storagePath: string;
59
- originalFileName: string;
60
- type: ChatAttachment["type"];
61
- size: number;
62
- };
63
- replyTo?: ChatMessageV1["replyTo"];
64
- };
65
- export type SendAttachmentFn = (input: SendAttachmentInput) => Promise<void>;
66
41
  export type ModerationHandlers = {
67
42
  onReportMessage?: (messageId: string, reason?: string) => void | Promise<void>;
68
43
  onReportThread?: (threadId: string, reason?: string) => void | Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAY7E,YAAY,EAAE,cAAc,EAAE,CAAC;AAM/B,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AAE5B,MAAM,MAAM,YAAY,GAAG;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;8DAC0D;IAC1D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAMF;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GAAG,iBAAiB,GAAG,oBAAoB,CAAC;AAMtE;;;;;;;;;;GAUG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE;QACV,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,EAAE,MAAM,CAAC;QACzB,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;QAC7B,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,OAAO,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAM7E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D,CAAC;AAMF,iEAAiE;AACjE,eAAO,MAAM,aAAa,MAAM,CAAC;AAMjC;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAEnE;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAU7D,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AAE5B,MAAM,MAAM,YAAY,GAAG;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;8DAC0D;IAC1D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAMF;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GAAG,iBAAiB,GAAG,oBAAoB,CAAC;AAMtE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D,CAAC;AAMF,iEAAiE;AACjE,eAAO,MAAM,aAAa,MAAM,CAAC;AAMjC;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAEnE;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA0GA,+CAA+C;AAC/C,kDAAkD;AAClD,+CAA+C;AAE/C,iEAAiE;AACjE,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC","sourcesContent":["import type { ChatAttachment, ReplyTo } from \"@ttt-productions/chat-schemas\";\n\n// ============================================\n// CHAT ATTACHMENT\n// ============================================\n\n// Re-exported from @ttt-productions/chat-schemas, the Tier 0 source of truth.\n// chat-schemas is server-safe and can be consumed by both chat-core (pure) and\n// backend code without forcing the chat UI dep graph on backend callers.\n// An attachment carries a lifecycle `status` (pending -> ready | failed): a\n// pending/failed placeholder is visible only to the sender (rule-enforced);\n// other participants see it once it is `ready` (or absent = ready/legacy).\nexport type { ChatAttachment };\n\n// ============================================\n// THREAD & MESSAGE\n// ============================================\n\nexport type ChatId = string;\n\nexport type ChatThreadV1 = {\n allowedUserIds: string[];\n participantUserIds?: string[];\n createdAt: number; // millis\n lastMessageAt: number; // millis\n status?: string; // opaque to chat-core\n meta?: Record<string, unknown>; // opaque\n};\n\nexport type ChatMessageV1 = {\n messageId: string;\n threadId: string;\n createdAt: number; // millis\n senderId: string;\n text?: string;\n type?: string; // optional for renderer registry\n attachment?: ChatAttachment; // single, not array\n replyTo?: ReplyTo;\n isSystemMessage?: boolean;\n /** Moderation tombstone flag on the stored message (backend-written); consumers\n * render a tombstone instead of the content when true. */\n hidden?: boolean;\n meta?: Record<string, unknown>;\n};\n\n// ============================================\n// ACCESS\n// ============================================\n\n/**\n * Access mode controls how chat decides whether the current user can\n * read/write a thread.\n *\n * - \"firestore-rules\" — trust Firestore rules. canAccessThread returns true\n * for any signed-in user; if rules deny, onSnapshot will surface\n * permission-denied. Use this when access depends on data the client doesn't\n * reliably know up-front (entity membership, invite participation, etc.).\n *\n * - \"explicit-allowlist\" — access is enforced client-side via\n * threadAllowedUserIds. The list is required in this mode. Use this when the\n * consumer already knows the participants (admin/support threads).\n *\n * Admins (`isAdmin: true`) bypass both modes.\n */\nexport type ChatAccessMode = \"firestore-rules\" | \"explicit-allowlist\";\n\n// ============================================\n// ATTACHMENT REGISTRATION CALLBACK\n// ============================================\n\n/**\n * Called by the composer after the file has been uploaded to Storage. The\n * consumer wires this to a backend callable that writes the pendingMedia doc\n * (caption text + reply pointer) AND a placeholder chat message doc with\n * `attachment.status: 'pending'`. The media processor later flips that same\n * message doc to `ready` (sets `url`) or `failed`.\n *\n * The placeholder bubble therefore arrives through the normal Firestore\n * listener — the composer does not need a separate optimistic local state.\n * While pending/failed the doc is sender-only (rule-enforced).\n */\nexport type SendAttachmentInput = {\n text: string;\n attachment: {\n attachmentId: string; // pendingMedia doc ID\n storagePath: string; // uploads/chat-attachment/{uid}/{attachmentId}\n originalFileName: string;\n type: ChatAttachment[\"type\"];\n size: number;\n };\n replyTo?: ChatMessageV1[\"replyTo\"];\n};\n\nexport type SendAttachmentFn = (input: SendAttachmentInput) => Promise<void>;\n\n// ============================================\n// MODERATION\n// ============================================\n\nexport type ModerationHandlers = {\n onReportMessage?: (messageId: string, reason?: string) => void | Promise<void>;\n onReportThread?: (threadId: string, reason?: string) => void | Promise<void>;\n onDeleteMessage?: (messageId: string) => void | Promise<void>; // admin only (gated)\n onDeleteThread?: (threadId: string) => void | Promise<void>; // admin only (gated)\n};\n\n// ============================================\n// MESSAGE GROUPING (internal, exported for tests)\n// ============================================\n\n/** Max seconds between messages to be grouped as continuation */\nexport const GROUP_GAP_SEC = 120;\n\n// ============================================\n// NAME RESOLUTION\n// ============================================\n\n/**\n * Resolves a senderId to a display name synchronously from app-side cache.\n * Returns null if the sender is unknown or the cache hasn't loaded yet —\n * the chat UI will render a stable fallback (\"User\") in that case.\n */\nexport type ChatNameResolver = (senderId: string) => string | null;\n\n/**\n * Optional pre-warm callback. The chat UI calls this with the deduped list of\n * senderIds visible in the current message page so the consuming app can\n * batch-fetch names into its cache. Implementations should be idempotent —\n * the same id list will be passed across re-renders.\n */\nexport type ChatPrewarmSenders = (senderIds: string[]) => void;\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAoEA,+CAA+C;AAC/C,kDAAkD;AAClD,+CAA+C;AAE/C,iEAAiE;AACjE,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC","sourcesContent":["import type { ReplyTo } from \"@ttt-productions/chat-schemas\";\n\n// ============================================\n// THREAD & MESSAGE\n// ============================================\n//\n// Chat is TEXT-ONLY. A message never carries a file: files belong to the\n// CONVERSATION (the Conversation Files list owned by the consuming app), not to\n// a message in its timeline. There is no attachment contract in this package.\n\nexport type ChatId = string;\n\nexport type ChatThreadV1 = {\n allowedUserIds: string[];\n participantUserIds?: string[];\n createdAt: number; // millis\n lastMessageAt: number; // millis\n status?: string; // opaque to chat-core\n meta?: Record<string, unknown>; // opaque\n};\n\nexport type ChatMessageV1 = {\n messageId: string;\n threadId: string;\n createdAt: number; // millis\n senderId: string;\n text?: string;\n type?: string; // optional for renderer registry\n replyTo?: ReplyTo;\n isSystemMessage?: boolean;\n /** Moderation tombstone flag on the stored message (backend-written); consumers\n * render a tombstone instead of the content when true. */\n hidden?: boolean;\n meta?: Record<string, unknown>;\n};\n\n// ============================================\n// ACCESS\n// ============================================\n\n/**\n * Access mode controls how chat decides whether the current user can\n * read/write a thread.\n *\n * - \"firestore-rules\" — trust Firestore rules. canAccessThread returns true\n * for any signed-in user; if rules deny, onSnapshot will surface\n * permission-denied. Use this when access depends on data the client doesn't\n * reliably know up-front (entity membership, invite participation, etc.).\n *\n * - \"explicit-allowlist\" — access is enforced client-side via\n * threadAllowedUserIds. The list is required in this mode. Use this when the\n * consumer already knows the participants (admin/support threads).\n *\n * Admins (`isAdmin: true`) bypass both modes.\n */\nexport type ChatAccessMode = \"firestore-rules\" | \"explicit-allowlist\";\n\n// ============================================\n// MODERATION\n// ============================================\n\nexport type ModerationHandlers = {\n onReportMessage?: (messageId: string, reason?: string) => void | Promise<void>;\n onReportThread?: (threadId: string, reason?: string) => void | Promise<void>;\n onDeleteMessage?: (messageId: string) => void | Promise<void>; // admin only (gated)\n onDeleteThread?: (threadId: string) => void | Promise<void>; // admin only (gated)\n};\n\n// ============================================\n// MESSAGE GROUPING (internal, exported for tests)\n// ============================================\n\n/** Max seconds between messages to be grouped as continuation */\nexport const GROUP_GAP_SEC = 120;\n\n// ============================================\n// NAME RESOLUTION\n// ============================================\n\n/**\n * Resolves a senderId to a display name synchronously from app-side cache.\n * Returns null if the sender is unknown or the cache hasn't loaded yet —\n * the chat UI will render a stable fallback (\"User\") in that case.\n */\nexport type ChatNameResolver = (senderId: string) => string | null;\n\n/**\n * Optional pre-warm callback. The chat UI calls this with the deduped list of\n * senderIds visible in the current message page so the consuming app can\n * batch-fetch names into its cache. Implementations should be idempotent —\n * the same id list will be passed across re-renders.\n */\nexport type ChatPrewarmSenders = (senderIds: string[]) => void;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/chat-core",
3
- "version": "0.10.2",
3
+ "version": "0.11.0",
4
4
  "description": "Pure chat contracts, mention parser/serializer, and grouping helpers for TTT Productions apps (no React, no Firebase)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -28,7 +28,7 @@
28
28
  "prepublishOnly": "npm run clean && npm run build"
29
29
  },
30
30
  "dependencies": {
31
- "@ttt-productions/chat-schemas": "^0.9.2"
31
+ "@ttt-productions/chat-schemas": "^0.10.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "typescript": "^5.8.3"