@cloff/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.
@@ -0,0 +1,18 @@
1
+ export declare class ClofWidget extends HTMLElement {
2
+ private shadow;
3
+ private apiKey;
4
+ private days;
5
+ private theme;
6
+ private accentColor;
7
+ constructor();
8
+ static get observedAttributes(): string[];
9
+ attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
10
+ connectedCallback(): void;
11
+ private fetchMetrics;
12
+ private getStyles;
13
+ private renderError;
14
+ private renderLoading;
15
+ private renderWidget;
16
+ private render;
17
+ }
18
+ export { ClofWidget as SocioDauWidget };
package/dist/widget.js ADDED
@@ -0,0 +1,623 @@
1
+ // Default database connection credentials
2
+ const DEFAULT_SUPABASE_URL = 'https://edhkrzyiiznotsmblbrt.supabase.co';
3
+ const DEFAULT_SUPABASE_ANON_KEY = 'sb_publishable_EzjTe8cJu-8gZmdccQr4aA_iZY8WIxL';
4
+ export class ClofWidget extends HTMLElement {
5
+ constructor() {
6
+ super();
7
+ this.apiKey = null;
8
+ this.days = 30;
9
+ this.theme = 'dark';
10
+ this.accentColor = '#8b5cf6'; // Indigo/violet default
11
+ this.shadow = this.attachShadow({ mode: 'open' });
12
+ }
13
+ static get observedAttributes() {
14
+ return ['api-key', 'days', 'theme', 'accent-color'];
15
+ }
16
+ attributeChangedCallback(name, oldValue, newValue) {
17
+ if (oldValue === newValue)
18
+ return;
19
+ if (name === 'api-key')
20
+ this.apiKey = newValue;
21
+ if (name === 'days')
22
+ this.days = parseInt(newValue, 10) || 30;
23
+ if (name === 'theme')
24
+ this.theme = newValue === 'light' ? 'light' : 'dark';
25
+ if (name === 'accent-color')
26
+ this.accentColor = newValue || '#8b5cf6';
27
+ if (this.isConnected && this.apiKey) {
28
+ this.render();
29
+ }
30
+ }
31
+ connectedCallback() {
32
+ this.apiKey = this.getAttribute('api-key');
33
+ this.days = parseInt(this.getAttribute('days') || '30', 10);
34
+ this.accentColor = this.getAttribute('accent-color') || '#8b5cf6';
35
+ const themeAttr = this.getAttribute('theme');
36
+ if (themeAttr === 'light' || themeAttr === 'dark') {
37
+ this.theme = themeAttr;
38
+ }
39
+ else {
40
+ // Auto-detect dark mode from page classes or system preferences
41
+ const hasDarkClass = document.documentElement.classList.contains('dark') || document.body.classList.contains('dark');
42
+ const hasLightClass = document.documentElement.classList.contains('light') || document.body.classList.contains('light');
43
+ if (hasDarkClass) {
44
+ this.theme = 'dark';
45
+ }
46
+ else if (hasLightClass) {
47
+ this.theme = 'light';
48
+ }
49
+ else {
50
+ this.theme = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
51
+ }
52
+ }
53
+ this.render();
54
+ }
55
+ async fetchMetrics() {
56
+ if (!this.apiKey) {
57
+ throw new Error('API Key is missing');
58
+ }
59
+ // Intercept Demo/Mock keys for local sandboxes or developer previews
60
+ if (this.apiKey.startsWith('socio_dau_live_demo_key_')) {
61
+ const seed = this.apiKey.includes('socionetwork') ? 1.5 : 0.8;
62
+ const metrics = [];
63
+ const today = new Date();
64
+ for (let i = this.days - 1; i >= 0; i--) {
65
+ const date = new Date(today);
66
+ date.setDate(today.getDate() - i);
67
+ const dateStr = date.toISOString().split('T')[0];
68
+ const base = 50 + (this.days - 1 - i) * 8 * seed;
69
+ const dayOfWeek = date.getDay();
70
+ const weekendDip = (dayOfWeek === 0 || dayOfWeek === 6) ? 0.7 : 1.0;
71
+ const randomNoise = 0.9 + Math.random() * 0.2;
72
+ metrics.push({
73
+ activity_date: dateStr,
74
+ active_users: Math.round(base * weekendDip * randomNoise)
75
+ });
76
+ }
77
+ const devices = [
78
+ { device_type: 'Mobile', count: Math.round(1482 * seed) },
79
+ { device_type: 'Desktop', count: Math.round(853 * seed) },
80
+ { device_type: 'Tablet', count: Math.round(124 * seed) }
81
+ ];
82
+ const locations = [
83
+ { location: 'America/New_York', count: Math.round(980 * seed) },
84
+ { location: 'Europe/London', count: Math.round(642 * seed) },
85
+ { location: 'Asia/Tokyo', count: Math.round(412 * seed) },
86
+ { location: 'Europe/Paris', count: Math.round(280 * seed) },
87
+ { location: 'Australia/Sydney', count: Math.round(144 * seed) }
88
+ ];
89
+ // Simulate a small network delay
90
+ await new Promise(resolve => setTimeout(resolve, 600));
91
+ return {
92
+ ok: true,
93
+ metrics,
94
+ devices,
95
+ locations
96
+ };
97
+ }
98
+ const response = await fetch(`${DEFAULT_SUPABASE_URL}/rest/v1/rpc/get_public_dau_metrics`, {
99
+ method: 'POST',
100
+ headers: {
101
+ 'apikey': DEFAULT_SUPABASE_ANON_KEY,
102
+ 'Authorization': `Bearer ${DEFAULT_SUPABASE_ANON_KEY}`,
103
+ 'Content-Type': 'application/json',
104
+ },
105
+ body: JSON.stringify({
106
+ p_api_key: this.apiKey,
107
+ p_days: this.days
108
+ })
109
+ });
110
+ if (!response.ok) {
111
+ const errText = await response.text();
112
+ throw new Error(errText || 'Failed to fetch public metrics');
113
+ }
114
+ const data = await response.json();
115
+ if (data && data.ok === false) {
116
+ throw new Error(data.error || 'Server rejected metrics request');
117
+ }
118
+ return data;
119
+ }
120
+ getStyles() {
121
+ const isDark = this.theme === 'dark';
122
+ const bg = isDark ? '#0f172a' : '#ffffff';
123
+ const text = isDark ? '#f8fafc' : '#0f172a';
124
+ const border = isDark ? '#1e293b' : '#e2e8f0';
125
+ const subtext = isDark ? '#94a3b8' : '#64748b';
126
+ const cardBg = isDark ? '#1e293b' : '#f8fafc';
127
+ const gridLine = isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.04)';
128
+ return `
129
+ :host {
130
+ display: block;
131
+ width: 100%;
132
+ max-width: 600px;
133
+ font-family: 'Outfit', 'Inter', system-ui, -apple-system, sans-serif;
134
+ box-sizing: border-box;
135
+ }
136
+ .widget-container {
137
+ background: ${bg};
138
+ color: ${text};
139
+ border: 1px solid ${border};
140
+ border-radius: 16px;
141
+ padding: 20px;
142
+ box-shadow: 0 4px 20px -2px rgba(0, 0, 0, 0.08);
143
+ display: flex;
144
+ flex-direction: column;
145
+ gap: 20px;
146
+ overflow: hidden;
147
+ transition: all 0.3s ease;
148
+ }
149
+ .header {
150
+ display: flex;
151
+ justify-content: space-between;
152
+ align-items: flex-start;
153
+ border-bottom: 1px solid ${border};
154
+ padding-bottom: 15px;
155
+ }
156
+ .title-section {
157
+ display: flex;
158
+ flex-direction: column;
159
+ gap: 4px;
160
+ }
161
+ .title {
162
+ font-size: 11px;
163
+ text-transform: uppercase;
164
+ font-weight: 700;
165
+ letter-spacing: 1.5px;
166
+ color: ${subtext};
167
+ }
168
+ .dau-count-container {
169
+ display: flex;
170
+ align-items: baseline;
171
+ gap: 8px;
172
+ }
173
+ .dau-count {
174
+ font-size: 32px;
175
+ font-weight: 800;
176
+ letter-spacing: -1px;
177
+ background: linear-gradient(to right, ${this.accentColor}, #6366f1);
178
+ -webkit-background-clip: text;
179
+ -webkit-text-fill-color: transparent;
180
+ }
181
+ .dau-label {
182
+ font-size: 12px;
183
+ color: ${subtext};
184
+ font-weight: 600;
185
+ }
186
+ .badge {
187
+ display: flex;
188
+ align-items: center;
189
+ gap: 4px;
190
+ background: ${cardBg};
191
+ border: 1px solid ${border};
192
+ padding: 6px 12px;
193
+ border-radius: 10px;
194
+ font-size: 11px;
195
+ font-weight: 700;
196
+ color: ${this.accentColor};
197
+ }
198
+ .badge-dot {
199
+ width: 6px;
200
+ height: 6px;
201
+ background: ${this.accentColor};
202
+ border-radius: 50%;
203
+ animation: pulse 2s infinite;
204
+ }
205
+ .chart-container {
206
+ position: relative;
207
+ width: 100%;
208
+ height: 180px;
209
+ }
210
+ svg {
211
+ width: 100%;
212
+ height: 100%;
213
+ overflow: visible;
214
+ }
215
+ .grid-line {
216
+ stroke: ${gridLine};
217
+ stroke-dasharray: 4;
218
+ }
219
+ .chart-line {
220
+ stroke: ${this.accentColor};
221
+ stroke-width: 3;
222
+ stroke-linecap: round;
223
+ stroke-linejoin: round;
224
+ fill: none;
225
+ }
226
+ .chart-area {
227
+ fill: url(#areaGradient);
228
+ }
229
+ .chart-dot {
230
+ fill: ${this.accentColor};
231
+ stroke: ${bg};
232
+ stroke-width: 2;
233
+ }
234
+ .stats-grid {
235
+ display: grid;
236
+ grid-template-columns: 1fr 1fr;
237
+ gap: 16px;
238
+ }
239
+ @media (max-width: 480px) {
240
+ .stats-grid {
241
+ grid-template-columns: 1fr;
242
+ }
243
+ }
244
+ .card {
245
+ background: ${cardBg};
246
+ border: 1px solid ${border};
247
+ border-radius: 12px;
248
+ padding: 14px;
249
+ display: flex;
250
+ flex-direction: column;
251
+ gap: 12px;
252
+ }
253
+ .card-title {
254
+ font-size: 12px;
255
+ font-weight: 700;
256
+ color: ${subtext};
257
+ text-transform: uppercase;
258
+ letter-spacing: 0.5px;
259
+ }
260
+ .device-bar {
261
+ display: flex;
262
+ height: 8px;
263
+ border-radius: 4px;
264
+ overflow: hidden;
265
+ background: ${border};
266
+ }
267
+ .device-segment {
268
+ height: 100%;
269
+ transition: width 0.3s;
270
+ }
271
+ .legend-list {
272
+ display: flex;
273
+ flex-direction: column;
274
+ gap: 6px;
275
+ }
276
+ .legend-item {
277
+ display: flex;
278
+ justify-content: space-between;
279
+ font-size: 11px;
280
+ font-weight: 600;
281
+ }
282
+ .legend-label {
283
+ display: flex;
284
+ align-items: center;
285
+ gap: 6px;
286
+ color: ${subtext};
287
+ }
288
+ .legend-dot {
289
+ width: 8px;
290
+ height: 8px;
291
+ border-radius: 50%;
292
+ }
293
+ .location-list {
294
+ display: flex;
295
+ flex-direction: column;
296
+ gap: 8px;
297
+ }
298
+ .location-row {
299
+ display: flex;
300
+ flex-direction: column;
301
+ gap: 4px;
302
+ }
303
+ .location-meta {
304
+ display: flex;
305
+ justify-content: space-between;
306
+ font-size: 11px;
307
+ font-weight: 600;
308
+ }
309
+ .location-name {
310
+ color: ${text};
311
+ white-space: nowrap;
312
+ overflow: hidden;
313
+ text-overflow: ellipsis;
314
+ max-width: 140px;
315
+ }
316
+ .location-bar-bg {
317
+ height: 4px;
318
+ background: ${border};
319
+ border-radius: 2px;
320
+ overflow: hidden;
321
+ }
322
+ .location-bar-fill {
323
+ height: 100%;
324
+ background: ${this.accentColor};
325
+ border-radius: 2px;
326
+ }
327
+ .footer {
328
+ display: flex;
329
+ justify-content: space-between;
330
+ font-size: 10px;
331
+ color: ${subtext};
332
+ border-top: 1px solid ${border};
333
+ padding-top: 12px;
334
+ margin-top: 5px;
335
+ }
336
+ .footer a {
337
+ color: ${this.accentColor};
338
+ text-decoration: none;
339
+ font-weight: 700;
340
+ }
341
+ .footer-badge {
342
+ display: flex;
343
+ align-items: center;
344
+ gap: 4px;
345
+ font-weight: 600;
346
+ }
347
+ .check-icon {
348
+ color: #10b981;
349
+ }
350
+ .loader-container {
351
+ display: flex;
352
+ flex-direction: column;
353
+ align-items: center;
354
+ justify-content: center;
355
+ height: 300px;
356
+ gap: 12px;
357
+ }
358
+ .spinner {
359
+ width: 32px;
360
+ height: 32px;
361
+ border: 3px solid ${border};
362
+ border-top-color: ${this.accentColor};
363
+ border-radius: 50%;
364
+ animation: spin 1s linear infinite;
365
+ }
366
+ .loader-text {
367
+ font-size: 13px;
368
+ color: ${subtext};
369
+ font-weight: 600;
370
+ }
371
+ .error-container {
372
+ display: flex;
373
+ flex-direction: column;
374
+ align-items: center;
375
+ justify-content: center;
376
+ height: 250px;
377
+ color: #ef4444;
378
+ text-align: center;
379
+ padding: 20px;
380
+ gap: 8px;
381
+ }
382
+ .error-title {
383
+ font-weight: 700;
384
+ font-size: 16px;
385
+ }
386
+ .error-desc {
387
+ font-size: 12px;
388
+ color: ${subtext};
389
+ }
390
+ @keyframes pulse {
391
+ 0%, 100% { opacity: 1; transform: scale(1); }
392
+ 50% { opacity: 0.4; transform: scale(1.3); }
393
+ }
394
+ @keyframes spin {
395
+ to { transform: rotate(360deg); }
396
+ }
397
+ `;
398
+ }
399
+ renderError(message) {
400
+ const isDark = this.theme === 'dark';
401
+ const bg = isDark ? '#0f172a' : '#ffffff';
402
+ const border = isDark ? '#1e293b' : '#e2e8f0';
403
+ const subtext = isDark ? '#94a3b8' : '#64748b';
404
+ this.shadow.innerHTML = `
405
+ <style>
406
+ ${this.getStyles()}
407
+ </style>
408
+ <div class="widget-container" style="background: ${bg}; border-color: ${border};">
409
+ <div class="error-container">
410
+ <div class="error-title">Unable to Load Metrics</div>
411
+ <div class="error-desc">${message}</div>
412
+ </div>
413
+ </div>
414
+ `;
415
+ }
416
+ renderLoading() {
417
+ const isDark = this.theme === 'dark';
418
+ const bg = isDark ? '#0f172a' : '#ffffff';
419
+ const border = isDark ? '#1e293b' : '#e2e8f0';
420
+ this.shadow.innerHTML = `
421
+ <style>
422
+ ${this.getStyles()}
423
+ </style>
424
+ <div class="widget-container" style="background: ${bg}; border-color: ${border};">
425
+ <div class="loader-container">
426
+ <div class="spinner"></div>
427
+ <div class="loader-text">Fetching verified metrics...</div>
428
+ </div>
429
+ </div>
430
+ `;
431
+ }
432
+ renderWidget(data) {
433
+ const metrics = data.metrics || [];
434
+ const devices = data.devices || [];
435
+ const locations = data.locations || [];
436
+ // Chronological order for chart
437
+ const sortedMetrics = [...metrics].sort((a, b) => new Date(a.activity_date).getTime() - new Date(b.activity_date).getTime());
438
+ const latestDau = sortedMetrics.length > 0 ? sortedMetrics[sortedMetrics.length - 1].active_users : 0;
439
+ // SVG coordinates setup
440
+ const width = 500;
441
+ const height = 150;
442
+ const paddingLeft = 30;
443
+ const paddingRight = 10;
444
+ const paddingTop = 15;
445
+ const paddingBottom = 15;
446
+ const chartWidth = width - paddingLeft - paddingRight;
447
+ const chartHeight = height - paddingTop - paddingBottom;
448
+ const maxVal = Math.max(...sortedMetrics.map(m => m.active_users), 0);
449
+ const scaleMax = maxVal === 0 ? 10 : Math.ceil(maxVal * 1.25);
450
+ const coordPoints = sortedMetrics.map((p, i) => {
451
+ const x = paddingLeft + (i / Math.max(sortedMetrics.length - 1, 1)) * chartWidth;
452
+ const y = paddingTop + chartHeight - (p.active_users / scaleMax) * chartHeight;
453
+ return { x, y };
454
+ });
455
+ // Generate path strings
456
+ let linePath = '';
457
+ let areaPath = '';
458
+ if (coordPoints.length > 0) {
459
+ linePath = coordPoints.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' ');
460
+ const lastX = coordPoints[coordPoints.length - 1].x;
461
+ const firstX = coordPoints[0].x;
462
+ const bottomY = paddingTop + chartHeight;
463
+ areaPath = `${linePath} L ${lastX} ${bottomY} L ${firstX} ${bottomY} Z`;
464
+ }
465
+ // Grid lines calculation
466
+ const gridLinesY = [0.25, 0.5, 0.75, 1.0].map(pct => {
467
+ return paddingTop + chartHeight - pct * chartHeight;
468
+ });
469
+ // Devices Calculations
470
+ const totalDevices = devices.reduce((sum, item) => sum + Number(item.count), 0);
471
+ const deviceColors = ['#8b5cf6', '#6366f1', '#10b981', '#f59e0b'];
472
+ // Locations Calculations
473
+ const totalLocations = locations.reduce((sum, item) => sum + Number(item.count), 0);
474
+ const isDark = this.theme === 'dark';
475
+ const subtext = isDark ? '#94a3b8' : '#64748b';
476
+ this.shadow.innerHTML = `
477
+ <style>
478
+ ${this.getStyles()}
479
+ </style>
480
+ <div class="widget-container">
481
+ <div class="header">
482
+ <div class="title-section">
483
+ <div class="title">Active Users History</div>
484
+ <div class="dau-count-container">
485
+ <span class="dau-count">${latestDau.toLocaleString()}</span>
486
+ <span class="dau-label">DAU today</span>
487
+ </div>
488
+ </div>
489
+ <div class="badge">
490
+ <div class="badge-dot"></div>
491
+ <span>Verified Metrics</span>
492
+ </div>
493
+ </div>
494
+
495
+ <!-- SVG Interactive Area Chart -->
496
+ <div class="chart-container">
497
+ <svg viewBox="0 0 ${width} ${height}" preserveAspectRatio="none">
498
+ <defs>
499
+ <linearGradient id="areaGradient" x1="0" y1="0" x2="0" y2="1">
500
+ <stop offset="0%" stop-color="${this.accentColor}" stop-opacity="0.3"/>
501
+ <stop offset="100%" stop-color="${this.accentColor}" stop-opacity="0.0"/>
502
+ </linearGradient>
503
+ </defs>
504
+
505
+ <!-- Grid Lines -->
506
+ ${gridLinesY.map((y) => `
507
+ <line class="grid-line" x1="${paddingLeft}" y1="${y}" x2="${width - paddingRight}" y2="${y}" />
508
+ `).join('')}
509
+
510
+ <!-- Y Axis bounds text -->
511
+ <text x="${paddingLeft - 5}" y="${paddingTop + 4}" text-anchor="end" font-size="8" fill="${subtext}" font-weight="600">${Math.round(scaleMax)}</text>
512
+ <text x="${paddingLeft - 5}" y="${paddingTop + chartHeight + 4}" text-anchor="end" font-size="8" fill="${subtext}" font-weight="600">0</text>
513
+
514
+ ${coordPoints.length > 0 ? `
515
+ <!-- Area Path -->
516
+ <path class="chart-area" d="${areaPath}" />
517
+ <!-- Line Path -->
518
+ <path class="chart-line" d="${linePath}" />
519
+ <!-- Endpoint Dot -->
520
+ <circle class="chart-dot" cx="${coordPoints[coordPoints.length - 1].x}" cy="${coordPoints[coordPoints.length - 1].y}" r="5" />
521
+ ` : ''}
522
+ </svg>
523
+ </div>
524
+
525
+ <!-- Breakdown Grid -->
526
+ <div class="stats-grid">
527
+
528
+ <!-- Devices Breakdown -->
529
+ <div class="card">
530
+ <div class="card-title">Environments</div>
531
+ ${totalDevices === 0 ? `
532
+ <div style="font-size: 11px; color: ${subtext}; text-align: center; margin: auto;">No device data</div>
533
+ ` : `
534
+ <div class="device-bar">
535
+ ${devices.map((dev, idx) => {
536
+ const pct = (dev.count / totalDevices) * 100;
537
+ return `<div class="device-segment" style="width: ${pct}%; background: ${deviceColors[idx % deviceColors.length]}"></div>`;
538
+ }).join('')}
539
+ </div>
540
+ <div class="legend-list">
541
+ ${devices.map((dev, idx) => {
542
+ const pct = Math.round((dev.count / totalDevices) * 100);
543
+ return `
544
+ <div class="legend-item">
545
+ <div class="legend-label">
546
+ <div class="legend-dot" style="background: ${deviceColors[idx % deviceColors.length]}"></div>
547
+ <span>${dev.device_type}</span>
548
+ </div>
549
+ <div>${pct}%</div>
550
+ </div>
551
+ `;
552
+ }).join('')}
553
+ </div>
554
+ `}
555
+ </div>
556
+
557
+ <!-- Locations Breakdown -->
558
+ <div class="card">
559
+ <div class="card-title">Top Locations</div>
560
+ ${totalLocations === 0 ? `
561
+ <div style="font-size: 11px; color: ${subtext}; text-align: center; margin: auto;">No location data</div>
562
+ ` : `
563
+ <div class="location-list">
564
+ ${locations.slice(0, 3).map((loc) => {
565
+ const pct = Math.round((loc.count / totalLocations) * 100);
566
+ const locVal = loc.location || loc.country || 'Unknown';
567
+ const shortName = locVal.split('/').pop()?.replace('_', ' ') || locVal;
568
+ return `
569
+ <div class="location-row">
570
+ <div class="location-meta">
571
+ <span class="location-name">${shortName}</span>
572
+ <span>${pct}%</span>
573
+ </div>
574
+ <div class="location-bar-bg">
575
+ <div class="location-bar-fill" style="width: ${pct}%; background: ${this.accentColor}"></div>
576
+ </div>
577
+ </div>
578
+ `;
579
+ }).join('')}
580
+ </div>
581
+ `}
582
+ </div>
583
+
584
+ </div>
585
+
586
+ <!-- Verified Footer -->
587
+ <div class="footer">
588
+ <div class="footer-badge">
589
+ <span class="check-icon">✓</span> Verified by <strong>Analytics</strong>
590
+ </div>
591
+ <div>
592
+ Powered by <a href="https://socio.kim" target="_blank" rel="noopener">Socio.kim</a>
593
+ </div>
594
+ </div>
595
+ </div>
596
+ `;
597
+ }
598
+ async render() {
599
+ if (!this.apiKey) {
600
+ this.renderError('API key attribute "api-key" is required.');
601
+ return;
602
+ }
603
+ this.renderLoading();
604
+ try {
605
+ const data = await this.fetchMetrics();
606
+ this.renderWidget(data);
607
+ }
608
+ catch (error) {
609
+ console.error('ClofWidget error:', error);
610
+ this.renderError(error.message || 'Network error fetching data.');
611
+ }
612
+ }
613
+ }
614
+ // Register the custom elements globally if in browser environment
615
+ if (typeof window !== 'undefined') {
616
+ if (!window.customElements.get('clof-widget')) {
617
+ window.customElements.define('clof-widget', ClofWidget);
618
+ }
619
+ if (!window.customElements.get('socio-dau-widget')) {
620
+ window.customElements.define('socio-dau-widget', ClofWidget);
621
+ }
622
+ }
623
+ export { ClofWidget as SocioDauWidget };
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@cloff/sdk",
3
+ "version": "1.0.0",
4
+ "description": "Real-time analytics and Session Replay SDK for Clof",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ },
12
+ "./widget": {
13
+ "import": "./dist/widget.js",
14
+ "types": "./dist/widget.d.ts"
15
+ }
16
+ },
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "dependencies": {
22
+ "@supabase/supabase-js": "^2.39.0"
23
+ },
24
+ "devDependencies": {
25
+ "typescript": "^5.0.0"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ }
30
+ }