@opiniom/core
v0.6.0
Published
Framework-agnostic core client for the OpinioM chat SDK — real-time messaging, WebSocket, authentication, and state management
Downloads
51
Maintainers
Readme
@opiniom/core
Framework-agnostic core client for the OpinioM chat SDK. Handles authentication, real-time messaging via Socket.io, state management, reconnection, and offline caching.
Use this package directly if you're building with vanilla JS, Svelte, Vue, Angular, or any non-React framework. For React, use @opiniom/react instead.
Install
npm install @opiniom/core @opiniom/typesQuick Start
import { createClient } from "@opiniom/core";
const client = await createClient({
projectKey: "pk_live_abc123",
jwt: customerSignedJwt,
});
// Listen for new messages
client.on("message:new", (message) => {
console.log(`${message.sender.name}: ${message.content}`);
});
// Send a message
await client.sendMessage("conversation-id", {
content: "Hello from OpinioM!",
});Configuration
const client = await createClient({
// Required
projectKey: "pk_live_abc123", // Your public project key
jwt: "eyJhbGciOi...", // JWT signed with your API secret
// Optional
apiUrl: "https://api.opiniom.io", // Custom API endpoint
wsUrl: "https://api.opiniom.io", // Custom WebSocket endpoint
mode: "floating", // "floating" | "inline"
container: "#chat-container", // Required when mode is "inline"
locale: "en", // UI language
startOpen: false, // Start with widget open
zIndex: 2147483600, // Widget z-index
debug: false, // Verbose console logging
cache: false, // IndexedDB offline persistence
// Callbacks
onReady: (client) => {}, // Client connected and ready
onError: (err) => {}, // Unhandled error
onTokenExpiring: async () => { // Return a fresh JWT
return await fetchNewToken();
},
});API Reference
Lifecycle
client.state; // "connected" | "connecting" | "reconnecting" | "disconnected" | ...
client.user; // Current user (User | null)
client.widgetConfig; // Widget theme config (WidgetConfig | null)
client.projectName; // Project name (string | null)
client.version; // SDK version
client.locale; // Current locale
client.destroy(); // Disconnect and clean up
await client.updateAuth(newJwt); // Swap JWT at runtime
await client.refreshSession(); // Force session refresh
client.setLocale("fr"); // Change UI languageConversations
// Create a conversation
const conv = await client.createConversation({
type: "direct", // "direct" | "group" | "channel" | "support"
member_ids: ["user-123"],
title: "Support Chat", // Optional
});
// List, join, leave
const conversations = client.listConversations();
await client.joinConversation(conversationId);
await client.leaveConversation(conversationId);Messages
// Send a message (with optimistic UI updates)
const message = await client.sendMessage(conversationId, {
content: "Hello!",
client_id: "optional-client-uuid", // For deduplication
});
// Edit and delete
await client.editMessage(messageId, conversationId, "Updated text");
await client.deleteMessage(messageId, conversationId);
// Fetch messages
const messages = client.getMessages(conversationId); // Cached
const older = await client.loadMoreMessages(conversationId); // PaginatedFile Upload
// Upload returns metadata for attaching to a message
const attachment = await client.uploadFile(fileObject);
// { file_name, file_size, mime_type, url }
await client.sendMessage(conversationId, {
content: "See attached",
attachments: [attachment],
});Reactions
await client.addReaction(messageId, "thumbsup");
await client.removeReaction(messageId, "thumbsup");Typing & Read Receipts
client.setTyping(conversationId, true); // Start typing
client.setTyping(conversationId, false); // Stop typing
client.markRead(conversationId, messageId);Events
Subscribe to real-time events:
const unsub = client.on("message:new", (message) => { ... });
client.on("message:updated", (message) => { ... });
client.on("message:deleted", ({ id, conversation_id }) => { ... });
client.on("conversation:created", (conversation) => { ... });
client.on("reaction:added", (reaction) => { ... });
client.on("reaction:removed", (reaction) => { ... });
client.on("presence:changed", (presence) => { ... });
client.on("typing:start", ({ conversation_id, user_id }) => { ... });
client.on("typing:stop", ({ conversation_id, user_id }) => { ... });
client.on("state:changed", ({ from, to }) => { ... });
client.on("error", (error) => { ... });
// Unsubscribe
unsub();
// or
client.off("message:new", handler);Connection States
The client follows this state machine:
idle → authenticating → connecting → connected
↓
degraded ←→ reconnecting
↓
disconnected → destroyedListen for state changes:
client.on("state:changed", ({ from, to }) => {
if (to === "reconnecting") showBanner("Reconnecting...");
if (to === "connected") hideBanner();
});JWT Format
Your backend signs a JWT with your OpinioM API secret key:
// Required claims
{
sub: "user-id", // Unique user identifier
name: "Jane Doe", // Display name
email: "[email protected]"
}The JWT must have exactly 3 dot-separated segments. Max lifetime: 24 hours.
Bundle Size
| Format | Size (gzip) | |---|---| | ESM | ~34 KB | | CJS | ~25 KB |
Related Packages
| Package | Description | |---|---| | @opiniom/react | React provider, hooks, and components | | @opiniom/web | Lit-based web component widget | | @opiniom/types | TypeScript type definitions |
License
MIT
