@paramms/chat-widget 1.0.39 → 1.0.41

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/outbox.js CHANGED
@@ -2,7 +2,7 @@ var u = Object.defineProperty;
2
2
  var l = (i, t, s) => t in i ? u(i, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : i[t] = s;
3
3
  var n = (i, t, s) => l(i, typeof t != "symbol" ? t + "" : t, s);
4
4
  import { d as p, i as g, e as y } from "./codec.js";
5
- const I = (i) => i, C = (i) => i, M = (i) => i, _ = (i) => i, x = (i) => i, q = (i) => i, A = (i) => i, O = (i) => i, T = I("anonymous");
5
+ const I = (i) => i, C = (i) => i, _ = (i) => i, x = (i) => i, M = (i) => i, q = (i) => i, A = (i) => i, O = (i) => i, T = I("anonymous");
6
6
  class R {
7
7
  constructor(t) {
8
8
  n(this, "socket", null);
@@ -188,7 +188,7 @@ class j {
188
188
  this.conversationId = t.conversation.id, this.state = t.conversation.state, t.subject && (this.subject = t.subject), t.annotations && (this.annotations = t.annotations, this.annotationVersion++);
189
189
  return;
190
190
  case "manifest":
191
- this.actions = t.actions, this.version = t.version, t.name && (this.name = t.name), (s = t.theme) != null && s.accent && (this.accent = t.theme.accent), t.e2e && (this.e2e = !0), t.offline && (this.offline = !0), t.offlineMessage && (this.offlineMessage = t.offlineMessage), t.launcherMessage && (this.launcherMessage = t.launcherMessage), t.whiteLabel && (this.whiteLabel = !0), t.preChat && (this.preChat = t.preChat);
191
+ this.actions = t.actions, this.version = t.version, t.name && (this.name = t.name), (s = t.theme) != null && s.accent && (this.accent = t.theme.accent), t.e2e && (this.e2e = !0), this.offline = t.offline === !0, this.offlineMessage = t.offlineMessage ?? "", t.launcherMessage && (this.launcherMessage = t.launcherMessage), t.whiteLabel && (this.whiteLabel = !0), t.preChat && (this.preChat = t.preChat);
192
192
  return;
193
193
  case "message":
194
194
  this.upsert({ ...t.message });
@@ -335,10 +335,10 @@ export {
335
335
  R as b,
336
336
  q as c,
337
337
  O as d,
338
- M as e,
339
- x as f,
338
+ _ as e,
339
+ M as f,
340
340
  A as g,
341
- _ as h,
341
+ x as h,
342
342
  I as i
343
343
  };
344
344
  //# sourceMappingURL=outbox.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"outbox.js","sources":["../src/protocol/ids.ts","../src/connection.ts","../src/store.ts","../src/outbox.ts"],"sourcesContent":["// Branded identifier types. A plain string can't be passed where a TenantId is\n// expected (and vice-versa), so id-confusion bugs — the kind that caused the\n// `t-1` vs `dev-tenant` mismatch in the previous system — become compile errors.\n\nexport type Brand<T, B extends string> = T & { readonly __brand: B }\n\nexport type TenantId = Brand<string, 'TenantId'>\nexport type UserId = Brand<string, 'UserId'>\nexport type ConversationId = Brand<string, 'ConversationId'>\nexport type SubjectId = Brand<string, 'SubjectId'>\nexport type MessageId = Brand<string, 'MessageId'>\nexport type ActionId = Brand<string, 'ActionId'>\nexport type ProfileId = Brand<string, 'ProfileId'>\nexport type ConnectionId = Brand<string, 'ConnectionId'>\n\nexport const asTenantId = (s: string): TenantId => s as TenantId\nexport const asUserId = (s: string): UserId => s as UserId\nexport const asConversationId = (s: string): ConversationId => s as ConversationId\nexport const asSubjectId = (s: string): SubjectId => s as SubjectId\nexport const asMessageId = (s: string): MessageId => s as MessageId\nexport const asActionId = (s: string): ActionId => s as ActionId\nexport const asProfileId = (s: string): ProfileId => s as ProfileId\nexport const asConnectionId = (s: string): ConnectionId => s as ConnectionId\n\n/** Anonymous guests live under this tenant; they bypass cross-tenant scoping but\n * are still gated by conversation membership. */\nexport const ANONYMOUS_TENANT = asTenantId('anonymous')\n","import {\n encodeFrame, decodeFrame, isClientFrame,\n type ClientFrame, type ServerFrame,\n} from './protocol/index.js'\n\n// Minimal socket surface so tests can inject a fake without a real WebSocket.\nexport interface SocketLike {\n binaryType: string\n send(data: Uint8Array): void\n close(): void\n onopen: (() => void) | null\n onclose: (() => void) | null\n onerror: (() => void) | null\n onmessage: ((ev: { data: ArrayBuffer }) => void) | null\n}\nexport type SocketFactory = (url: string) => SocketLike\n\nexport interface ConnectionOptions {\n url: string\n token: string\n /** Authenticated embeds: called when the server rejects the token\n * (typically an expired signed JWT). Return a freshly minted token to\n * resume seamlessly, or null to give up (shows the fatal error). */\n refreshToken?: () => Promise<string | null>\n open: ClientFrame // frame sent right after auth (e.g. open a conversation, or subscribe_inbox)\n onFrame: (frame: ServerFrame) => void\n getCursor: () => number // highest seq seen (for sync on reconnect)\n onStatusChange?: (status: 'connecting' | 'open' | 'reconnecting' | 'error', message?: string) => void\n socketFactory?: SocketFactory\n backoffBaseMs?: number\n backoffMaxMs?: number\n maxOutbox?: number\n}\n\ntype State = 'idle' | 'connecting' | 'open' | 'closed'\n\nexport class ConnectionManager {\n private socket: SocketLike | null = null\n private state: State = 'idle'\n private authed = false\n private everAuthed = false\n private attempt = 0\n private outbox: ClientFrame[] = []\n private stopped = false\n private timer: ReturnType<typeof setTimeout> | null = null\n\n constructor(private readonly opts: ConnectionOptions) {}\n\n connect(): void {\n if (this.state === 'connecting' || this.state === 'open') return\n this.stopped = false\n this.state = 'connecting'\n this.authed = false\n this.opts.onStatusChange?.(this.attempt > 0 ? 'reconnecting' : 'connecting')\n const make = this.opts.socketFactory ?? defaultFactory\n const sock = make(this.opts.url)\n sock.binaryType = 'arraybuffer'\n this.socket = sock\n\n sock.onopen = () => {\n // Don't reset attempt here — reset only after successful auth ('authed').\n // A connection that opens but fails during auth (bad token, server restart)\n // should still back off, not immediately retry at base delay.\n this.raw({ type: 'auth', token: this.opts.token })\n }\n sock.onmessage = (ev) => {\n const frame = decodeFrame(new Uint8Array(ev.data))\n if (!frame || isClientFrame(frame)) return // ignore non-server frames\n this.handle(frame)\n }\n sock.onclose = () => this.onClosed()\n sock.onerror = () => { try { sock.close() } catch { /* */ } }\n }\n\n /** Queue a frame; sent immediately if open, else flushed on (re)connect.\n * The outbox is bounded so a prolonged outage can't grow memory without limit\n * — oldest queued frames are dropped past the cap. */\n send(frame: ClientFrame): void {\n if (this.state === 'open' && this.authed) { this.raw(frame); return }\n const cap = this.opts.maxOutbox ?? 1_000\n if (this.outbox.length >= cap) {\n // Evict oldest half when full to amortise the O(n) cost of overflow.\n this.outbox = this.outbox.slice(this.outbox.length - (cap >> 1))\n }\n this.outbox.push(frame)\n }\n\n close(): void {\n this.stopped = true\n if (this.timer) clearTimeout(this.timer)\n this.state = 'closed'\n try { this.socket?.close() } catch { /* */ }\n }\n\n private handle(frame: ServerFrame): void {\n if (frame.type === 'authed') {\n this.attempt = 0 // reset backoff only after a fully successful auth\n this.state = 'open'\n this.authed = true; this.everAuthed = true\n this.opts.onStatusChange?.('open')\n // Open/resolve the conversation. The catch-up `sync` is sent on 'opened'\n // (below), i.e. only after the server has joined us to the room — sending\n // it here would race the async open and be rejected as \"not joined\".\n this.raw(this.opts.open)\n this.flush()\n }\n // 'opened' confirms we're joined — now catch up from our cursor.\n if (frame.type === 'opened') {\n this.raw({ type: 'sync', conversationId: frame.conversation.id, sinceSeq: this.opts.getCursor() })\n }\n // A CONNECTION-FATAL error (bad or rejected token, closed or missing\n // chatroom) will never succeed on retry — stop the reconnect loop and report\n // a clear reason instead of spinning on \"connecting…\" forever. Per-frame\n // errors (rate limit, one bad message) are NOT fatal and fall through.\n if (frame.type === 'error' && FATAL_ERRORS.has(frame.code)) {\n // Token refresh (authenticated embeds): a signed JWT expiring mid-session\n // used to be a dead end — the widget showed a fatal error until reload.\n // If the host supplied refreshToken, ask it to mint a fresh one and\n // reconnect. One in-flight attempt at a time; a refresh that returns\n // null/throws (user logged out, backend down) falls through to fatal.\n if (frame.code === 'UNAUTHORIZED' && this.opts.refreshToken && !this.refreshing) {\n this.refreshing = true\n this.opts.onStatusChange?.('reconnecting', 'Renewing session…')\n void this.opts.refreshToken()\n .then((fresh) => {\n this.refreshing = false\n if (!fresh) { this.fatal(frame); return }\n this.opts.token = fresh\n try { this.socket?.close() } catch { /* */ }\n // onClosed schedules the reconnect, which re-auths with the new token.\n })\n .catch(() => { this.refreshing = false; this.fatal(frame) })\n return\n }\n this.fatal(frame)\n return\n }\n this.opts.onFrame(frame)\n }\n\n private refreshing = false\n\n private fatal(frame: Extract<ServerFrame, { type: 'error' }>): void {\n this.stopped = true\n try { this.socket?.close() } catch { /* */ }\n this.state = 'closed'\n this.opts.onStatusChange?.('error', friendlyError(frame.code))\n this.opts.onFrame(frame)\n }\n\n private flush(): void {\n const pending = this.outbox\n this.outbox = []\n for (const f of pending) this.raw(f)\n }\n\n private raw(frame: ClientFrame): void {\n try { this.socket?.send(encodeFrame(frame)) } catch { this.outbox.push(frame) }\n }\n\n private onClosed(): void {\n this.authed = false\n this.socket = null\n if (this.stopped) { this.state = 'closed'; return }\n this.state = 'idle'\n // Exponential backoff with jitter; reconnect re-auths, re-opens, re-syncs.\n const base = this.opts.backoffBaseMs ?? 500\n const max = this.opts.backoffMaxMs ?? 15_000\n const delay = Math.min(max, base * 2 ** this.attempt) * (0.5 + Math.random() * 0.5)\n this.attempt++\n // If we've never once connected after several tries, the relay is likely\n // unreachable (wrong URL, server down, blocked) — say so, but keep retrying.\n if (this.attempt >= 3 && !this.everAuthed) {\n this.opts.onStatusChange?.('reconnecting', \"Can't reach chat — retrying…\")\n }\n this.timer = setTimeout(() => this.connect(), delay)\n }\n}\n\n/** Connection-fatal error codes — retrying can't fix these. ONLY auth-handshake\n * failure qualifies: FORBIDDEN / NOT_FOUND are per-REQUEST errors (a stale\n * reference, one permission check) and must NOT tear down the whole socket. */\nconst FATAL_ERRORS = new Set(['UNAUTHORIZED'])\nfunction friendlyError(code: string): string {\n switch (code) {\n case 'UNAUTHORIZED': return 'Chat unavailable — sign-in/token was rejected'\n default: return 'Chat unavailable'\n }\n}\n\nfunction defaultFactory(url: string): SocketLike {\n return new WebSocket(url) as unknown as SocketLike\n}\n","import type {\n ServerFrame, Message, ManifestAction, MessageContent,\n ConversationId, MessageId, UserId, Subject, AnnotationStroke,\n} from './protocol/index.js'\n\nexport type SendStatus = 'pending' | 'sent' | 'delivered' | 'read'\n\nexport interface RenderMessage extends Message {\n clientMsgId?: string\n status?: SendStatus\n}\n\n/** Pure, DOM-free conversation state. Feed it ServerFrames (and local optimistic\n * sends); read an ordered, de-duplicated view out. Ordering is by `seq`; the\n * same message arriving twice (live + sync on reconnect) is collapsed by id —\n * the structural fix for the old duplicate-bubble bug. */\nexport class ChatStore {\n conversationId?: ConversationId\n state = ''\n version = 0\n hasMoreHistory = false\n lastReadByOthers = 0\n assignedAgentId: UserId | undefined\n accent: string | undefined\n subject: Subject | undefined\n name: string | undefined\n e2e = false\n offline = false\n offlineMessage = ''\n launcherMessage: { title: string; subtitle?: string } | null = null\n preChat: import('./protocol/frames.js').PreChatConfig | null = null\n whiteLabel = false\n readonly typing = new Set<string>()\n readonly online = new Set<string>()\n /** Co-browsing: shared whiteboard strokes for this conversation. */\n annotations: AnnotationStroke[] = []\n /** Bumped on any annotation change so the renderer can cheaply detect updates. */\n annotationVersion = 0\n /** Live sentiment of the guest's latest message (agent-side only). */\n sentiment: 'positive' | 'neutral' | 'frustrated' | undefined\n sentimentScore: number | undefined\n\n private actions: ManifestAction[] = []\n private readonly byId = new Map<string, RenderMessage>()\n private readonly keyByClient = new Map<string, string>()\n private _maxSeq = 0\n private _sorted: RenderMessage[] | null = null\n\n constructor(private readonly me: UserId) {}\n\n messages(): RenderMessage[] {\n if (!this._sorted) {\n this._sorted = [...this.byId.values()].sort((a, b) => {\n const ap = a.status === 'pending', bp = b.status === 'pending'\n if (ap !== bp) return ap ? 1 : -1\n if (ap && bp) return a.ts - b.ts\n return a.seq - b.seq\n })\n }\n return this._sorted\n }\n\n visibleActions(): ManifestAction[] {\n return this.actions.filter(a => !a.availableInStates || a.availableInStates.includes(this.state))\n }\n\n highestSeq(): number { return this._maxSeq }\n\n addOptimistic(clientMsgId: string, content: MessageContent): RenderMessage {\n const msg: RenderMessage = {\n id: clientMsgId as MessageId, conversationId: this.conversationId as ConversationId,\n seq: 0, senderId: this.me, senderRole: 'guest', content, ts: Date.now(),\n clientMsgId, status: 'pending',\n }\n this.byId.set(clientMsgId, msg)\n this.keyByClient.set(clientMsgId, clientMsgId)\n this._sorted = null\n return msg\n }\n\n apply(frame: ServerFrame): void {\n switch (frame.type) {\n case 'opened':\n this.conversationId = frame.conversation.id\n this.state = frame.conversation.state\n if (frame.subject) this.subject = frame.subject\n if (frame.annotations) { this.annotations = frame.annotations; this.annotationVersion++ }\n return\n case 'manifest':\n this.actions = frame.actions\n this.version = frame.version\n if (frame.name) this.name = frame.name\n if (frame.theme?.accent) this.accent = frame.theme.accent\n if (frame.e2e) this.e2e = true\n if (frame.offline) this.offline = true\n if (frame.offlineMessage) this.offlineMessage = frame.offlineMessage\n if (frame.launcherMessage) this.launcherMessage = frame.launcherMessage\n if (frame.whiteLabel) this.whiteLabel = true\n if (frame.preChat) this.preChat = frame.preChat\n return\n case 'message':\n this.upsert({ ...frame.message })\n return\n case 'ack': {\n const key = this.keyByClient.get(frame.clientMsgId)\n const msg = key ? this.byId.get(key) : undefined\n if (msg && key) {\n this.byId.delete(key)\n const confirmed: RenderMessage = { ...msg, id: frame.messageId, seq: frame.seq, ts: frame.ts, status: 'sent' }\n this.byId.set(frame.messageId, confirmed)\n this.keyByClient.set(frame.clientMsgId, frame.messageId)\n if (frame.seq > this._maxSeq) this._maxSeq = frame.seq\n }\n this._sorted = null\n return\n }\n case 'delivered':\n this.markOwnStatus(frame.seq, 'delivered')\n return\n case 'read':\n if (frame.by !== this.me) {\n this.lastReadByOthers = Math.max(this.lastReadByOthers, frame.seq)\n this.markOwnStatus(frame.seq, 'read')\n }\n return\n case 'sync':\n for (const m of frame.messages) this.upsert({ ...m })\n return\n case 'history':\n for (const m of frame.messages) this.upsert({ ...m })\n this.hasMoreHistory = frame.hasMore\n return\n case 'typing':\n if (frame.userId !== this.me) {\n if (frame.isTyping) this.typing.add(frame.userId)\n else this.typing.delete(frame.userId)\n }\n return\n case 'reaction': {\n const m = this.byId.get(frame.messageId)\n if (!m) return\n const reactions: Record<string, UserId[]> = { ...(m.reactions ?? {}) }\n const users = (reactions[frame.emoji] ?? []).filter(u => u !== frame.by)\n if (!frame.removed) users.push(frame.by)\n if (users.length) reactions[frame.emoji] = users; else delete reactions[frame.emoji]\n this.byId.set(frame.messageId, { ...m, reactions })\n this._sorted = null\n return\n }\n case 'edited': {\n const m = this.byId.get(frame.messageId)\n if (m) { this.byId.set(frame.messageId, { ...m, content: frame.content, editedAt: frame.editedAt }); this._sorted = null }\n return\n }\n case 'deleted': {\n const m = this.byId.get(frame.messageId)\n if (m) { this.byId.set(frame.messageId, { ...m, deletedAt: frame.ts }); this._sorted = null }\n return\n }\n case 'state':\n this.state = frame.state\n return\n case 'assigned':\n this.assignedAgentId = frame.agentId ?? undefined\n return\n case 'presence':\n if (frame.status === 'online') this.online.add(frame.userId)\n else this.online.delete(frame.userId)\n return\n case 'subjectState':\n case 'invoked':\n case 'authed':\n case 'error':\n case 'pong':\n return\n case 'annotation':\n this.annotations = [...this.annotations, frame.stroke]\n this.annotationVersion++\n return\n case 'annotation_clear':\n this.annotations = []\n this.annotationVersion++\n return\n case 'sentiment':\n this.sentiment = frame.label\n this.sentimentScore = frame.score\n return\n default:\n return\n }\n }\n\n private upsert(msg: RenderMessage): void {\n const existing = this.byId.get(msg.id)\n this.byId.set(msg.id, existing ? { ...existing, ...msg } : msg)\n if (msg.seq > this._maxSeq) this._maxSeq = msg.seq\n this._sorted = null\n }\n\n private markOwnStatus(uptoSeq: number, status: SendStatus): void {\n const targetRank = rank(status)\n let changed = false\n for (const [k, m] of this.byId) {\n if (m.senderId !== this.me || m.seq <= 0 || m.seq > uptoSeq) continue\n if (rank(m.status) >= targetRank) continue // already at or above target — skip\n this.byId.set(k, { ...m, status })\n changed = true\n }\n if (changed) this._sorted = null\n }\n}\nfunction rank(s: SendStatus | undefined): number {\n switch (s) { case 'read': return 3; case 'delivered': return 2; case 'sent': return 1; default: return 0 }\n}\n","import type { MessageContent } from './protocol/index.js'\n\nexport interface OutboxItem {\n clientMsgId: string\n content: MessageContent\n ts: number\n}\n\nconst MAX_ITEMS = 200 // cap so a long outage can't grow storage unboundedly\nconst MAX_AGE_MS = 7 * 86_400_000 // drop anything older than 7 days on load\n\n/** Persists not-yet-acknowledged outgoing messages to localStorage, keyed by\n * guest token, so a page reload during a connectivity drop doesn't silently\n * lose what the user typed (the \"WhatsApp\" guarantee: your message is queued\n * until it's confirmed sent, even across app restarts). */\nexport class PersistentOutbox {\n private readonly key: string\n\n constructor(token: string) {\n this.key = `ocw_outbox_${token}`\n }\n\n /** All pending items, oldest first, with stale (>7d) entries dropped. */\n load(): OutboxItem[] {\n try {\n const raw = localStorage.getItem(this.key)\n if (!raw) return []\n const items = JSON.parse(raw) as OutboxItem[]\n const cutoff = Date.now() - MAX_AGE_MS\n const fresh = items.filter(i => i.ts >= cutoff)\n if (fresh.length !== items.length) this.save(fresh)\n return fresh\n } catch {\n return []\n }\n }\n\n add(item: OutboxItem): void {\n try {\n const items = this.load()\n items.push(item)\n // Evict oldest when full — matches the in-memory ConnectionManager outbox policy.\n this.save(items.length > MAX_ITEMS ? items.slice(items.length - MAX_ITEMS) : items)\n } catch { /* localStorage unavailable (private mode, quota) — best-effort only */ }\n }\n\n /** Remove an item once it's been acknowledged by the server. */\n remove(clientMsgId: string): void {\n try {\n const items = this.load().filter(i => i.clientMsgId !== clientMsgId)\n this.save(items)\n } catch { /* best-effort */ }\n }\n\n private save(items: OutboxItem[]): void {\n try { localStorage.setItem(this.key, JSON.stringify(items)) } catch { /* quota exceeded — drop silently */ }\n }\n}\n"],"names":["asTenantId","s","asUserId","asConversationId","asSubjectId","asMessageId","asActionId","asProfileId","asConnectionId","ANONYMOUS_TENANT","ConnectionManager","opts","__publicField","_b","_a","sock","defaultFactory","ev","frame","decodeFrame","isClientFrame","cap","FATAL_ERRORS","_d","_c","fresh","friendlyError","pending","f","encodeFrame","base","max","delay","code","url","ChatStore","me","a","b","ap","bp","clientMsgId","content","msg","key","confirmed","m","reactions","users","u","existing","uptoSeq","status","targetRank","rank","changed","k","MAX_ITEMS","MAX_AGE_MS","PersistentOutbox","token","raw","items","cutoff","i","item"],"mappings":"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"}
1
+ {"version":3,"file":"outbox.js","sources":["../src/protocol/ids.ts","../src/connection.ts","../src/store.ts","../src/outbox.ts"],"sourcesContent":["// Branded identifier types. A plain string can't be passed where a TenantId is\n// expected (and vice-versa), so id-confusion bugs — the kind that caused the\n// `t-1` vs `dev-tenant` mismatch in the previous system — become compile errors.\n\nexport type Brand<T, B extends string> = T & { readonly __brand: B }\n\nexport type TenantId = Brand<string, 'TenantId'>\nexport type UserId = Brand<string, 'UserId'>\nexport type ConversationId = Brand<string, 'ConversationId'>\nexport type SubjectId = Brand<string, 'SubjectId'>\nexport type MessageId = Brand<string, 'MessageId'>\nexport type ActionId = Brand<string, 'ActionId'>\nexport type ProfileId = Brand<string, 'ProfileId'>\nexport type ConnectionId = Brand<string, 'ConnectionId'>\n\nexport const asTenantId = (s: string): TenantId => s as TenantId\nexport const asUserId = (s: string): UserId => s as UserId\nexport const asConversationId = (s: string): ConversationId => s as ConversationId\nexport const asSubjectId = (s: string): SubjectId => s as SubjectId\nexport const asMessageId = (s: string): MessageId => s as MessageId\nexport const asActionId = (s: string): ActionId => s as ActionId\nexport const asProfileId = (s: string): ProfileId => s as ProfileId\nexport const asConnectionId = (s: string): ConnectionId => s as ConnectionId\n\n/** Anonymous guests live under this tenant; they bypass cross-tenant scoping but\n * are still gated by conversation membership. */\nexport const ANONYMOUS_TENANT = asTenantId('anonymous')\n","import {\n encodeFrame, decodeFrame, isClientFrame,\n type ClientFrame, type ServerFrame,\n} from './protocol/index.js'\n\n// Minimal socket surface so tests can inject a fake without a real WebSocket.\nexport interface SocketLike {\n binaryType: string\n send(data: Uint8Array): void\n close(): void\n onopen: (() => void) | null\n onclose: (() => void) | null\n onerror: (() => void) | null\n onmessage: ((ev: { data: ArrayBuffer }) => void) | null\n}\nexport type SocketFactory = (url: string) => SocketLike\n\nexport interface ConnectionOptions {\n url: string\n token: string\n /** Authenticated embeds: called when the server rejects the token\n * (typically an expired signed JWT). Return a freshly minted token to\n * resume seamlessly, or null to give up (shows the fatal error). */\n refreshToken?: () => Promise<string | null>\n open: ClientFrame // frame sent right after auth (e.g. open a conversation, or subscribe_inbox)\n onFrame: (frame: ServerFrame) => void\n getCursor: () => number // highest seq seen (for sync on reconnect)\n onStatusChange?: (status: 'connecting' | 'open' | 'reconnecting' | 'error', message?: string) => void\n socketFactory?: SocketFactory\n backoffBaseMs?: number\n backoffMaxMs?: number\n maxOutbox?: number\n}\n\ntype State = 'idle' | 'connecting' | 'open' | 'closed'\n\nexport class ConnectionManager {\n private socket: SocketLike | null = null\n private state: State = 'idle'\n private authed = false\n private everAuthed = false\n private attempt = 0\n private outbox: ClientFrame[] = []\n private stopped = false\n private timer: ReturnType<typeof setTimeout> | null = null\n\n constructor(private readonly opts: ConnectionOptions) {}\n\n connect(): void {\n if (this.state === 'connecting' || this.state === 'open') return\n this.stopped = false\n this.state = 'connecting'\n this.authed = false\n this.opts.onStatusChange?.(this.attempt > 0 ? 'reconnecting' : 'connecting')\n const make = this.opts.socketFactory ?? defaultFactory\n const sock = make(this.opts.url)\n sock.binaryType = 'arraybuffer'\n this.socket = sock\n\n sock.onopen = () => {\n // Don't reset attempt here — reset only after successful auth ('authed').\n // A connection that opens but fails during auth (bad token, server restart)\n // should still back off, not immediately retry at base delay.\n this.raw({ type: 'auth', token: this.opts.token })\n }\n sock.onmessage = (ev) => {\n const frame = decodeFrame(new Uint8Array(ev.data))\n if (!frame || isClientFrame(frame)) return // ignore non-server frames\n this.handle(frame)\n }\n sock.onclose = () => this.onClosed()\n sock.onerror = () => { try { sock.close() } catch { /* */ } }\n }\n\n /** Queue a frame; sent immediately if open, else flushed on (re)connect.\n * The outbox is bounded so a prolonged outage can't grow memory without limit\n * — oldest queued frames are dropped past the cap. */\n send(frame: ClientFrame): void {\n if (this.state === 'open' && this.authed) { this.raw(frame); return }\n const cap = this.opts.maxOutbox ?? 1_000\n if (this.outbox.length >= cap) {\n // Evict oldest half when full to amortise the O(n) cost of overflow.\n this.outbox = this.outbox.slice(this.outbox.length - (cap >> 1))\n }\n this.outbox.push(frame)\n }\n\n close(): void {\n this.stopped = true\n if (this.timer) clearTimeout(this.timer)\n this.state = 'closed'\n try { this.socket?.close() } catch { /* */ }\n }\n\n private handle(frame: ServerFrame): void {\n if (frame.type === 'authed') {\n this.attempt = 0 // reset backoff only after a fully successful auth\n this.state = 'open'\n this.authed = true; this.everAuthed = true\n this.opts.onStatusChange?.('open')\n // Open/resolve the conversation. The catch-up `sync` is sent on 'opened'\n // (below), i.e. only after the server has joined us to the room — sending\n // it here would race the async open and be rejected as \"not joined\".\n this.raw(this.opts.open)\n this.flush()\n }\n // 'opened' confirms we're joined — now catch up from our cursor.\n if (frame.type === 'opened') {\n this.raw({ type: 'sync', conversationId: frame.conversation.id, sinceSeq: this.opts.getCursor() })\n }\n // A CONNECTION-FATAL error (bad or rejected token, closed or missing\n // chatroom) will never succeed on retry — stop the reconnect loop and report\n // a clear reason instead of spinning on \"connecting…\" forever. Per-frame\n // errors (rate limit, one bad message) are NOT fatal and fall through.\n if (frame.type === 'error' && FATAL_ERRORS.has(frame.code)) {\n // Token refresh (authenticated embeds): a signed JWT expiring mid-session\n // used to be a dead end — the widget showed a fatal error until reload.\n // If the host supplied refreshToken, ask it to mint a fresh one and\n // reconnect. One in-flight attempt at a time; a refresh that returns\n // null/throws (user logged out, backend down) falls through to fatal.\n if (frame.code === 'UNAUTHORIZED' && this.opts.refreshToken && !this.refreshing) {\n this.refreshing = true\n this.opts.onStatusChange?.('reconnecting', 'Renewing session…')\n void this.opts.refreshToken()\n .then((fresh) => {\n this.refreshing = false\n if (!fresh) { this.fatal(frame); return }\n this.opts.token = fresh\n try { this.socket?.close() } catch { /* */ }\n // onClosed schedules the reconnect, which re-auths with the new token.\n })\n .catch(() => { this.refreshing = false; this.fatal(frame) })\n return\n }\n this.fatal(frame)\n return\n }\n this.opts.onFrame(frame)\n }\n\n private refreshing = false\n\n private fatal(frame: Extract<ServerFrame, { type: 'error' }>): void {\n this.stopped = true\n try { this.socket?.close() } catch { /* */ }\n this.state = 'closed'\n this.opts.onStatusChange?.('error', friendlyError(frame.code))\n this.opts.onFrame(frame)\n }\n\n private flush(): void {\n const pending = this.outbox\n this.outbox = []\n for (const f of pending) this.raw(f)\n }\n\n private raw(frame: ClientFrame): void {\n try { this.socket?.send(encodeFrame(frame)) } catch { this.outbox.push(frame) }\n }\n\n private onClosed(): void {\n this.authed = false\n this.socket = null\n if (this.stopped) { this.state = 'closed'; return }\n this.state = 'idle'\n // Exponential backoff with jitter; reconnect re-auths, re-opens, re-syncs.\n const base = this.opts.backoffBaseMs ?? 500\n const max = this.opts.backoffMaxMs ?? 15_000\n const delay = Math.min(max, base * 2 ** this.attempt) * (0.5 + Math.random() * 0.5)\n this.attempt++\n // If we've never once connected after several tries, the relay is likely\n // unreachable (wrong URL, server down, blocked) — say so, but keep retrying.\n if (this.attempt >= 3 && !this.everAuthed) {\n this.opts.onStatusChange?.('reconnecting', \"Can't reach chat — retrying…\")\n }\n this.timer = setTimeout(() => this.connect(), delay)\n }\n}\n\n/** Connection-fatal error codes — retrying can't fix these. ONLY auth-handshake\n * failure qualifies: FORBIDDEN / NOT_FOUND are per-REQUEST errors (a stale\n * reference, one permission check) and must NOT tear down the whole socket. */\nconst FATAL_ERRORS = new Set(['UNAUTHORIZED'])\nfunction friendlyError(code: string): string {\n switch (code) {\n case 'UNAUTHORIZED': return 'Chat unavailable — sign-in/token was rejected'\n default: return 'Chat unavailable'\n }\n}\n\nfunction defaultFactory(url: string): SocketLike {\n return new WebSocket(url) as unknown as SocketLike\n}\n","import type {\n ServerFrame, Message, ManifestAction, MessageContent,\n ConversationId, MessageId, UserId, Subject, AnnotationStroke,\n} from './protocol/index.js'\n\nexport type SendStatus = 'pending' | 'sent' | 'delivered' | 'read'\n\nexport interface RenderMessage extends Message {\n clientMsgId?: string\n status?: SendStatus\n}\n\n/** Pure, DOM-free conversation state. Feed it ServerFrames (and local optimistic\n * sends); read an ordered, de-duplicated view out. Ordering is by `seq`; the\n * same message arriving twice (live + sync on reconnect) is collapsed by id —\n * the structural fix for the old duplicate-bubble bug. */\nexport class ChatStore {\n conversationId?: ConversationId\n state = ''\n version = 0\n hasMoreHistory = false\n lastReadByOthers = 0\n assignedAgentId: UserId | undefined\n accent: string | undefined\n subject: Subject | undefined\n name: string | undefined\n e2e = false\n offline = false\n offlineMessage = ''\n launcherMessage: { title: string; subtitle?: string } | null = null\n preChat: import('./protocol/frames.js').PreChatConfig | null = null\n whiteLabel = false\n readonly typing = new Set<string>()\n readonly online = new Set<string>()\n /** Co-browsing: shared whiteboard strokes for this conversation. */\n annotations: AnnotationStroke[] = []\n /** Bumped on any annotation change so the renderer can cheaply detect updates. */\n annotationVersion = 0\n /** Live sentiment of the guest's latest message (agent-side only). */\n sentiment: 'positive' | 'neutral' | 'frustrated' | undefined\n sentimentScore: number | undefined\n\n private actions: ManifestAction[] = []\n private readonly byId = new Map<string, RenderMessage>()\n private readonly keyByClient = new Map<string, string>()\n private _maxSeq = 0\n private _sorted: RenderMessage[] | null = null\n\n constructor(private readonly me: UserId) {}\n\n messages(): RenderMessage[] {\n if (!this._sorted) {\n this._sorted = [...this.byId.values()].sort((a, b) => {\n const ap = a.status === 'pending', bp = b.status === 'pending'\n if (ap !== bp) return ap ? 1 : -1\n if (ap && bp) return a.ts - b.ts\n return a.seq - b.seq\n })\n }\n return this._sorted\n }\n\n visibleActions(): ManifestAction[] {\n return this.actions.filter(a => !a.availableInStates || a.availableInStates.includes(this.state))\n }\n\n highestSeq(): number { return this._maxSeq }\n\n addOptimistic(clientMsgId: string, content: MessageContent): RenderMessage {\n const msg: RenderMessage = {\n id: clientMsgId as MessageId, conversationId: this.conversationId as ConversationId,\n seq: 0, senderId: this.me, senderRole: 'guest', content, ts: Date.now(),\n clientMsgId, status: 'pending',\n }\n this.byId.set(clientMsgId, msg)\n this.keyByClient.set(clientMsgId, clientMsgId)\n this._sorted = null\n return msg\n }\n\n apply(frame: ServerFrame): void {\n switch (frame.type) {\n case 'opened':\n this.conversationId = frame.conversation.id\n this.state = frame.conversation.state\n if (frame.subject) this.subject = frame.subject\n if (frame.annotations) { this.annotations = frame.annotations; this.annotationVersion++ }\n return\n case 'manifest':\n this.actions = frame.actions\n this.version = frame.version\n if (frame.name) this.name = frame.name\n if (frame.theme?.accent) this.accent = frame.theme.accent\n if (frame.e2e) this.e2e = true\n // Track the manifest EXACTLY: a sticky `offline` (only ever set, never\n // cleared) kept the widget in away-mode for the whole session once a\n // single manifest said so — which used to hide the composer entirely.\n this.offline = frame.offline === true\n this.offlineMessage = frame.offlineMessage ?? ''\n if (frame.launcherMessage) this.launcherMessage = frame.launcherMessage\n if (frame.whiteLabel) this.whiteLabel = true\n if (frame.preChat) this.preChat = frame.preChat\n return\n case 'message':\n this.upsert({ ...frame.message })\n return\n case 'ack': {\n const key = this.keyByClient.get(frame.clientMsgId)\n const msg = key ? this.byId.get(key) : undefined\n if (msg && key) {\n this.byId.delete(key)\n const confirmed: RenderMessage = { ...msg, id: frame.messageId, seq: frame.seq, ts: frame.ts, status: 'sent' }\n this.byId.set(frame.messageId, confirmed)\n this.keyByClient.set(frame.clientMsgId, frame.messageId)\n if (frame.seq > this._maxSeq) this._maxSeq = frame.seq\n }\n this._sorted = null\n return\n }\n case 'delivered':\n this.markOwnStatus(frame.seq, 'delivered')\n return\n case 'read':\n if (frame.by !== this.me) {\n this.lastReadByOthers = Math.max(this.lastReadByOthers, frame.seq)\n this.markOwnStatus(frame.seq, 'read')\n }\n return\n case 'sync':\n for (const m of frame.messages) this.upsert({ ...m })\n return\n case 'history':\n for (const m of frame.messages) this.upsert({ ...m })\n this.hasMoreHistory = frame.hasMore\n return\n case 'typing':\n if (frame.userId !== this.me) {\n if (frame.isTyping) this.typing.add(frame.userId)\n else this.typing.delete(frame.userId)\n }\n return\n case 'reaction': {\n const m = this.byId.get(frame.messageId)\n if (!m) return\n const reactions: Record<string, UserId[]> = { ...(m.reactions ?? {}) }\n const users = (reactions[frame.emoji] ?? []).filter(u => u !== frame.by)\n if (!frame.removed) users.push(frame.by)\n if (users.length) reactions[frame.emoji] = users; else delete reactions[frame.emoji]\n this.byId.set(frame.messageId, { ...m, reactions })\n this._sorted = null\n return\n }\n case 'edited': {\n const m = this.byId.get(frame.messageId)\n if (m) { this.byId.set(frame.messageId, { ...m, content: frame.content, editedAt: frame.editedAt }); this._sorted = null }\n return\n }\n case 'deleted': {\n const m = this.byId.get(frame.messageId)\n if (m) { this.byId.set(frame.messageId, { ...m, deletedAt: frame.ts }); this._sorted = null }\n return\n }\n case 'state':\n this.state = frame.state\n return\n case 'assigned':\n this.assignedAgentId = frame.agentId ?? undefined\n return\n case 'presence':\n if (frame.status === 'online') this.online.add(frame.userId)\n else this.online.delete(frame.userId)\n return\n case 'subjectState':\n case 'invoked':\n case 'authed':\n case 'error':\n case 'pong':\n return\n case 'annotation':\n this.annotations = [...this.annotations, frame.stroke]\n this.annotationVersion++\n return\n case 'annotation_clear':\n this.annotations = []\n this.annotationVersion++\n return\n case 'sentiment':\n this.sentiment = frame.label\n this.sentimentScore = frame.score\n return\n default:\n return\n }\n }\n\n private upsert(msg: RenderMessage): void {\n const existing = this.byId.get(msg.id)\n this.byId.set(msg.id, existing ? { ...existing, ...msg } : msg)\n if (msg.seq > this._maxSeq) this._maxSeq = msg.seq\n this._sorted = null\n }\n\n private markOwnStatus(uptoSeq: number, status: SendStatus): void {\n const targetRank = rank(status)\n let changed = false\n for (const [k, m] of this.byId) {\n if (m.senderId !== this.me || m.seq <= 0 || m.seq > uptoSeq) continue\n if (rank(m.status) >= targetRank) continue // already at or above target — skip\n this.byId.set(k, { ...m, status })\n changed = true\n }\n if (changed) this._sorted = null\n }\n}\nfunction rank(s: SendStatus | undefined): number {\n switch (s) { case 'read': return 3; case 'delivered': return 2; case 'sent': return 1; default: return 0 }\n}\n","import type { MessageContent } from './protocol/index.js'\n\nexport interface OutboxItem {\n clientMsgId: string\n content: MessageContent\n ts: number\n}\n\nconst MAX_ITEMS = 200 // cap so a long outage can't grow storage unboundedly\nconst MAX_AGE_MS = 7 * 86_400_000 // drop anything older than 7 days on load\n\n/** Persists not-yet-acknowledged outgoing messages to localStorage, keyed by\n * guest token, so a page reload during a connectivity drop doesn't silently\n * lose what the user typed (the \"WhatsApp\" guarantee: your message is queued\n * until it's confirmed sent, even across app restarts). */\nexport class PersistentOutbox {\n private readonly key: string\n\n constructor(token: string) {\n this.key = `ocw_outbox_${token}`\n }\n\n /** All pending items, oldest first, with stale (>7d) entries dropped. */\n load(): OutboxItem[] {\n try {\n const raw = localStorage.getItem(this.key)\n if (!raw) return []\n const items = JSON.parse(raw) as OutboxItem[]\n const cutoff = Date.now() - MAX_AGE_MS\n const fresh = items.filter(i => i.ts >= cutoff)\n if (fresh.length !== items.length) this.save(fresh)\n return fresh\n } catch {\n return []\n }\n }\n\n add(item: OutboxItem): void {\n try {\n const items = this.load()\n items.push(item)\n // Evict oldest when full — matches the in-memory ConnectionManager outbox policy.\n this.save(items.length > MAX_ITEMS ? items.slice(items.length - MAX_ITEMS) : items)\n } catch { /* localStorage unavailable (private mode, quota) — best-effort only */ }\n }\n\n /** Remove an item once it's been acknowledged by the server. */\n remove(clientMsgId: string): void {\n try {\n const items = this.load().filter(i => i.clientMsgId !== clientMsgId)\n this.save(items)\n } catch { /* best-effort */ }\n }\n\n private save(items: OutboxItem[]): void {\n try { localStorage.setItem(this.key, JSON.stringify(items)) } catch { /* quota exceeded — drop silently */ }\n }\n}\n"],"names":["asTenantId","s","asUserId","asConversationId","asSubjectId","asMessageId","asActionId","asProfileId","asConnectionId","ANONYMOUS_TENANT","ConnectionManager","opts","__publicField","_b","_a","sock","defaultFactory","ev","frame","decodeFrame","isClientFrame","cap","FATAL_ERRORS","_d","_c","fresh","friendlyError","pending","f","encodeFrame","base","max","delay","code","url","ChatStore","me","a","b","ap","bp","clientMsgId","content","msg","key","confirmed","m","reactions","users","u","existing","uptoSeq","status","targetRank","rank","changed","k","MAX_ITEMS","MAX_AGE_MS","PersistentOutbox","token","raw","items","cutoff","i","item"],"mappings":"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"}
@@ -72,7 +72,7 @@ export declare class Renderer {
72
72
  private readonly quick;
73
73
  private readonly formHost;
74
74
  private readonly csatPanel;
75
- private readonly offlinePanel;
75
+ private readonly awayNotice;
76
76
  private readonly preChatPanel;
77
77
  private readonly deflectPanel;
78
78
  private preChatBuilt;
@@ -116,7 +116,6 @@ export declare class Renderer {
116
116
  private signalTyping;
117
117
  render(store: ChatStore): void;
118
118
  setConnStatus(status: 'connecting' | 'open' | 'reconnecting' | 'error', message?: string): void;
119
- private buildOfflinePanel;
120
119
  private buildPreChatPanel;
121
120
  /** Mark pre-chat complete (submitted now or in a previous session). */
122
121
  completePreChat(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paramms/chat-widget",
3
- "version": "1.0.39",
3
+ "version": "1.0.41",
4
4
  "description": "Embeddable real-time chat widget for the Relay platform",
5
5
  "license": "MIT",
6
6
  "repository": {