core-outline 1.1.23 → 1.1.24
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 +45 -0
- package/dist/index.es.js +177 -69
- package/dist/index.js +177 -69
- package/package.json +1 -1
- package/src/components/CoreOutline/ingest.js +45 -4
package/README.md
CHANGED
|
@@ -12,6 +12,17 @@ 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
|
+
|
|
15
26
|
The response contains your `data_source_id`, `data_source_secret`, and `warehouse_id`. **The secret is shown once — store it securely.**
|
|
16
27
|
|
|
17
28
|
```json
|
|
@@ -79,3 +90,37 @@ flag_item_clicked('sku-123');
|
|
|
79
90
|
// Track a purchase (price is optional, maps to value_num in ClickHouse)
|
|
80
91
|
flag_item_purchased('sku-123', 49.99);
|
|
81
92
|
```
|
|
93
|
+
|
|
94
|
+
## Data Pipeline
|
|
95
|
+
|
|
96
|
+
Events flow through the following pipeline:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
Browser → POST /api/ingest/events → RabbitMQ ({warehouse_id}.analytics.events)
|
|
100
|
+
→ Consumer worker → ClickHouse atlas_analytics
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The ClickHouse tables populated are:
|
|
104
|
+
|
|
105
|
+
| Table | Populated by |
|
|
106
|
+
|-------|-------------|
|
|
107
|
+
| `fact_event` | All events except session start/end |
|
|
108
|
+
| `fact_session` | `session_start` and `session_end` events |
|
|
109
|
+
| `fact_pageview` | `pageview` events |
|
|
110
|
+
| `stg_events_sdk_event` | All events (raw staging) |
|
|
111
|
+
| `stg_rrweb_event` | Session recording chunks |
|
|
112
|
+
|
|
113
|
+
These tables are queried by the [atlas-analytics](https://analytics.atlas.coreoutline.com) API under `/metrics/saas/traffic`, `/metrics/saas/engagement`, and related endpoints.
|
|
114
|
+
|
|
115
|
+
## Anonymous vs Session Identity
|
|
116
|
+
|
|
117
|
+
- **`anonymous_id`** — a UUID stored in `localStorage`; persists across browser sessions, used to stitch cross-session user journeys.
|
|
118
|
+
- **`session_id`** — a UUID stored in `sessionStorage`; reset on each new tab or browser close, matching the standard session definition.
|
|
119
|
+
|
|
120
|
+
## Service URLs
|
|
121
|
+
|
|
122
|
+
| Service | URL |
|
|
123
|
+
|---------|-----|
|
|
124
|
+
| Hermes API (credential management) | `https://atlas-orchestrator.atlas.coreoutline.com` |
|
|
125
|
+
| Maia (data engineering / pipelines) | `https://data-engineering.atlas.coreoutline.com` |
|
|
126
|
+
| Atlas Analytics API | `https://analytics.atlas.coreoutline.com` |
|
package/dist/index.es.js
CHANGED
|
@@ -4411,25 +4411,57 @@ function getPagePath() {
|
|
|
4411
4411
|
|
|
4412
4412
|
var INGEST_BASE = 'https://atlas-orchestrator.atlas.coreoutline.com';
|
|
4413
4413
|
var FLUSH_INTERVAL_MS = 5000;
|
|
4414
|
+
// Refresh the token this many milliseconds before it actually expires.
|
|
4415
|
+
var TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000; // 5 minutes
|
|
4416
|
+
|
|
4414
4417
|
var _token = null;
|
|
4415
4418
|
var _warehouseId = null;
|
|
4416
4419
|
var _organizationId = null;
|
|
4417
4420
|
var _dataSourceId = null;
|
|
4421
|
+
var _dataSourceSecret = null;
|
|
4418
4422
|
var _buffer = [];
|
|
4419
4423
|
var _flushInterval = null;
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4424
|
+
var _refreshTimeout = null;
|
|
4425
|
+
function _scheduleTokenRefresh(expiresInSeconds) {
|
|
4426
|
+
if (_refreshTimeout) {
|
|
4427
|
+
clearTimeout(_refreshTimeout);
|
|
4428
|
+
_refreshTimeout = null;
|
|
4429
|
+
}
|
|
4430
|
+
var refreshInMs = expiresInSeconds * 1000 - TOKEN_REFRESH_BUFFER_MS;
|
|
4431
|
+
if (refreshInMs <= 0) {
|
|
4432
|
+
// Token already near expiry — refresh immediately on next action.
|
|
4433
|
+
return;
|
|
4434
|
+
}
|
|
4435
|
+
_refreshTimeout = setTimeout(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
|
|
4436
|
+
var ok;
|
|
4426
4437
|
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
4427
4438
|
while (1) switch (_context.prev = _context.next) {
|
|
4428
4439
|
case 0:
|
|
4429
|
-
|
|
4430
|
-
_dataSourceId
|
|
4431
|
-
|
|
4432
|
-
|
|
4440
|
+
_context.next = 2;
|
|
4441
|
+
return _doAuthorize(_warehouseId, _dataSourceId, _dataSourceSecret);
|
|
4442
|
+
case 2:
|
|
4443
|
+
ok = _context.sent;
|
|
4444
|
+
if (!ok) {
|
|
4445
|
+
console.warn('[CoreOutline] Proactive token refresh failed; will retry on next flush.');
|
|
4446
|
+
}
|
|
4447
|
+
case 4:
|
|
4448
|
+
case "end":
|
|
4449
|
+
return _context.stop();
|
|
4450
|
+
}
|
|
4451
|
+
}, _callee);
|
|
4452
|
+
})), refreshInMs);
|
|
4453
|
+
}
|
|
4454
|
+
function _doAuthorize(_x, _x2, _x3) {
|
|
4455
|
+
return _doAuthorize2.apply(this, arguments);
|
|
4456
|
+
}
|
|
4457
|
+
function _doAuthorize2() {
|
|
4458
|
+
_doAuthorize2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2(warehouseId, dataSourceId, dataSourceSecret) {
|
|
4459
|
+
var res, data, expiresIn;
|
|
4460
|
+
return _regeneratorRuntime().wrap(function _callee2$(_context2) {
|
|
4461
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
4462
|
+
case 0:
|
|
4463
|
+
_context2.prev = 0;
|
|
4464
|
+
_context2.next = 3;
|
|
4433
4465
|
return fetch("".concat(INGEST_BASE, "/api/ingest/authorize"), {
|
|
4434
4466
|
method: 'POST',
|
|
4435
4467
|
headers: {
|
|
@@ -4441,35 +4473,85 @@ function _initIngest() {
|
|
|
4441
4473
|
data_source_secret: dataSourceSecret
|
|
4442
4474
|
})
|
|
4443
4475
|
});
|
|
4444
|
-
case
|
|
4445
|
-
res =
|
|
4476
|
+
case 3:
|
|
4477
|
+
res = _context2.sent;
|
|
4446
4478
|
if (res.ok) {
|
|
4447
|
-
|
|
4479
|
+
_context2.next = 7;
|
|
4448
4480
|
break;
|
|
4449
4481
|
}
|
|
4450
4482
|
console.error('[CoreOutline] Authorization failed:', res.status);
|
|
4451
|
-
return
|
|
4452
|
-
case
|
|
4453
|
-
|
|
4483
|
+
return _context2.abrupt("return", false);
|
|
4484
|
+
case 7:
|
|
4485
|
+
_context2.next = 9;
|
|
4454
4486
|
return res.json();
|
|
4455
|
-
case
|
|
4456
|
-
data =
|
|
4487
|
+
case 9:
|
|
4488
|
+
data = _context2.sent;
|
|
4457
4489
|
_token = data.access_token;
|
|
4458
4490
|
_organizationId = data.organization_id;
|
|
4459
|
-
|
|
4491
|
+
expiresIn = data.expires_in || 24 * 3600;
|
|
4492
|
+
_scheduleTokenRefresh(expiresIn);
|
|
4493
|
+
return _context2.abrupt("return", true);
|
|
4460
4494
|
case 17:
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
console.error('[CoreOutline] Authorization error:',
|
|
4464
|
-
return
|
|
4495
|
+
_context2.prev = 17;
|
|
4496
|
+
_context2.t0 = _context2["catch"](0);
|
|
4497
|
+
console.error('[CoreOutline] Authorization error:', _context2.t0);
|
|
4498
|
+
return _context2.abrupt("return", false);
|
|
4465
4499
|
case 21:
|
|
4466
4500
|
case "end":
|
|
4467
|
-
return
|
|
4501
|
+
return _context2.stop();
|
|
4502
|
+
}
|
|
4503
|
+
}, _callee2, null, [[0, 17]]);
|
|
4504
|
+
}));
|
|
4505
|
+
return _doAuthorize2.apply(this, arguments);
|
|
4506
|
+
}
|
|
4507
|
+
function initIngest(_x4, _x5, _x6) {
|
|
4508
|
+
return _initIngest.apply(this, arguments);
|
|
4509
|
+
}
|
|
4510
|
+
function _initIngest() {
|
|
4511
|
+
_initIngest = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee3(warehouseId, dataSourceId, dataSourceSecret) {
|
|
4512
|
+
return _regeneratorRuntime().wrap(function _callee3$(_context3) {
|
|
4513
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
4514
|
+
case 0:
|
|
4515
|
+
_warehouseId = warehouseId;
|
|
4516
|
+
_dataSourceId = dataSourceId;
|
|
4517
|
+
_dataSourceSecret = dataSourceSecret;
|
|
4518
|
+
return _context3.abrupt("return", _doAuthorize(warehouseId, dataSourceId, dataSourceSecret));
|
|
4519
|
+
case 4:
|
|
4520
|
+
case "end":
|
|
4521
|
+
return _context3.stop();
|
|
4468
4522
|
}
|
|
4469
|
-
},
|
|
4523
|
+
}, _callee3);
|
|
4470
4524
|
}));
|
|
4471
4525
|
return _initIngest.apply(this, arguments);
|
|
4472
4526
|
}
|
|
4527
|
+
function _ensureFreshToken() {
|
|
4528
|
+
return _ensureFreshToken2.apply(this, arguments);
|
|
4529
|
+
}
|
|
4530
|
+
function _ensureFreshToken2() {
|
|
4531
|
+
_ensureFreshToken2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee4() {
|
|
4532
|
+
return _regeneratorRuntime().wrap(function _callee4$(_context4) {
|
|
4533
|
+
while (1) switch (_context4.prev = _context4.next) {
|
|
4534
|
+
case 0:
|
|
4535
|
+
if (!_token) {
|
|
4536
|
+
_context4.next = 2;
|
|
4537
|
+
break;
|
|
4538
|
+
}
|
|
4539
|
+
return _context4.abrupt("return");
|
|
4540
|
+
case 2:
|
|
4541
|
+
if (!(_warehouseId && _dataSourceId && _dataSourceSecret)) {
|
|
4542
|
+
_context4.next = 5;
|
|
4543
|
+
break;
|
|
4544
|
+
}
|
|
4545
|
+
_context4.next = 5;
|
|
4546
|
+
return _doAuthorize(_warehouseId, _dataSourceId, _dataSourceSecret);
|
|
4547
|
+
case 5:
|
|
4548
|
+
case "end":
|
|
4549
|
+
return _context4.stop();
|
|
4550
|
+
}
|
|
4551
|
+
}, _callee4);
|
|
4552
|
+
}));
|
|
4553
|
+
return _ensureFreshToken2.apply(this, arguments);
|
|
4554
|
+
}
|
|
4473
4555
|
function trackEvent(eventType, payload) {
|
|
4474
4556
|
if (!_token) return;
|
|
4475
4557
|
var event = _objectSpread2({
|
|
@@ -4483,49 +4565,75 @@ function trackEvent(eventType, payload) {
|
|
|
4483
4565
|
}
|
|
4484
4566
|
}
|
|
4485
4567
|
function flush() {
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4568
|
+
return _flush.apply(this, arguments);
|
|
4569
|
+
}
|
|
4570
|
+
function _flush() {
|
|
4571
|
+
_flush = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee5() {
|
|
4572
|
+
var events, body, headers, blob;
|
|
4573
|
+
return _regeneratorRuntime().wrap(function _callee5$(_context5) {
|
|
4574
|
+
while (1) switch (_context5.prev = _context5.next) {
|
|
4575
|
+
case 0:
|
|
4576
|
+
_context5.next = 2;
|
|
4577
|
+
return _ensureFreshToken();
|
|
4578
|
+
case 2:
|
|
4579
|
+
if (!(!_token || _buffer.length === 0)) {
|
|
4580
|
+
_context5.next = 4;
|
|
4581
|
+
break;
|
|
4582
|
+
}
|
|
4583
|
+
return _context5.abrupt("return");
|
|
4584
|
+
case 4:
|
|
4585
|
+
events = _buffer.splice(0, _buffer.length);
|
|
4586
|
+
body = JSON.stringify({
|
|
4587
|
+
warehouse_id: _warehouseId,
|
|
4588
|
+
organization_id: _organizationId,
|
|
4589
|
+
events: events
|
|
4590
|
+
});
|
|
4591
|
+
headers = {
|
|
4592
|
+
'Content-Type': 'application/json',
|
|
4593
|
+
Authorization: "Bearer ".concat(_token)
|
|
4594
|
+
};
|
|
4595
|
+
if (navigator.sendBeacon) {
|
|
4596
|
+
blob = new Blob([body], {
|
|
4597
|
+
type: 'application/json'
|
|
4598
|
+
});
|
|
4599
|
+
navigator.sendBeacon("".concat(INGEST_BASE, "/api/ingest/events"), blob);
|
|
4600
|
+
} else {
|
|
4601
|
+
fetch("".concat(INGEST_BASE, "/api/ingest/events"), {
|
|
4602
|
+
method: 'POST',
|
|
4603
|
+
headers: headers,
|
|
4604
|
+
body: body,
|
|
4605
|
+
keepalive: true
|
|
4606
|
+
})["catch"](function (err) {
|
|
4607
|
+
return console.error('[CoreOutline] Flush error:', err);
|
|
4608
|
+
});
|
|
4609
|
+
}
|
|
4610
|
+
case 8:
|
|
4611
|
+
case "end":
|
|
4612
|
+
return _context5.stop();
|
|
4613
|
+
}
|
|
4614
|
+
}, _callee5);
|
|
4615
|
+
}));
|
|
4616
|
+
return _flush.apply(this, arguments);
|
|
4512
4617
|
}
|
|
4513
|
-
function sendRrwebBatch(
|
|
4618
|
+
function sendRrwebBatch(_x7, _x8) {
|
|
4514
4619
|
return _sendRrwebBatch.apply(this, arguments);
|
|
4515
4620
|
}
|
|
4516
4621
|
function _sendRrwebBatch() {
|
|
4517
|
-
_sendRrwebBatch = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function
|
|
4518
|
-
return _regeneratorRuntime().wrap(function
|
|
4519
|
-
while (1) switch (
|
|
4622
|
+
_sendRrwebBatch = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee6(chunks, sessionId) {
|
|
4623
|
+
return _regeneratorRuntime().wrap(function _callee6$(_context6) {
|
|
4624
|
+
while (1) switch (_context6.prev = _context6.next) {
|
|
4520
4625
|
case 0:
|
|
4626
|
+
_context6.next = 2;
|
|
4627
|
+
return _ensureFreshToken();
|
|
4628
|
+
case 2:
|
|
4521
4629
|
if (!(!_token || !chunks || chunks.length === 0)) {
|
|
4522
|
-
|
|
4630
|
+
_context6.next = 4;
|
|
4523
4631
|
break;
|
|
4524
4632
|
}
|
|
4525
|
-
return
|
|
4526
|
-
case
|
|
4527
|
-
|
|
4528
|
-
|
|
4633
|
+
return _context6.abrupt("return");
|
|
4634
|
+
case 4:
|
|
4635
|
+
_context6.prev = 4;
|
|
4636
|
+
_context6.next = 7;
|
|
4529
4637
|
return fetch("".concat(INGEST_BASE, "/api/ingest/rrweb"), {
|
|
4530
4638
|
method: 'POST',
|
|
4531
4639
|
headers: {
|
|
@@ -4541,18 +4649,18 @@ function _sendRrwebBatch() {
|
|
|
4541
4649
|
}),
|
|
4542
4650
|
keepalive: true
|
|
4543
4651
|
});
|
|
4544
|
-
case 5:
|
|
4545
|
-
_context2.next = 10;
|
|
4546
|
-
break;
|
|
4547
4652
|
case 7:
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4653
|
+
_context6.next = 12;
|
|
4654
|
+
break;
|
|
4655
|
+
case 9:
|
|
4656
|
+
_context6.prev = 9;
|
|
4657
|
+
_context6.t0 = _context6["catch"](4);
|
|
4658
|
+
console.error('[CoreOutline] rrweb upload error:', _context6.t0);
|
|
4659
|
+
case 12:
|
|
4552
4660
|
case "end":
|
|
4553
|
-
return
|
|
4661
|
+
return _context6.stop();
|
|
4554
4662
|
}
|
|
4555
|
-
},
|
|
4663
|
+
}, _callee6, null, [[4, 9]]);
|
|
4556
4664
|
}));
|
|
4557
4665
|
return _sendRrwebBatch.apply(this, arguments);
|
|
4558
4666
|
}
|
package/dist/index.js
CHANGED
|
@@ -4419,25 +4419,57 @@ function getPagePath() {
|
|
|
4419
4419
|
|
|
4420
4420
|
var INGEST_BASE = 'https://atlas-orchestrator.atlas.coreoutline.com';
|
|
4421
4421
|
var FLUSH_INTERVAL_MS = 5000;
|
|
4422
|
+
// Refresh the token this many milliseconds before it actually expires.
|
|
4423
|
+
var TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000; // 5 minutes
|
|
4424
|
+
|
|
4422
4425
|
var _token = null;
|
|
4423
4426
|
var _warehouseId = null;
|
|
4424
4427
|
var _organizationId = null;
|
|
4425
4428
|
var _dataSourceId = null;
|
|
4429
|
+
var _dataSourceSecret = null;
|
|
4426
4430
|
var _buffer = [];
|
|
4427
4431
|
var _flushInterval = null;
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4432
|
+
var _refreshTimeout = null;
|
|
4433
|
+
function _scheduleTokenRefresh(expiresInSeconds) {
|
|
4434
|
+
if (_refreshTimeout) {
|
|
4435
|
+
clearTimeout(_refreshTimeout);
|
|
4436
|
+
_refreshTimeout = null;
|
|
4437
|
+
}
|
|
4438
|
+
var refreshInMs = expiresInSeconds * 1000 - TOKEN_REFRESH_BUFFER_MS;
|
|
4439
|
+
if (refreshInMs <= 0) {
|
|
4440
|
+
// Token already near expiry — refresh immediately on next action.
|
|
4441
|
+
return;
|
|
4442
|
+
}
|
|
4443
|
+
_refreshTimeout = setTimeout(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
|
|
4444
|
+
var ok;
|
|
4434
4445
|
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
4435
4446
|
while (1) switch (_context.prev = _context.next) {
|
|
4436
4447
|
case 0:
|
|
4437
|
-
|
|
4438
|
-
_dataSourceId
|
|
4439
|
-
|
|
4440
|
-
|
|
4448
|
+
_context.next = 2;
|
|
4449
|
+
return _doAuthorize(_warehouseId, _dataSourceId, _dataSourceSecret);
|
|
4450
|
+
case 2:
|
|
4451
|
+
ok = _context.sent;
|
|
4452
|
+
if (!ok) {
|
|
4453
|
+
console.warn('[CoreOutline] Proactive token refresh failed; will retry on next flush.');
|
|
4454
|
+
}
|
|
4455
|
+
case 4:
|
|
4456
|
+
case "end":
|
|
4457
|
+
return _context.stop();
|
|
4458
|
+
}
|
|
4459
|
+
}, _callee);
|
|
4460
|
+
})), refreshInMs);
|
|
4461
|
+
}
|
|
4462
|
+
function _doAuthorize(_x, _x2, _x3) {
|
|
4463
|
+
return _doAuthorize2.apply(this, arguments);
|
|
4464
|
+
}
|
|
4465
|
+
function _doAuthorize2() {
|
|
4466
|
+
_doAuthorize2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2(warehouseId, dataSourceId, dataSourceSecret) {
|
|
4467
|
+
var res, data, expiresIn;
|
|
4468
|
+
return _regeneratorRuntime().wrap(function _callee2$(_context2) {
|
|
4469
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
4470
|
+
case 0:
|
|
4471
|
+
_context2.prev = 0;
|
|
4472
|
+
_context2.next = 3;
|
|
4441
4473
|
return fetch("".concat(INGEST_BASE, "/api/ingest/authorize"), {
|
|
4442
4474
|
method: 'POST',
|
|
4443
4475
|
headers: {
|
|
@@ -4449,35 +4481,85 @@ function _initIngest() {
|
|
|
4449
4481
|
data_source_secret: dataSourceSecret
|
|
4450
4482
|
})
|
|
4451
4483
|
});
|
|
4452
|
-
case
|
|
4453
|
-
res =
|
|
4484
|
+
case 3:
|
|
4485
|
+
res = _context2.sent;
|
|
4454
4486
|
if (res.ok) {
|
|
4455
|
-
|
|
4487
|
+
_context2.next = 7;
|
|
4456
4488
|
break;
|
|
4457
4489
|
}
|
|
4458
4490
|
console.error('[CoreOutline] Authorization failed:', res.status);
|
|
4459
|
-
return
|
|
4460
|
-
case
|
|
4461
|
-
|
|
4491
|
+
return _context2.abrupt("return", false);
|
|
4492
|
+
case 7:
|
|
4493
|
+
_context2.next = 9;
|
|
4462
4494
|
return res.json();
|
|
4463
|
-
case
|
|
4464
|
-
data =
|
|
4495
|
+
case 9:
|
|
4496
|
+
data = _context2.sent;
|
|
4465
4497
|
_token = data.access_token;
|
|
4466
4498
|
_organizationId = data.organization_id;
|
|
4467
|
-
|
|
4499
|
+
expiresIn = data.expires_in || 24 * 3600;
|
|
4500
|
+
_scheduleTokenRefresh(expiresIn);
|
|
4501
|
+
return _context2.abrupt("return", true);
|
|
4468
4502
|
case 17:
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
console.error('[CoreOutline] Authorization error:',
|
|
4472
|
-
return
|
|
4503
|
+
_context2.prev = 17;
|
|
4504
|
+
_context2.t0 = _context2["catch"](0);
|
|
4505
|
+
console.error('[CoreOutline] Authorization error:', _context2.t0);
|
|
4506
|
+
return _context2.abrupt("return", false);
|
|
4473
4507
|
case 21:
|
|
4474
4508
|
case "end":
|
|
4475
|
-
return
|
|
4509
|
+
return _context2.stop();
|
|
4510
|
+
}
|
|
4511
|
+
}, _callee2, null, [[0, 17]]);
|
|
4512
|
+
}));
|
|
4513
|
+
return _doAuthorize2.apply(this, arguments);
|
|
4514
|
+
}
|
|
4515
|
+
function initIngest(_x4, _x5, _x6) {
|
|
4516
|
+
return _initIngest.apply(this, arguments);
|
|
4517
|
+
}
|
|
4518
|
+
function _initIngest() {
|
|
4519
|
+
_initIngest = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee3(warehouseId, dataSourceId, dataSourceSecret) {
|
|
4520
|
+
return _regeneratorRuntime().wrap(function _callee3$(_context3) {
|
|
4521
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
4522
|
+
case 0:
|
|
4523
|
+
_warehouseId = warehouseId;
|
|
4524
|
+
_dataSourceId = dataSourceId;
|
|
4525
|
+
_dataSourceSecret = dataSourceSecret;
|
|
4526
|
+
return _context3.abrupt("return", _doAuthorize(warehouseId, dataSourceId, dataSourceSecret));
|
|
4527
|
+
case 4:
|
|
4528
|
+
case "end":
|
|
4529
|
+
return _context3.stop();
|
|
4476
4530
|
}
|
|
4477
|
-
},
|
|
4531
|
+
}, _callee3);
|
|
4478
4532
|
}));
|
|
4479
4533
|
return _initIngest.apply(this, arguments);
|
|
4480
4534
|
}
|
|
4535
|
+
function _ensureFreshToken() {
|
|
4536
|
+
return _ensureFreshToken2.apply(this, arguments);
|
|
4537
|
+
}
|
|
4538
|
+
function _ensureFreshToken2() {
|
|
4539
|
+
_ensureFreshToken2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee4() {
|
|
4540
|
+
return _regeneratorRuntime().wrap(function _callee4$(_context4) {
|
|
4541
|
+
while (1) switch (_context4.prev = _context4.next) {
|
|
4542
|
+
case 0:
|
|
4543
|
+
if (!_token) {
|
|
4544
|
+
_context4.next = 2;
|
|
4545
|
+
break;
|
|
4546
|
+
}
|
|
4547
|
+
return _context4.abrupt("return");
|
|
4548
|
+
case 2:
|
|
4549
|
+
if (!(_warehouseId && _dataSourceId && _dataSourceSecret)) {
|
|
4550
|
+
_context4.next = 5;
|
|
4551
|
+
break;
|
|
4552
|
+
}
|
|
4553
|
+
_context4.next = 5;
|
|
4554
|
+
return _doAuthorize(_warehouseId, _dataSourceId, _dataSourceSecret);
|
|
4555
|
+
case 5:
|
|
4556
|
+
case "end":
|
|
4557
|
+
return _context4.stop();
|
|
4558
|
+
}
|
|
4559
|
+
}, _callee4);
|
|
4560
|
+
}));
|
|
4561
|
+
return _ensureFreshToken2.apply(this, arguments);
|
|
4562
|
+
}
|
|
4481
4563
|
function trackEvent(eventType, payload) {
|
|
4482
4564
|
if (!_token) return;
|
|
4483
4565
|
var event = _objectSpread2({
|
|
@@ -4491,49 +4573,75 @@ function trackEvent(eventType, payload) {
|
|
|
4491
4573
|
}
|
|
4492
4574
|
}
|
|
4493
4575
|
function flush() {
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4576
|
+
return _flush.apply(this, arguments);
|
|
4577
|
+
}
|
|
4578
|
+
function _flush() {
|
|
4579
|
+
_flush = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee5() {
|
|
4580
|
+
var events, body, headers, blob;
|
|
4581
|
+
return _regeneratorRuntime().wrap(function _callee5$(_context5) {
|
|
4582
|
+
while (1) switch (_context5.prev = _context5.next) {
|
|
4583
|
+
case 0:
|
|
4584
|
+
_context5.next = 2;
|
|
4585
|
+
return _ensureFreshToken();
|
|
4586
|
+
case 2:
|
|
4587
|
+
if (!(!_token || _buffer.length === 0)) {
|
|
4588
|
+
_context5.next = 4;
|
|
4589
|
+
break;
|
|
4590
|
+
}
|
|
4591
|
+
return _context5.abrupt("return");
|
|
4592
|
+
case 4:
|
|
4593
|
+
events = _buffer.splice(0, _buffer.length);
|
|
4594
|
+
body = JSON.stringify({
|
|
4595
|
+
warehouse_id: _warehouseId,
|
|
4596
|
+
organization_id: _organizationId,
|
|
4597
|
+
events: events
|
|
4598
|
+
});
|
|
4599
|
+
headers = {
|
|
4600
|
+
'Content-Type': 'application/json',
|
|
4601
|
+
Authorization: "Bearer ".concat(_token)
|
|
4602
|
+
};
|
|
4603
|
+
if (navigator.sendBeacon) {
|
|
4604
|
+
blob = new Blob([body], {
|
|
4605
|
+
type: 'application/json'
|
|
4606
|
+
});
|
|
4607
|
+
navigator.sendBeacon("".concat(INGEST_BASE, "/api/ingest/events"), blob);
|
|
4608
|
+
} else {
|
|
4609
|
+
fetch("".concat(INGEST_BASE, "/api/ingest/events"), {
|
|
4610
|
+
method: 'POST',
|
|
4611
|
+
headers: headers,
|
|
4612
|
+
body: body,
|
|
4613
|
+
keepalive: true
|
|
4614
|
+
})["catch"](function (err) {
|
|
4615
|
+
return console.error('[CoreOutline] Flush error:', err);
|
|
4616
|
+
});
|
|
4617
|
+
}
|
|
4618
|
+
case 8:
|
|
4619
|
+
case "end":
|
|
4620
|
+
return _context5.stop();
|
|
4621
|
+
}
|
|
4622
|
+
}, _callee5);
|
|
4623
|
+
}));
|
|
4624
|
+
return _flush.apply(this, arguments);
|
|
4520
4625
|
}
|
|
4521
|
-
function sendRrwebBatch(
|
|
4626
|
+
function sendRrwebBatch(_x7, _x8) {
|
|
4522
4627
|
return _sendRrwebBatch.apply(this, arguments);
|
|
4523
4628
|
}
|
|
4524
4629
|
function _sendRrwebBatch() {
|
|
4525
|
-
_sendRrwebBatch = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function
|
|
4526
|
-
return _regeneratorRuntime().wrap(function
|
|
4527
|
-
while (1) switch (
|
|
4630
|
+
_sendRrwebBatch = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee6(chunks, sessionId) {
|
|
4631
|
+
return _regeneratorRuntime().wrap(function _callee6$(_context6) {
|
|
4632
|
+
while (1) switch (_context6.prev = _context6.next) {
|
|
4528
4633
|
case 0:
|
|
4634
|
+
_context6.next = 2;
|
|
4635
|
+
return _ensureFreshToken();
|
|
4636
|
+
case 2:
|
|
4529
4637
|
if (!(!_token || !chunks || chunks.length === 0)) {
|
|
4530
|
-
|
|
4638
|
+
_context6.next = 4;
|
|
4531
4639
|
break;
|
|
4532
4640
|
}
|
|
4533
|
-
return
|
|
4534
|
-
case
|
|
4535
|
-
|
|
4536
|
-
|
|
4641
|
+
return _context6.abrupt("return");
|
|
4642
|
+
case 4:
|
|
4643
|
+
_context6.prev = 4;
|
|
4644
|
+
_context6.next = 7;
|
|
4537
4645
|
return fetch("".concat(INGEST_BASE, "/api/ingest/rrweb"), {
|
|
4538
4646
|
method: 'POST',
|
|
4539
4647
|
headers: {
|
|
@@ -4549,18 +4657,18 @@ function _sendRrwebBatch() {
|
|
|
4549
4657
|
}),
|
|
4550
4658
|
keepalive: true
|
|
4551
4659
|
});
|
|
4552
|
-
case 5:
|
|
4553
|
-
_context2.next = 10;
|
|
4554
|
-
break;
|
|
4555
4660
|
case 7:
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4661
|
+
_context6.next = 12;
|
|
4662
|
+
break;
|
|
4663
|
+
case 9:
|
|
4664
|
+
_context6.prev = 9;
|
|
4665
|
+
_context6.t0 = _context6["catch"](4);
|
|
4666
|
+
console.error('[CoreOutline] rrweb upload error:', _context6.t0);
|
|
4667
|
+
case 12:
|
|
4560
4668
|
case "end":
|
|
4561
|
-
return
|
|
4669
|
+
return _context6.stop();
|
|
4562
4670
|
}
|
|
4563
|
-
},
|
|
4671
|
+
}, _callee6, null, [[4, 9]]);
|
|
4564
4672
|
}));
|
|
4565
4673
|
return _sendRrwebBatch.apply(this, arguments);
|
|
4566
4674
|
}
|
package/package.json
CHANGED
|
@@ -2,18 +2,39 @@ import { v4 as uuidv4 } from 'uuid';
|
|
|
2
2
|
|
|
3
3
|
const INGEST_BASE = 'https://atlas-orchestrator.atlas.coreoutline.com';
|
|
4
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
|
|
5
7
|
|
|
6
8
|
let _token = null;
|
|
7
9
|
let _warehouseId = null;
|
|
8
10
|
let _organizationId = null;
|
|
9
11
|
let _dataSourceId = null;
|
|
12
|
+
let _dataSourceSecret = null;
|
|
10
13
|
let _buffer = [];
|
|
11
14
|
let _flushInterval = null;
|
|
15
|
+
let _refreshTimeout = null;
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
+
}
|
|
16
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) {
|
|
17
38
|
try {
|
|
18
39
|
const res = await fetch(`${INGEST_BASE}/api/ingest/authorize`, {
|
|
19
40
|
method: 'POST',
|
|
@@ -33,6 +54,9 @@ export async function initIngest(warehouseId, dataSourceId, dataSourceSecret) {
|
|
|
33
54
|
const data = await res.json();
|
|
34
55
|
_token = data.access_token;
|
|
35
56
|
_organizationId = data.organization_id;
|
|
57
|
+
|
|
58
|
+
const expiresIn = data.expires_in || 24 * 3600;
|
|
59
|
+
_scheduleTokenRefresh(expiresIn);
|
|
36
60
|
return true;
|
|
37
61
|
} catch (err) {
|
|
38
62
|
console.error('[CoreOutline] Authorization error:', err);
|
|
@@ -40,6 +64,21 @@ export async function initIngest(warehouseId, dataSourceId, dataSourceSecret) {
|
|
|
40
64
|
}
|
|
41
65
|
}
|
|
42
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
|
+
|
|
43
82
|
export function trackEvent(eventType, payload) {
|
|
44
83
|
if (!_token) return;
|
|
45
84
|
|
|
@@ -56,7 +95,8 @@ export function trackEvent(eventType, payload) {
|
|
|
56
95
|
}
|
|
57
96
|
}
|
|
58
97
|
|
|
59
|
-
export function flush() {
|
|
98
|
+
export async function flush() {
|
|
99
|
+
await _ensureFreshToken();
|
|
60
100
|
if (!_token || _buffer.length === 0) return;
|
|
61
101
|
|
|
62
102
|
const events = _buffer.splice(0, _buffer.length);
|
|
@@ -85,6 +125,7 @@ export function flush() {
|
|
|
85
125
|
}
|
|
86
126
|
|
|
87
127
|
export async function sendRrwebBatch(chunks, sessionId) {
|
|
128
|
+
await _ensureFreshToken();
|
|
88
129
|
if (!_token || !chunks || chunks.length === 0) return;
|
|
89
130
|
|
|
90
131
|
try {
|