core-outline 1.1.24 → 1.1.25

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 CHANGED
@@ -12,28 +12,6 @@ npm install core-outline
12
12
  yarn add core-outline
13
13
  ```
14
14
 
15
- ## Prerequisites
16
-
17
- Before adding the component to your app, create an SDK data source through the Hermes API to obtain your credentials. This requires your Firebase authentication token.
18
-
19
- ```sh
20
- curl -X POST https://atlas-orchestrator.atlas.coreoutline.com/api/ingest/sdk-sources \
21
- -H "Authorization: Bearer <your-firebase-token>" \
22
- -H "Content-Type: application/json" \
23
- -d '{"name": "My Website"}'
24
- ```
25
-
26
- The response contains your `data_source_id`, `data_source_secret`, and `warehouse_id`. **The secret is shown once — store it securely.**
27
-
28
- ```json
29
- {
30
- "data_source_id": "3f4a1c2d-...",
31
- "data_source_secret": "aB3xK9mZqR...",
32
- "warehouse_id": "cad1acc0-...",
33
- "name": "My Website"
34
- }
35
- ```
36
-
37
15
  ## Usage
38
16
 
39
17
  Wrap your app's root component with `<CoreOutline>`. All tracking is automatic from that point — no additional configuration is needed.
package/dist/index.es.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import React, { useState, useRef, useEffect } from 'react';
2
3
  import { v4 } from 'uuid';
3
4
 
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  'use strict';
2
3
 
3
4
  Object.defineProperty(exports, '__esModule', { value: true });
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "core-outline",
3
- "version": "1.1.24",
3
+ "version": "1.1.25",
4
4
  "description": "A React component for Core&Outline",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.es.js",
7
+ "files": [
8
+ "dist"
9
+ ],
7
10
  "scripts": {
8
11
  "test": "echo \"Error: no test specified\" && exit 1",
9
12
  "storybook": "storybook dev -p 6006",
package/.babelrc DELETED
@@ -1,3 +0,0 @@
1
- {
2
- "presets": ["@babel/preset-env", "@babel/preset-react"]
3
- }
package/.eslintignore DELETED
@@ -1,2 +0,0 @@
1
- node_modules
2
- dist
package/.eslintrc.json DELETED
@@ -1,23 +0,0 @@
1
- {
2
- "env": {
3
- "browser": true,
4
- "es2021": true
5
- },
6
- "extends": ["eslint:recommended", "plugin:react/recommended", "prettier"],
7
- "parserOptions": {
8
- "ecmaFeatures": {
9
- "jsx": true
10
- },
11
- "ecmaVersion": 12,
12
- "sourceType": "module"
13
- },
14
- "plugins": ["react"],
15
- "rules": {
16
- "react/react-in-jsx-scope": "off"
17
- },
18
- "settings": {
19
- "react": {
20
- "version": "detect"
21
- }
22
- }
23
- }
package/.prettierignore DELETED
@@ -1,2 +0,0 @@
1
- node_modules
2
- dist
package/.prettierrc DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "singleQuote": true,
3
- "trailingComma": "es5",
4
- "printWidth": 80,
5
- "tabWidth": 2,
6
- "semi": true
7
- }
@@ -1,16 +0,0 @@
1
- /** @type { import('@storybook/react-webpack5').StorybookConfig } */
2
- const config = {
3
- stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
4
- addons: [
5
- '@storybook/addon-webpack5-compiler-swc',
6
- '@storybook/addon-onboarding',
7
- '@storybook/addon-essentials',
8
- '@chromatic-com/storybook',
9
- '@storybook/addon-interactions',
10
- ],
11
- framework: {
12
- name: '@storybook/react-webpack5',
13
- options: {},
14
- },
15
- };
16
- export default config;
@@ -1,13 +0,0 @@
1
- /** @type { import('@storybook/react').Preview } */
2
- const preview = {
3
- parameters: {
4
- controls: {
5
- matchers: {
6
- color: /(background|color)$/i,
7
- date: /Date$/i,
8
- },
9
- },
10
- },
11
- };
12
-
13
- export default preview;
@@ -1,44 +0,0 @@
1
- # Tracking Pipeline — react-component Changes
2
- Date: 2026-06-27
3
-
4
- ## Summary
5
- Replaced Socket.IO with direct HTTP calls to the Hermes ingest API. Added warehouse_id-based routing, improved event schema, and removed dev artifacts.
6
-
7
- ## Breaking Changes
8
- - `<CoreOutline>` now requires a `warehouse_id` prop (in addition to existing `data_source_id` + `data_source_secret`)
9
- - The `data_source_secret` is now validated against Hermes's Vault store; the old `api.coreoutline.com/data-source/authorize` endpoint is no longer used
10
-
11
- ## New Files
12
- - `src/components/CoreOutline/helpers.js` — browser utilities: `getAnonymousId`, `getSessionId`, `getUtmParams`, `detectDeviceType`, `detectOS`, `getBrowserName`, `getPagePath`
13
- - `src/components/CoreOutline/ingest.js` — HTTP ingest client: `initIngest`, `trackEvent`, `flush`, `sendRrwebBatch`, `stopFlushInterval`
14
-
15
- ## Modified Files
16
-
17
- ### `src/components/CoreOutline/CoreOutline.js`
18
- - Added `warehouse_id` prop
19
- - Replaced all `socket.send()` calls with `trackEvent()` from `ingest.js`
20
- - Events now include: UTM params, device type, OS, page_url, page_path, anonymous_id (persistent cross-session), session_id (per-tab via sessionStorage)
21
- - Removed `getLocation()` / geolocation (replaced by server-side IP geolocation)
22
- - Removed dev-only `<button>Save Events Locally</button>` from rendered output
23
- - `flag_item_clicked(item_id)` and `flag_item_purchased(item_id, price)` rewritten to use `trackEvent()`; `flag_item_purchased` now accepts an optional `price` parameter mapped to `value_num`
24
- - Events auto-flush every 5 seconds and on page unload/visibility change
25
- - rrweb recordings uploaded via `sendRrwebBatch` at session end
26
-
27
- ### `package.json`
28
- - Removed: `socket.io-client` (~100KB bundle savings)
29
- - Added: `uuid` as explicit dependency (was transitive via rrweb)
30
-
31
- ## Event Types Emitted
32
- | event_type | event_category | Destination table |
33
- |---|---|---|
34
- | `session_start` | `session` | `fact_session` |
35
- | `session_end` | `session` | `fact_session` (update via ReplacingMergeTree) |
36
- | `pageview` | `navigation` | `fact_pageview` + `fact_event` |
37
- | `item_clicked` | `interaction` | `fact_event` |
38
- | `product_clicked` | `commerce` | `fact_event` |
39
- | `product_purchased` | `commerce` | `fact_event` (value_num = price) |
40
-
41
- ## Data Flow
42
- react-component → `POST https://api.coreoutline.com/api/ingest/authorize` (get JWT)
43
- → `POST https://api.coreoutline.com/api/ingest/events` (batch, every 5s + on unload)
44
- → `POST https://api.coreoutline.com/api/ingest/rrweb` (session end)
package/rollup.config.js DELETED
@@ -1,35 +0,0 @@
1
- import babel from 'rollup-plugin-babel';
2
- import resolve from '@rollup/plugin-node-resolve';
3
- import external from 'rollup-plugin-peer-deps-external';
4
- import { terser } from 'rollup-plugin-terser';
5
- import postcss from 'rollup-plugin-postcss';
6
-
7
- export default [
8
- {
9
- input: './src/components/CoreOutline/CoreOutline.js',
10
- output: [
11
- {
12
- file: 'dist/index.js',
13
- format: 'cjs',
14
- },
15
- {
16
- file: 'dist/index.es.js',
17
- format: 'es',
18
- exports: 'named',
19
- },
20
- ],
21
- plugins: [
22
- postcss({
23
- plugins: [],
24
- minimize: true,
25
- }),
26
- babel({
27
- exclude: 'node_modules/**',
28
- presets: ['@babel/preset-react'],
29
- }),
30
- external(),
31
- resolve(),
32
- // terser(),
33
- ],
34
- },
35
- ];
@@ -1,223 +0,0 @@
1
- import React, { useEffect, useRef, useState } from 'react';
2
- import { record } from 'rrweb';
3
- import {
4
- EventType,
5
- IncrementalSource,
6
- MouseInteractions,
7
- } from 'rrweb';
8
- import {
9
- getAnonymousId,
10
- getSessionId,
11
- getUtmParams,
12
- detectDeviceType,
13
- detectOS,
14
- getBrowserName,
15
- getPagePath,
16
- } from './helpers';
17
- import {
18
- initIngest,
19
- trackEvent,
20
- flush,
21
- sendRrwebBatch,
22
- stopFlushInterval,
23
- } from './ingest';
24
-
25
- const CoreOutline = ({ children, data_source_id, data_source_secret, warehouse_id }) => {
26
- const [replayEvents, setReplayEvents] = useState([]);
27
- const recorderActive = useRef(false);
28
- const [currentPage, setCurrentPage] = useState(window.location.href);
29
- const pageviewCountRef = useRef(0);
30
- const eventCountRef = useRef(0);
31
- const sessionStartRef = useRef(Date.now());
32
-
33
- const sessionId = getSessionId();
34
- const anonymousId = getAnonymousId();
35
-
36
- useEffect(() => {
37
- const utmParams = getUtmParams();
38
- const deviceType = detectDeviceType();
39
- const os = detectOS();
40
- const browser = getBrowserName();
41
- const referrer = document.referrer || 'direct';
42
-
43
- const basePayload = {
44
- session_id: sessionId,
45
- anonymous_id: anonymousId,
46
- account_id: data_source_id,
47
- platform: 'web',
48
- device_type: deviceType,
49
- os,
50
- browser,
51
- referrer,
52
- page_url: window.location.href,
53
- page_path: getPagePath(),
54
- ...utmParams,
55
- };
56
-
57
- let initialized = false;
58
-
59
- const init = async () => {
60
- initialized = await initIngest(warehouse_id, data_source_id, data_source_secret);
61
- if (!initialized) return;
62
-
63
- pageviewCountRef.current += 1;
64
- trackEvent('pageview', { ...basePayload, event_name: 'pageview', event_category: 'navigation' });
65
- trackEvent('session_start', {
66
- ...basePayload,
67
- event_name: 'session_start',
68
- event_category: 'session',
69
- session_start_ts: new Date().toISOString(),
70
- });
71
- };
72
-
73
- init();
74
-
75
- const handleSessionEnd = () => {
76
- if (!initialized) return;
77
- const durationMs = Date.now() - sessionStartRef.current;
78
- trackEvent('session_end', {
79
- ...basePayload,
80
- event_name: 'session_end',
81
- event_category: 'session',
82
- session_end_ts: new Date().toISOString(),
83
- pageviews: pageviewCountRef.current,
84
- events_count: eventCountRef.current,
85
- duration_ms: durationMs,
86
- });
87
- flush();
88
- stopFlushInterval();
89
- sendRrwebBatch(replayEvents, sessionId);
90
- };
91
-
92
- const handleVisibilityChange = () => {
93
- if (document.visibilityState === 'hidden') handleSessionEnd();
94
- };
95
-
96
- window.addEventListener('beforeunload', handleSessionEnd);
97
- document.addEventListener('visibilitychange', handleVisibilityChange);
98
-
99
- return () => {
100
- handleSessionEnd();
101
- window.removeEventListener('beforeunload', handleSessionEnd);
102
- document.removeEventListener('visibilitychange', handleVisibilityChange);
103
- };
104
- }, []);
105
-
106
- useEffect(() => {
107
- const utmParams = getUtmParams();
108
-
109
- const handleNavigation = () => {
110
- setCurrentPage(window.location.href);
111
- pageviewCountRef.current += 1;
112
- trackEvent('pageview', {
113
- session_id: sessionId,
114
- anonymous_id: anonymousId,
115
- account_id: data_source_id,
116
- platform: 'web',
117
- page_url: window.location.href,
118
- page_path: getPagePath(),
119
- referrer: document.referrer || 'direct',
120
- event_name: 'pageview',
121
- event_category: 'navigation',
122
- ...utmParams,
123
- });
124
- };
125
-
126
- window.addEventListener('popstate', handleNavigation);
127
- const originalPushState = window.history.pushState;
128
- window.history.pushState = function (...args) {
129
- originalPushState.apply(this, args);
130
- handleNavigation();
131
- };
132
-
133
- return () => {
134
- window.removeEventListener('popstate', handleNavigation);
135
- window.history.pushState = originalPushState;
136
- };
137
- }, [currentPage]);
138
-
139
- useEffect(() => {
140
- let stopFn;
141
- if (!recorderActive.current) {
142
- stopFn = record({
143
- emit(event) {
144
- setReplayEvents((prev) => [...prev, event]);
145
- },
146
- maskAllInputs: false,
147
- maskTextSelector: null,
148
- });
149
- recorderActive.current = true;
150
- }
151
-
152
- return () => {
153
- if (stopFn) {
154
- stopFn();
155
- recorderActive.current = false;
156
- }
157
- };
158
- }, []);
159
-
160
- useEffect(() => {
161
- const handleClick = (event) => {
162
- const clickedElement = document.elementFromPoint(event.clientX, event.clientY);
163
- const metadata = clickedElement
164
- ? {
165
- label: clickedElement.getAttribute('data-label') || clickedElement.innerText || null,
166
- value: clickedElement.getAttribute('value') || clickedElement.innerText || null,
167
- id: clickedElement.getAttribute('id') || null,
168
- name: clickedElement.getAttribute('name') || null,
169
- class: clickedElement.getAttribute('class') || null,
170
- tag: clickedElement.tagName,
171
- inner_text: event.target.innerText || null,
172
- }
173
- : {};
174
-
175
- eventCountRef.current += 1;
176
- trackEvent('item_clicked', {
177
- session_id: sessionId,
178
- anonymous_id: anonymousId,
179
- account_id: data_source_id,
180
- platform: 'web',
181
- page_url: window.location.href,
182
- page_path: getPagePath(),
183
- event_name: 'item_clicked',
184
- event_category: 'interaction',
185
- properties_json: JSON.stringify(metadata),
186
- });
187
- };
188
-
189
- document.addEventListener('click', handleClick);
190
- return () => document.removeEventListener('click', handleClick);
191
- }, []);
192
-
193
- return <>{children}</>;
194
- };
195
-
196
- export const flag_item_clicked = (item_id) => {
197
- const sessionId = sessionStorage.getItem('co_session_id') || '';
198
- const anonymousId = localStorage.getItem('co_anon_id') || '';
199
- trackEvent('product_clicked', {
200
- session_id: sessionId,
201
- anonymous_id: anonymousId,
202
- event_name: 'product_clicked',
203
- event_category: 'commerce',
204
- feature_key: item_id,
205
- });
206
- return { status: 'success', message: 'Item click flagged successfully.' };
207
- };
208
-
209
- export const flag_item_purchased = (item_id, price = null) => {
210
- const sessionId = sessionStorage.getItem('co_session_id') || '';
211
- const anonymousId = localStorage.getItem('co_anon_id') || '';
212
- trackEvent('product_purchased', {
213
- session_id: sessionId,
214
- anonymous_id: anonymousId,
215
- event_name: 'product_purchased',
216
- event_category: 'commerce',
217
- feature_key: item_id,
218
- value_num: price != null ? Number(price) : null,
219
- });
220
- return { status: 'success', message: 'Item purchase flagged successfully.' };
221
- };
222
-
223
- export default CoreOutline;
@@ -1,26 +0,0 @@
1
- import { useEffect, useRef } from 'react';
2
- import { record } from 'rrweb';
3
-
4
- const useRRWebRecorder = () => {
5
- const eventsRef = useRef([]);
6
-
7
- useEffect(() => {
8
- const stopRecording = record({
9
- emit(event) {
10
- if (event) {
11
- eventsRef.current.push(event);
12
- }
13
- },
14
- });
15
-
16
- return () => {
17
- if (stopRecording) {
18
- stopRecording();
19
- }
20
- };
21
- }, []);
22
-
23
- return eventsRef;
24
- };
25
-
26
- export default useRRWebRecorder;
@@ -1,66 +0,0 @@
1
- import { v4 as uuidv4 } from 'uuid';
2
-
3
- const ANON_ID_KEY = 'co_anon_id';
4
- const SESSION_ID_KEY = 'co_session_id';
5
-
6
- export function getAnonymousId() {
7
- let id = localStorage.getItem(ANON_ID_KEY);
8
- if (!id) {
9
- id = uuidv4();
10
- localStorage.setItem(ANON_ID_KEY, id);
11
- }
12
- return id;
13
- }
14
-
15
- export function getSessionId() {
16
- let id = sessionStorage.getItem(SESSION_ID_KEY);
17
- if (!id) {
18
- id = uuidv4();
19
- sessionStorage.setItem(SESSION_ID_KEY, id);
20
- }
21
- return id;
22
- }
23
-
24
- export function getUtmParams() {
25
- const params = new URLSearchParams(window.location.search);
26
- return {
27
- utm_source: params.get('utm_source') || undefined,
28
- utm_medium: params.get('utm_medium') || undefined,
29
- utm_campaign: params.get('utm_campaign') || undefined,
30
- utm_term: params.get('utm_term') || undefined,
31
- utm_content: params.get('utm_content') || undefined,
32
- };
33
- }
34
-
35
- export function detectDeviceType() {
36
- const ua = navigator.userAgent;
37
- if (/Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua)) {
38
- if (/iPad|Tablet/i.test(ua)) return 'tablet';
39
- return 'mobile';
40
- }
41
- return 'desktop';
42
- }
43
-
44
- export function detectOS() {
45
- const ua = navigator.userAgent;
46
- if (/iPhone|iPad|iPod/i.test(ua)) return 'iOS';
47
- if (/Android/i.test(ua)) return 'Android';
48
- if (/Win/i.test(ua)) return 'Windows';
49
- if (/Mac/i.test(ua)) return 'macOS';
50
- if (/Linux/i.test(ua)) return 'Linux';
51
- return 'Other';
52
- }
53
-
54
- export function getBrowserName() {
55
- const ua = navigator.userAgent;
56
- if (ua.indexOf('Firefox') > -1) return 'Firefox';
57
- if (ua.indexOf('Opera') > -1 || ua.indexOf('OPR') > -1) return 'Opera';
58
- if (ua.indexOf('Chrome') > -1) return 'Chrome';
59
- if (ua.indexOf('Safari') > -1) return 'Safari';
60
- if (ua.indexOf('MSIE') > -1 || ua.indexOf('Trident/') > -1) return 'IE';
61
- return 'Unknown';
62
- }
63
-
64
- export function getPagePath() {
65
- return window.location.pathname;
66
- }
@@ -1 +0,0 @@
1
- export * from './CoreOutline';
@@ -1,157 +0,0 @@
1
- import { v4 as uuidv4 } from 'uuid';
2
-
3
- const INGEST_BASE = 'https://atlas-orchestrator.atlas.coreoutline.com';
4
- const FLUSH_INTERVAL_MS = 5000;
5
- // Refresh the token this many milliseconds before it actually expires.
6
- const TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000; // 5 minutes
7
-
8
- let _token = null;
9
- let _warehouseId = null;
10
- let _organizationId = null;
11
- let _dataSourceId = null;
12
- let _dataSourceSecret = null;
13
- let _buffer = [];
14
- let _flushInterval = null;
15
- let _refreshTimeout = null;
16
-
17
- function _scheduleTokenRefresh(expiresInSeconds) {
18
- if (_refreshTimeout) {
19
- clearTimeout(_refreshTimeout);
20
- _refreshTimeout = null;
21
- }
22
-
23
- const refreshInMs = expiresInSeconds * 1000 - TOKEN_REFRESH_BUFFER_MS;
24
- if (refreshInMs <= 0) {
25
- // Token already near expiry — refresh immediately on next action.
26
- return;
27
- }
28
-
29
- _refreshTimeout = setTimeout(async () => {
30
- const ok = await _doAuthorize(_warehouseId, _dataSourceId, _dataSourceSecret);
31
- if (!ok) {
32
- console.warn('[CoreOutline] Proactive token refresh failed; will retry on next flush.');
33
- }
34
- }, refreshInMs);
35
- }
36
-
37
- async function _doAuthorize(warehouseId, dataSourceId, dataSourceSecret) {
38
- try {
39
- const res = await fetch(`${INGEST_BASE}/api/ingest/authorize`, {
40
- method: 'POST',
41
- headers: { 'Content-Type': 'application/json' },
42
- body: JSON.stringify({
43
- warehouse_id: warehouseId,
44
- data_source_id: dataSourceId,
45
- data_source_secret: dataSourceSecret,
46
- }),
47
- });
48
-
49
- if (!res.ok) {
50
- console.error('[CoreOutline] Authorization failed:', res.status);
51
- return false;
52
- }
53
-
54
- const data = await res.json();
55
- _token = data.access_token;
56
- _organizationId = data.organization_id;
57
-
58
- const expiresIn = data.expires_in || 24 * 3600;
59
- _scheduleTokenRefresh(expiresIn);
60
- return true;
61
- } catch (err) {
62
- console.error('[CoreOutline] Authorization error:', err);
63
- return false;
64
- }
65
- }
66
-
67
- export async function initIngest(warehouseId, dataSourceId, dataSourceSecret) {
68
- _warehouseId = warehouseId;
69
- _dataSourceId = dataSourceId;
70
- _dataSourceSecret = dataSourceSecret;
71
-
72
- return _doAuthorize(warehouseId, dataSourceId, dataSourceSecret);
73
- }
74
-
75
- async function _ensureFreshToken() {
76
- if (_token) return;
77
- if (_warehouseId && _dataSourceId && _dataSourceSecret) {
78
- await _doAuthorize(_warehouseId, _dataSourceId, _dataSourceSecret);
79
- }
80
- }
81
-
82
- export function trackEvent(eventType, payload) {
83
- if (!_token) return;
84
-
85
- const event = {
86
- event_id: uuidv4(),
87
- event_type: eventType,
88
- event_ts: new Date().toISOString(),
89
- ...payload,
90
- };
91
- _buffer.push(event);
92
-
93
- if (!_flushInterval) {
94
- _flushInterval = setInterval(flush, FLUSH_INTERVAL_MS);
95
- }
96
- }
97
-
98
- export async function flush() {
99
- await _ensureFreshToken();
100
- if (!_token || _buffer.length === 0) return;
101
-
102
- const events = _buffer.splice(0, _buffer.length);
103
- const body = JSON.stringify({
104
- warehouse_id: _warehouseId,
105
- organization_id: _organizationId,
106
- events,
107
- });
108
-
109
- const headers = {
110
- 'Content-Type': 'application/json',
111
- Authorization: `Bearer ${_token}`,
112
- };
113
-
114
- if (navigator.sendBeacon) {
115
- const blob = new Blob([body], { type: 'application/json' });
116
- navigator.sendBeacon(`${INGEST_BASE}/api/ingest/events`, blob);
117
- } else {
118
- fetch(`${INGEST_BASE}/api/ingest/events`, {
119
- method: 'POST',
120
- headers,
121
- body,
122
- keepalive: true,
123
- }).catch((err) => console.error('[CoreOutline] Flush error:', err));
124
- }
125
- }
126
-
127
- export async function sendRrwebBatch(chunks, sessionId) {
128
- await _ensureFreshToken();
129
- if (!_token || !chunks || chunks.length === 0) return;
130
-
131
- try {
132
- await fetch(`${INGEST_BASE}/api/ingest/rrweb`, {
133
- method: 'POST',
134
- headers: {
135
- 'Content-Type': 'application/json',
136
- Authorization: `Bearer ${_token}`,
137
- },
138
- body: JSON.stringify({
139
- warehouse_id: _warehouseId,
140
- organization_id: _organizationId,
141
- session_id: sessionId,
142
- data_source_id: _dataSourceId,
143
- chunks,
144
- }),
145
- keepalive: true,
146
- });
147
- } catch (err) {
148
- console.error('[CoreOutline] rrweb upload error:', err);
149
- }
150
- }
151
-
152
- export function stopFlushInterval() {
153
- if (_flushInterval) {
154
- clearInterval(_flushInterval);
155
- _flushInterval = null;
156
- }
157
- }
@@ -1,3 +0,0 @@
1
- import { io } from 'socket.io-client';
2
- const socket = io('http://streams.coreoutline.com');
3
- export default socket;
package/src/index.js DELETED
@@ -1 +0,0 @@
1
- export * from './components/CoreOutline';