@journyio/messaging-sdk 1.0.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/README.md ADDED
@@ -0,0 +1,256 @@
1
+ # @journyio/messaging-sdk
2
+
3
+ A standalone JavaScript library for displaying in-app messages from Journy. This library can be integrated via a `<script>` tag, ES modules, or npm package, similar to analytics libraries like Segment or Google Analytics.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Standalone Library**: Works without a build step in the host application
8
+ - ⚛️ **React Support**: Automatically renders React components when React is available
9
+ - 🔒 **XSS Protection**: Built-in HTML sanitization using DOMPurify
10
+ - 📦 **Lightweight**: Minimal dependencies, small bundle size
11
+ - 🎨 **Customizable**: Flexible styling and message types
12
+ - 📊 **Event Tracking**: Automatic tracking of message interactions
13
+ - 🔄 **Message Queue**: Smart queue management with priority support
14
+ - ⏱️ **Auto-polling**: Automatically fetches new messages
15
+
16
+ ## Installation
17
+
18
+ ### Method 1: Script Tag (Simplest)
19
+
20
+ ```html
21
+ <!DOCTYPE html>
22
+ <html>
23
+ <head>
24
+ <title>My App</title>
25
+ </head>
26
+ <body>
27
+ <!-- Your app content -->
28
+
29
+ <!-- Load React and ReactDOM (if not already loaded) -->
30
+ <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
31
+ <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
32
+
33
+ <!-- Load Journy Messages Library -->
34
+ <script src="https://cdn.journy.io/messages/journy-messages.min.js"></script>
35
+
36
+ <script>
37
+ // Initialize
38
+ const messaging = new JournyMessages({
39
+ writeKey: 'your-write-key',
40
+ userId: 'user-123',
41
+ entityType: 'user',
42
+ });
43
+ </script>
44
+ </body>
45
+ </html>
46
+ ```
47
+
48
+ ### Method 2: npm Package
49
+
50
+ ```bash
51
+ npm install @journyio/messaging-sdk
52
+ ```
53
+
54
+ ```javascript
55
+ import { JournyMessaging } from '@journyio/messaging-sdk';
56
+
57
+ const messaging = new JournyMessaging({
58
+ writeKey: 'your-write-key',
59
+ userId: 'user-123',
60
+ entityType: 'user',
61
+ });
62
+ ```
63
+
64
+ ### Method 3: ES Modules
65
+
66
+ ```javascript
67
+ import JournyMessages from '@journyio/messaging-sdk';
68
+
69
+ const messaging = new JournyMessages({
70
+ writeKey: 'your-write-key',
71
+ userId: 'user-123',
72
+ entityType: 'user',
73
+ });
74
+ ```
75
+
76
+ ## Configuration
77
+
78
+ ### Basic Configuration
79
+
80
+ ```typescript
81
+ const messaging = new JournyMessaging({
82
+ writeKey: 'your-write-key', // Required: Your Journy write key
83
+ userId: 'user-123', // Optional: User ID
84
+ accountId: 'account-456', // Optional: Account ID
85
+ entityType: 'user', // Required: 'user' or 'account'
86
+ apiEndpoint: 'https://jtm.journy.io', // Optional: API base URL
87
+ pollingInterval: 30000, // Optional: Polling interval in ms (default: 30000)
88
+ });
89
+ ```
90
+
91
+ ### Configuration Options
92
+
93
+ | Option | Type | Required | Default | Description |
94
+ |--------|------|----------|---------|-------------|
95
+ | `writeKey` | `string` | Yes | - | Your Journy write key for authentication |
96
+ | `userId` | `string` | No | - | Current user ID |
97
+ | `accountId` | `string` | No | - | Current account ID |
98
+ | `entityType` | `'user' \| 'account'` | Yes | - | Type of entity to fetch messages for |
99
+ | `apiEndpoint` | `string` | No | `'https://jtm.journy.io'` | API base URL |
100
+ | `pollingInterval` | `number` | No | `30000` | Interval in milliseconds to poll for new messages |
101
+
102
+ ## API Reference
103
+
104
+ ### Methods
105
+
106
+ #### `markAsRead(messageId: string): Promise<void>`
107
+
108
+ Marks a message as read and removes it from the queue.
109
+
110
+ ```typescript
111
+ await messaging.markAsRead('message-123');
112
+ ```
113
+
114
+ #### `trackLinkClick(messageId: string, linkUrl: string): void`
115
+
116
+ Tracks when a user clicks a link in a message.
117
+
118
+ ```typescript
119
+ messaging.trackLinkClick('message-123', 'https://example.com');
120
+ ```
121
+
122
+ #### `trackMessageClosed(messageId: string): void`
123
+
124
+ Tracks when a user closes a message.
125
+
126
+ ```typescript
127
+ messaging.trackMessageClosed('message-123');
128
+ ```
129
+
130
+ #### `trackMessageOpened(messageId: string): void`
131
+
132
+ Tracks when a message is opened/displayed.
133
+
134
+ ```typescript
135
+ messaging.trackMessageOpened('message-123');
136
+ ```
137
+
138
+ #### `destroy(): void`
139
+
140
+ Cleans up the messaging instance, stops polling, and removes UI elements.
141
+
142
+ ```typescript
143
+ messaging.destroy();
144
+ ```
145
+
146
+ ## Message Format
147
+
148
+ Messages from the API should follow this format:
149
+
150
+ ```typescript
151
+ interface Message {
152
+ id: string;
153
+ content: string; // HTML content (will be sanitized)
154
+ title?: string; // Optional title
155
+ type?: 'info' | 'success' | 'warning' | 'error';
156
+ priority?: number; // Higher numbers = higher priority
157
+ createdAt: string; // ISO 8601 timestamp
158
+ expiresAt?: string; // ISO 8601 timestamp
159
+ actions?: MessageAction[]; // Optional action buttons
160
+ metadata?: Record<string, any>;
161
+ }
162
+
163
+ interface MessageAction {
164
+ label: string;
165
+ url?: string;
166
+ action?: string;
167
+ style?: 'primary' | 'secondary' | 'link';
168
+ }
169
+ ```
170
+
171
+ ## Styling
172
+
173
+ ### Default styles
174
+
175
+ When you do not pass a `styles` option (or set `styles: 'default'`), the SDK **injects the default styles automatically**. You do not need to add a `<link>` tag; a single script tag is enough for the widget to look correct.
176
+
177
+ If you prefer to load the CSS yourself (e.g. for caching), you can still link the built file and set `styles: 'none'` then include `journy-messages.css` in your page—but the typical use is to omit `styles` and let the SDK inject the default CSS.
178
+
179
+ ### Configurable styles
180
+
181
+ You can control styling via the `styles` config option:
182
+
183
+ - **`styles: 'default'` or omitted** – The SDK injects the default styles inline. No separate CSS file needed.
184
+ - **`styles: 'none'`** – No SDK styles are injected. You provide all CSS (e.g. target `.journy-message-widget`, `.journy-message-popup`, etc.) in your own stylesheet.
185
+ - **`styles: { url: 'https://...' }`** – The SDK injects a `<link rel="stylesheet" href="...">` pointing to your stylesheet.
186
+ - **`styles: { css: '.journy-message-widget { ... }' }`** – The SDK injects a `<style>` tag with the given CSS.
187
+
188
+ Example with custom stylesheet URL:
189
+
190
+ ```javascript
191
+ const messaging = new JournyMessages({
192
+ writeKey: 'your-write-key',
193
+ entityType: 'user',
194
+ styles: { url: 'https://my-app.com/journy-messages-theme.css' },
195
+ });
196
+ ```
197
+
198
+ Example with no library styles (you style everything):
199
+
200
+ ```javascript
201
+ const messaging = new JournyMessages({
202
+ writeKey: 'your-write-key',
203
+ entityType: 'user',
204
+ styles: 'none',
205
+ });
206
+ ```
207
+
208
+ See `examples/alternative-styles.html` and `examples/alternative-styles.css` for a test theme and `styles: { url: '...' }` usage.
209
+
210
+ ### Overriding default styles
211
+
212
+ When using default styles, you can still customize by overriding these CSS classes in your own CSS:
213
+
214
+ - `.journy-message-overlay` - The backdrop overlay
215
+ - `.journy-message-popup` - The message popup container
216
+ - `.journy-message-title` - Message title
217
+ - `.journy-message-content` - Message content area
218
+ - `.journy-message-close` - Close button
219
+ - `.journy-message-actions` - Action buttons container
220
+ - `.journy-message-action` - Individual action button
221
+
222
+ ### Message Type Classes
223
+
224
+ - `.journy-message-info` - Info messages (blue border)
225
+ - `.journy-message-success` - Success messages (green border)
226
+ - `.journy-message-warning` - Warning messages (orange border)
227
+ - `.journy-message-error` - Error messages (red border)
228
+
229
+ ## Security
230
+
231
+ ### XSS Prevention
232
+
233
+ The library uses [DOMPurify](https://github.com/cure53/DOMPurify) to sanitize all HTML content before rendering. Only the following HTML tags and attributes are allowed:
234
+
235
+ **Allowed Tags:**
236
+ - `a`, `b`, `i`, `em`, `strong`, `p`, `br`, `ul`, `ol`, `li`
237
+
238
+ **Allowed Attributes:**
239
+ - `href`, `target`, `rel` (for links)
240
+
241
+ All other HTML is stripped to prevent XSS attacks.
242
+
243
+ ## Browser Support
244
+
245
+ - Chrome (latest)
246
+ - Firefox (latest)
247
+ - Safari (latest)
248
+ - Edge (latest)
249
+
250
+ ## License
251
+
252
+ MIT
253
+
254
+ ## Support
255
+
256
+ For issues and questions, please visit [GitHub Issues](https://github.com/journy-io/journy-in-app-messages/issues).
@@ -0,0 +1,6 @@
1
+ import { JournyMessaging } from './core/JournyMessaging';
2
+ export { JournyMessaging };
3
+ export type { JournyMessagingConfig } from './core/JournyMessaging';
4
+ export type { Message, AppDisplayMode, StylesConfig } from './types';
5
+ export default JournyMessaging;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ .journy-message-widget{background:#fff;border-radius:12px;box-shadow:0 4px 20px rgba(0,0,0,.15);overflow:hidden;position:fixed;transform-origin:bottom right;user-select:none;z-index:10000}.journy-message-widget.journy-message-widget-dragging{cursor:grabbing;transition:none;z-index:10001}.journy-message-widget.journy-message-widget-expanded{display:flex;flex-direction:column;min-height:0}.journy-message-widget.journy-message-widget-resizing{transition:none}.journy-message-widget-header{align-items:center;background:#f9fafb;border-bottom:1px solid #e5e7eb;display:flex;justify-content:space-between;padding:12px 16px;user-select:none}.journy-message-widget-drag-handle{align-items:center;color:#9ca3af;cursor:grab;display:flex;flex-shrink:0;justify-content:center;margin-right:8px;padding:4px 8px;transition:color .2s}.journy-message-widget-drag-handle:active{color:#6b7280;cursor:grabbing}.journy-message-widget-drag-handle:hover{color:#6b7280}.journy-message-widget-drag-handle svg{display:block}.journy-message-widget-header-content{align-items:center;cursor:pointer;display:flex;flex:1;flex-wrap:wrap;gap:10px}.journy-message-widget-badge{align-items:center;background:#3b82f6;border-radius:12px;color:#fff;display:inline-flex;font-size:12px;font-weight:600;height:24px;justify-content:center;min-width:24px;padding:0 8px}.journy-message-widget-title{color:#111827;font-size:14px;font-weight:600}.journy-message-widget-read-count{color:#6b7280;font-size:12px;font-weight:400}.journy-message-widget-controls{align-items:center;display:flex;gap:8px}.journy-message-widget-close,.journy-message-widget-toggle{align-items:center;background:none;border:none;border-radius:4px;color:#6b7280;cursor:pointer;display:flex;font-size:18px;height:28px;justify-content:center;line-height:1;padding:4px 8px;transition:background-color .2s,color .2s;width:28px}.journy-message-widget-close:hover,.journy-message-widget-toggle:hover{background-color:#e5e7eb;color:#374151}.journy-message-widget-content{overflow-x:hidden;overflow-y:auto;padding:16px}.journy-message-widget-content,.journy-message-widget-content--single .journy-message-widget-message{display:flex;flex:1;flex-direction:column;min-height:0}.journy-message-widget-content--single .journy-message-widget-message .journy-message-content{flex:1;min-height:0;overflow-y:auto}.journy-message-widget-resize-handle{align-items:center;background:linear-gradient(135deg,transparent,transparent 40%,#e5e7eb 0,#e5e7eb);bottom:0;cursor:nwse-resize;display:flex;height:20px;justify-content:center;position:absolute;right:0;transition:background .2s;width:20px;z-index:10}.journy-message-widget-resize-handle:hover{background:linear-gradient(135deg,transparent,transparent 40%,#d1d5db 0,#d1d5db)}.journy-message-widget-resize-handle svg{height:12px;opacity:.6;width:12px}.journy-message-widget-resize-handle:hover svg{opacity:1}.journy-message-widget-message{min-height:60px;padding:0;position:relative}.journy-message-widget-message-close{align-items:center;background:transparent;border:none;border-radius:4px;color:#6b7280;cursor:pointer;display:flex;font-size:18px;height:24px;justify-content:center;line-height:1;padding:0;position:absolute;right:4px;top:4px;transition:background-color .2s,color .2s;width:24px;z-index:1}.journy-message-widget-message-close:hover{background-color:#e5e7eb;color:#374151}.journy-message-widget-message:has(.journy-message-widget-message-close) .journy-message-content{padding-right:28px}.journy-message-widget-nav{align-items:center;background:none;border:none;border-radius:4px;color:#6b7280;cursor:pointer;display:flex;font-size:18px;height:28px;justify-content:center;line-height:1;padding:4px 6px;transition:background-color .2s,color .2s;width:28px}.journy-message-widget-nav:hover:not(:disabled){background-color:#e5e7eb;color:#374151}.journy-message-widget-nav:disabled{cursor:default;opacity:.4}.journy-message-widget-message-separated{border-bottom:1px solid #e5e7eb;margin-bottom:24px;padding-bottom:24px}.journy-message-widget-message-count{color:#6b7280;font-size:12px;font-weight:400;margin-left:8px}.journy-message-widget-position{color:#9ca3af;flex-shrink:0;font-size:11px;font-weight:500;padding:4px 12px;text-align:right}.journy-message-widget-message.journy-message-info{border-left:4px solid #3b82f6;padding-left:12px}.journy-message-widget-message.journy-message-success{border-left:4px solid #10b981;padding-left:12px}.journy-message-widget-message.journy-message-warning{border-left:4px solid #f59e0b;padding-left:12px}.journy-message-widget-message.journy-message-error{border-left:4px solid #ef4444;padding-left:12px}.journy-message-widget-message.journy-message-viewed{border-left:4px solid #6b7280;padding-left:12px}.journy-message-modal-overlay{align-items:center;background-color:rgba(0,0,0,.5);bottom:0;display:flex;justify-content:center;left:0;padding:20px;position:fixed;right:0;top:0;z-index:10002}.journy-message-modal{background:#fff;border-radius:12px;box-shadow:0 4px 24px rgba(0,0,0,.2);display:flex;flex-direction:column;height:60vh;max-height:60vh;max-width:80vw;overflow:hidden;position:relative;width:80vw}.journy-message-modal-header{align-items:center;background:#f9fafb;border-bottom:1px solid #e5e7eb;display:flex;flex-shrink:0;justify-content:space-between;padding:16px 24px}.journy-message-modal-title{color:#111827;font-size:18px;font-weight:600}.journy-message-modal-close{background:none;border:none;border-radius:4px;color:#6b7280;cursor:pointer;font-size:24px;line-height:1;padding:4px 8px;transition:background-color .2s,color .2s}.journy-message-modal-close:hover{background-color:#e5e7eb;color:#374151}.journy-message-modal .journy-message-widget-message{flex:1;min-height:0;overflow-y:auto;padding:24px}.journy-message-timestamp{color:#6b7280;font-size:12px;line-height:1.4;margin-top:4px}.journy-message-content-clickable{cursor:pointer}.journy-message-content-clickable:hover{opacity:.95}.journy-message-overlay{align-items:center;background-color:rgba(0,0,0,.5);bottom:0;display:flex;justify-content:center;left:0;opacity:0;pointer-events:none;position:fixed;right:0;top:0;transition:opacity .3s ease-in-out;z-index:10000}.journy-message-overlay.journy-message-visible{opacity:1;pointer-events:all}.journy-message-popup{background:#fff;border-radius:8px;box-shadow:0 4px 20px rgba(0,0,0,.15);max-height:80vh;max-width:500px;overflow-y:auto;padding:24px;position:relative;transform:scale(.9);transition:transform .3s ease-in-out;width:90%}.journy-message-overlay.journy-message-visible .journy-message-popup{transform:scale(1)}.journy-message-popup.journy-message-info{border-top:4px solid #3b82f6}.journy-message-popup.journy-message-success{border-top:4px solid #10b981}.journy-message-popup.journy-message-warning{border-top:4px solid #f59e0b}.journy-message-popup.journy-message-error{border-top:4px solid #ef4444}.journy-message-popup.journy-message-viewed{border-top:4px solid #6b7280}.journy-message-close{background:none;border:none;border-radius:4px;color:#6b7280;cursor:pointer;font-size:24px;line-height:1;padding:4px 8px;position:absolute;right:12px;top:12px;transition:background-color .2s,color .2s}.journy-message-close:hover{background-color:#f3f4f6;color:#374151}.journy-message-title{color:#111827;font-size:20px;font-weight:600;margin-bottom:12px}.journy-message-content{color:#374151;font-size:16px;line-height:1.6;margin-bottom:16px}.journy-message-content a{color:#3b82f6;text-decoration:underline;transition:color .2s}.journy-message-content a:hover{color:#2563eb}.journy-message-content p{margin:0 0 12px}.journy-message-content p:last-child{margin-bottom:0}.journy-message-content ol,.journy-message-content ul{margin:12px 0;padding-left:24px}.journy-message-content li{margin-bottom:8px}.journy-message-content h1,.journy-message-content h2,.journy-message-content h3,.journy-message-content h4,.journy-message-content h5,.journy-message-content h6{color:#111827;font-weight:600;line-height:1.3;margin:16px 0 12px}.journy-message-content h1{font-size:24px}.journy-message-content h2{font-size:20px}.journy-message-content h3{font-size:18px}.journy-message-content h4{font-size:16px}.journy-message-content h5,.journy-message-content h6{font-size:14px}.journy-message-content code{background-color:#f3f4f6;border-radius:4px;color:#ef4444;font-size:.9em;padding:2px 6px}.journy-message-content code,.journy-message-content pre{font-family:Monaco,Menlo,Ubuntu Mono,Consolas,source-code-pro,monospace}.journy-message-content pre{background-color:#1f2937;border-radius:8px;color:#f9fafb;font-size:14px;line-height:1.5;margin:12px 0;overflow-x:auto;padding:16px}.journy-message-content pre code{background-color:transparent;border-radius:0;color:inherit;font-size:inherit;padding:0}.journy-message-content u{text-decoration:underline}.journy-message-content del,.journy-message-content s,.journy-message-content strike{text-decoration:line-through}.journy-message-content strong{font-weight:600}.journy-message-content em{font-style:italic}.journy-message-content blockquote{border-left:4px solid #e5e7eb;color:#6b7280;font-style:italic;margin:12px 0;padding-left:16px}.journy-message-content table{border-collapse:collapse;margin:12px 0;width:100%}.journy-message-content td,.journy-message-content th{border:1px solid #e5e7eb;padding:8px 12px;text-align:left}.journy-message-content th{background-color:#f9fafb;font-weight:600}.journy-message-content hr{border:none;border-top:1px solid #e5e7eb;margin:16px 0}.journy-message-actions{display:flex;flex-wrap:wrap;gap:12px;margin-top:20px}.journy-message-action{border:none;border-radius:6px;cursor:pointer;font-size:14px;font-weight:500;padding:10px 20px;transition:all .2s}.journy-message-action-primary{background-color:#3b82f6;color:#fff}.journy-message-action-primary:hover{background-color:#2563eb}.journy-message-action-secondary{background-color:#e5e7eb;color:#374151}.journy-message-action-secondary:hover{background-color:#d1d5db}.journy-message-action-link{background:none;color:#3b82f6;padding:10px 0;text-decoration:underline}.journy-message-action-link:hover{color:#2563eb}.journy-settings-panel{background:#fff;bottom:0;display:flex;flex-direction:column;position:absolute;right:0;top:0;transform:translateX(100%);transition:transform .3s ease;width:100%;z-index:10}.journy-settings-panel-open{transform:translateX(0)}.journy-settings-panel-closed{transform:translateX(100%)}.journy-settings-header{align-items:center;background:#f9fafb;border-bottom:1px solid #e5e7eb;display:flex;flex-shrink:0;justify-content:space-between;padding:12px 16px}.journy-settings-title{color:#111827;font-size:14px;font-weight:600}.journy-settings-close{align-items:center;background:none;border:none;border-radius:4px;color:#6b7280;cursor:pointer;display:flex;font-size:18px;height:28px;justify-content:center;line-height:1;padding:4px 8px;transition:background-color .2s,color .2s;width:28px}.journy-settings-close:hover{background-color:#e5e7eb;color:#374151}.journy-settings-body{flex:1;overflow-y:auto;padding:16px}.journy-settings-item{align-items:center;border-bottom:1px solid #f3f4f6;display:flex;justify-content:space-between;padding:12px 0}.journy-settings-item:last-child{border-bottom:none}.journy-settings-label{color:#374151;font-size:13px;font-weight:500}.journy-settings-select{background:#fff;border:1px solid #d1d5db;border-radius:6px;color:#374151;cursor:pointer;font-size:13px;outline:none;padding:6px 10px;transition:border-color .2s}.journy-settings-select:focus{border-color:#3b82f6}.journy-settings-toggle{background:#d1d5db;border:none;border-radius:11px;cursor:pointer;flex-shrink:0;height:22px;padding:0;position:relative;transition:background-color .2s;width:40px}.journy-settings-toggle-on{background:#3b82f6}.journy-settings-toggle-knob{background:#fff;border-radius:50%;box-shadow:0 1px 3px rgba(0,0,0,.15);height:18px;left:2px;position:absolute;top:2px;transition:transform .2s;width:18px}.journy-settings-toggle-on .journy-settings-toggle-knob{transform:translateX(18px)}.journy-settings-item-vertical{align-items:flex-start;flex-direction:column;gap:6px}.journy-settings-input{background:#fff;border:1px solid #d1d5db;border-radius:6px;box-sizing:border-box;color:#374151;font-size:13px;outline:none;padding:6px 10px;transition:border-color .2s;width:100%}.journy-settings-input:focus{border-color:#3b82f6}.journy-settings-input::placeholder{color:#9ca3af}.journy-settings-value{color:#6b7280;font-size:13px;font-weight:400;max-width:60%;text-align:right;word-break:break-all}.journy-message-widget-empty{align-items:center;color:#9ca3af;display:flex;flex:1;font-size:14px;justify-content:center;min-height:80px}.journy-settings-advanced-btn{background:none;border:none;color:#6b7280;cursor:pointer;font-size:13px;font-weight:500;padding:0;transition:color .2s}.journy-settings-advanced-btn:hover{color:#374151}.journy-message-widget-settings-btn{align-items:center;background:none;border:none;border-radius:4px;color:#6b7280;cursor:pointer;display:flex;font-size:16px;height:28px;justify-content:center;line-height:1;padding:4px 8px;transition:background-color .2s,color .2s;width:28px}.journy-message-widget-settings-btn:hover{background-color:#e5e7eb;color:#374151}@media (max-width:640px){.journy-message-widget{left:16px!important;max-width:calc(100vw - 32px);min-width:calc(100vw - 32px);right:16px!important}.journy-message-popup{padding:20px;width:95%}.journy-message-title{font-size:18px}.journy-message-content{font-size:14px}.journy-message-actions{flex-direction:column}.journy-message-action{width:100%}}