@realtimex/realtimex-alchemy 1.0.44 → 1.0.46
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/dist/CHANGELOG.md +12 -0
- package/dist/api/index.js +7 -11
- package/dist/api/services/MinerService.js +35 -22
- package/dist/assets/index-DgmsaloF.js +125 -0
- package/dist/index.html +1 -1
- package/package.json +1 -1
- package/dist/assets/index-DKtbsbuu.js +0 -125
package/dist/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.46] - 2026-01-26
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Browser Mining**: Fixed Safari history extraction query and timestamp conversion (CFAbsoluteTime).
|
|
12
|
+
- **Browser Mining**: Updated Chrome/Edge timestamp logic to use BigInt for high-precision microsecond conversion, resolving potential checkpoint drift.
|
|
13
|
+
- **Sync Logic**: Switched history extraction order to Ascending (oldest first) to ensure checkpoints correctly advance from the start date.
|
|
14
|
+
|
|
15
|
+
## [1.0.45] - 2026-01-26
|
|
16
|
+
|
|
17
|
+
### Security
|
|
18
|
+
- **Migration Tool**: Enforced Access Token authentication for the `POST /api/migrate` endpoint and `migrate.sh` script. Database passwords are no longer accepted as a security hardening measure.
|
|
19
|
+
|
|
8
20
|
## [1.0.44] - 2026-01-26
|
|
9
21
|
|
|
10
22
|
### Added
|
package/dist/api/index.js
CHANGED
|
@@ -30,10 +30,13 @@ app.get('/health', (req, res) => {
|
|
|
30
30
|
});
|
|
31
31
|
// Run database migrations (SSE stream)
|
|
32
32
|
app.post('/api/migrate', (req, res) => {
|
|
33
|
-
const { projectId,
|
|
33
|
+
const { projectId, accessToken } = req.body;
|
|
34
34
|
if (!projectId) {
|
|
35
35
|
return res.status(400).json({ error: 'Project ID is required' });
|
|
36
36
|
}
|
|
37
|
+
if (!accessToken) {
|
|
38
|
+
return res.status(400).json({ error: 'Access token is required' });
|
|
39
|
+
}
|
|
37
40
|
// Set up SSE for streaming output
|
|
38
41
|
res.setHeader('Content-Type', 'text/event-stream');
|
|
39
42
|
res.setHeader('Cache-Control', 'no-cache');
|
|
@@ -59,22 +62,15 @@ app.post('/api/migrate', (req, res) => {
|
|
|
59
62
|
}
|
|
60
63
|
sendEvent('info', `Found script at: ${scriptPath}`);
|
|
61
64
|
sendEvent('info', `Working directory: ${projectRoot}`);
|
|
62
|
-
// Prepare environment
|
|
65
|
+
// Prepare environment with access token for Supabase CLI
|
|
63
66
|
const env = {
|
|
64
67
|
...process.env,
|
|
65
68
|
SUPABASE_PROJECT_ID: projectId,
|
|
69
|
+
SUPABASE_ACCESS_TOKEN: accessToken,
|
|
66
70
|
// Ensure PATH includes common locations for supabase CLI
|
|
67
71
|
PATH: `${process.env.PATH}:/usr/local/bin:/opt/homebrew/bin:${projectRoot}/node_modules/.bin`
|
|
68
72
|
};
|
|
69
|
-
|
|
70
|
-
if (accessToken) {
|
|
71
|
-
env.SUPABASE_ACCESS_TOKEN = accessToken;
|
|
72
|
-
sendEvent('info', 'Using access token for authentication');
|
|
73
|
-
}
|
|
74
|
-
if (dbPassword) {
|
|
75
|
-
env.SUPABASE_DB_PASSWORD = dbPassword;
|
|
76
|
-
sendEvent('info', 'Using database password for authentication');
|
|
77
|
-
}
|
|
73
|
+
sendEvent('info', 'Using access token for authentication');
|
|
78
74
|
// Track process state
|
|
79
75
|
let processCompleted = false;
|
|
80
76
|
// Spawn the migration script in its own process group
|
|
@@ -6,9 +6,9 @@ import { CONFIG } from '../config/index.js';
|
|
|
6
6
|
import { ProcessingEventService } from './ProcessingEventService.js';
|
|
7
7
|
import { UrlNormalizer } from '../utils/UrlNormalizer.js';
|
|
8
8
|
export class MinerService {
|
|
9
|
-
// Timestamp conversion constants
|
|
10
|
-
static WEBKIT_EPOCH_OFFSET_MS =
|
|
11
|
-
static SAFARI_EPOCH_OFFSET_SEC = 978307200; // Seconds between 1970-01-01
|
|
9
|
+
// Timestamp conversion constants (using BigInt for precision)
|
|
10
|
+
static WEBKIT_EPOCH_OFFSET_MS = 11644473600000n; // Milliseconds between 1601-01-01 and 1970-01-01
|
|
11
|
+
static SAFARI_EPOCH_OFFSET_SEC = 978307200; // Seconds between 1970-01-01 and 2001-01-01
|
|
12
12
|
static SANITY_CHECK_THRESHOLD = 3000000000000; // Timestamp threshold for Year ~2065, used to detect invalid/raw format timestamps
|
|
13
13
|
processingEvents = ProcessingEventService.getInstance();
|
|
14
14
|
debugMode = false;
|
|
@@ -155,20 +155,24 @@ export class MinerService {
|
|
|
155
155
|
SELECT url, title, visit_count, last_visit_date as last_visit_time
|
|
156
156
|
FROM moz_places
|
|
157
157
|
WHERE last_visit_date > ? AND url LIKE 'http%'
|
|
158
|
-
ORDER BY last_visit_date
|
|
158
|
+
ORDER BY last_visit_date ASC
|
|
159
159
|
LIMIT ?
|
|
160
160
|
`;
|
|
161
161
|
}
|
|
162
162
|
else {
|
|
163
163
|
// Chrome, Edge, Brave, Arc, Safari (usually)
|
|
164
164
|
if (source.browser === 'safari') {
|
|
165
|
-
// Safari
|
|
166
|
-
// Not fully implemented yet, but keeping placeholder
|
|
165
|
+
// Safari: Join visits and items
|
|
167
166
|
query = `
|
|
168
|
-
SELECT
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
167
|
+
SELECT
|
|
168
|
+
i.url,
|
|
169
|
+
i.title,
|
|
170
|
+
i.visit_count,
|
|
171
|
+
v.visit_time as last_visit_time
|
|
172
|
+
FROM history_visits v
|
|
173
|
+
JOIN history_items i ON v.history_item = i.id
|
|
174
|
+
WHERE v.visit_time > ?
|
|
175
|
+
ORDER BY v.visit_time ASC
|
|
172
176
|
LIMIT ?
|
|
173
177
|
`;
|
|
174
178
|
}
|
|
@@ -177,7 +181,7 @@ export class MinerService {
|
|
|
177
181
|
SELECT url, title, visit_count, last_visit_time
|
|
178
182
|
FROM urls
|
|
179
183
|
WHERE last_visit_time > ?
|
|
180
|
-
ORDER BY last_visit_time
|
|
184
|
+
ORDER BY last_visit_time ASC
|
|
181
185
|
LIMIT ?
|
|
182
186
|
`;
|
|
183
187
|
}
|
|
@@ -239,8 +243,10 @@ export class MinerService {
|
|
|
239
243
|
if (skippedDuplicates > 0 || skippedNonContent > 0 || skippedBlacklist > 0) {
|
|
240
244
|
this.debug(`URL Filtering: ${skippedDuplicates} duplicates, ${skippedNonContent} non-content, ${skippedBlacklist} blacklisted`);
|
|
241
245
|
}
|
|
242
|
-
if (
|
|
243
|
-
|
|
246
|
+
if (rows.length > 0) {
|
|
247
|
+
// Since we order ASC, the last row is the latest time in this batch
|
|
248
|
+
const lastRow = rows[rows.length - 1];
|
|
249
|
+
const newestTime = this.toUnixMs(lastRow.last_visit_time, source.browser);
|
|
244
250
|
await this.saveCheckpoint(source.path, newestTime, supabase, userId);
|
|
245
251
|
// Also update last_sync_checkpoint in settings for global tracking
|
|
246
252
|
if (userId && settings) {
|
|
@@ -261,29 +267,36 @@ export class MinerService {
|
|
|
261
267
|
return Date.now();
|
|
262
268
|
if (browser === 'firefox') {
|
|
263
269
|
// Firefox: Microseconds -> Milliseconds
|
|
264
|
-
return
|
|
270
|
+
return Number(BigInt(timestamp) / 1000n);
|
|
265
271
|
}
|
|
266
272
|
else if (browser === 'safari') {
|
|
267
|
-
// Safari: Seconds since 2001
|
|
268
|
-
|
|
273
|
+
// Safari: Seconds (float) since 2001 -> Unix Ms
|
|
274
|
+
// Safari uses floats (CFAbsoluteTime), keep as Number
|
|
275
|
+
return Math.floor((Number(timestamp) + MinerService.SAFARI_EPOCH_OFFSET_SEC) * 1000);
|
|
269
276
|
}
|
|
270
277
|
else {
|
|
271
|
-
// Chrome/Webkit: Microseconds since 1601
|
|
272
|
-
|
|
278
|
+
// Chrome/Webkit: Microseconds since 1601 -> Unix Ms
|
|
279
|
+
const ts = BigInt(timestamp);
|
|
280
|
+
const microDiff = ts - (MinerService.WEBKIT_EPOCH_OFFSET_MS * 1000n);
|
|
281
|
+
return Number(microDiff / 1000n);
|
|
273
282
|
}
|
|
274
283
|
}
|
|
275
284
|
fromUnixMs(unixMs, browser) {
|
|
276
285
|
if (!unixMs)
|
|
277
|
-
return 0;
|
|
286
|
+
return 0;
|
|
278
287
|
if (browser === 'firefox') {
|
|
279
|
-
|
|
288
|
+
// Firefox: Milliseconds -> Microseconds (BigInt)
|
|
289
|
+
return BigInt(unixMs) * 1000n;
|
|
280
290
|
}
|
|
281
291
|
else if (browser === 'safari') {
|
|
292
|
+
// Safari: Unix Ms -> Seconds since 2001 (Float)
|
|
282
293
|
return (unixMs / 1000) - MinerService.SAFARI_EPOCH_OFFSET_SEC;
|
|
283
294
|
}
|
|
284
295
|
else {
|
|
285
|
-
// Chrome/Webkit
|
|
286
|
-
|
|
296
|
+
// Chrome/Webkit: Unix Ms -> Microseconds since 1601 (BigInt)
|
|
297
|
+
// (UnixMs + OffsetMs) * 1000 = Microseconds
|
|
298
|
+
const unixBig = BigInt(unixMs);
|
|
299
|
+
return (unixBig + MinerService.WEBKIT_EPOCH_OFFSET_MS) * 1000n;
|
|
287
300
|
}
|
|
288
301
|
}
|
|
289
302
|
async getCheckpoint(browser, supabase) {
|