mcp-scraper 0.49.0 → 0.50.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 +1 -1
- package/dist/{analytics-repository-WWXVU3VW.js → analytics-repository-IKE53S3E.js} +10 -2
- package/dist/bin/api-server.cjs +573 -139
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +4 -4
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +30 -6
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +4 -4
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +3 -3
- package/dist/{chunk-MFNGUM4L.js → chunk-2XTYLJQK.js} +15 -1
- package/dist/chunk-2XTYLJQK.js.map +1 -0
- package/dist/{chunk-64WBUDPC.js → chunk-44LNKZTE.js} +32 -8
- package/dist/chunk-44LNKZTE.js.map +1 -0
- package/dist/{chunk-YW2LXLDU.js → chunk-5BEPOFKG.js} +160 -15
- package/dist/chunk-5BEPOFKG.js.map +1 -0
- package/dist/chunk-C7NN57WH.js +7 -0
- package/dist/chunk-C7NN57WH.js.map +1 -0
- package/dist/{chunk-MFE6RMIA.js → chunk-GOZIG6HD.js} +2 -2
- package/dist/{chunk-R66PJOZW.js → chunk-NNZXZTJ2.js} +2 -2
- package/dist/{chunk-6DPIE262.js → chunk-QHVFKFME.js} +2 -2
- package/dist/{chunk-PODASLGT.js → chunk-QXAY44SA.js} +2 -2
- package/dist/{chunk-ZEMHL36J.js → chunk-YMSZO62N.js} +2 -2
- package/dist/{db-A4YUPV4Z.js → db-UPSS5BGM.js} +2 -2
- package/dist/{extract-bundle-2HONVC5T.js → extract-bundle-RCTNANCH.js} +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/dist/{location-data-repository-YWFJGNIP.js → location-data-repository-UUSHH5M2.js} +3 -3
- package/dist/{server-JE7SLZLJ.js → server-3ND2EXYO.js} +364 -128
- package/dist/server-3ND2EXYO.js.map +1 -0
- package/dist/{site-extract-repository-STNOI5OH.js → site-extract-repository-3VQAALNR.js} +3 -3
- package/dist/{worker-4MHZS3OB.js → worker-KQN673JF.js} +5 -5
- package/package.json +1 -1
- package/dist/chunk-64WBUDPC.js.map +0 -1
- package/dist/chunk-MFNGUM4L.js.map +0 -1
- package/dist/chunk-XLEDHV3J.js +0 -7
- package/dist/chunk-XLEDHV3J.js.map +0 -1
- package/dist/chunk-YW2LXLDU.js.map +0 -1
- package/dist/server-JE7SLZLJ.js.map +0 -1
- /package/dist/{analytics-repository-WWXVU3VW.js.map → analytics-repository-IKE53S3E.js.map} +0 -0
- /package/dist/{chunk-MFE6RMIA.js.map → chunk-GOZIG6HD.js.map} +0 -0
- /package/dist/{chunk-R66PJOZW.js.map → chunk-NNZXZTJ2.js.map} +0 -0
- /package/dist/{chunk-6DPIE262.js.map → chunk-QHVFKFME.js.map} +0 -0
- /package/dist/{chunk-PODASLGT.js.map → chunk-QXAY44SA.js.map} +0 -0
- /package/dist/{chunk-ZEMHL36J.js.map → chunk-YMSZO62N.js.map} +0 -0
- /package/dist/{db-A4YUPV4Z.js.map → db-UPSS5BGM.js.map} +0 -0
- /package/dist/{extract-bundle-2HONVC5T.js.map → extract-bundle-RCTNANCH.js.map} +0 -0
- /package/dist/{location-data-repository-YWFJGNIP.js.map → location-data-repository-UUSHH5M2.js.map} +0 -0
- /package/dist/{site-extract-repository-STNOI5OH.js.map → site-extract-repository-3VQAALNR.js.map} +0 -0
- /package/dist/{worker-4MHZS3OB.js.map → worker-KQN673JF.js.map} +0 -0
package/dist/bin/api-server.cjs
CHANGED
|
@@ -41,11 +41,12 @@ function harvestTimeoutBudget(maxQuestions, serpOnly = false) {
|
|
|
41
41
|
const clientMs = Math.min(serverMs + CLIENT_OVER_SERVER_MARGIN_MS, VERCEL_FUNCTION_MAX_MS - 5e3);
|
|
42
42
|
return { serverMs, clientMs };
|
|
43
43
|
}
|
|
44
|
-
var VERCEL_FUNCTION_MAX_MS, CLIENT_OVER_SERVER_MARGIN_MS;
|
|
44
|
+
var VERCEL_FUNCTION_MAX_MS, DEFAULT_TOOL_CLIENT_TIMEOUT_MS, CLIENT_OVER_SERVER_MARGIN_MS;
|
|
45
45
|
var init_harvest_timeout = __esm({
|
|
46
46
|
"src/harvest-timeout.ts"() {
|
|
47
47
|
"use strict";
|
|
48
48
|
VERCEL_FUNCTION_MAX_MS = 3e5;
|
|
49
|
+
DEFAULT_TOOL_CLIENT_TIMEOUT_MS = 3e5;
|
|
49
50
|
CLIENT_OVER_SERVER_MARGIN_MS = 15e3;
|
|
50
51
|
}
|
|
51
52
|
});
|
|
@@ -4067,6 +4068,7 @@ async function migrate(options = {}) {
|
|
|
4067
4068
|
`);
|
|
4068
4069
|
await db.execute(`CREATE INDEX IF NOT EXISTS concurrency_locks_user_status ON concurrency_locks(user_id, status, expires_at)`);
|
|
4069
4070
|
await db.execute(`CREATE INDEX IF NOT EXISTS concurrency_locks_operation ON concurrency_locks(operation, status, expires_at)`);
|
|
4071
|
+
await db.execute(`CREATE INDEX IF NOT EXISTS concurrency_locks_active_user ON concurrency_locks(user_id, expires_at) WHERE status = 'active'`);
|
|
4070
4072
|
await db.execute(`
|
|
4071
4073
|
CREATE TABLE IF NOT EXISTS ledger (
|
|
4072
4074
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -4253,6 +4255,16 @@ async function migrate(options = {}) {
|
|
|
4253
4255
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4254
4256
|
)
|
|
4255
4257
|
`);
|
|
4258
|
+
await db.execute(`
|
|
4259
|
+
CREATE TABLE IF NOT EXISTS connected_account_charges (
|
|
4260
|
+
user_id INTEGER NOT NULL,
|
|
4261
|
+
connection_id TEXT NOT NULL,
|
|
4262
|
+
period_start TEXT NOT NULL,
|
|
4263
|
+
amount_mc INTEGER NOT NULL,
|
|
4264
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4265
|
+
PRIMARY KEY (user_id, connection_id, period_start)
|
|
4266
|
+
)
|
|
4267
|
+
`);
|
|
4256
4268
|
await db.execute(`
|
|
4257
4269
|
CREATE TABLE IF NOT EXISTS billing_credits (
|
|
4258
4270
|
idempotency_key TEXT PRIMARY KEY,
|
|
@@ -4441,6 +4453,7 @@ async function migrate(options = {}) {
|
|
|
4441
4453
|
)
|
|
4442
4454
|
`);
|
|
4443
4455
|
await db.execute(`CREATE INDEX IF NOT EXISTS request_events_user_created_at ON request_events(user_id, created_at DESC)`);
|
|
4456
|
+
await db.execute(`CREATE INDEX IF NOT EXISTS request_events_created_at ON request_events(created_at)`);
|
|
4444
4457
|
await db.execute(`
|
|
4445
4458
|
CREATE TABLE IF NOT EXISTS workflow_schedules (
|
|
4446
4459
|
id TEXT PRIMARY KEY,
|
|
@@ -6746,6 +6759,7 @@ var init_db = __esm({
|
|
|
6746
6759
|
"billing_debits_user_status",
|
|
6747
6760
|
"billing_events_authorization",
|
|
6748
6761
|
"billing_events_user_created_at",
|
|
6762
|
+
"concurrency_locks_active_user",
|
|
6749
6763
|
"concurrency_locks_operation",
|
|
6750
6764
|
"concurrency_locks_user_status",
|
|
6751
6765
|
"credit_lots_user_active",
|
|
@@ -6765,6 +6779,7 @@ var init_db = __esm({
|
|
|
6765
6779
|
"oauth_tokens_identity",
|
|
6766
6780
|
"page_snapshots_user_checked_at",
|
|
6767
6781
|
"prt_user_id",
|
|
6782
|
+
"request_events_created_at",
|
|
6768
6783
|
"request_events_user_created_at",
|
|
6769
6784
|
"scheduled_run_view_links_expiry",
|
|
6770
6785
|
"scheduled_run_view_links_owner_run",
|
|
@@ -23288,40 +23303,15 @@ function safeMetadata(metadata) {
|
|
|
23288
23303
|
return null;
|
|
23289
23304
|
}
|
|
23290
23305
|
}
|
|
23291
|
-
async function ensureConcurrencyGateSchema() {
|
|
23292
|
-
if (!schemaReady) {
|
|
23293
|
-
schemaReady = (async () => {
|
|
23294
|
-
const db = getDb();
|
|
23295
|
-
await db.execute(`
|
|
23296
|
-
CREATE TABLE IF NOT EXISTS concurrency_locks (
|
|
23297
|
-
id TEXT PRIMARY KEY,
|
|
23298
|
-
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
23299
|
-
operation TEXT NOT NULL,
|
|
23300
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
23301
|
-
acquired_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
23302
|
-
expires_at TEXT NOT NULL,
|
|
23303
|
-
released_at TEXT,
|
|
23304
|
-
metadata TEXT
|
|
23305
|
-
)
|
|
23306
|
-
`);
|
|
23307
|
-
await db.execute(`CREATE INDEX IF NOT EXISTS concurrency_locks_user_status ON concurrency_locks(user_id, status, expires_at)`);
|
|
23308
|
-
await db.execute(`CREATE INDEX IF NOT EXISTS concurrency_locks_operation ON concurrency_locks(operation, status, expires_at)`);
|
|
23309
|
-
})();
|
|
23310
|
-
}
|
|
23311
|
-
return schemaReady;
|
|
23312
|
-
}
|
|
23313
23306
|
async function expireConcurrencyLocksForUser(userId) {
|
|
23314
|
-
await ensureConcurrencyGateSchema();
|
|
23315
23307
|
await getDb().execute({
|
|
23316
|
-
sql: `
|
|
23317
|
-
SET status = 'expired', released_at = datetime('now')
|
|
23308
|
+
sql: `DELETE FROM concurrency_locks
|
|
23318
23309
|
WHERE user_id = ? AND status = 'active' AND expires_at <= datetime('now')`,
|
|
23319
23310
|
args: [userId]
|
|
23320
23311
|
});
|
|
23321
23312
|
}
|
|
23322
|
-
async function countActiveUsageForUser(userId) {
|
|
23323
|
-
await
|
|
23324
|
-
await expireConcurrencyLocksForUser(userId);
|
|
23313
|
+
async function countActiveUsageForUser(userId, alreadyExpired = false) {
|
|
23314
|
+
if (!alreadyExpired) await expireConcurrencyLocksForUser(userId);
|
|
23325
23315
|
const res = await getDb().execute({
|
|
23326
23316
|
sql: `SELECT
|
|
23327
23317
|
(
|
|
@@ -23341,7 +23331,6 @@ async function countActiveUsageForUser(userId) {
|
|
|
23341
23331
|
}
|
|
23342
23332
|
async function reuseExistingConcurrencyGate(userId, lockId, ttlSeconds) {
|
|
23343
23333
|
if (!/^cl_[a-f0-9]{24,32}$/.test(lockId)) return false;
|
|
23344
|
-
await ensureConcurrencyGateSchema();
|
|
23345
23334
|
await expireConcurrencyLocksForUser(userId);
|
|
23346
23335
|
const res = await getDb().execute({
|
|
23347
23336
|
sql: `UPDATE concurrency_locks
|
|
@@ -23355,13 +23344,12 @@ async function reuseExistingConcurrencyGate(userId, lockId, ttlSeconds) {
|
|
|
23355
23344
|
return Number(res.rowsAffected ?? 0) > 0;
|
|
23356
23345
|
}
|
|
23357
23346
|
async function acquireConcurrencyGate(user, operation, options = {}) {
|
|
23358
|
-
await ensureConcurrencyGateSchema();
|
|
23359
23347
|
const limit = concurrencyLimitForUser(user);
|
|
23360
23348
|
if (isConcurrencyLimitBypassed(user)) {
|
|
23361
23349
|
return { ok: true, lockId: null, active: 0, limit, operation, bypassed: true };
|
|
23362
23350
|
}
|
|
23363
23351
|
if (options.reuseLockId && await reuseExistingConcurrencyGate(user.id, options.reuseLockId, options.ttlSeconds)) {
|
|
23364
|
-
return { ok: true, lockId: null, active: await countActiveUsageForUser(user.id), limit, operation, reused: true };
|
|
23352
|
+
return { ok: true, lockId: null, active: await countActiveUsageForUser(user.id, true), limit, operation, reused: true };
|
|
23365
23353
|
}
|
|
23366
23354
|
await expireConcurrencyLocksForUser(user.id);
|
|
23367
23355
|
const lockId = `cl_${(0, import_node_crypto10.randomUUID)().replace(/-/g, "").slice(0, 24)}`;
|
|
@@ -23392,7 +23380,7 @@ async function acquireConcurrencyGate(user, operation, options = {}) {
|
|
|
23392
23380
|
limit
|
|
23393
23381
|
]
|
|
23394
23382
|
});
|
|
23395
|
-
const active = await countActiveUsageForUser(user.id);
|
|
23383
|
+
const active = await countActiveUsageForUser(user.id, true);
|
|
23396
23384
|
if (Number(res.rowsAffected ?? 0) > 0) {
|
|
23397
23385
|
return { ok: true, lockId, active, limit, operation };
|
|
23398
23386
|
}
|
|
@@ -23400,17 +23388,13 @@ async function acquireConcurrencyGate(user, operation, options = {}) {
|
|
|
23400
23388
|
}
|
|
23401
23389
|
async function releaseConcurrencyGate(lockId) {
|
|
23402
23390
|
if (!lockId) return;
|
|
23403
|
-
await ensureConcurrencyGateSchema();
|
|
23404
23391
|
await getDb().execute({
|
|
23405
|
-
sql: `
|
|
23406
|
-
SET status = 'released', released_at = datetime('now')
|
|
23407
|
-
WHERE id = ? AND status = 'active'`,
|
|
23392
|
+
sql: `DELETE FROM concurrency_locks WHERE id = ?`,
|
|
23408
23393
|
args: [lockId]
|
|
23409
23394
|
});
|
|
23410
23395
|
}
|
|
23411
23396
|
async function extendConcurrencyGate(lockId, ttlSeconds = DEFAULT_LOCK_TTL_SECONDS) {
|
|
23412
23397
|
if (!lockId) return;
|
|
23413
|
-
await ensureConcurrencyGateSchema();
|
|
23414
23398
|
await getDb().execute({
|
|
23415
23399
|
sql: `UPDATE concurrency_locks
|
|
23416
23400
|
SET expires_at = datetime('now', ?)
|
|
@@ -23418,7 +23402,7 @@ async function extendConcurrencyGate(lockId, ttlSeconds = DEFAULT_LOCK_TTL_SECON
|
|
|
23418
23402
|
args: [lockTtlModifier(ttlSeconds), lockId]
|
|
23419
23403
|
});
|
|
23420
23404
|
}
|
|
23421
|
-
var import_node_crypto10, DEFAULT_LOCK_TTL_SECONDS, DEFAULT_RETRY_AFTER_SECONDS, MAX_LOCK_TTL_SECONDS
|
|
23405
|
+
var import_node_crypto10, DEFAULT_LOCK_TTL_SECONDS, DEFAULT_RETRY_AFTER_SECONDS, MAX_LOCK_TTL_SECONDS;
|
|
23422
23406
|
var init_concurrency_gates = __esm({
|
|
23423
23407
|
"src/api/concurrency-gates.ts"() {
|
|
23424
23408
|
"use strict";
|
|
@@ -23428,7 +23412,6 @@ var init_concurrency_gates = __esm({
|
|
|
23428
23412
|
DEFAULT_LOCK_TTL_SECONDS = 15 * 60;
|
|
23429
23413
|
DEFAULT_RETRY_AFTER_SECONDS = 30;
|
|
23430
23414
|
MAX_LOCK_TTL_SECONDS = 24 * 60 * 60;
|
|
23431
|
-
schemaReady = null;
|
|
23432
23415
|
}
|
|
23433
23416
|
});
|
|
23434
23417
|
|
|
@@ -27530,7 +27513,7 @@ async function recoverSettlement(row, user, authorization, duplicate) {
|
|
|
27530
27513
|
}
|
|
27531
27514
|
}
|
|
27532
27515
|
await getDb().execute({
|
|
27533
|
-
sql: "UPDATE billing_events SET status = 'settled', settled_at = datetime('now') WHERE id = ? AND status
|
|
27516
|
+
sql: "UPDATE billing_events SET status = 'settled', settled_at = datetime('now') WHERE id = ? AND status IN ('settling', 'cost_pending')",
|
|
27534
27517
|
args: [row.id]
|
|
27535
27518
|
});
|
|
27536
27519
|
await getDb().execute({
|
|
@@ -27575,11 +27558,13 @@ async function settleScheduledRun(args) {
|
|
|
27575
27558
|
const rawCostNanos = rawCostUsd == null ? null : Math.round(rawCostUsd * USD_NANOS);
|
|
27576
27559
|
const modelMc = rawCostUsd == null ? 0 : vendorCostUsdToMc(rawCostUsd, SCHEDULED_AGENT_LLM_MARKUP_BPS);
|
|
27577
27560
|
const maximumModelMc = Math.max(0, Number(authorization.maximum_amount_mc) - SCHEDULED_RUN_BASE_MC);
|
|
27578
|
-
const
|
|
27561
|
+
const modelCostUnreported = args.modelCostStatus === "missing";
|
|
27562
|
+
const pendingReason = modelMc > maximumModelMc ? "max_credits_per_run_exceeded" : null;
|
|
27579
27563
|
const metadata = safeMetadata2({
|
|
27580
27564
|
...args.metadata,
|
|
27581
27565
|
billingPhase: "settlement",
|
|
27582
27566
|
chargeMode: "model_delta",
|
|
27567
|
+
...modelCostUnreported ? { modelCostUnreported: true } : {},
|
|
27583
27568
|
...pendingReason ? { reason: pendingReason } : {}
|
|
27584
27569
|
});
|
|
27585
27570
|
const eventId = existing?.id ?? (0, import_node_crypto14.randomUUID)();
|
|
@@ -27623,6 +27608,72 @@ async function settleScheduledRun(args) {
|
|
|
27623
27608
|
}
|
|
27624
27609
|
return recoverSettlement(existing, user, authorization, duplicateClaim);
|
|
27625
27610
|
}
|
|
27611
|
+
async function reconcileScheduledRunSettlements(limit = 25, minimumAgeHours = 24) {
|
|
27612
|
+
const stale = await getDb().execute({
|
|
27613
|
+
sql: `SELECT id, authorization_id, status, amount_mc, raw_cost_usd_nanos, multiplier_bps, metadata
|
|
27614
|
+
FROM billing_events
|
|
27615
|
+
WHERE billing_class = ? AND status = 'cost_pending'
|
|
27616
|
+
AND created_at < datetime('now', ?)
|
|
27617
|
+
ORDER BY created_at
|
|
27618
|
+
LIMIT ?`,
|
|
27619
|
+
args: [SCHEDULED_RUN_BILLING_CLASS, `-${Math.max(1, Math.trunc(minimumAgeHours))} hours`, Math.max(1, Math.trunc(limit))]
|
|
27620
|
+
});
|
|
27621
|
+
const result = { checked: stale.rows.length, settled: 0, chargedMc: 0, failed: 0 };
|
|
27622
|
+
for (const raw of stale.rows) {
|
|
27623
|
+
const row = raw;
|
|
27624
|
+
try {
|
|
27625
|
+
const authorization = await authorizationById(row.authorization_id);
|
|
27626
|
+
if (!authorization) {
|
|
27627
|
+
result.failed += 1;
|
|
27628
|
+
continue;
|
|
27629
|
+
}
|
|
27630
|
+
const cap = Math.max(0, Number(authorization.maximum_amount_mc) - SCHEDULED_RUN_BASE_MC);
|
|
27631
|
+
const chargeMc = Math.min(Math.max(0, Math.round(Number(row.amount_mc))), cap);
|
|
27632
|
+
if (chargeMc > 0) {
|
|
27633
|
+
const debit = await debitMcIdempotent(
|
|
27634
|
+
Number(authorization.user_id),
|
|
27635
|
+
chargeMc,
|
|
27636
|
+
LedgerOperation.SCHEDULED_RUN,
|
|
27637
|
+
"scheduled-run:" + authorization.id + ":model:reconciled",
|
|
27638
|
+
authorization.id + ":model"
|
|
27639
|
+
);
|
|
27640
|
+
if (!debit.ok) {
|
|
27641
|
+
result.failed += 1;
|
|
27642
|
+
continue;
|
|
27643
|
+
}
|
|
27644
|
+
}
|
|
27645
|
+
await getDb().execute({
|
|
27646
|
+
sql: `UPDATE billing_events
|
|
27647
|
+
SET status = 'settled', amount_mc = ?, settled_at = datetime('now'), metadata = ?
|
|
27648
|
+
WHERE id = ? AND status = 'cost_pending'`,
|
|
27649
|
+
args: [
|
|
27650
|
+
chargeMc,
|
|
27651
|
+
safeMetadata2({ ...metadataReason(row) ? { reason: metadataReason(row) } : {}, autoReconciled: true, cappedAtAuthorizedMaximum: chargeMc < Number(row.amount_mc) }),
|
|
27652
|
+
row.id
|
|
27653
|
+
]
|
|
27654
|
+
});
|
|
27655
|
+
await getDb().execute({
|
|
27656
|
+
sql: "UPDATE billing_authorizations SET status = 'settled', settled_at = datetime('now') WHERE id = ? AND status IN ('started', 'cost_pending')",
|
|
27657
|
+
args: [authorization.id]
|
|
27658
|
+
});
|
|
27659
|
+
result.settled += 1;
|
|
27660
|
+
result.chargedMc += chargeMc;
|
|
27661
|
+
console.error(JSON.stringify({
|
|
27662
|
+
event: "scheduled_run_settlement_reconciled",
|
|
27663
|
+
authorization_id: authorization.id,
|
|
27664
|
+
charged_mc: chargeMc,
|
|
27665
|
+
reason: metadataReason(row) ?? "unreported_cost"
|
|
27666
|
+
}));
|
|
27667
|
+
} catch (err) {
|
|
27668
|
+
result.failed += 1;
|
|
27669
|
+
console.error(JSON.stringify({
|
|
27670
|
+
event: "scheduled_run_reconciliation_failed",
|
|
27671
|
+
message: err instanceof Error ? err.message : String(err)
|
|
27672
|
+
}));
|
|
27673
|
+
}
|
|
27674
|
+
}
|
|
27675
|
+
return result;
|
|
27676
|
+
}
|
|
27626
27677
|
async function voidScheduledRunAuthorization(args) {
|
|
27627
27678
|
const user = await resolveSchedulingUser(args.identity);
|
|
27628
27679
|
const authorization = await authorizationById(args.authorizationId);
|
|
@@ -28609,28 +28660,6 @@ var init_inbox_routes = __esm({
|
|
|
28609
28660
|
});
|
|
28610
28661
|
|
|
28611
28662
|
// src/api/connected-account-billing.ts
|
|
28612
|
-
async function ensureConnectedAccountBillingSchema() {
|
|
28613
|
-
await getDb().execute(`CREATE TABLE IF NOT EXISTS connected_account_billing (
|
|
28614
|
-
user_id INTEGER PRIMARY KEY,
|
|
28615
|
-
stripe_subscription_id TEXT,
|
|
28616
|
-
stripe_subscription_item_id TEXT,
|
|
28617
|
-
price_id TEXT,
|
|
28618
|
-
quantity INTEGER NOT NULL DEFAULT 0,
|
|
28619
|
-
status TEXT NOT NULL DEFAULT 'pending',
|
|
28620
|
-
last_error_code TEXT,
|
|
28621
|
-
checked_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
28622
|
-
synced_at TEXT,
|
|
28623
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
28624
|
-
)`);
|
|
28625
|
-
await getDb().execute(`CREATE TABLE IF NOT EXISTS connected_account_charges (
|
|
28626
|
-
user_id INTEGER NOT NULL,
|
|
28627
|
-
connection_id TEXT NOT NULL,
|
|
28628
|
-
period_start TEXT NOT NULL,
|
|
28629
|
-
amount_mc INTEGER NOT NULL,
|
|
28630
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
28631
|
-
PRIMARY KEY (user_id, connection_id, period_start)
|
|
28632
|
-
)`);
|
|
28633
|
-
}
|
|
28634
28663
|
function connectedBillingPeriodStart(now = /* @__PURE__ */ new Date()) {
|
|
28635
28664
|
return `${now.getUTCFullYear()}-${String(now.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
28636
28665
|
}
|
|
@@ -28638,7 +28667,6 @@ function isBillableConnection(connection) {
|
|
|
28638
28667
|
return BILLABLE_LIFECYCLE.has(String(connection.lifecycleStatus ?? ""));
|
|
28639
28668
|
}
|
|
28640
28669
|
async function chargedConnectionIds(userId, periodStart) {
|
|
28641
|
-
await ensureConnectedAccountBillingSchema();
|
|
28642
28670
|
const result = await getDb().execute({
|
|
28643
28671
|
sql: "SELECT connection_id FROM connected_account_charges WHERE user_id = ? AND period_start = ?",
|
|
28644
28672
|
args: [Number(userId), periodStart]
|
|
@@ -28646,7 +28674,6 @@ async function chargedConnectionIds(userId, periodStart) {
|
|
|
28646
28674
|
return new Set(result.rows.map((row) => String(row.connection_id)));
|
|
28647
28675
|
}
|
|
28648
28676
|
async function getConnectedAccountBillingState2(userId) {
|
|
28649
|
-
await ensureConnectedAccountBillingSchema();
|
|
28650
28677
|
const result = await getDb().execute({
|
|
28651
28678
|
sql: "SELECT * FROM connected_account_billing WHERE user_id = ? LIMIT 1",
|
|
28652
28679
|
args: [Number(userId)]
|
|
@@ -28654,7 +28681,6 @@ async function getConnectedAccountBillingState2(userId) {
|
|
|
28654
28681
|
return result.rows[0] ? result.rows[0] : null;
|
|
28655
28682
|
}
|
|
28656
28683
|
async function setConnectedAccountBillingState2(input) {
|
|
28657
|
-
await ensureConnectedAccountBillingSchema();
|
|
28658
28684
|
await getDb().execute({
|
|
28659
28685
|
sql: `INSERT INTO connected_account_billing
|
|
28660
28686
|
(user_id, stripe_subscription_id, stripe_subscription_item_id, price_id, quantity, status, last_error_code, checked_at, synced_at, updated_at)
|
|
@@ -37670,6 +37696,89 @@ var init_paa_harvest_reconciliation = __esm({
|
|
|
37670
37696
|
}
|
|
37671
37697
|
});
|
|
37672
37698
|
|
|
37699
|
+
// src/api/retention-sweeps.ts
|
|
37700
|
+
function cutoffFor(rule) {
|
|
37701
|
+
const cutoff = new Date(Date.now() - rule.retentionDays * 24 * 60 * 60 * 1e3);
|
|
37702
|
+
if (rule.timestampFormat === "iso8601") return cutoff.toISOString();
|
|
37703
|
+
return cutoff.toISOString().replace("T", " ").slice(0, 19);
|
|
37704
|
+
}
|
|
37705
|
+
async function tableExists(table) {
|
|
37706
|
+
const res = await getDb().execute({
|
|
37707
|
+
sql: "SELECT 1 FROM sqlite_master WHERE type='table' AND name = ? LIMIT 1",
|
|
37708
|
+
args: [table]
|
|
37709
|
+
});
|
|
37710
|
+
return res.rows.length > 0;
|
|
37711
|
+
}
|
|
37712
|
+
async function sweepRule(rule, maxDeletes) {
|
|
37713
|
+
if (!await tableExists(rule.table)) return 0;
|
|
37714
|
+
const filter = rule.where ? ` AND ${rule.where}` : "";
|
|
37715
|
+
const cutoff = cutoffFor(rule);
|
|
37716
|
+
let deleted = 0;
|
|
37717
|
+
while (deleted < maxDeletes) {
|
|
37718
|
+
const res = await getDb().execute({
|
|
37719
|
+
sql: `DELETE FROM ${rule.table} WHERE rowid IN (
|
|
37720
|
+
SELECT rowid FROM ${rule.table}
|
|
37721
|
+
WHERE ${rule.timestampColumn} < ?${filter}
|
|
37722
|
+
LIMIT ?)`,
|
|
37723
|
+
args: [cutoff, Math.min(DEFAULT_BATCH, maxDeletes - deleted)]
|
|
37724
|
+
});
|
|
37725
|
+
const n = Number(res.rowsAffected ?? 0);
|
|
37726
|
+
deleted += n;
|
|
37727
|
+
if (n === 0) break;
|
|
37728
|
+
}
|
|
37729
|
+
return deleted;
|
|
37730
|
+
}
|
|
37731
|
+
async function sweepRetention(options = {}) {
|
|
37732
|
+
const maxDeletes = options.maxDeletesPerRule ?? DEFAULT_MAX_DELETES_PER_RULE;
|
|
37733
|
+
const deadline = Date.now() + (options.deadlineMs ?? DEFAULT_SWEEP_DEADLINE_MS);
|
|
37734
|
+
const perTable = {};
|
|
37735
|
+
let deleted = 0;
|
|
37736
|
+
let truncated = false;
|
|
37737
|
+
for (const rule of RETENTION_RULES) {
|
|
37738
|
+
const key = rule.where ? `${rule.table}:${rule.where}` : rule.table;
|
|
37739
|
+
if (Date.now() >= deadline) {
|
|
37740
|
+
truncated = true;
|
|
37741
|
+
break;
|
|
37742
|
+
}
|
|
37743
|
+
try {
|
|
37744
|
+
const n = await sweepRule(rule, maxDeletes);
|
|
37745
|
+
perTable[key] = n;
|
|
37746
|
+
deleted += n;
|
|
37747
|
+
if (n >= maxDeletes) truncated = true;
|
|
37748
|
+
} catch (err) {
|
|
37749
|
+
perTable[key] = -1;
|
|
37750
|
+
console.error(JSON.stringify({
|
|
37751
|
+
event: "retention_sweep_failed",
|
|
37752
|
+
table: rule.table,
|
|
37753
|
+
message: err instanceof Error ? err.message : String(err)
|
|
37754
|
+
}));
|
|
37755
|
+
}
|
|
37756
|
+
}
|
|
37757
|
+
return { deleted, perTable, truncated };
|
|
37758
|
+
}
|
|
37759
|
+
var REPORTING_WINDOW_DAYS, TELEMETRY_RETENTION_DAYS, FAILURE_RETENTION_DAYS, RETENTION_RULES, DEFAULT_BATCH, DEFAULT_MAX_DELETES_PER_RULE, DEFAULT_SWEEP_DEADLINE_MS;
|
|
37760
|
+
var init_retention_sweeps = __esm({
|
|
37761
|
+
"src/api/retention-sweeps.ts"() {
|
|
37762
|
+
"use strict";
|
|
37763
|
+
init_db();
|
|
37764
|
+
REPORTING_WINDOW_DAYS = 30;
|
|
37765
|
+
TELEMETRY_RETENTION_DAYS = 45;
|
|
37766
|
+
FAILURE_RETENTION_DAYS = 90;
|
|
37767
|
+
RETENTION_RULES = [
|
|
37768
|
+
{ table: "request_events", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS },
|
|
37769
|
+
{ table: "kernel_session_log", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS },
|
|
37770
|
+
{ table: "vendor_usage_log", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS },
|
|
37771
|
+
{ table: "harvest_attempts", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS, timestampFormat: "iso8601" },
|
|
37772
|
+
{ table: "browser_agent_actions", timestampColumn: "created_at", retentionDays: REPORTING_WINDOW_DAYS },
|
|
37773
|
+
{ table: "jobs", timestampColumn: "created_at", retentionDays: TELEMETRY_RETENTION_DAYS, where: "status = 'done'" },
|
|
37774
|
+
{ table: "jobs", timestampColumn: "created_at", retentionDays: FAILURE_RETENTION_DAYS, where: "status = 'failed'" }
|
|
37775
|
+
];
|
|
37776
|
+
DEFAULT_BATCH = 500;
|
|
37777
|
+
DEFAULT_MAX_DELETES_PER_RULE = 1e3;
|
|
37778
|
+
DEFAULT_SWEEP_DEADLINE_MS = 15e3;
|
|
37779
|
+
}
|
|
37780
|
+
});
|
|
37781
|
+
|
|
37673
37782
|
// src/api/directory-workflow-reconciliation.ts
|
|
37674
37783
|
async function reconcileDirectoryWorkflowSettlements(limit = 25) {
|
|
37675
37784
|
const expiredStarts = await listExpiredDirectoryWorkflowStarts(limit);
|
|
@@ -41371,7 +41480,7 @@ var PACKAGE_VERSION;
|
|
|
41371
41480
|
var init_version = __esm({
|
|
41372
41481
|
"src/version.ts"() {
|
|
41373
41482
|
"use strict";
|
|
41374
|
-
PACKAGE_VERSION = "0.
|
|
41483
|
+
PACKAGE_VERSION = "0.50.0";
|
|
41375
41484
|
}
|
|
41376
41485
|
});
|
|
41377
41486
|
|
|
@@ -46762,6 +46871,29 @@ var init_paa_mcp_server = __esm({
|
|
|
46762
46871
|
});
|
|
46763
46872
|
|
|
46764
46873
|
// src/mcp/http-mcp-tool-executor.ts
|
|
46874
|
+
function unclassifiedTransportFailure(path6, err) {
|
|
46875
|
+
const name = err instanceof Error ? err.name : typeof err;
|
|
46876
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
46877
|
+
console.error(JSON.stringify({
|
|
46878
|
+
event: "mcp_executor_transport_failed",
|
|
46879
|
+
path: path6,
|
|
46880
|
+
error_name: name,
|
|
46881
|
+
error_message: sanitizeVendorName(detail).slice(0, 400)
|
|
46882
|
+
}));
|
|
46883
|
+
return {
|
|
46884
|
+
content: [{
|
|
46885
|
+
type: "text",
|
|
46886
|
+
text: JSON.stringify({
|
|
46887
|
+
error: "service_unavailable",
|
|
46888
|
+
error_type: name,
|
|
46889
|
+
retryable: true,
|
|
46890
|
+
path: path6,
|
|
46891
|
+
message: publicErrorMessage("service_unavailable")
|
|
46892
|
+
})
|
|
46893
|
+
}],
|
|
46894
|
+
isError: true
|
|
46895
|
+
};
|
|
46896
|
+
}
|
|
46765
46897
|
function youtubeVideoIdFromUrl(url) {
|
|
46766
46898
|
if (!url) return null;
|
|
46767
46899
|
try {
|
|
@@ -46871,7 +47003,7 @@ var init_http_mcp_tool_executor = __esm({
|
|
|
46871
47003
|
const rawOverride = process.env.MCP_SCRAPER_HTTP_TIMEOUT_MS;
|
|
46872
47004
|
const parsedOverride = rawOverride === void 0 ? NaN : Number(rawOverride);
|
|
46873
47005
|
this.httpTimeoutOverrideMs = Number.isFinite(parsedOverride) && parsedOverride > 0 ? parsedOverride : null;
|
|
46874
|
-
this.timeoutMs = this.httpTimeoutOverrideMs ??
|
|
47006
|
+
this.timeoutMs = this.httpTimeoutOverrideMs ?? DEFAULT_TOOL_CLIENT_TIMEOUT_MS;
|
|
46875
47007
|
const configuredSerpIntelligenceTimeoutMs = Number(process.env.MCP_SCRAPER_SERP_INTELLIGENCE_HTTP_TIMEOUT_MS ?? this.timeoutMs);
|
|
46876
47008
|
this.serpIntelligenceTimeoutMs = Number.isFinite(configuredSerpIntelligenceTimeoutMs) && configuredSerpIntelligenceTimeoutMs > 0 ? configuredSerpIntelligenceTimeoutMs : this.timeoutMs;
|
|
46877
47009
|
}
|
|
@@ -46910,7 +47042,7 @@ var init_http_mcp_tool_executor = __esm({
|
|
|
46910
47042
|
isError: true
|
|
46911
47043
|
};
|
|
46912
47044
|
}
|
|
46913
|
-
return
|
|
47045
|
+
return unclassifiedTransportFailure(path6, err);
|
|
46914
47046
|
}
|
|
46915
47047
|
}
|
|
46916
47048
|
async callConnectedMutation(path6, body, idempotencyKey3, timeoutMs = this.timeoutMs) {
|
|
@@ -46944,8 +47076,8 @@ var init_http_mcp_tool_executor = __esm({
|
|
|
46944
47076
|
return { content: [{ type: "text", text: JSON.stringify(httpErrorPayload(path6, res, data)) }], isError: true };
|
|
46945
47077
|
}
|
|
46946
47078
|
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
46947
|
-
} catch {
|
|
46948
|
-
return
|
|
47079
|
+
} catch (err) {
|
|
47080
|
+
return unclassifiedTransportFailure(path6, err);
|
|
46949
47081
|
}
|
|
46950
47082
|
}
|
|
46951
47083
|
async getTextArtifact(path6, maxBytes, timeoutMs = this.timeoutMs) {
|
|
@@ -46982,7 +47114,7 @@ var init_http_mcp_tool_executor = __esm({
|
|
|
46982
47114
|
}]
|
|
46983
47115
|
};
|
|
46984
47116
|
} catch (err) {
|
|
46985
|
-
return
|
|
47117
|
+
return unclassifiedTransportFailure(path6, err);
|
|
46986
47118
|
}
|
|
46987
47119
|
}
|
|
46988
47120
|
harvestPaa(input) {
|
|
@@ -53305,13 +53437,21 @@ async function setBilledMc(id, billedMc) {
|
|
|
53305
53437
|
args: [Math.round(billedMc), id]
|
|
53306
53438
|
});
|
|
53307
53439
|
}
|
|
53308
|
-
async function markSessionClosed(id) {
|
|
53440
|
+
async function markSessionClosed(id, runtimeClosed = true) {
|
|
53309
53441
|
const db = getDb();
|
|
53310
53442
|
await db.execute({
|
|
53311
|
-
sql: `UPDATE browser_agent_sessions SET status =
|
|
53312
|
-
args: [id]
|
|
53443
|
+
sql: `UPDATE browser_agent_sessions SET status = ?, closed_at = datetime('now') WHERE id = ?`,
|
|
53444
|
+
args: [runtimeClosed ? "closed" : ORPHANED_SESSION_STATUS, id]
|
|
53313
53445
|
});
|
|
53314
53446
|
}
|
|
53447
|
+
async function listOrphanedSessionRows(limit = 50) {
|
|
53448
|
+
const db = getDb();
|
|
53449
|
+
const res = await db.execute({
|
|
53450
|
+
sql: `SELECT * FROM browser_agent_sessions WHERE status = ? ORDER BY closed_at LIMIT ?`,
|
|
53451
|
+
args: [ORPHANED_SESSION_STATUS, Math.max(1, Math.min(200, Math.round(limit)))]
|
|
53452
|
+
});
|
|
53453
|
+
return res.rows;
|
|
53454
|
+
}
|
|
53315
53455
|
async function recordAction(input) {
|
|
53316
53456
|
const db = getDb();
|
|
53317
53457
|
await db.execute({
|
|
@@ -53358,7 +53498,7 @@ async function listReplayRows(sessionId) {
|
|
|
53358
53498
|
});
|
|
53359
53499
|
return res.rows;
|
|
53360
53500
|
}
|
|
53361
|
-
var import_node_crypto28, _ready2, _migrationPromise2;
|
|
53501
|
+
var import_node_crypto28, _ready2, _migrationPromise2, ORPHANED_SESSION_STATUS;
|
|
53362
53502
|
var init_browser_agent_db = __esm({
|
|
53363
53503
|
"src/api/browser-agent-db.ts"() {
|
|
53364
53504
|
"use strict";
|
|
@@ -53366,6 +53506,7 @@ var init_browser_agent_db = __esm({
|
|
|
53366
53506
|
init_db();
|
|
53367
53507
|
_ready2 = false;
|
|
53368
53508
|
_migrationPromise2 = null;
|
|
53509
|
+
ORPHANED_SESSION_STATUS = "runtime_close_pending";
|
|
53369
53510
|
}
|
|
53370
53511
|
});
|
|
53371
53512
|
|
|
@@ -54522,6 +54663,21 @@ async function charge(sessionId, userId, _startedAtMs) {
|
|
|
54522
54663
|
if (res.ok) await setBilledMc(sessionId, owed);
|
|
54523
54664
|
}
|
|
54524
54665
|
}
|
|
54666
|
+
async function closeRuntimeSession(runtimeSessionId, sessionId) {
|
|
54667
|
+
try {
|
|
54668
|
+
await closeSession(runtimeSessionId);
|
|
54669
|
+
return true;
|
|
54670
|
+
} catch (err) {
|
|
54671
|
+
console.error(JSON.stringify({
|
|
54672
|
+
event: "browser_runtime_close_failed",
|
|
54673
|
+
session_id: sessionId,
|
|
54674
|
+
runtime_session_id: runtimeSessionId,
|
|
54675
|
+
error_name: err instanceof Error ? err.name : typeof err,
|
|
54676
|
+
message: sanitizeVendorName(err instanceof Error ? err.message : String(err)).slice(0, 300)
|
|
54677
|
+
}));
|
|
54678
|
+
return false;
|
|
54679
|
+
}
|
|
54680
|
+
}
|
|
54525
54681
|
async function closeAndSettleSession(row) {
|
|
54526
54682
|
if (row.user_id != null) {
|
|
54527
54683
|
try {
|
|
@@ -54529,15 +54685,24 @@ async function closeAndSettleSession(row) {
|
|
|
54529
54685
|
} catch {
|
|
54530
54686
|
}
|
|
54531
54687
|
}
|
|
54532
|
-
|
|
54533
|
-
|
|
54534
|
-
} catch {
|
|
54535
|
-
}
|
|
54536
|
-
await markSessionClosed(row.id).catch(() => {
|
|
54688
|
+
const runtimeClosed = await closeRuntimeSession(row.runtime_session_id, row.id);
|
|
54689
|
+
await markSessionClosed(row.id, runtimeClosed).catch(() => {
|
|
54537
54690
|
});
|
|
54538
54691
|
await releaseConcurrencyGate(row.concurrency_lock_id).catch(() => {
|
|
54539
54692
|
});
|
|
54540
54693
|
}
|
|
54694
|
+
async function retryOrphanedBrowserSessions(limit = 50) {
|
|
54695
|
+
const rows = await listOrphanedSessionRows(limit);
|
|
54696
|
+
let recovered = 0;
|
|
54697
|
+
for (const row of rows) {
|
|
54698
|
+
if (await closeRuntimeSession(row.runtime_session_id, row.id)) {
|
|
54699
|
+
await markSessionClosed(row.id, true).catch(() => {
|
|
54700
|
+
});
|
|
54701
|
+
recovered++;
|
|
54702
|
+
}
|
|
54703
|
+
}
|
|
54704
|
+
return { recovered, stillOpen: rows.length - recovered };
|
|
54705
|
+
}
|
|
54541
54706
|
async function completeAuthConnection(connectionId) {
|
|
54542
54707
|
const connection = await getAuthConnectionRow(connectionId);
|
|
54543
54708
|
if (!connection) return { ok: false, error: "not found" };
|
|
@@ -54562,7 +54727,7 @@ async function reapIdleBrowserSessions(idleSeconds = 120) {
|
|
|
54562
54727
|
function publicSession(row) {
|
|
54563
54728
|
return {
|
|
54564
54729
|
session_id: row.id,
|
|
54565
|
-
status: row.status,
|
|
54730
|
+
status: row.status === ORPHANED_SESSION_STATUS ? "closed" : row.status,
|
|
54566
54731
|
label: row.label,
|
|
54567
54732
|
watch_url: `/console/${row.id}`,
|
|
54568
54733
|
created_at: row.created_at,
|
|
@@ -59447,6 +59612,38 @@ var init_editorial_reading_room_routes = __esm({
|
|
|
59447
59612
|
}
|
|
59448
59613
|
});
|
|
59449
59614
|
|
|
59615
|
+
// src/api/schema-presence.ts
|
|
59616
|
+
async function loadSchemaObjects() {
|
|
59617
|
+
const res = await getDb().execute(
|
|
59618
|
+
"SELECT name FROM sqlite_master WHERE type IN ('table', 'index')"
|
|
59619
|
+
);
|
|
59620
|
+
return new Set(res.rows.map((row) => String(row.name)));
|
|
59621
|
+
}
|
|
59622
|
+
function invalidateSchemaPresence() {
|
|
59623
|
+
cache = null;
|
|
59624
|
+
}
|
|
59625
|
+
async function schemaObjectsPresent(names) {
|
|
59626
|
+
if (names.length === 0) return true;
|
|
59627
|
+
try {
|
|
59628
|
+
if (!cache) cache = loadSchemaObjects();
|
|
59629
|
+
const present = await cache;
|
|
59630
|
+
if (names.every((name) => present.has(name))) return true;
|
|
59631
|
+
invalidateSchemaPresence();
|
|
59632
|
+
return false;
|
|
59633
|
+
} catch {
|
|
59634
|
+
invalidateSchemaPresence();
|
|
59635
|
+
return false;
|
|
59636
|
+
}
|
|
59637
|
+
}
|
|
59638
|
+
var cache;
|
|
59639
|
+
var init_schema_presence = __esm({
|
|
59640
|
+
"src/api/schema-presence.ts"() {
|
|
59641
|
+
"use strict";
|
|
59642
|
+
init_db();
|
|
59643
|
+
cache = null;
|
|
59644
|
+
}
|
|
59645
|
+
});
|
|
59646
|
+
|
|
59450
59647
|
// src/api/commons-linksets.ts
|
|
59451
59648
|
function normalizeCommonsClaims(input) {
|
|
59452
59649
|
if (input === void 0) return void 0;
|
|
@@ -59632,7 +59829,11 @@ var init_commons_linksets = __esm({
|
|
|
59632
59829
|
|
|
59633
59830
|
// src/api/commons-repository.ts
|
|
59634
59831
|
async function ensureCommonsSchema() {
|
|
59635
|
-
if (
|
|
59832
|
+
if (schemaReady) return;
|
|
59833
|
+
if (await schemaObjectsPresent(COMMONS_SCHEMA_OBJECTS)) {
|
|
59834
|
+
schemaReady = true;
|
|
59835
|
+
return;
|
|
59836
|
+
}
|
|
59636
59837
|
const db = getDb();
|
|
59637
59838
|
await db.batch([
|
|
59638
59839
|
{
|
|
@@ -59853,7 +60054,7 @@ async function ensureCommonsSchema() {
|
|
|
59853
60054
|
args: [COMMONS_SCHEMA_VERSION]
|
|
59854
60055
|
}
|
|
59855
60056
|
], "write");
|
|
59856
|
-
|
|
60057
|
+
schemaReady = true;
|
|
59857
60058
|
}
|
|
59858
60059
|
async function searchCommonsEntities(filters = {}, userId) {
|
|
59859
60060
|
await ensureCommonsSchema();
|
|
@@ -61568,12 +61769,13 @@ function jsonLikeValue(value) {
|
|
|
61568
61769
|
function escapeLike(value) {
|
|
61569
61770
|
return value.replace(/[%_]/g, "");
|
|
61570
61771
|
}
|
|
61571
|
-
var import_node_crypto35, COMMONS_SCHEMA_VERSION, DEFAULT_COMMONS_BASE_URL, DEFAULT_ENTITY_TYPE, COMMONS_ENTITY_PROFILES,
|
|
61772
|
+
var import_node_crypto35, COMMONS_SCHEMA_VERSION, DEFAULT_COMMONS_BASE_URL, DEFAULT_ENTITY_TYPE, COMMONS_ENTITY_PROFILES, schemaReady, COMMONS_SCHEMA_OBJECTS, CommonsRepositoryError;
|
|
61572
61773
|
var init_commons_repository = __esm({
|
|
61573
61774
|
"src/api/commons-repository.ts"() {
|
|
61574
61775
|
"use strict";
|
|
61575
61776
|
import_node_crypto35 = require("crypto");
|
|
61576
61777
|
init_db();
|
|
61778
|
+
init_schema_presence();
|
|
61577
61779
|
init_rates();
|
|
61578
61780
|
init_commons_linksets();
|
|
61579
61781
|
COMMONS_SCHEMA_VERSION = "2026-08-05.1";
|
|
@@ -61724,7 +61926,32 @@ var init_commons_repository = __esm({
|
|
|
61724
61926
|
]
|
|
61725
61927
|
}
|
|
61726
61928
|
};
|
|
61727
|
-
|
|
61929
|
+
schemaReady = false;
|
|
61930
|
+
COMMONS_SCHEMA_OBJECTS = [
|
|
61931
|
+
"schema_migrations",
|
|
61932
|
+
"commons_entities",
|
|
61933
|
+
"commons_claims",
|
|
61934
|
+
"commons_proposals",
|
|
61935
|
+
"commons_ledger",
|
|
61936
|
+
"commons_saved_filters",
|
|
61937
|
+
"commons_index_documents",
|
|
61938
|
+
"commons_publications",
|
|
61939
|
+
"commons_publication_editions",
|
|
61940
|
+
"commons_entities_status_updated",
|
|
61941
|
+
"commons_entities_type_updated",
|
|
61942
|
+
"commons_entities_source_domain",
|
|
61943
|
+
"commons_claims_subject_predicate",
|
|
61944
|
+
"commons_claims_object_entity",
|
|
61945
|
+
"commons_proposals_user_created",
|
|
61946
|
+
"commons_proposals_entity_created",
|
|
61947
|
+
"commons_ledger_entity_created",
|
|
61948
|
+
"commons_ledger_user_created",
|
|
61949
|
+
"commons_saved_filters_user_updated",
|
|
61950
|
+
"commons_index_documents_status_updated",
|
|
61951
|
+
"commons_index_documents_entity_type",
|
|
61952
|
+
"commons_publications_updated",
|
|
61953
|
+
"commons_publication_editions_publication_published"
|
|
61954
|
+
];
|
|
61728
61955
|
CommonsRepositoryError = class extends Error {
|
|
61729
61956
|
constructor(code, message, httpStatus = 400) {
|
|
61730
61957
|
super(message);
|
|
@@ -62681,7 +62908,10 @@ var init_analytics_attribution = __esm({
|
|
|
62681
62908
|
// src/api/analytics-repository.ts
|
|
62682
62909
|
var analytics_repository_exports = {};
|
|
62683
62910
|
__export(analytics_repository_exports, {
|
|
62911
|
+
ANALYTICS_CONTENT_SORTS: () => ANALYTICS_CONTENT_SORTS,
|
|
62684
62912
|
AnalyticsRepositoryError: () => AnalyticsRepositoryError,
|
|
62913
|
+
ENGAGED_SESSION_MS: () => ENGAGED_SESSION_MS,
|
|
62914
|
+
MAX_ENGAGED_MS: () => MAX_ENGAGED_MS,
|
|
62685
62915
|
analyticsAcquisition: () => analyticsAcquisition,
|
|
62686
62916
|
analyticsBusinessMetrics: () => analyticsBusinessMetrics,
|
|
62687
62917
|
analyticsChannelBreakdown: () => analyticsChannelBreakdown,
|
|
@@ -62725,6 +62955,7 @@ __export(analytics_repository_exports, {
|
|
|
62725
62955
|
migrateAnalytics: () => migrateAnalytics,
|
|
62726
62956
|
normalizeAnalyticsPath: () => normalizeAnalyticsPath,
|
|
62727
62957
|
normalizeAnalyticsUrl: () => normalizeAnalyticsUrl,
|
|
62958
|
+
normalizeContentOptions: () => normalizeContentOptions,
|
|
62728
62959
|
normalizeObservedHostname: () => normalizeObservedHostname,
|
|
62729
62960
|
queueAnalyticsActivation: () => queueAnalyticsActivation,
|
|
62730
62961
|
queueAnalyticsFormDelivery: () => queueAnalyticsFormDelivery,
|
|
@@ -63075,6 +63306,12 @@ async function migrateAnalytics() {
|
|
|
63075
63306
|
await client2.query(
|
|
63076
63307
|
`ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS utm_content text`
|
|
63077
63308
|
);
|
|
63309
|
+
await client2.query(
|
|
63310
|
+
`ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS engaged_ms integer`
|
|
63311
|
+
);
|
|
63312
|
+
await client2.query(
|
|
63313
|
+
`ALTER TABLE analytics_events ADD COLUMN IF NOT EXISTS scroll_depth smallint`
|
|
63314
|
+
);
|
|
63078
63315
|
await client2.query(
|
|
63079
63316
|
`ALTER TABLE analytics_conversions ADD COLUMN IF NOT EXISTS person_id uuid REFERENCES analytics_people(id) ON DELETE SET NULL`
|
|
63080
63317
|
);
|
|
@@ -63600,6 +63837,12 @@ async function setAnalyticsPixelDomainState(input) {
|
|
|
63600
63837
|
404
|
|
63601
63838
|
);
|
|
63602
63839
|
}
|
|
63840
|
+
function boundedEngagedMs(value) {
|
|
63841
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.min(Math.trunc(value), MAX_ENGAGED_MS) : null;
|
|
63842
|
+
}
|
|
63843
|
+
function boundedScrollDepth(value) {
|
|
63844
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.min(Math.trunc(value), 100) : null;
|
|
63845
|
+
}
|
|
63603
63846
|
function normalizeAnalyticsPath(value) {
|
|
63604
63847
|
if (!value) return "/";
|
|
63605
63848
|
try {
|
|
@@ -63787,10 +64030,11 @@ async function ingestAnalyticsEvents(input) {
|
|
|
63787
64030
|
id, event_id, site_id, pixel_id, hostname, visitor_id, session_id, event_name,
|
|
63788
64031
|
occurred_at, path, canonical_url, title, referrer, source, medium, campaign,
|
|
63789
64032
|
device_class, properties, country_code, region_code, click_ids, person_id,
|
|
63790
|
-
channel_family, platform, campaign_id, ad_set_id, ad_id, creative_id, placement, utm_term, utm_content
|
|
64033
|
+
channel_family, platform, campaign_id, ad_set_id, ad_id, creative_id, placement, utm_term, utm_content,
|
|
64034
|
+
engaged_ms, scroll_depth
|
|
63791
64035
|
) VALUES (
|
|
63792
64036
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18::jsonb, $19, $20, $21::jsonb, $22,
|
|
63793
|
-
$23, $24, $25, $26, $27, $28, $29, $30, $31
|
|
64037
|
+
$23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33
|
|
63794
64038
|
) ON CONFLICT(site_id, event_id) DO NOTHING`,
|
|
63795
64039
|
[
|
|
63796
64040
|
(0, import_node_crypto37.randomUUID)(),
|
|
@@ -63823,7 +64067,9 @@ async function ingestAnalyticsEvents(input) {
|
|
|
63823
64067
|
attribution.creativeId,
|
|
63824
64068
|
attribution.placement,
|
|
63825
64069
|
attribution.term,
|
|
63826
|
-
attribution.content
|
|
64070
|
+
attribution.content,
|
|
64071
|
+
boundedEngagedMs(event2.engagedMs),
|
|
64072
|
+
boundedScrollDepth(event2.scrollDepth)
|
|
63827
64073
|
]
|
|
63828
64074
|
);
|
|
63829
64075
|
if (inserted.rowCount) accepted += 1;
|
|
@@ -64334,10 +64580,21 @@ async function analyticsChannelBreakdown(siteId, userId, filters) {
|
|
|
64334
64580
|
dataFreshThrough: (/* @__PURE__ */ new Date()).toISOString()
|
|
64335
64581
|
};
|
|
64336
64582
|
}
|
|
64337
|
-
|
|
64583
|
+
function normalizeContentOptions(options) {
|
|
64584
|
+
const sort = ANALYTICS_CONTENT_SORTS.includes(options?.sort) ? options.sort : "pageviews";
|
|
64585
|
+
return {
|
|
64586
|
+
sort,
|
|
64587
|
+
direction: options?.direction === "asc" ? "asc" : "desc",
|
|
64588
|
+
search: options?.search?.trim().slice(0, 300) || null
|
|
64589
|
+
};
|
|
64590
|
+
}
|
|
64591
|
+
async function analyticsContent(siteId, userId, filters, page, options) {
|
|
64338
64592
|
const db = getAnalyticsPool();
|
|
64339
64593
|
await accessFor(db, siteId, userId);
|
|
64340
64594
|
const query = eventFilterSql(siteId, filters);
|
|
64595
|
+
const table = normalizeContentOptions(options);
|
|
64596
|
+
const searchParam = `$${query.values.length + 1}`;
|
|
64597
|
+
const order = table.sort === "path" ? `metrics.path ${table.direction === "asc" ? "ASC" : "DESC"}` : `${table.sort} ${table.direction === "asc" ? "ASC" : "DESC"} NULLS LAST, metrics.path ASC`;
|
|
64341
64598
|
const result = await db.query(
|
|
64342
64599
|
`WITH filtered AS (
|
|
64343
64600
|
SELECT e.* FROM analytics_events e WHERE ${query.where}
|
|
@@ -64345,7 +64602,7 @@ async function analyticsContent(siteId, userId, filters, page) {
|
|
|
64345
64602
|
SELECT path, count(DISTINCT visitor_id)::int AS visitors,
|
|
64346
64603
|
count(DISTINCT session_id)::int AS sessions,
|
|
64347
64604
|
count(*) FILTER (WHERE event_name = 'page_view')::int AS pageviews,
|
|
64348
|
-
count(*) FILTER (WHERE event_name
|
|
64605
|
+
count(*) FILTER (WHERE event_name NOT IN ('page_view', 'page_engagement'))::int AS engagement_events,
|
|
64349
64606
|
count(*)::int AS events
|
|
64350
64607
|
FROM filtered GROUP BY path
|
|
64351
64608
|
), session_bounds AS (
|
|
@@ -64353,35 +64610,98 @@ async function analyticsContent(siteId, userId, filters, page) {
|
|
|
64353
64610
|
(array_agg(path ORDER BY occurred_at ASC, id ASC))[1] AS entrance_path,
|
|
64354
64611
|
(array_agg(path ORDER BY occurred_at DESC, id DESC))[1] AS exit_path
|
|
64355
64612
|
FROM filtered WHERE session_id IS NOT NULL GROUP BY session_id
|
|
64613
|
+
), session_pageviews AS (
|
|
64614
|
+
SELECT session_id, count(*) FILTER (WHERE event_name = 'page_view')::int AS pageviews
|
|
64615
|
+
FROM filtered WHERE session_id IS NOT NULL GROUP BY session_id
|
|
64616
|
+
), page_dwell AS (
|
|
64617
|
+
SELECT session_id, path,
|
|
64618
|
+
sum(engaged_ms)::bigint AS engaged_ms,
|
|
64619
|
+
max(scroll_depth)::int AS scroll_depth
|
|
64620
|
+
FROM filtered
|
|
64621
|
+
WHERE event_name = 'page_engagement' AND session_id IS NOT NULL
|
|
64622
|
+
GROUP BY session_id, path
|
|
64623
|
+
), dwell_metrics AS (
|
|
64624
|
+
SELECT path,
|
|
64625
|
+
avg(engaged_ms) FILTER (WHERE engaged_ms IS NOT NULL) AS avg_engaged_ms,
|
|
64626
|
+
avg(scroll_depth) FILTER (WHERE scroll_depth IS NOT NULL) AS avg_scroll,
|
|
64627
|
+
count(*) FILTER (WHERE engaged_ms IS NOT NULL)::int AS measured_pageviews
|
|
64628
|
+
FROM page_dwell GROUP BY path
|
|
64356
64629
|
), entrances AS (
|
|
64357
64630
|
SELECT entrance_path AS path, count(*)::int AS entrances FROM session_bounds GROUP BY entrance_path
|
|
64358
64631
|
), exits AS (
|
|
64359
64632
|
SELECT exit_path AS path, count(*)::int AS exits FROM session_bounds GROUP BY exit_path
|
|
64633
|
+
), bounces AS (
|
|
64634
|
+
SELECT b.entrance_path AS path, count(*)::int AS bounces
|
|
64635
|
+
FROM session_bounds b JOIN session_pageviews p USING(session_id)
|
|
64636
|
+
WHERE p.pageviews <= 1
|
|
64637
|
+
GROUP BY b.entrance_path
|
|
64638
|
+
), converted_sessions AS (
|
|
64639
|
+
SELECT DISTINCT c.session_id
|
|
64640
|
+
FROM analytics_conversions c
|
|
64641
|
+
WHERE c.site_id = $1 AND c.occurred_at >= $2::timestamptz AND c.occurred_at < $3::timestamptz
|
|
64642
|
+
AND c.session_id IS NOT NULL
|
|
64643
|
+
), path_sessions AS (
|
|
64644
|
+
SELECT DISTINCT path, session_id FROM filtered WHERE session_id IS NOT NULL
|
|
64645
|
+
), engaged AS (
|
|
64646
|
+
SELECT ps.path,
|
|
64647
|
+
count(*) FILTER (
|
|
64648
|
+
WHERE sp.pageviews >= 2
|
|
64649
|
+
OR COALESCE(pd.engaged_ms, 0) >= ${ENGAGED_SESSION_MS}
|
|
64650
|
+
OR cs.session_id IS NOT NULL
|
|
64651
|
+
)::int AS engaged_sessions
|
|
64652
|
+
FROM path_sessions ps
|
|
64653
|
+
JOIN session_pageviews sp USING(session_id)
|
|
64654
|
+
LEFT JOIN page_dwell pd ON pd.session_id = ps.session_id AND pd.path = ps.path
|
|
64655
|
+
LEFT JOIN converted_sessions cs ON cs.session_id = ps.session_id
|
|
64656
|
+
GROUP BY ps.path
|
|
64360
64657
|
), assisted AS (
|
|
64361
64658
|
SELECT f.path, count(DISTINCT c.id)::int AS conversions
|
|
64362
64659
|
FROM analytics_conversions c JOIN filtered f ON f.session_id = c.session_id
|
|
64363
64660
|
WHERE c.site_id = $1 AND c.occurred_at >= $2::timestamptz AND c.occurred_at < $3::timestamptz
|
|
64364
64661
|
GROUP BY f.path
|
|
64365
64662
|
)
|
|
64366
|
-
SELECT metrics.*,
|
|
64663
|
+
SELECT metrics.*,
|
|
64664
|
+
COALESCE(entrances.entrances, 0)::int AS entrances,
|
|
64367
64665
|
COALESCE(exits.exits, 0)::int AS exits,
|
|
64368
|
-
COALESCE(assisted.conversions, 0)::int AS conversions
|
|
64666
|
+
COALESCE(assisted.conversions, 0)::int AS conversions,
|
|
64667
|
+
COALESCE(dwell.measured_pageviews, 0)::int AS measured_pageviews,
|
|
64668
|
+
CASE WHEN dwell.avg_engaged_ms IS NULL THEN NULL
|
|
64669
|
+
ELSE round((dwell.avg_engaged_ms / 1000.0)::numeric, 1) END AS avg_seconds_on_page,
|
|
64670
|
+
CASE WHEN dwell.avg_scroll IS NULL THEN NULL
|
|
64671
|
+
ELSE round(dwell.avg_scroll::numeric, 1) END AS avg_scroll_depth,
|
|
64672
|
+
CASE WHEN COALESCE(entrances.entrances, 0) = 0 THEN NULL
|
|
64673
|
+
ELSE round(COALESCE(bounces.bounces, 0)::numeric * 100 / entrances.entrances, 1) END AS bounce_rate,
|
|
64674
|
+
CASE WHEN metrics.pageviews = 0 THEN NULL
|
|
64675
|
+
ELSE round(COALESCE(exits.exits, 0)::numeric * 100 / metrics.pageviews, 1) END AS exit_rate,
|
|
64676
|
+
CASE WHEN metrics.sessions = 0 THEN NULL
|
|
64677
|
+
ELSE round(COALESCE(engaged.engaged_sessions, 0)::numeric * 100 / metrics.sessions, 1) END AS engagement_rate,
|
|
64678
|
+
CASE WHEN metrics.sessions = 0 THEN NULL
|
|
64679
|
+
ELSE round(metrics.pageviews::numeric / metrics.sessions, 2) END AS pageviews_per_session
|
|
64369
64680
|
FROM page_metrics metrics
|
|
64370
64681
|
LEFT JOIN entrances USING(path)
|
|
64371
64682
|
LEFT JOIN exits USING(path)
|
|
64372
64683
|
LEFT JOIN assisted USING(path)
|
|
64373
|
-
|
|
64684
|
+
LEFT JOIN bounces USING(path)
|
|
64685
|
+
LEFT JOIN engaged USING(path)
|
|
64686
|
+
LEFT JOIN dwell_metrics dwell USING(path)
|
|
64687
|
+
WHERE ${searchParam}::text IS NULL OR metrics.path ILIKE '%' || ${searchParam} || '%'
|
|
64688
|
+
ORDER BY ${order}
|
|
64374
64689
|
LIMIT 1000`,
|
|
64375
|
-
query.values
|
|
64690
|
+
[...query.values, table.search]
|
|
64376
64691
|
);
|
|
64377
64692
|
const paged = paginateRows(
|
|
64378
64693
|
result.rows,
|
|
64379
64694
|
page,
|
|
64380
|
-
pageFingerprint(
|
|
64695
|
+
pageFingerprint(
|
|
64696
|
+
`content:${table.sort}:${table.direction}:${table.search ?? ""}`,
|
|
64697
|
+
siteId,
|
|
64698
|
+
filters
|
|
64699
|
+
)
|
|
64381
64700
|
);
|
|
64382
64701
|
return {
|
|
64383
64702
|
content: paged.items,
|
|
64384
64703
|
pageInfo: paged.pageInfo,
|
|
64704
|
+
table,
|
|
64385
64705
|
dataFreshThrough: (/* @__PURE__ */ new Date()).toISOString()
|
|
64386
64706
|
};
|
|
64387
64707
|
}
|
|
@@ -65211,7 +65531,7 @@ async function refreshAnalyticsDailyRollups(input) {
|
|
|
65211
65531
|
try {
|
|
65212
65532
|
await client2.query("BEGIN");
|
|
65213
65533
|
await client2.query(
|
|
65214
|
-
`DELETE FROM analytics_rollups_daily WHERE bucket >= $1::date AND bucket
|
|
65534
|
+
`DELETE FROM analytics_rollups_daily WHERE bucket >= $1::date AND bucket <= $2::date`,
|
|
65215
65535
|
[input.start, input.end]
|
|
65216
65536
|
);
|
|
65217
65537
|
const inserted = await client2.query(
|
|
@@ -65228,7 +65548,14 @@ async function refreshAnalyticsDailyRollups(input) {
|
|
|
65228
65548
|
now()
|
|
65229
65549
|
FROM analytics_events
|
|
65230
65550
|
WHERE occurred_at >= $1::timestamptz AND occurred_at < $2::timestamptz
|
|
65231
|
-
GROUP BY 1, 2, 3, 4, 5, 6, 7
|
|
65551
|
+
GROUP BY 1, 2, 3, 4, 5, 6, 7
|
|
65552
|
+
ON CONFLICT (site_id, pixel_id, hostname, bucket, source, medium, campaign)
|
|
65553
|
+
DO UPDATE SET
|
|
65554
|
+
pageviews = EXCLUDED.pageviews,
|
|
65555
|
+
events = EXCLUDED.events,
|
|
65556
|
+
visitors = EXCLUDED.visitors,
|
|
65557
|
+
sessions = EXCLUDED.sessions,
|
|
65558
|
+
refreshed_at = EXCLUDED.refreshed_at`,
|
|
65232
65559
|
[input.start, input.end]
|
|
65233
65560
|
);
|
|
65234
65561
|
await client2.query("COMMIT");
|
|
@@ -65240,9 +65567,17 @@ async function refreshAnalyticsDailyRollups(input) {
|
|
|
65240
65567
|
return { runId, rows };
|
|
65241
65568
|
} catch (error) {
|
|
65242
65569
|
await client2.query("ROLLBACK");
|
|
65570
|
+
const pgCode = error?.code;
|
|
65571
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
65572
|
+
console.error(JSON.stringify({
|
|
65573
|
+
event: "analytics_rollup_failed",
|
|
65574
|
+
runId,
|
|
65575
|
+
pgCode: pgCode ?? null,
|
|
65576
|
+
message: detail.slice(0, 400)
|
|
65577
|
+
}));
|
|
65243
65578
|
await db.query(
|
|
65244
|
-
`UPDATE analytics_rollup_runs SET status = 'failed', completed_at = now(), error_code =
|
|
65245
|
-
[runId]
|
|
65579
|
+
`UPDATE analytics_rollup_runs SET status = 'failed', completed_at = now(), error_code = $2 WHERE id = $1`,
|
|
65580
|
+
[runId, (pgCode ? `pg_${pgCode}` : "rollup_failed").slice(0, 60)]
|
|
65246
65581
|
);
|
|
65247
65582
|
throw error;
|
|
65248
65583
|
} finally {
|
|
@@ -65295,6 +65630,24 @@ function rowsToCsv2(rows) {
|
|
|
65295
65630
|
)
|
|
65296
65631
|
].join("\n");
|
|
65297
65632
|
}
|
|
65633
|
+
function markdownHeading(column) {
|
|
65634
|
+
return column.split("_").map((word, index) => index ? word : word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
65635
|
+
}
|
|
65636
|
+
function markdownCell(value) {
|
|
65637
|
+
if (value === null || value === void 0) return "\u2014";
|
|
65638
|
+
return String(value).replaceAll("|", "\\|").replaceAll("\n", " ");
|
|
65639
|
+
}
|
|
65640
|
+
function rowsToMarkdown(rows) {
|
|
65641
|
+
const columns = [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
65642
|
+
if (!columns.length) return ["No rows matched this window and filter set."];
|
|
65643
|
+
return [
|
|
65644
|
+
`| ${columns.map(markdownHeading).join(" | ")} |`,
|
|
65645
|
+
`| ${columns.map(() => "---").join(" | ")} |`,
|
|
65646
|
+
...rows.map(
|
|
65647
|
+
(row) => `| ${columns.map((column) => markdownCell(row[column])).join(" | ")} |`
|
|
65648
|
+
)
|
|
65649
|
+
];
|
|
65650
|
+
}
|
|
65298
65651
|
async function createAnalyticsExport(input) {
|
|
65299
65652
|
const site = (await listAnalyticsSites(input.userId)).find(
|
|
65300
65653
|
(candidate) => candidate.id === input.siteId
|
|
@@ -65362,6 +65715,7 @@ async function createAnalyticsExport(input) {
|
|
|
65362
65715
|
`- Verified conversions: ${overview.conversions}`,
|
|
65363
65716
|
`- Verified revenue: ${(overview.revenueMinor / 100).toFixed(2)}`,
|
|
65364
65717
|
"",
|
|
65718
|
+
...input.report === "overview" ? [] : [`## ${markdownHeading(input.report)} rows`, "", ...rowsToMarkdown(rows), ""],
|
|
65365
65719
|
`Generated ${(/* @__PURE__ */ new Date()).toISOString()} from the governed ${input.report} report contract.`
|
|
65366
65720
|
].join("\n");
|
|
65367
65721
|
}
|
|
@@ -65398,7 +65752,7 @@ async function createAnalyticsExport(input) {
|
|
|
65398
65752
|
}
|
|
65399
65753
|
return describeArtifact(replay.rows[0]);
|
|
65400
65754
|
}
|
|
65401
|
-
var import_node_crypto37, import_pg, AnalyticsRepositoryError, pool2, blockedPropertyName, inferredFamilySql, clickIdKeys;
|
|
65755
|
+
var import_node_crypto37, import_pg, AnalyticsRepositoryError, pool2, MAX_ENGAGED_MS, ENGAGED_SESSION_MS, blockedPropertyName, inferredFamilySql, ANALYTICS_CONTENT_SORTS, clickIdKeys;
|
|
65402
65756
|
var init_analytics_repository = __esm({
|
|
65403
65757
|
"src/api/analytics-repository.ts"() {
|
|
65404
65758
|
"use strict";
|
|
@@ -65417,6 +65771,8 @@ var init_analytics_repository = __esm({
|
|
|
65417
65771
|
status;
|
|
65418
65772
|
};
|
|
65419
65773
|
pool2 = null;
|
|
65774
|
+
MAX_ENGAGED_MS = 30 * 60 * 1e3;
|
|
65775
|
+
ENGAGED_SESSION_MS = 1e4;
|
|
65420
65776
|
blockedPropertyName = /(email|phone|password|token|secret|authorization|cookie|name|address|form|body)/i;
|
|
65421
65777
|
inferredFamilySql = `COALESCE(e.channel_family, CASE
|
|
65422
65778
|
WHEN lower(COALESCE(e.source,'') || ' ' || COALESCE(e.referrer,'')) ~ '(perplexity|chatgpt|openai|claude|anthropic|grok|xai|ai[_ -]?overview|google[_ -]?ai|gemini)' THEN 'llm'
|
|
@@ -65425,6 +65781,22 @@ var init_analytics_repository = __esm({
|
|
|
65425
65781
|
WHEN COALESCE(e.source,'') = '' THEN 'direct'
|
|
65426
65782
|
WHEN lower(COALESCE(e.medium,'')) ~ 'email|newsletter' THEN 'email'
|
|
65427
65783
|
ELSE 'other' END)`;
|
|
65784
|
+
ANALYTICS_CONTENT_SORTS = [
|
|
65785
|
+
"path",
|
|
65786
|
+
"visitors",
|
|
65787
|
+
"sessions",
|
|
65788
|
+
"pageviews",
|
|
65789
|
+
"entrances",
|
|
65790
|
+
"exits",
|
|
65791
|
+
"engagement_events",
|
|
65792
|
+
"conversions",
|
|
65793
|
+
"avg_seconds_on_page",
|
|
65794
|
+
"avg_scroll_depth",
|
|
65795
|
+
"bounce_rate",
|
|
65796
|
+
"exit_rate",
|
|
65797
|
+
"engagement_rate",
|
|
65798
|
+
"pageviews_per_session"
|
|
65799
|
+
];
|
|
65428
65800
|
clickIdKeys = /* @__PURE__ */ new Set([
|
|
65429
65801
|
"fbclid",
|
|
65430
65802
|
"gclid",
|
|
@@ -65744,7 +66116,10 @@ function dashboardOriginAllowed(origin) {
|
|
|
65744
66116
|
return (process.env.ALLOWED_ORIGINS ?? process.env.APP_ORIGIN ?? "").split(",").map((value) => value.trim()).includes(origin);
|
|
65745
66117
|
}
|
|
65746
66118
|
function analyticsPixelSdk() {
|
|
65747
|
-
return `(()=>{const s=document.currentScript;if(!s)return;const pixel=s.dataset.pixel;if(!pixel)return;const endpoint=new URL('/analytics/v1/events',s.src).toString(),ttl=18e5;const key='mcp_analytics_visitor',sessionKey='mcp_analytics_session',touchKey='mcp_analytics_touch';const id=()=>crypto.randomUUID?crypto.randomUUID():'e_'+Date.now().toString(36)+Math.random().toString(36).slice(2);let consent=s.dataset.consent==='granted'&&!navigator.globalPrivacyControl,visitor=null,session=null;const read=k=>{try{return localStorage.getItem(k)}catch{return null}},write=(k,v)=>{try{localStorage.setItem(k,v)}catch{}};const ensure=()=>{if(!consent)return;visitor=read(key)||id();const now=Date.now();let saved={};try{saved=JSON.parse(read(sessionKey)||'{}')}catch{}session=saved.id&&now-Number(saved.seen||0)<ttl?saved.id:id();write(key,visitor);write(sessionKey,JSON.stringify({id:session,seen:now}))};const attribution=()=>{const q=new URLSearchParams(location.search),clickIds={},properties={},map={utm_term:'utm_term',utm_content:'utm_content',utm_id:'campaign_id',campaign_id:'campaign_id',adset_id:'ad_set_id',ad_set_id:'ad_set_id',adgroup_id:'ad_group_id',ad_group_id:'ad_group_id',ad_id:'ad_id',creative_id:'creative_id',placement:'placement'};for(const k of ['fbclid','gclid','gbraid','wbraid','ttclid','rdt_cid','msclkid']){const v=q.get(k);if(v)clickIds[k]=v}for(const [from,to] of Object.entries(map)){const v=q.get(from);if(v)properties[to]=v}const current={source:q.get('utm_source')||undefined,medium:q.get('utm_medium')||undefined,campaign:q.get('utm_campaign')||undefined,clickIds,properties};if(current.source||Object.keys(clickIds).length)write(touchKey,JSON.stringify(current));else{try{return JSON.parse(read(touchKey)||'null')||current}catch{}}return current};const send=(name,properties={})=>{if(!consent||navigator.globalPrivacyControl)return;ensure();const a=attribution();const body=JSON.stringify({pixelId:pixel,events:[{eventId:id(),eventName:name,occurredAt:new Date().toISOString(),visitorId:visitor,sessionId:session,path:location.pathname,canonicalUrl:location.origin+location.pathname,title:document.title,referrer:document.referrer||undefined,...a,deviceClass:innerWidth<640?'mobile':innerWidth<1024?'tablet':'desktop',properties:{...a.properties,...properties}}]});if(navigator.sendBeacon&&navigator.sendBeacon(endpoint,new Blob([body],{type:'application/json'})))return;fetch(endpoint,{method:'POST',headers:{'content-type':'application/json'},body,keepalive:true,credentials:'omit'}).catch(()=>{})};const api={page:()=>send('page_view'),track:send,consent:state=>{consent=(state==='granted'||state===true)&&!navigator.globalPrivacyControl;if(consent){ensure();api.page()}},debug:()=>({pixel,endpoint,consent,gpc:Boolean(navigator.globalPrivacyControl),visitorId:visitor,sessionId:session,hostname:location.hostname,path:location.pathname,...attribution()})};window.mcpAnalytics=api;ensure();if(consent)api.page();let current=location.href;const changed=()=>{if(location.href!==current){current=location.href;api.page()}};for(const method of ['pushState','replaceState']){const original=history[method];history[method]=function(...args){const result=original.apply(this,args);queueMicrotask(changed);return result}}addEventListener('popstate',changed)})();`;
|
|
66119
|
+
return `(()=>{const s=document.currentScript;if(!s)return;const pixel=s.dataset.pixel;if(!pixel)return;const endpoint=new URL('/analytics/v1/events',s.src).toString(),ttl=18e5;const key='mcp_analytics_visitor',sessionKey='mcp_analytics_session',touchKey='mcp_analytics_touch';const id=()=>crypto.randomUUID?crypto.randomUUID():'e_'+Date.now().toString(36)+Math.random().toString(36).slice(2);let consent=s.dataset.consent==='granted'&&!navigator.globalPrivacyControl,visitor=null,session=null;const read=k=>{try{return localStorage.getItem(k)}catch{return null}},write=(k,v)=>{try{localStorage.setItem(k,v)}catch{}};const ensure=()=>{if(!consent)return;visitor=read(key)||id();const now=Date.now();let saved={};try{saved=JSON.parse(read(sessionKey)||'{}')}catch{}session=saved.id&&now-Number(saved.seen||0)<ttl?saved.id:id();write(key,visitor);write(sessionKey,JSON.stringify({id:session,seen:now}))};const attribution=()=>{const q=new URLSearchParams(location.search),clickIds={},properties={},map={utm_term:'utm_term',utm_content:'utm_content',utm_id:'campaign_id',campaign_id:'campaign_id',adset_id:'ad_set_id',ad_set_id:'ad_set_id',adgroup_id:'ad_group_id',ad_group_id:'ad_group_id',ad_id:'ad_id',creative_id:'creative_id',placement:'placement'};for(const k of ['fbclid','gclid','gbraid','wbraid','ttclid','rdt_cid','msclkid']){const v=q.get(k);if(v)clickIds[k]=v}for(const [from,to] of Object.entries(map)){const v=q.get(from);if(v)properties[to]=v}const current={source:q.get('utm_source')||undefined,medium:q.get('utm_medium')||undefined,campaign:q.get('utm_campaign')||undefined,clickIds,properties};if(current.source||Object.keys(clickIds).length)write(touchKey,JSON.stringify(current));else{try{return JSON.parse(read(touchKey)||'null')||current}catch{}}return current};const send=(name,properties={},over={})=>{if(!consent||navigator.globalPrivacyControl)return;ensure();const a=attribution();const body=JSON.stringify({pixelId:pixel,events:[{eventId:id(),eventName:name,occurredAt:new Date().toISOString(),visitorId:visitor,sessionId:session,path:location.pathname,canonicalUrl:location.origin+location.pathname,title:document.title,referrer:document.referrer||undefined,...a,deviceClass:innerWidth<640?'mobile':innerWidth<1024?'tablet':'desktop',properties:{...a.properties,...properties},...over}]});if(navigator.sendBeacon&&navigator.sendBeacon(endpoint,new Blob([body],{type:'application/json'})))return;fetch(endpoint,{method:'POST',headers:{'content-type':'application/json'},body,keepalive:true,credentials:'omit'}).catch(()=>{})};let visibleSince=document.visibilityState==='visible'?Date.now():0,dwellMs=0,maxScroll=0,dwellPath=location.pathname;const depth=()=>{const d=document.documentElement,h=d.scrollHeight||1;return Math.max(0,Math.min(100,Math.round(((d.scrollTop||document.body.scrollTop||0)+innerHeight)/h*100)))};const accrue=()=>{if(visibleSince){dwellMs+=Date.now()-visibleSince;visibleSince=0}};const resume=()=>{if(!visibleSince)visibleSince=Date.now()};const flush=()=>{accrue();const ms=Math.min(dwellMs,18e5),sd=Math.max(maxScroll,depth());dwellMs=0;maxScroll=0;if(ms<250)return;send('page_engagement',{},{path:dwellPath,canonicalUrl:location.origin+dwellPath,engagedMs:ms,scrollDepth:sd})};addEventListener('scroll',()=>{const d=depth();if(d>maxScroll)maxScroll=d},{passive:true});document.addEventListener('visibilitychange',()=>{if(document.visibilityState==='hidden')flush();else resume()});addEventListener('pagehide',flush);const api={page:()=>send('page_view'),track:send,consent:state=>{consent=(state==='granted'||state===true)&&!navigator.globalPrivacyControl;if(consent){ensure();resume();api.page()}},debug:()=>({pixel,endpoint,consent,gpc:Boolean(navigator.globalPrivacyControl),visitorId:visitor,sessionId:session,hostname:location.hostname,path:location.pathname,engagedMs:dwellMs+(visibleSince?Date.now()-visibleSince:0),scrollDepth:Math.max(maxScroll,depth()),...attribution()})};window.mcpAnalytics=api;ensure();if(consent)api.page();let current=location.href;const changed=()=>{if(location.href!==current){flush();current=location.href;dwellPath=location.pathname;resume();api.page()}};for(const method of ['pushState','replaceState']){const original=history[method];history[method]=function(...args){const result=original.apply(this,args);queueMicrotask(changed);return result}}addEventListener('popstate',changed)})();`;
|
|
66120
|
+
}
|
|
66121
|
+
function pagePathFilter(value) {
|
|
66122
|
+
return value && value.startsWith("/") ? value : void 0;
|
|
65748
66123
|
}
|
|
65749
66124
|
function reportFilters(url) {
|
|
65750
66125
|
const end = url.searchParams.get("end") || (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -65766,7 +66141,7 @@ function reportFilters(url) {
|
|
|
65766
66141
|
channelFamily: url.searchParams.get("channelFamily") || void 0,
|
|
65767
66142
|
platform: url.searchParams.get("platform") || void 0,
|
|
65768
66143
|
referrer: url.searchParams.get("referrer") || void 0,
|
|
65769
|
-
path: url.searchParams.get("path")
|
|
66144
|
+
path: pagePathFilter(url.searchParams.get("path")),
|
|
65770
66145
|
deviceClass: url.searchParams.get("deviceClass") || void 0,
|
|
65771
66146
|
countryCode: url.searchParams.get("countryCode") || void 0,
|
|
65772
66147
|
regionCode: url.searchParams.get("regionCode") || void 0,
|
|
@@ -65902,7 +66277,9 @@ var init_analytics_routes = __esm({
|
|
|
65902
66277
|
campaign: import_zod53.z.string().trim().max(240).optional(),
|
|
65903
66278
|
clickIds: import_zod53.z.record(import_zod53.z.string(), import_zod53.z.string().trim().max(500)).optional(),
|
|
65904
66279
|
deviceClass: import_zod53.z.enum(["desktop", "tablet", "mobile", "unknown"]).optional(),
|
|
65905
|
-
properties: import_zod53.z.record(import_zod53.z.string(), import_zod53.z.unknown()).optional()
|
|
66280
|
+
properties: import_zod53.z.record(import_zod53.z.string(), import_zod53.z.unknown()).optional(),
|
|
66281
|
+
engagedMs: import_zod53.z.number().int().min(0).max(MAX_ENGAGED_MS).optional(),
|
|
66282
|
+
scrollDepth: import_zod53.z.number().int().min(0).max(100).optional()
|
|
65906
66283
|
}).strict();
|
|
65907
66284
|
IngestionSchema = import_zod53.z.object({
|
|
65908
66285
|
pixelId: import_zod53.z.string().trim().min(8).max(160),
|
|
@@ -66902,14 +67279,13 @@ Imported through the Site Analytics CRM workflow.
|
|
|
66902
67279
|
["/sites/:siteId/overview", analyticsOverview],
|
|
66903
67280
|
["/sites/:siteId/timeseries", analyticsTimeseries],
|
|
66904
67281
|
["/sites/:siteId/acquisition", analyticsAcquisition],
|
|
66905
|
-
["/sites/:siteId/content", analyticsContent],
|
|
66906
67282
|
["/sites/:siteId/events", analyticsEventCounts]
|
|
66907
67283
|
]) {
|
|
66908
67284
|
analyticsApp.get(path6, auth3, async (c) => {
|
|
66909
67285
|
const url = new URL(c.req.url);
|
|
66910
67286
|
const filters = reportFilters(url);
|
|
66911
67287
|
validateReportWindow(filters);
|
|
66912
|
-
const paginated = path6.endsWith("/acquisition") || path6.endsWith("/
|
|
67288
|
+
const paginated = path6.endsWith("/acquisition") || path6.endsWith("/events");
|
|
66913
67289
|
const data = await load(
|
|
66914
67290
|
c.req.param("siteId"),
|
|
66915
67291
|
Number(c.get("user").id),
|
|
@@ -66919,6 +67295,24 @@ Imported through the Site Analytics CRM workflow.
|
|
|
66919
67295
|
return c.json({ ok: true, data, filters });
|
|
66920
67296
|
});
|
|
66921
67297
|
}
|
|
67298
|
+
analyticsApp.get("/sites/:siteId/content", auth3, async (c) => {
|
|
67299
|
+
const url = new URL(c.req.url);
|
|
67300
|
+
const filters = reportFilters(url);
|
|
67301
|
+
validateReportWindow(filters);
|
|
67302
|
+
const requestedSort = url.searchParams.get("sort");
|
|
67303
|
+
const data = await analyticsContent(
|
|
67304
|
+
c.req.param("siteId"),
|
|
67305
|
+
Number(c.get("user").id),
|
|
67306
|
+
filters,
|
|
67307
|
+
reportPage(url),
|
|
67308
|
+
{
|
|
67309
|
+
sort: ANALYTICS_CONTENT_SORTS.includes(requestedSort) ? requestedSort : void 0,
|
|
67310
|
+
direction: url.searchParams.get("direction") === "asc" ? "asc" : "desc",
|
|
67311
|
+
search: url.searchParams.get("search") || void 0
|
|
67312
|
+
}
|
|
67313
|
+
);
|
|
67314
|
+
return c.json({ ok: true, data, filters });
|
|
67315
|
+
});
|
|
66922
67316
|
analyticsApp.get("/sites/:siteId/dashboard", auth3, async (c) => {
|
|
66923
67317
|
const url = new URL(c.req.url);
|
|
66924
67318
|
const filters = reportFilters(url);
|
|
@@ -67152,7 +67546,7 @@ var init_scheduled_artifact_owner = __esm({
|
|
|
67152
67546
|
|
|
67153
67547
|
// src/api/scheduled-run-view-links.ts
|
|
67154
67548
|
async function ensureScheduledRunViewLinksSchema() {
|
|
67155
|
-
if (
|
|
67549
|
+
if (schemaReady2) return;
|
|
67156
67550
|
const db = getDb();
|
|
67157
67551
|
await db.execute(`
|
|
67158
67552
|
CREATE TABLE IF NOT EXISTS scheduled_run_view_links (
|
|
@@ -67200,7 +67594,7 @@ async function ensureScheduledRunViewLinksSchema() {
|
|
|
67200
67594
|
CREATE INDEX IF NOT EXISTS scheduled_run_view_links_expiry
|
|
67201
67595
|
ON scheduled_run_view_links(expires_at, revoked_at)
|
|
67202
67596
|
`);
|
|
67203
|
-
|
|
67597
|
+
schemaReady2 = true;
|
|
67204
67598
|
}
|
|
67205
67599
|
function tokenHash2(token4) {
|
|
67206
67600
|
return (0, import_node_crypto41.createHash)("sha256").update(token4).digest("hex");
|
|
@@ -67288,13 +67682,13 @@ async function revokeScheduledRunViewLink(ownerId, runId, shareId, now = /* @__P
|
|
|
67288
67682
|
});
|
|
67289
67683
|
return result.rowsAffected > 0;
|
|
67290
67684
|
}
|
|
67291
|
-
var import_node_crypto41,
|
|
67685
|
+
var import_node_crypto41, schemaReady2;
|
|
67292
67686
|
var init_scheduled_run_view_links = __esm({
|
|
67293
67687
|
"src/api/scheduled-run-view-links.ts"() {
|
|
67294
67688
|
"use strict";
|
|
67295
67689
|
import_node_crypto41 = require("crypto");
|
|
67296
67690
|
init_db();
|
|
67297
|
-
|
|
67691
|
+
schemaReady2 = false;
|
|
67298
67692
|
}
|
|
67299
67693
|
});
|
|
67300
67694
|
|
|
@@ -67904,9 +68298,9 @@ var init_public_scheduled_result_routes = __esm({
|
|
|
67904
68298
|
// src/api/service-connections.ts
|
|
67905
68299
|
function ensureServiceConnectionsSchema() {
|
|
67906
68300
|
const currentDb = getDb();
|
|
67907
|
-
if (
|
|
68301
|
+
if (schemaReady3 && schemaDb4 === currentDb) return schemaReady3;
|
|
67908
68302
|
schemaDb4 = currentDb;
|
|
67909
|
-
|
|
68303
|
+
schemaReady3 = (async () => {
|
|
67910
68304
|
const db = currentDb;
|
|
67911
68305
|
await db.execute(`
|
|
67912
68306
|
CREATE TABLE IF NOT EXISTS service_connections (
|
|
@@ -67974,11 +68368,11 @@ function ensureServiceConnectionsSchema() {
|
|
|
67974
68368
|
} catch {
|
|
67975
68369
|
}
|
|
67976
68370
|
})().catch((error) => {
|
|
67977
|
-
|
|
68371
|
+
schemaReady3 = null;
|
|
67978
68372
|
schemaDb4 = null;
|
|
67979
68373
|
throw error;
|
|
67980
68374
|
});
|
|
67981
|
-
return
|
|
68375
|
+
return schemaReady3;
|
|
67982
68376
|
}
|
|
67983
68377
|
function parseTools(value) {
|
|
67984
68378
|
if (typeof value !== "string") return [];
|
|
@@ -68177,13 +68571,13 @@ async function claimServiceConnectionAction(args) {
|
|
|
68177
68571
|
...result !== void 0 ? { result } : {}
|
|
68178
68572
|
};
|
|
68179
68573
|
}
|
|
68180
|
-
var import_node_crypto45,
|
|
68574
|
+
var import_node_crypto45, schemaReady3, schemaDb4;
|
|
68181
68575
|
var init_service_connections = __esm({
|
|
68182
68576
|
"src/api/service-connections.ts"() {
|
|
68183
68577
|
"use strict";
|
|
68184
68578
|
import_node_crypto45 = require("crypto");
|
|
68185
68579
|
init_db();
|
|
68186
|
-
|
|
68580
|
+
schemaReady3 = null;
|
|
68187
68581
|
schemaDb4 = null;
|
|
68188
68582
|
}
|
|
68189
68583
|
});
|
|
@@ -73113,7 +73507,31 @@ async function chargeTierChangeNow(stripeClient, subscriptionId, customerId) {
|
|
|
73113
73507
|
return { ok: false, amountDue: 0, error: err instanceof Error ? err.message : "Unable to charge the plan change immediately." };
|
|
73114
73508
|
}
|
|
73115
73509
|
}
|
|
73116
|
-
|
|
73510
|
+
function settleWithinTickBudget(label, unfinished, work, onDeadlineOrError) {
|
|
73511
|
+
return new Promise((resolve2) => {
|
|
73512
|
+
const timer = setTimeout(() => {
|
|
73513
|
+
unfinished.push(`${label}:deadline`);
|
|
73514
|
+
resolve2(onDeadlineOrError);
|
|
73515
|
+
}, CRON_TICK_BUDGET_MS);
|
|
73516
|
+
work.then(
|
|
73517
|
+
(value) => {
|
|
73518
|
+
clearTimeout(timer);
|
|
73519
|
+
resolve2(value);
|
|
73520
|
+
},
|
|
73521
|
+
(err) => {
|
|
73522
|
+
clearTimeout(timer);
|
|
73523
|
+
unfinished.push(`${label}:error`);
|
|
73524
|
+
console.error(JSON.stringify({
|
|
73525
|
+
event: "cron_tick_job_failed",
|
|
73526
|
+
job: label,
|
|
73527
|
+
message: err instanceof Error ? err.message : String(err)
|
|
73528
|
+
}));
|
|
73529
|
+
resolve2(onDeadlineOrError);
|
|
73530
|
+
}
|
|
73531
|
+
);
|
|
73532
|
+
});
|
|
73533
|
+
}
|
|
73534
|
+
var import_resend3, import_node_crypto55, import_hono36, import_hono37, import_factory8, import_cookie2, import_stripe2, secureCookies2, isProduction2, sessionCookieOptions2, requireAllowedOrigin, auth4, sessionAuth, requireIntegrationsTier, requirePaidSchedulingTier, app, deploymentProfile, STRIPE_API_VERSION, SYNC_HARVEST_TIMEOUT_OVERRIDE_MS, CRON_TICK_BUDGET_MS, CRON_TICK_DRAIN_BUDGET_MS;
|
|
73117
73535
|
var init_server = __esm({
|
|
73118
73536
|
"src/api/server.ts"() {
|
|
73119
73537
|
"use strict";
|
|
@@ -73167,6 +73585,8 @@ var init_server = __esm({
|
|
|
73167
73585
|
init_directory_workflow_dispatch();
|
|
73168
73586
|
init_paa_harvest_dispatch();
|
|
73169
73587
|
init_paa_harvest_reconciliation();
|
|
73588
|
+
init_unified_billing();
|
|
73589
|
+
init_retention_sweeps();
|
|
73170
73590
|
init_directory_workflow_reconciliation();
|
|
73171
73591
|
init_directory_artifacts();
|
|
73172
73592
|
init_workflow_routes();
|
|
@@ -75858,35 +76278,49 @@ var init_server = __esm({
|
|
|
75858
76278
|
ledger
|
|
75859
76279
|
});
|
|
75860
76280
|
});
|
|
76281
|
+
CRON_TICK_BUDGET_MS = 5e4;
|
|
76282
|
+
CRON_TICK_DRAIN_BUDGET_MS = 4e4;
|
|
75861
76283
|
app.get("/cron/tick", async (c) => {
|
|
75862
76284
|
const secret2 = c.req.header("authorization");
|
|
75863
76285
|
if (!process.env.CRON_SECRET || secret2 !== `Bearer ${process.env.CRON_SECRET}`) {
|
|
75864
76286
|
return c.json({ error: "Unauthorized" }, 401);
|
|
75865
76287
|
}
|
|
76288
|
+
const startedAt = Date.now();
|
|
75866
76289
|
const { drainQueue: drainQueue2 } = await Promise.resolve().then(() => (init_worker(), worker_exports));
|
|
75867
|
-
const budget = { maxJobs: 10, deadlineMs:
|
|
75868
|
-
const
|
|
75869
|
-
const
|
|
75870
|
-
|
|
75871
|
-
|
|
75872
|
-
|
|
75873
|
-
|
|
75874
|
-
|
|
75875
|
-
|
|
75876
|
-
|
|
75877
|
-
|
|
75878
|
-
|
|
75879
|
-
|
|
75880
|
-
|
|
75881
|
-
|
|
75882
|
-
|
|
75883
|
-
|
|
75884
|
-
|
|
75885
|
-
|
|
75886
|
-
|
|
75887
|
-
|
|
76290
|
+
const budget = { maxJobs: 10, deadlineMs: startedAt + CRON_TICK_DRAIN_BUDGET_MS };
|
|
76291
|
+
const origin = `${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`;
|
|
76292
|
+
const unfinished = [];
|
|
76293
|
+
const [results, sweepResult, reapResult, expiredResult, blobCleanup, connectedDataArtifactCleanup, connectedAccountBilling, directoryWorkflowDispatch, directoryWorkflowReconciliation, directoryArtifactCleanup, siteExtractArtifactCleanup, siteExtractRedispatch, siteExtractReconciliation, paaHarvestRedispatch, paaHarvestReconciliation, localSourcebookRedispatch, analyticsRollup, analyticsFormDelivery, workflowDispatchResult, scheduledRunReconciliation, retentionSweep] = await Promise.all([
|
|
76294
|
+
settleWithinTickBudget("drainQueue", unfinished, drainQueue2(budget), []),
|
|
76295
|
+
settleWithinTickBudget("monthlyRefreshSweep", unfinished, runMonthlyRefreshSweep(), { status: "skipped_deadline" }),
|
|
76296
|
+
settleWithinTickBudget("reapBrowserSessions", unfinished, reapIdleBrowserSessions(120), { reaped: 0 }),
|
|
76297
|
+
settleWithinTickBudget("retryOrphanedBrowserSessions", unfinished, retryOrphanedBrowserSessions(50), { recovered: 0, stillOpen: 0 }),
|
|
76298
|
+
settleWithinTickBudget("expireLots", unfinished, expireOldLots(), { expired_lots: 0, expired_mc: 0 }),
|
|
76299
|
+
settleWithinTickBudget("scrapeBlobCleanup", unfinished, cleanupExpiredScrapeBlobs(), { deleted: 0, store: "none" }),
|
|
76300
|
+
settleWithinTickBudget("connectedDataCleanup", unfinished, cleanupExpiredConnectedDataArtifacts(), { deleted: 0, store: "none" }),
|
|
76301
|
+
settleWithinTickBudget("connectedBillingSweep", unfinished, reconcileConnectedAccountBillingSweep(), { checked: 0, synced: 0, failed: 0 }),
|
|
76302
|
+
settleWithinTickBudget("directoryDispatch", unfinished, dispatchPendingDirectoryWorkflows(5), { claimed: 0, dispatched: 0, failed: 0 }),
|
|
76303
|
+
settleWithinTickBudget("directoryReconcile", unfinished, reconcileDirectoryWorkflowSettlements(5), { checked: 0, settled: 0, failed: 0 }),
|
|
76304
|
+
settleWithinTickBudget("directoryArtifactCleanup", unfinished, cleanupExpiredDirectoryArtifacts(), { deleted: 0, store: "none" }),
|
|
76305
|
+
settleWithinTickBudget("siteExtractArtifactCleanup", unfinished, cleanupExpiredSiteExtractArtifacts(), { deleted: 0, store: "none" }),
|
|
76306
|
+
settleWithinTickBudget("siteExtractRedispatch", unfinished, redispatchFundedSiteExtractJobs(10), { checked: 0, dispatched: 0, failed: 0 }),
|
|
76307
|
+
settleWithinTickBudget("siteExtractReconcile", unfinished, reconcileSiteExtractSettlements(10), { checked: 0, settled: 0, failed: 0 }),
|
|
76308
|
+
settleWithinTickBudget("paaRedispatch", unfinished, redispatchPendingPaaHarvests(10), { checked: 0, dispatched: 0, failed: 0 }),
|
|
76309
|
+
settleWithinTickBudget("paaReconcile", unfinished, reconcilePaaHarvestSettlements(10), { checked: 0, staleFailed: 0, settled: 0, failed: 0 }),
|
|
76310
|
+
settleWithinTickBudget("localSourcebookRedispatch", unfinished, redispatchQueuedLocalSourcebookAcquisitions(5), { checked: 0, dispatched: 0, failed: 0 }),
|
|
76311
|
+
settleWithinTickBudget("analyticsRollup", unfinished, refreshAnalyticsDailyRollupsIfDue(), { status: "failed", rows: 0 }),
|
|
76312
|
+
settleWithinTickBudget("analyticsForms", unfinished, deliverPendingAnalyticsForms(10), { claimed: 0, delivered: 0, deferred: 0 }),
|
|
76313
|
+
settleWithinTickBudget("workflowDispatch", unfinished, dispatchDueWorkflowSchedules(origin), { dispatched: 0, status: "skipped_deadline" }),
|
|
76314
|
+
settleWithinTickBudget("scheduledRunReconcile", unfinished, reconcileScheduledRunSettlements(25), { checked: 0, settled: 0, chargedMc: 0, failed: 0 }),
|
|
76315
|
+
settleWithinTickBudget("retentionSweep", unfinished, sweepRetention(), { deleted: 0, perTable: {}, truncated: false })
|
|
75888
76316
|
]);
|
|
75889
76317
|
return c.json({
|
|
76318
|
+
durationMs: Date.now() - startedAt,
|
|
76319
|
+
tickBudgetMs: CRON_TICK_BUDGET_MS,
|
|
76320
|
+
unfinished,
|
|
76321
|
+
complete: unfinished.length === 0,
|
|
76322
|
+
scheduledRunReconciliation,
|
|
76323
|
+
retentionSweep,
|
|
75890
76324
|
drained: results.length,
|
|
75891
76325
|
results,
|
|
75892
76326
|
sweepResult,
|