@secondlayer/subgraphs 1.0.0-beta.1 → 1.0.0-beta.2
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/src/runtime/processor.js +162 -21
- package/dist/src/runtime/processor.js.map +11 -5
- package/dist/src/service.js +164 -23
- package/dist/src/service.js.map +10 -4
- package/package.json +1 -1
|
@@ -1503,7 +1503,7 @@ import {
|
|
|
1503
1503
|
listSubgraphs as listSubgraphs2,
|
|
1504
1504
|
updateSubgraphStatus as updateSubgraphStatus2
|
|
1505
1505
|
} from "@secondlayer/shared/db/queries/subgraphs";
|
|
1506
|
-
import { logger as
|
|
1506
|
+
import { logger as logger9 } from "@secondlayer/shared/logger";
|
|
1507
1507
|
import { listen as listen2 } from "@secondlayer/shared/queue/listener";
|
|
1508
1508
|
|
|
1509
1509
|
// src/runtime/emitter.ts
|
|
@@ -1513,10 +1513,98 @@ import {
|
|
|
1513
1513
|
import {
|
|
1514
1514
|
getSubscriptionSigningSecret
|
|
1515
1515
|
} from "@secondlayer/shared/db/queries/subscriptions";
|
|
1516
|
-
import { logger as
|
|
1516
|
+
import { logger as logger8 } from "@secondlayer/shared/logger";
|
|
1517
1517
|
import { listen } from "@secondlayer/shared/queue/listener";
|
|
1518
1518
|
import { sql as sql4 } from "kysely";
|
|
1519
1519
|
|
|
1520
|
+
// src/runtime/formats/index.ts
|
|
1521
|
+
import { logger as logger7 } from "@secondlayer/shared/logger";
|
|
1522
|
+
|
|
1523
|
+
// src/runtime/formats/cloudevents.ts
|
|
1524
|
+
function buildCloudEvents(outboxRow, _sub) {
|
|
1525
|
+
const event = {
|
|
1526
|
+
specversion: "1.0",
|
|
1527
|
+
type: outboxRow.event_type,
|
|
1528
|
+
source: `secondlayer:${outboxRow.subgraph_name}`,
|
|
1529
|
+
id: outboxRow.id,
|
|
1530
|
+
time: new Date(outboxRow.created_at).toISOString(),
|
|
1531
|
+
datacontenttype: "application/json",
|
|
1532
|
+
data: outboxRow.payload
|
|
1533
|
+
};
|
|
1534
|
+
return {
|
|
1535
|
+
body: JSON.stringify(event),
|
|
1536
|
+
headers: {
|
|
1537
|
+
"content-type": "application/cloudevents+json; charset=utf-8"
|
|
1538
|
+
}
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
// src/runtime/formats/cloudflare.ts
|
|
1543
|
+
import { decryptSecret } from "@secondlayer/shared/crypto/secrets";
|
|
1544
|
+
function resolveBearer(sub) {
|
|
1545
|
+
const cfg = sub.auth_config;
|
|
1546
|
+
if (cfg.tokenEnc) {
|
|
1547
|
+
try {
|
|
1548
|
+
return decryptSecret(Buffer.from(cfg.tokenEnc, "base64"));
|
|
1549
|
+
} catch {
|
|
1550
|
+
return null;
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
return cfg.token ?? null;
|
|
1554
|
+
}
|
|
1555
|
+
function buildCloudflare(outboxRow, sub) {
|
|
1556
|
+
const body = JSON.stringify({
|
|
1557
|
+
params: {
|
|
1558
|
+
...outboxRow.payload,
|
|
1559
|
+
_type: outboxRow.event_type,
|
|
1560
|
+
_outboxId: outboxRow.id
|
|
1561
|
+
}
|
|
1562
|
+
});
|
|
1563
|
+
const headers = {
|
|
1564
|
+
"content-type": "application/json"
|
|
1565
|
+
};
|
|
1566
|
+
const token = resolveBearer(sub);
|
|
1567
|
+
if (token)
|
|
1568
|
+
headers.authorization = `Bearer ${token}`;
|
|
1569
|
+
return { body, headers };
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
// src/runtime/formats/inngest.ts
|
|
1573
|
+
var INNGEST_VERSION = "2026-04-23.v1";
|
|
1574
|
+
function buildInngest(outboxRow) {
|
|
1575
|
+
const event = {
|
|
1576
|
+
name: outboxRow.event_type,
|
|
1577
|
+
data: outboxRow.payload,
|
|
1578
|
+
id: outboxRow.id,
|
|
1579
|
+
ts: new Date(outboxRow.created_at).getTime(),
|
|
1580
|
+
v: INNGEST_VERSION
|
|
1581
|
+
};
|
|
1582
|
+
return {
|
|
1583
|
+
body: JSON.stringify([event]),
|
|
1584
|
+
headers: {
|
|
1585
|
+
"content-type": "application/json"
|
|
1586
|
+
}
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// src/runtime/formats/raw.ts
|
|
1591
|
+
function buildRaw(outboxRow, sub) {
|
|
1592
|
+
const cfg = sub.auth_config;
|
|
1593
|
+
const headers = {
|
|
1594
|
+
"content-type": cfg.contentType ?? "application/json",
|
|
1595
|
+
...cfg.headers ?? {}
|
|
1596
|
+
};
|
|
1597
|
+
if (cfg.authType === "bearer" && cfg.token) {
|
|
1598
|
+
headers.authorization = `Bearer ${cfg.token}`;
|
|
1599
|
+
} else if (cfg.authType === "basic" && cfg.basicAuth) {
|
|
1600
|
+
headers.authorization = `Basic ${cfg.basicAuth}`;
|
|
1601
|
+
}
|
|
1602
|
+
return {
|
|
1603
|
+
body: JSON.stringify(outboxRow.payload),
|
|
1604
|
+
headers
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1520
1608
|
// src/runtime/formats/standard-webhooks.ts
|
|
1521
1609
|
import { sign } from "@secondlayer/shared/crypto/standard-webhooks";
|
|
1522
1610
|
function buildStandardWebhooks(outboxRow, signingSecret) {
|
|
@@ -1539,6 +1627,59 @@ function buildStandardWebhooks(outboxRow, signingSecret) {
|
|
|
1539
1627
|
};
|
|
1540
1628
|
}
|
|
1541
1629
|
|
|
1630
|
+
// src/runtime/formats/trigger.ts
|
|
1631
|
+
import { decryptSecret as decryptSecret2 } from "@secondlayer/shared/crypto/secrets";
|
|
1632
|
+
function resolveBearer2(sub) {
|
|
1633
|
+
const cfg = sub.auth_config;
|
|
1634
|
+
if (cfg.tokenEnc) {
|
|
1635
|
+
try {
|
|
1636
|
+
return decryptSecret2(Buffer.from(cfg.tokenEnc, "base64"));
|
|
1637
|
+
} catch {
|
|
1638
|
+
return null;
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
return cfg.token ?? null;
|
|
1642
|
+
}
|
|
1643
|
+
function buildTrigger(outboxRow, sub) {
|
|
1644
|
+
const body = JSON.stringify({
|
|
1645
|
+
payload: outboxRow.payload,
|
|
1646
|
+
options: {
|
|
1647
|
+
idempotencyKey: outboxRow.id
|
|
1648
|
+
}
|
|
1649
|
+
});
|
|
1650
|
+
const headers = {
|
|
1651
|
+
"content-type": "application/json"
|
|
1652
|
+
};
|
|
1653
|
+
const token = resolveBearer2(sub);
|
|
1654
|
+
if (token)
|
|
1655
|
+
headers.authorization = `Bearer ${token}`;
|
|
1656
|
+
return { body, headers };
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
// src/runtime/formats/index.ts
|
|
1660
|
+
function buildForFormat(outboxRow, sub, signingSecret) {
|
|
1661
|
+
switch (sub.format) {
|
|
1662
|
+
case "inngest":
|
|
1663
|
+
return buildInngest(outboxRow);
|
|
1664
|
+
case "trigger":
|
|
1665
|
+
return buildTrigger(outboxRow, sub);
|
|
1666
|
+
case "cloudflare":
|
|
1667
|
+
return buildCloudflare(outboxRow, sub);
|
|
1668
|
+
case "cloudevents":
|
|
1669
|
+
return buildCloudEvents(outboxRow, sub);
|
|
1670
|
+
case "raw":
|
|
1671
|
+
return buildRaw(outboxRow, sub);
|
|
1672
|
+
case "standard-webhooks":
|
|
1673
|
+
return buildStandardWebhooks(outboxRow, signingSecret);
|
|
1674
|
+
default:
|
|
1675
|
+
logger7.warn("Unknown subscription format, falling back to standard-webhooks", {
|
|
1676
|
+
format: sub.format,
|
|
1677
|
+
subscriptionId: sub.id
|
|
1678
|
+
});
|
|
1679
|
+
return buildStandardWebhooks(outboxRow, signingSecret);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1542
1683
|
// src/runtime/emitter.ts
|
|
1543
1684
|
var BATCH_SIZE = 50;
|
|
1544
1685
|
var BACKOFF_SECONDS = [30, 120, 600, 3600, 21600, 86400, 259200];
|
|
@@ -1547,7 +1688,7 @@ function nextDelaySeconds(attempt) {
|
|
|
1547
1688
|
return BACKOFF_SECONDS[Math.min(attempt, BACKOFF_SECONDS.length - 1)];
|
|
1548
1689
|
}
|
|
1549
1690
|
async function dispatchOne(db, outboxRow, sub) {
|
|
1550
|
-
const { body, headers } =
|
|
1691
|
+
const { body, headers } = buildForFormat(outboxRow, sub, getSubscriptionSigningSecret(sub));
|
|
1551
1692
|
const start = performance.now();
|
|
1552
1693
|
let statusCode = null;
|
|
1553
1694
|
let error = null;
|
|
@@ -1625,7 +1766,7 @@ async function settleFailed(db, outboxRow, sub, errText) {
|
|
|
1625
1766
|
updated_at: new Date
|
|
1626
1767
|
}).where("id", "=", outboxRow.subscription_id).execute();
|
|
1627
1768
|
if (shouldTripCircuit) {
|
|
1628
|
-
|
|
1769
|
+
logger8.warn("Subscription circuit tripped — paused after consecutive failures", {
|
|
1629
1770
|
subscription: sub.name,
|
|
1630
1771
|
failures: newFailures
|
|
1631
1772
|
});
|
|
@@ -1695,7 +1836,7 @@ async function drainForSub(db, state, sub, rows) {
|
|
|
1695
1836
|
await settleFailed(db, row, sub, err);
|
|
1696
1837
|
}
|
|
1697
1838
|
} catch (err) {
|
|
1698
|
-
|
|
1839
|
+
logger8.error("Emitter dispatch crashed", {
|
|
1699
1840
|
outboxId: row.id,
|
|
1700
1841
|
error: err instanceof Error ? err.message : String(err)
|
|
1701
1842
|
});
|
|
@@ -1732,30 +1873,30 @@ async function startEmitter(opts) {
|
|
|
1732
1873
|
};
|
|
1733
1874
|
const pollIntervalMs = opts?.pollIntervalMs ?? 120000;
|
|
1734
1875
|
const retentionIntervalMs = opts?.retentionIntervalMs ?? 60 * 60000;
|
|
1735
|
-
|
|
1876
|
+
logger8.info("[emitter] started", { id: emitterId });
|
|
1736
1877
|
await refreshMatcher(db).catch((err) => {
|
|
1737
|
-
|
|
1878
|
+
logger8.error("[emitter] initial matcher refresh failed", {
|
|
1738
1879
|
error: err instanceof Error ? err.message : String(err)
|
|
1739
1880
|
});
|
|
1740
1881
|
});
|
|
1741
1882
|
const stopNew = await listen("subscriptions:new_outbox", () => {
|
|
1742
1883
|
if (!state.running)
|
|
1743
1884
|
return;
|
|
1744
|
-
claimAndDrain(db, state, emitterId).catch((err) =>
|
|
1885
|
+
claimAndDrain(db, state, emitterId).catch((err) => logger8.error("[emitter] claim failed", {
|
|
1745
1886
|
error: err instanceof Error ? err.message : String(err)
|
|
1746
1887
|
}));
|
|
1747
1888
|
});
|
|
1748
1889
|
const stopChanged = await listen("subscriptions:changed", () => {
|
|
1749
1890
|
if (!state.running)
|
|
1750
1891
|
return;
|
|
1751
|
-
refreshMatcher(db).catch((err) =>
|
|
1892
|
+
refreshMatcher(db).catch((err) => logger8.error("[emitter] matcher refresh failed", {
|
|
1752
1893
|
error: err instanceof Error ? err.message : String(err)
|
|
1753
1894
|
}));
|
|
1754
1895
|
});
|
|
1755
1896
|
const poll = setInterval(() => {
|
|
1756
1897
|
if (!state.running)
|
|
1757
1898
|
return;
|
|
1758
|
-
claimAndDrain(db, state, emitterId).catch((err) =>
|
|
1899
|
+
claimAndDrain(db, state, emitterId).catch((err) => logger8.error("[emitter] poll claim failed", {
|
|
1759
1900
|
error: err instanceof Error ? err.message : String(err)
|
|
1760
1901
|
}));
|
|
1761
1902
|
}, pollIntervalMs);
|
|
@@ -1763,7 +1904,7 @@ async function startEmitter(opts) {
|
|
|
1763
1904
|
const retention = setInterval(() => {
|
|
1764
1905
|
if (!state.running)
|
|
1765
1906
|
return;
|
|
1766
|
-
runRetention(db).catch((err) =>
|
|
1907
|
+
runRetention(db).catch((err) => logger8.error("[emitter] retention failed", {
|
|
1767
1908
|
error: err instanceof Error ? err.message : String(err)
|
|
1768
1909
|
}));
|
|
1769
1910
|
}, retentionIntervalMs);
|
|
@@ -1773,7 +1914,7 @@ async function startEmitter(opts) {
|
|
|
1773
1914
|
clearInterval(retention);
|
|
1774
1915
|
await stopNew();
|
|
1775
1916
|
await stopChanged();
|
|
1776
|
-
|
|
1917
|
+
logger8.info("[emitter] stopped", { id: emitterId });
|
|
1777
1918
|
};
|
|
1778
1919
|
}
|
|
1779
1920
|
|
|
@@ -1811,7 +1952,7 @@ async function loadSubgraphDefinition(sg) {
|
|
|
1811
1952
|
knownVersions.set(sg.name, sg.version);
|
|
1812
1953
|
definitionCache.set(sg.name, def);
|
|
1813
1954
|
if (prevVersion && prevVersion !== sg.version) {
|
|
1814
|
-
|
|
1955
|
+
logger9.info("Subgraph handler reloaded", {
|
|
1815
1956
|
subgraph: sg.name,
|
|
1816
1957
|
from: prevVersion,
|
|
1817
1958
|
to: sg.version
|
|
@@ -1831,7 +1972,7 @@ function cleanupCaches(active) {
|
|
|
1831
1972
|
async function startSubgraphProcessor(opts) {
|
|
1832
1973
|
const concurrency = opts?.concurrency ?? DEFAULT_CONCURRENCY;
|
|
1833
1974
|
let running = true;
|
|
1834
|
-
|
|
1975
|
+
logger9.info("Starting subgraph processor", { concurrency });
|
|
1835
1976
|
const targetDb = getTargetDb5();
|
|
1836
1977
|
const activeSubgraphs = (await listSubgraphs2(targetDb)).filter((v) => v.status === "active");
|
|
1837
1978
|
for (const sg of activeSubgraphs) {
|
|
@@ -1843,7 +1984,7 @@ async function startSubgraphProcessor(opts) {
|
|
|
1843
1984
|
if (isHandlerNotFoundError(err)) {
|
|
1844
1985
|
await updateSubgraphStatus2(targetDb, sg.name, "error");
|
|
1845
1986
|
}
|
|
1846
|
-
|
|
1987
|
+
logger9.error("Subgraph catch-up failed on startup", {
|
|
1847
1988
|
subgraph: sg.name,
|
|
1848
1989
|
error: msg
|
|
1849
1990
|
});
|
|
@@ -1864,7 +2005,7 @@ async function startSubgraphProcessor(opts) {
|
|
|
1864
2005
|
if (isHandlerNotFoundError(err)) {
|
|
1865
2006
|
await updateSubgraphStatus2(db, sg.name, "error");
|
|
1866
2007
|
}
|
|
1867
|
-
|
|
2008
|
+
logger9.error("Subgraph processing failed", {
|
|
1868
2009
|
subgraph: sg.name,
|
|
1869
2010
|
error: msg
|
|
1870
2011
|
});
|
|
@@ -1881,7 +2022,7 @@ async function startSubgraphProcessor(opts) {
|
|
|
1881
2022
|
await handleSubgraphReorg(blockHeight, loadSubgraphDefinition);
|
|
1882
2023
|
}
|
|
1883
2024
|
} catch (err) {
|
|
1884
|
-
|
|
2025
|
+
logger9.error("Subgraph reorg handling failed", {
|
|
1885
2026
|
error: getErrorMessage3(err)
|
|
1886
2027
|
});
|
|
1887
2028
|
}
|
|
@@ -1897,7 +2038,7 @@ async function startSubgraphProcessor(opts) {
|
|
|
1897
2038
|
const def = await loadSubgraphDefinition(sg);
|
|
1898
2039
|
await catchUpSubgraph(def, sg.name);
|
|
1899
2040
|
} catch (err) {
|
|
1900
|
-
|
|
2041
|
+
logger9.error("Subgraph poll processing failed", {
|
|
1901
2042
|
subgraph: sg.name,
|
|
1902
2043
|
error: getErrorMessage3(err)
|
|
1903
2044
|
});
|
|
@@ -1905,19 +2046,19 @@ async function startSubgraphProcessor(opts) {
|
|
|
1905
2046
|
}
|
|
1906
2047
|
}, POLL_INTERVAL_MS);
|
|
1907
2048
|
const stopEmitter = await startEmitter();
|
|
1908
|
-
|
|
2049
|
+
logger9.info("Subgraph processor ready");
|
|
1909
2050
|
return async () => {
|
|
1910
2051
|
running = false;
|
|
1911
2052
|
clearInterval(pollInterval);
|
|
1912
2053
|
await stopListening();
|
|
1913
2054
|
await stopReorgListening();
|
|
1914
2055
|
await stopEmitter();
|
|
1915
|
-
|
|
2056
|
+
logger9.info("Subgraph processor stopped");
|
|
1916
2057
|
};
|
|
1917
2058
|
}
|
|
1918
2059
|
export {
|
|
1919
2060
|
startSubgraphProcessor
|
|
1920
2061
|
};
|
|
1921
2062
|
|
|
1922
|
-
//# debugId=
|
|
2063
|
+
//# debugId=F94CB61F26C58C5F64756E2164756E21
|
|
1923
2064
|
//# sourceMappingURL=processor.js.map
|