@chatlychat/sdk-js 0.0.0-stage → 0.1.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/live.js ADDED
@@ -0,0 +1,158 @@
1
+ /**
2
+ * How many message ids a subscription remembers.
3
+ *
4
+ * Unbounded, a long-lived tab on a busy conversation keeps every id it has
5
+ * ever seen. The cap is far above any realistic poll window, and the oldest
6
+ * are dropped first — a message older than the last thousand cannot appear as
7
+ * "new" in a later poll of the most recent page.
8
+ */
9
+ const MAX_REMEMBERED = 1_000;
10
+ /**
11
+ * The messages in `batch` that `seen` has not yet been told about, oldest
12
+ * first, with `seen` updated.
13
+ *
14
+ * Ordered by `createdAt` rather than by the order the server returned: a list
15
+ * endpoint is free to answer newest-first, and a consumer appending in
16
+ * receive order would render the conversation upside down.
17
+ *
18
+ * Deliberately NOT the widget's `mergePolled`. That one reconciles optimistic
19
+ * `local-*` rows against their server copies, which only matters to a UI that
20
+ * renders a message before the server has one. A headless consumer owns that
21
+ * decision themselves; all they need from us is "what is new".
22
+ */
23
+ export function newMessagesSince(seen, batch) {
24
+ const fresh = [];
25
+ for (const message of batch) {
26
+ const id = message?.id;
27
+ if (typeof id !== 'string' || id.length === 0) {
28
+ // A row we cannot key is a row we would emit again on every poll.
29
+ continue;
30
+ }
31
+ if (seen.has(id)) {
32
+ continue;
33
+ }
34
+ seen.add(id);
35
+ fresh.push(message);
36
+ }
37
+ fresh.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
38
+ if (seen.size > MAX_REMEMBERED) {
39
+ // Insertion-ordered, so the first entries are the oldest.
40
+ const excess = seen.size - MAX_REMEMBERED;
41
+ let dropped = 0;
42
+ for (const id of seen) {
43
+ if (dropped >= excess) {
44
+ break;
45
+ }
46
+ seen.delete(id);
47
+ dropped += 1;
48
+ }
49
+ }
50
+ return fresh;
51
+ }
52
+ /**
53
+ * How long a "typing" indicator survives without another `typing.start`.
54
+ *
55
+ * ── Why the SDK owns this timeout and not the consumer ──────────────────
56
+ *
57
+ * `typing.stop` is best-effort. The dashboard emits one after 5s of idle
58
+ * keystrokes and on send, and its hook emits one on unmount — but an agent
59
+ * who closes the laptop mid-sentence, loses their network, or whose tab is
60
+ * killed sends nothing at all. A consumer rendering the raw start/stop pair
61
+ * gets an indicator that says "Sarah is typing…" forever, which is the exact
62
+ * shape of bug this codebase keeps finding: it appears to work.
63
+ *
64
+ * Leaving it to the consumer means every consumer writes this same timer, and
65
+ * the ones who do not ship the stuck indicator. 3,500ms is the widget's own
66
+ * window (`use-conversation-realtime.tsx`), so a custom UI built on the SDK
67
+ * behaves like the widget rather than subtly differently.
68
+ */
69
+ export const TYPING_STOP_AFTER_MS = 3_500;
70
+ /**
71
+ * Turn a stream of `typing.start` / `typing.stop` events into the single
72
+ * boolean a UI actually renders.
73
+ *
74
+ * Edge-triggered: `onChange` fires only when the answer changes, so a
75
+ * keystroke every 300ms does not re-render the consumer's component ten times
76
+ * a second. Exported for its own test — the timer behaviour is the part worth
77
+ * pinning, and it is invisible from the outside.
78
+ */
79
+ export function createTypingTracker(opts) {
80
+ const stopAfterMs = opts.stopAfterMs ?? TYPING_STOP_AFTER_MS;
81
+ let timer = null;
82
+ let active = null;
83
+ const clear = () => {
84
+ if (timer !== null) {
85
+ clearTimeout(timer);
86
+ timer = null;
87
+ }
88
+ };
89
+ const settle = (actor, isTyping) => {
90
+ if (isTyping === (active !== null)) {
91
+ return;
92
+ }
93
+ active = isTyping ? actor : null;
94
+ opts.onChange({ ...actor, isTyping });
95
+ };
96
+ return {
97
+ start(actor) {
98
+ settle(actor, true);
99
+ clear();
100
+ timer = setTimeout(() => {
101
+ timer = null;
102
+ settle(actor, false);
103
+ }, stopAfterMs);
104
+ },
105
+ stop(actor) {
106
+ clear();
107
+ // The ACTIVE actor is preferred over the one passed in, so the
108
+ // `isTyping: false` signal names whoever the consumer was told about —
109
+ // not an empty placeholder from a `message.created` envelope.
110
+ const subject = active ?? actor;
111
+ if (subject === undefined || subject === null) {
112
+ return;
113
+ }
114
+ settle(subject, false);
115
+ },
116
+ dispose() {
117
+ // No `onChange` on dispose: the subscription is going away and the
118
+ // consumer is tearing their UI down with it. Firing a last event into a
119
+ // component that has unmounted is how a wrapper earns a React warning.
120
+ clear();
121
+ active = null;
122
+ },
123
+ };
124
+ }
125
+ /**
126
+ * The actor a `typing.*` envelope is about, or null if it is not one we show.
127
+ *
128
+ * Two exclusions, both load-bearing:
129
+ *
130
+ * - `kind: 'viewing'`. The agent dashboard publishes room presence and
131
+ * collaborative-cursor frames AS `typing.start` (see `viewing.cursor` in
132
+ * `apps/realtime/src/gateway.ts`), ~10 per second while an agent moves
133
+ * their mouse. Rendering those as typing would make the indicator flicker
134
+ * whenever an agent so much as looked at the conversation.
135
+ * - anything that is not an agent or a bot. The gateway's visitor allowlist
136
+ * already forwards only agent typing, so this is the second of two checks
137
+ * rather than the only one — but a visitor must never be shown their OWN
138
+ * typing as the agent's, and the day the allowlist widens is not the day
139
+ * to discover that.
140
+ */
141
+ export function typingActorOf(payload) {
142
+ if (typeof payload !== 'object' || payload === null) {
143
+ return null;
144
+ }
145
+ const p = payload;
146
+ if (p.kind === 'viewing') {
147
+ return null;
148
+ }
149
+ if (p.actorType !== 'agent' && p.actorType !== 'bot') {
150
+ return null;
151
+ }
152
+ return {
153
+ actorType: p.actorType,
154
+ actorId: typeof p.actorId === 'string' ? p.actorId : '',
155
+ ...(typeof p.displayName === 'string' ? { displayName: p.displayName } : {}),
156
+ };
157
+ }
158
+ //# sourceMappingURL=live.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live.js","sourceRoot":"","sources":["../src/live.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Public types for the headless SDK.
3
+ *
4
+ * Declared here rather than imported from `@livechat/shared`: this bundle
5
+ * ships to a customer's website and importing from the shared package drags
6
+ * zod in with it — measured at ~11KB gzipped when it happened to the widget.
7
+ * The shapes are the API's response bodies, which are a published contract.
8
+ */
9
+ /** One message as the visitor endpoints return it. */
10
+ export interface HeadlessMessage {
11
+ readonly id: string;
12
+ readonly body: string;
13
+ readonly authorType: 'contact' | 'agent' | 'bot' | 'system';
14
+ readonly createdAt: string;
15
+ /** Rich blocks, when the message carries them. Opaque here — render or ignore. */
16
+ readonly blocks?: unknown;
17
+ /** A Tier 2/3 interactive step. Mutually exclusive with `blocks`. */
18
+ readonly canvas?: unknown;
19
+ readonly attachments?: ReadonlyArray<{
20
+ readonly id: string;
21
+ readonly filename: string;
22
+ readonly mimeType: string;
23
+ readonly url: string;
24
+ }>;
25
+ }
26
+ /** One thread in the visitor's own list. */
27
+ export interface HeadlessConversation {
28
+ readonly id: string;
29
+ readonly status?: string;
30
+ readonly lastMessageAt?: string;
31
+ readonly subject?: string | null;
32
+ }
33
+ /**
34
+ * "The agent is typing", as the consumer should render it.
35
+ *
36
+ * `isTyping` rather than two callbacks, because a UI holds one boolean and
37
+ * the two-callback shape makes the consumer own the state machine — including
38
+ * the part they cannot know about (see `TYPING_STOP_AFTER_MS`).
39
+ */
40
+ export interface TypingSignal {
41
+ /** Show the indicator, or hide it. */
42
+ readonly isTyping: boolean;
43
+ /**
44
+ * Who. `'agent'` in every case the server currently forwards — a visitor
45
+ * never receives their own typing, and agent-presence cursors are filtered
46
+ * out on both sides.
47
+ */
48
+ readonly actorType: 'agent' | 'bot';
49
+ readonly actorId: string;
50
+ /** The agent's display name, when the dashboard sent one. */
51
+ readonly displayName?: string;
52
+ }
53
+ /** What `subscribe()` reports. */
54
+ export interface SubscribeHandlers {
55
+ /** A message the consumer has not been given before. */
56
+ readonly onMessage?: (message: HeadlessMessage) => void;
57
+ /**
58
+ * The agent started or stopped typing.
59
+ *
60
+ * **PUSH ONLY.** Typing is a socket event with no HTTP route, so a polling
61
+ * consumer never receives this — not rarely, never. There is nothing to
62
+ * poll and nothing to warn about at subscribe time either, because a
63
+ * consumer who passes `onTyping` without `io` has asked for something the
64
+ * transport cannot carry: `subscribe` says so once, in the console, rather
65
+ * than leaving them to wonder why an agent who is visibly typing in the
66
+ * dashboard produces nothing. (A 6-second poll would be a useless indicator
67
+ * even if a route existed — the dashboard stops sending after 5s of idle
68
+ * keystrokes, so a poll could easily see neither edge.)
69
+ *
70
+ * Called with `isTyping: false` either when the agent's `typing.stop`
71
+ * arrives OR after `TYPING_STOP_AFTER_MS` with no further start — see that
72
+ * constant for why the timeout is not the consumer's problem to solve.
73
+ */
74
+ readonly onTyping?: (signal: TypingSignal) => void;
75
+ /**
76
+ * A poll or socket failed. Called rather than thrown: `subscribe` is
77
+ * long-lived, and a rejected promise nobody is awaiting becomes an
78
+ * unhandled rejection on the customer's page.
79
+ */
80
+ readonly onError?: (error: unknown) => void;
81
+ }
82
+ export interface SubscribeOptions {
83
+ /** Poll interval. Ignored when `io` is supplied. Floored at 2s. */
84
+ readonly intervalMs?: number;
85
+ /**
86
+ * Your own `socket.io-client` factory, for push instead of polling:
87
+ *
88
+ * import { io } from 'socket.io-client';
89
+ * chatly.subscribe(id, handlers, { io });
90
+ *
91
+ * The SDK does not depend on it, so a consumer who does not want the bytes
92
+ * does not ship them.
93
+ */
94
+ readonly io?: SocketIoFactory;
95
+ }
96
+ /** The shape of `socket.io-client`'s default export that the SDK uses. */
97
+ export type SocketIoFactory = (url: string, opts: Record<string, unknown>) => {
98
+ on: (event: string, handler: (payload: unknown) => void) => void;
99
+ emit: (event: string, payload: unknown) => void;
100
+ disconnect: () => void;
101
+ };
102
+ /**
103
+ * What `subscribe()` returns.
104
+ *
105
+ * Callable, so `const stop = subscribe(...); stop()` keeps working — it was
106
+ * documented and shipped as a plain unsubscribe function, and changing that
107
+ * shape hours later would break the first consumers for no gain. The extras
108
+ * hang off it.
109
+ *
110
+ * ── Why RECEIVING typing is not here ────────────────────────────────────
111
+ *
112
+ * A typing indicator needs both directions, and they land in different
113
+ * places. SENDING is an imperative call the consumer makes at a moment of
114
+ * their choosing (a keystroke), so it has to hang off the handle — there is
115
+ * nowhere else to put it. RECEIVING is an event, and every other event this
116
+ * SDK reports arrives through `SubscribeHandlers` (`onMessage`, `onError`).
117
+ *
118
+ * The three alternatives were:
119
+ *
120
+ * 1. **`onTyping` in the handlers.** Chosen. Zero change to the return
121
+ * type, so no existing caller can break, and it is the shape a consumer
122
+ * already knows from `onMessage`.
123
+ * 2. **An emitter on the handle** (`sub.onTyping(fn)`). A second
124
+ * subscription idiom in one SDK, with its own unsubscribe to get wrong,
125
+ * for one event.
126
+ * 3. **Returning an object instead of a function.** The breaking change the
127
+ * note above rejects, and it buys nothing: `setTyping` already proved the
128
+ * callable-plus-properties shape carries whatever we need.
129
+ */
130
+ export type Subscription = (() => void) & {
131
+ /**
132
+ * Tell the agent this visitor is typing.
133
+ *
134
+ * Only the PUSH path can carry this: typing is a socket event
135
+ * (`apps/realtime/src/gateway.ts` listens for `typing.start`) and there is
136
+ * no HTTP route for it. On the polling path it warns once rather than
137
+ * silently doing nothing — a no-op that looks like it worked is how an
138
+ * integrator spends an hour on their own code.
139
+ */
140
+ setTyping: (isTyping: boolean) => void;
141
+ };
142
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,sDAAsD;AACtD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,SAAS,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC5D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,aAAa,CAAC;QACnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;CACJ;AAED,4CAA4C;AAC5C,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,KAAK,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,kCAAkC;AAClC,MAAM,WAAW,iBAAiB;IAChC,wDAAwD;IACxD,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACxD;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IACnD;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,gBAAgB;IAC/B,mEAAmE;IACnE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,CAC5B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B;IACH,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,KAAK,IAAI,CAAC;IACjE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG;IACxC;;;;;;;;OAQG;IACH,SAAS,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Public types for the headless SDK.
3
+ *
4
+ * Declared here rather than imported from `@livechat/shared`: this bundle
5
+ * ships to a customer's website and importing from the shared package drags
6
+ * zod in with it — measured at ~11KB gzipped when it happened to the widget.
7
+ * The shapes are the API's response bodies, which are a published contract.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
package/package.json CHANGED
@@ -1,6 +1,62 @@
1
1
  {
2
2
  "name": "@chatlychat/sdk-js",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Browser SDK for the LiveChat platform — identify visitors, fire events, control the widget.",
5
+ "license": "MIT",
6
+ "homepage": "https://github.com/livechat/platform/tree/main/packages/sdk-js",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/livechat/platform.git",
10
+ "directory": "packages/sdk-js"
11
+ },
12
+ "bugs": "https://github.com/livechat/platform/issues",
13
+ "keywords": [
14
+ "livechat",
15
+ "chat",
16
+ "messaging",
17
+ "support",
18
+ "widget",
19
+ "sdk"
20
+ ],
21
+ "author": "LiveChat Platform Engineering",
22
+ "type": "module",
23
+ "main": "./dist/index.js",
24
+ "module": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js",
30
+ "default": "./dist/index.js"
31
+ }
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "!dist/**/*.spec.*",
36
+ "README.md",
37
+ "LICENSE",
38
+ "CHANGELOG.md"
39
+ ],
40
+ "sideEffects": false,
41
+ "scripts": {
42
+ "build": "tsc -p tsconfig.json",
43
+ "prepublishOnly": "pnpm build",
44
+ "typecheck": "tsc -p tsconfig.json --noEmit",
45
+ "lint": "eslint src --max-warnings=0",
46
+ "test": "vitest run"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public",
50
+ "registry": "https://registry.npmjs.org/",
51
+ "provenance": true
52
+ },
53
+ "engines": {
54
+ "node": ">=18"
55
+ },
56
+ "devDependencies": {
57
+ "@livechat/config": "workspace:*",
58
+ "jsdom": "^30.0.1",
59
+ "typescript": "catalog:",
60
+ "vitest": "catalog:"
61
+ }
62
+ }