@economicagents/graph 0.1.0 → 0.2.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 +12 -13
- package/dist/cli.d.ts +0 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +42 -18
- package/dist/fleet-alerts.d.ts.map +1 -1
- package/dist/fleet-alerts.js +4 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/queries.js +8 -8
- package/dist/recommendations.js +1 -1
- package/dist/store.d.ts +5 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +49 -10
- package/dist/viem-transport.d.ts +6 -0
- package/dist/viem-transport.d.ts.map +1 -0
- package/dist/viem-transport.js +12 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @economicagents/graph
|
|
2
2
|
|
|
3
|
-
Economic graph for AEP:
|
|
3
|
+
Economic graph for AEP: sync on-chain payments and credit events into SQLite (`graph.db`), compute credit scores, and provider recommendations.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,15 +8,15 @@ Economic graph for AEP: payments, credit events, credit scoring, and provider re
|
|
|
8
8
|
pnpm add @economicagents/graph
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
From
|
|
11
|
+
**From a local clone** of [economicagents/AEP](https://github.com/economicagents/AEP): `cd packages/graph && pnpm run build`.
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
#
|
|
16
|
+
# Standalone binary when installed
|
|
17
17
|
aep-graph sync
|
|
18
18
|
|
|
19
|
-
# Via
|
|
19
|
+
# Via CLI meta-package
|
|
20
20
|
aep graph sync
|
|
21
21
|
aep analytics <address>
|
|
22
22
|
aep credit-score <address>
|
|
@@ -30,26 +30,25 @@ const score = computeCreditScore(graphPath, accountAddress);
|
|
|
30
30
|
const recs = getRecommendations(graphPath, providers, accountAddress, capability, limit);
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
The resolver uses `graphPath` + `accountAddress` for recommendation boost.
|
|
34
34
|
|
|
35
35
|
## Configuration
|
|
36
36
|
|
|
37
|
-
- **Graph path:** `~/.aep/graph.db
|
|
38
|
-
- **RPC:**
|
|
39
|
-
- **Config:** `~/.aep/config.json`
|
|
37
|
+
- **Graph path:** default `~/.aep/graph.db`; override `graphPath` in `~/.aep/config.json`
|
|
38
|
+
- **RPC:** `RPC_URL` or config `rpcUrl` for `graph sync`
|
|
40
39
|
|
|
41
40
|
## Dependencies
|
|
42
41
|
|
|
43
|
-
Optional
|
|
42
|
+
Optional native `better-sqlite3` (v12+); tests fall back to `sql.js` when bindings are unavailable.
|
|
44
43
|
|
|
45
|
-
## Build &
|
|
44
|
+
## Build & test
|
|
46
45
|
|
|
47
46
|
```bash
|
|
48
47
|
pnpm run build
|
|
49
48
|
pnpm run test
|
|
50
49
|
```
|
|
51
50
|
|
|
52
|
-
##
|
|
51
|
+
## Documentation
|
|
53
52
|
|
|
54
|
-
- [Cookbook](
|
|
55
|
-
- [Architecture](
|
|
53
|
+
- [Cookbook](https://github.com/economicagents/AEP/blob/main/docs/COOKBOOK.md) — Fleet, analytics
|
|
54
|
+
- [Architecture](https://github.com/economicagents/AEP/blob/main/docs/ARCHITECTURE.md) — Graph role in the stack
|
package/dist/cli.d.ts
CHANGED
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;GAEG"}
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* CLI for AEP economic graph sync.
|
|
4
|
-
* Usage: npx aep-graph sync [options]
|
|
5
4
|
*/
|
|
6
5
|
import { join } from "path";
|
|
7
6
|
import { homedir } from "os";
|
|
8
7
|
import { existsSync, readFileSync } from "fs";
|
|
8
|
+
import { Command } from "commander";
|
|
9
9
|
import { syncGraph } from "./index.js";
|
|
10
10
|
function getConfigPath() {
|
|
11
11
|
const env = process.env.AEP_CONFIG_PATH;
|
|
@@ -29,29 +29,36 @@ function loadConfig() {
|
|
|
29
29
|
}
|
|
30
30
|
return {};
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
const program = new Command();
|
|
33
|
+
program
|
|
34
|
+
.name("aep-graph")
|
|
35
|
+
.description("AEP economic graph: sync on-chain events into local SQLite graph store")
|
|
36
|
+
.version("0.2.0")
|
|
37
|
+
.addHelpText("after", `\nExamples:\n $ aep-graph sync\n $ aep-graph sync -r https://sepolia.base.org --graph-path ~/.aep/graph\n`);
|
|
38
|
+
program
|
|
39
|
+
.command("sync")
|
|
40
|
+
.description("Incremental graph sync (accounts, payments, credit, escrow, splitter, SLA)")
|
|
41
|
+
.option("-r, --rpc <url>", "JSON-RPC URL", DEFAULT_RPC)
|
|
42
|
+
.option("--graph-path <path>", "Graph database path", DEFAULT_GRAPH_PATH)
|
|
43
|
+
.option("--chain-id <id>", "Chain id (overrides config when set)")
|
|
44
|
+
.action(async (opts) => {
|
|
35
45
|
const config = loadConfig();
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
console.error(" sync [--rpc <url>] [--graph-path <path>] [--chain-id <id>]");
|
|
39
|
-
process.exit(1);
|
|
40
|
-
}
|
|
41
|
-
const rpcIndex = args.indexOf("--rpc");
|
|
42
|
-
const rpcUrl = rpcIndex >= 0 ? args[rpcIndex + 1] : config.rpcUrl ?? DEFAULT_RPC;
|
|
43
|
-
const pathIndex = args.indexOf("--graph-path");
|
|
44
|
-
const graphPath = pathIndex >= 0 ? args[pathIndex + 1] : config.graphPath ?? DEFAULT_GRAPH_PATH;
|
|
46
|
+
const rpcUrl = opts.rpc ?? config.rpcUrl ?? DEFAULT_RPC;
|
|
47
|
+
const graphPath = opts.graphPath ?? config.graphPath ?? DEFAULT_GRAPH_PATH;
|
|
45
48
|
const factoryAddress = (config.aepAccountFactoryAddress ?? config.factoryAddress);
|
|
46
49
|
if (!factoryAddress || factoryAddress === "0x0000000000000000000000000000000000000000") {
|
|
47
50
|
console.error("Error: factoryAddress or aepAccountFactoryAddress required in config (deploy factory first)");
|
|
48
51
|
process.exit(1);
|
|
49
52
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
let chainId;
|
|
54
|
+
if (opts.chainId != null && opts.chainId !== "") {
|
|
55
|
+
chainId = parseInt(String(opts.chainId), 10);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
chainId =
|
|
59
|
+
config.chainId ??
|
|
60
|
+
parseInt(process.env.AEP_CHAIN_ID ?? process.env.BASE_SEPOLIA_CHAIN_ID ?? "84532", 10);
|
|
61
|
+
}
|
|
55
62
|
if (Number.isNaN(chainId) || chainId <= 0) {
|
|
56
63
|
console.error("Error: invalid chain-id (use e.g. 84532 for Base Sepolia, 8453 for Base mainnet)");
|
|
57
64
|
process.exit(1);
|
|
@@ -77,5 +84,22 @@ async function main() {
|
|
|
77
84
|
console.error("Error:", err instanceof Error ? err.message : String(err));
|
|
78
85
|
process.exit(1);
|
|
79
86
|
}
|
|
87
|
+
});
|
|
88
|
+
async function main() {
|
|
89
|
+
let argv = process.argv.slice(2);
|
|
90
|
+
if (argv.length === 0) {
|
|
91
|
+
argv = ["sync"];
|
|
92
|
+
}
|
|
93
|
+
const first = argv[0];
|
|
94
|
+
if (first &&
|
|
95
|
+
!first.startsWith("-") &&
|
|
96
|
+
first !== "sync" &&
|
|
97
|
+
first !== "-h" &&
|
|
98
|
+
first !== "--help" &&
|
|
99
|
+
first !== "-V" &&
|
|
100
|
+
first !== "--version") {
|
|
101
|
+
argv = ["sync", ...argv];
|
|
102
|
+
}
|
|
103
|
+
await program.parseAsync(argv, { from: "user" });
|
|
80
104
|
}
|
|
81
105
|
main();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleet-alerts.d.ts","sourceRoot":"","sources":["../src/fleet-alerts.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"fleet-alerts.d.ts","sourceRoot":"","sources":["../src/fleet-alerts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAOpC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,EAAE,EAC1B,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,UAAU,EAAE,CAAC,CAwOvB"}
|
package/dist/fleet-alerts.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Fleet alerts — one-shot query for security-relevant events for fleet accounts.
|
|
3
3
|
* Queries graph DB for linked facilities/SLAs, then RPC for on-chain events.
|
|
4
4
|
*/
|
|
5
|
-
import { createPublicClient,
|
|
5
|
+
import { createPublicClient, parseAbiItem, isAddress } from "viem";
|
|
6
|
+
import { transportFromRpcUrl } from "@economicagents/viem-rpc";
|
|
6
7
|
import { base, baseSepolia } from "viem/chains";
|
|
7
8
|
import { getDatabase } from "./store.js";
|
|
8
9
|
const CHUNK_SIZE = 9999n;
|
|
@@ -17,7 +18,7 @@ export async function getFleetAlerts(graphPath, rpcUrl, accountAddresses, option
|
|
|
17
18
|
if (!rpcUrl || typeof rpcUrl !== "string" || !rpcUrl.trim()) {
|
|
18
19
|
throw new Error("getFleetAlerts requires a non-empty rpcUrl");
|
|
19
20
|
}
|
|
20
|
-
const db = getDatabase(graphPath);
|
|
21
|
+
const db = getDatabase(graphPath, { readonly: true });
|
|
21
22
|
// 1. Facilities where lender or borrower in fleet accounts
|
|
22
23
|
const facilityAddresses = [];
|
|
23
24
|
if (normalized.length > 0) {
|
|
@@ -40,7 +41,7 @@ export async function getFleetAlerts(graphPath, rpcUrl, accountAddresses, option
|
|
|
40
41
|
const chain = chainId === base.id ? base : baseSepolia;
|
|
41
42
|
const client = createPublicClient({
|
|
42
43
|
chain,
|
|
43
|
-
transport:
|
|
44
|
+
transport: transportFromRpcUrl(rpcUrl),
|
|
44
45
|
});
|
|
45
46
|
const toBlock = options?.toBlock ?? Number(await client.getBlockNumber());
|
|
46
47
|
const fromBlock = options?.fromBlock ?? Math.max(0, toBlock - DEFAULT_BLOCK_RANGE);
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export { getPaymentsFrom, getPaymentsTo, getAccountAnalytics, getAccountAnalytic
|
|
|
8
8
|
export { getFleetAlerts } from "./fleet-alerts.js";
|
|
9
9
|
export { getRecommendations } from "./recommendations.js";
|
|
10
10
|
export type { GraphConfig, SyncResult, PaymentSource, } from "./types.js";
|
|
11
|
+
export type { GraphDatabaseOptions } from "./store.js";
|
|
11
12
|
export type { PaymentRow, AccountAnalytics, CreditScoreResult, PaymentTrend, FleetSummary, } from "./queries.js";
|
|
12
13
|
export type { FleetAlert, GetFleetAlertsOptions } from "./fleet-alerts.js";
|
|
13
14
|
export type { ProviderRecommendation, ProviderInfo } from "./recommendations.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAwBH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAK1D,wBAAsB,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAokBxE;AAED,OAAO,EACL,WAAW,EACX,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,EACf,aAAa,EACb,mBAAmB,EACnB,0BAA0B,EAC1B,kBAAkB,EAClB,yBAAyB,EACzB,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EACV,WAAW,EACX,UAAU,EACV,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACvD,YAAY,EACV,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,YAAY,GACb,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,YAAY,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Economic graph sync - indexes on-chain events to build transaction graph.
|
|
3
3
|
*/
|
|
4
|
-
import { createPublicClient,
|
|
4
|
+
import { createPublicClient, parseAbiItem } from "viem";
|
|
5
|
+
import { transportFromRpcUrl } from "@economicagents/viem-rpc";
|
|
5
6
|
import { base, baseSepolia } from "viem/chains";
|
|
6
7
|
import { getDatabase, insertAccount, insertPayment, insertUserOp, insertFacility, insertEscrow, insertSplitter, insertSLA, insertSLAEvent, getSyncState, setSyncState, getAccountAddresses, getFacilityAddresses, getEscrowAddresses, getSplitterAddresses, getSLAAddresses, } from "./store.js";
|
|
7
8
|
const CHUNK_SIZE = 9999n;
|
|
@@ -10,7 +11,7 @@ export async function syncGraph(config) {
|
|
|
10
11
|
const chain = config.chainId === base.id ? base : baseSepolia;
|
|
11
12
|
const client = createPublicClient({
|
|
12
13
|
chain,
|
|
13
|
-
transport:
|
|
14
|
+
transport: transportFromRpcUrl(config.rpcUrl),
|
|
14
15
|
});
|
|
15
16
|
const db = getDatabase(config.graphPath);
|
|
16
17
|
const result = {
|
package/dist/queries.js
CHANGED
|
@@ -16,7 +16,7 @@ export function getBlockRangeForPeriod(graphPath, period) {
|
|
|
16
16
|
const blocks = BLOCKS_PER_PERIOD[period];
|
|
17
17
|
if (!blocks)
|
|
18
18
|
return null;
|
|
19
|
-
const db = getDatabase(graphPath);
|
|
19
|
+
const db = getDatabase(graphPath, { readonly: true });
|
|
20
20
|
const maxBlock = getMaxBlock(db);
|
|
21
21
|
return { fromBlock: Math.max(0, maxBlock - blocks), toBlock: maxBlock };
|
|
22
22
|
}
|
|
@@ -33,7 +33,7 @@ export function getPaymentsTo(db, address) {
|
|
|
33
33
|
return rows;
|
|
34
34
|
}
|
|
35
35
|
export function getAccountAnalytics(graphPath, address) {
|
|
36
|
-
const db = getDatabase(graphPath);
|
|
36
|
+
const db = getDatabase(graphPath, { readonly: true });
|
|
37
37
|
const addr = address.toLowerCase();
|
|
38
38
|
const outflows = db
|
|
39
39
|
.prepare("SELECT amount, source FROM payments WHERE fromAddr = ?")
|
|
@@ -81,7 +81,7 @@ export function getAccountAnalytics(graphPath, address) {
|
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
83
|
export function computeCreditScore(graphPath, address) {
|
|
84
|
-
const db = getDatabase(graphPath);
|
|
84
|
+
const db = getDatabase(graphPath, { readonly: true });
|
|
85
85
|
const addr = address.toLowerCase();
|
|
86
86
|
const outflows = getPaymentsFrom(db, addr);
|
|
87
87
|
const inflows = getPaymentsTo(db, addr);
|
|
@@ -119,7 +119,7 @@ export function computeCreditScore(graphPath, address) {
|
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
121
|
export function computeCreditScoreInRange(graphPath, address, fromBlock, toBlock) {
|
|
122
|
-
const db = getDatabase(graphPath);
|
|
122
|
+
const db = getDatabase(graphPath, { readonly: true });
|
|
123
123
|
const addr = address.toLowerCase();
|
|
124
124
|
const outflows = db
|
|
125
125
|
.prepare("SELECT fromAddr, toAddr, amount, token, blockNumber, source FROM payments WHERE fromAddr = ? AND blockNumber >= ? AND blockNumber <= ? ORDER BY blockNumber")
|
|
@@ -162,7 +162,7 @@ export function computeCreditScoreInRange(graphPath, address, fromBlock, toBlock
|
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
164
|
export function getAccountAnalyticsInRange(graphPath, address, fromBlock, toBlock) {
|
|
165
|
-
const db = getDatabase(graphPath);
|
|
165
|
+
const db = getDatabase(graphPath, { readonly: true });
|
|
166
166
|
const addr = address.toLowerCase();
|
|
167
167
|
const outflows = db
|
|
168
168
|
.prepare("SELECT amount, source FROM payments WHERE fromAddr = ? AND blockNumber >= ? AND blockNumber <= ?")
|
|
@@ -211,7 +211,7 @@ export function getAccountAnalyticsInRange(graphPath, address, fromBlock, toBloc
|
|
|
211
211
|
}
|
|
212
212
|
/** Rolling spend/revenue trends. Groups by block bucket (~1 day = 43200 blocks on Base). */
|
|
213
213
|
export function getPaymentTrends(graphPath, address, period) {
|
|
214
|
-
const db = getDatabase(graphPath);
|
|
214
|
+
const db = getDatabase(graphPath, { readonly: true });
|
|
215
215
|
const addr = address.toLowerCase();
|
|
216
216
|
const maxBlock = getMaxBlock(db);
|
|
217
217
|
const blocks = BLOCKS_PER_PERIOD[period] ?? BLOCKS_PER_PERIOD["30d"];
|
|
@@ -252,7 +252,7 @@ export function getPaymentTrends(graphPath, address, period) {
|
|
|
252
252
|
});
|
|
253
253
|
}
|
|
254
254
|
export function exportPaymentsCsv(graphPath, address, fromBlock, toBlock) {
|
|
255
|
-
const db = getDatabase(graphPath);
|
|
255
|
+
const db = getDatabase(graphPath, { readonly: true });
|
|
256
256
|
const addr = address.toLowerCase();
|
|
257
257
|
let sql = "SELECT fromAddr, toAddr, amount, token, blockNumber, txHash, source FROM payments WHERE (fromAddr = ? OR toAddr = ?)";
|
|
258
258
|
const args = [addr, addr];
|
|
@@ -278,7 +278,7 @@ export function exportPaymentsCsv(graphPath, address, fromBlock, toBlock) {
|
|
|
278
278
|
return [header, ...lines].join("\n");
|
|
279
279
|
}
|
|
280
280
|
export function getFleetSummary(graphPath, accountAddresses) {
|
|
281
|
-
const db = getDatabase(graphPath);
|
|
281
|
+
const db = getDatabase(graphPath, { readonly: true });
|
|
282
282
|
const normalized = accountAddresses.map((a) => a.toLowerCase());
|
|
283
283
|
let totalOutflow = 0n;
|
|
284
284
|
let totalInflow = 0n;
|
package/dist/recommendations.js
CHANGED
|
@@ -10,7 +10,7 @@ import { getDatabase } from "./store.js";
|
|
|
10
10
|
* @param providers - Provider list (e.g. from loadProviders(indexPath))
|
|
11
11
|
*/
|
|
12
12
|
export function getRecommendations(graphPath, providers, accountAddress, capability, limit = 5) {
|
|
13
|
-
const db = getDatabase(graphPath);
|
|
13
|
+
const db = getDatabase(graphPath, { readonly: true });
|
|
14
14
|
const addr = accountAddress.toLowerCase();
|
|
15
15
|
const paymentWalletToProvider = new Map();
|
|
16
16
|
for (const p of providers) {
|
package/dist/store.d.ts
CHANGED
|
@@ -33,8 +33,12 @@ export declare function setSqlJsFallback(SQL: {
|
|
|
33
33
|
export declare function isUsingBetterSqlite3(): boolean;
|
|
34
34
|
/** Exported for tests: whether SQLite (better-sqlite3 or sql.js fallback) is available. */
|
|
35
35
|
export declare function isSqliteAvailable(): boolean;
|
|
36
|
+
export type GraphDatabaseOptions = {
|
|
37
|
+
/** Use read-only mode (API / analytics). Lets graph sync hold the write lock without SQLITE_BUSY. */
|
|
38
|
+
readonly?: boolean;
|
|
39
|
+
};
|
|
36
40
|
export declare function ensureGraphDir(graphPath: string): void;
|
|
37
|
-
export declare function getDatabase(graphPath: string): SqliteDb;
|
|
41
|
+
export declare function getDatabase(graphPath: string, options?: GraphDatabaseOptions): SqliteDb;
|
|
38
42
|
export declare function closeDatabase(): void;
|
|
39
43
|
export declare function insertAccount(database: SqliteDb, address: string, owner: string, firstSeenBlock: number): void;
|
|
40
44
|
export declare function insertPayment(database: SqliteDb, fromAddr: string, toAddr: string, amount: string, token: string, blockNumber: number, txHash: string, logIndex: number | null, source: string): void;
|
package/dist/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK;QACxB,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QACjD,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;QACrC,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,CAAC;KACxC,CAAC;IACF,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AAEF,qFAAqF;AACrF,KAAK,aAAa,GAAG;IACnB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAC/C,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK;QACxB,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;QAClC,IAAI,EAAE,MAAM,OAAO,CAAC;QACpB,WAAW,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC3C,IAAI,EAAE,MAAM,IAAI,CAAC;KAClB,CAAC;IACF,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/store.js
CHANGED
|
@@ -8,8 +8,12 @@ import { join } from "path";
|
|
|
8
8
|
const require = createRequire(import.meta.url);
|
|
9
9
|
let db = null;
|
|
10
10
|
let dbPath = null;
|
|
11
|
+
/** Read-only handle (API analytics) — avoids write locks vs graph sync in a second process */
|
|
12
|
+
let roDb = null;
|
|
13
|
+
let roDbPath = null;
|
|
11
14
|
let sqliteAvailable = null;
|
|
12
15
|
let sqlJsFallback = null;
|
|
16
|
+
const GRAPH_SQLITE_BUSY_TIMEOUT_MS = 30_000;
|
|
13
17
|
/** For tests: set sql.js as fallback when better-sqlite3 native bindings are unavailable. */
|
|
14
18
|
export function setSqlJsFallback(SQL) {
|
|
15
19
|
sqlJsFallback = SQL;
|
|
@@ -74,14 +78,29 @@ export function isSqliteAvailable() {
|
|
|
74
78
|
return true;
|
|
75
79
|
return sqlJsFallback !== null;
|
|
76
80
|
}
|
|
77
|
-
function getDb(graphPath) {
|
|
81
|
+
function getDb(graphPath, options) {
|
|
82
|
+
const wantsReadonly = options?.readonly === true;
|
|
83
|
+
/** sql.js tests use one in-memory DB; read-only file semantics do not apply */
|
|
84
|
+
isSqliteAvailable();
|
|
85
|
+
const readonly = wantsReadonly && sqliteAvailable === true && sqlJsFallback === null;
|
|
78
86
|
const resolvedPath = join(graphPath, "graph.db");
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
if (readonly) {
|
|
88
|
+
if (roDb && roDbPath === resolvedPath)
|
|
89
|
+
return roDb;
|
|
90
|
+
if (roDb) {
|
|
91
|
+
roDb.close();
|
|
92
|
+
roDb = null;
|
|
93
|
+
roDbPath = null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
if (db && dbPath === resolvedPath)
|
|
98
|
+
return db;
|
|
99
|
+
if (db) {
|
|
100
|
+
db.close();
|
|
101
|
+
db = null;
|
|
102
|
+
dbPath = null;
|
|
103
|
+
}
|
|
85
104
|
}
|
|
86
105
|
if (!isSqliteAvailable()) {
|
|
87
106
|
throw new Error("better-sqlite3 or sql.js required for graph. Install: npm install better-sqlite3");
|
|
@@ -89,8 +108,23 @@ function getDb(graphPath) {
|
|
|
89
108
|
try {
|
|
90
109
|
if (sqliteAvailable) {
|
|
91
110
|
const Database = require("better-sqlite3");
|
|
111
|
+
if (readonly) {
|
|
112
|
+
const nativeDb = new Database(resolvedPath, {
|
|
113
|
+
readonly: true,
|
|
114
|
+
fileMustExist: true,
|
|
115
|
+
timeout: GRAPH_SQLITE_BUSY_TIMEOUT_MS,
|
|
116
|
+
});
|
|
117
|
+
roDb = nativeDb;
|
|
118
|
+
roDbPath = resolvedPath;
|
|
119
|
+
return nativeDb;
|
|
120
|
+
}
|
|
92
121
|
mkdirSync(graphPath, { recursive: true });
|
|
93
|
-
|
|
122
|
+
const nativeDb = new Database(resolvedPath, {
|
|
123
|
+
timeout: GRAPH_SQLITE_BUSY_TIMEOUT_MS,
|
|
124
|
+
});
|
|
125
|
+
nativeDb.pragma("journal_mode = WAL");
|
|
126
|
+
nativeDb.pragma(`busy_timeout = ${GRAPH_SQLITE_BUSY_TIMEOUT_MS}`);
|
|
127
|
+
db = nativeDb;
|
|
94
128
|
dbPath = resolvedPath;
|
|
95
129
|
}
|
|
96
130
|
else if (sqlJsFallback) {
|
|
@@ -192,8 +226,8 @@ function initSchema(database) {
|
|
|
192
226
|
export function ensureGraphDir(graphPath) {
|
|
193
227
|
mkdirSync(graphPath, { recursive: true });
|
|
194
228
|
}
|
|
195
|
-
export function getDatabase(graphPath) {
|
|
196
|
-
return getDb(graphPath);
|
|
229
|
+
export function getDatabase(graphPath, options) {
|
|
230
|
+
return getDb(graphPath, options);
|
|
197
231
|
}
|
|
198
232
|
export function closeDatabase() {
|
|
199
233
|
if (db) {
|
|
@@ -201,6 +235,11 @@ export function closeDatabase() {
|
|
|
201
235
|
db = null;
|
|
202
236
|
dbPath = null;
|
|
203
237
|
}
|
|
238
|
+
if (roDb) {
|
|
239
|
+
roDb.close();
|
|
240
|
+
roDb = null;
|
|
241
|
+
roDbPath = null;
|
|
242
|
+
}
|
|
204
243
|
}
|
|
205
244
|
export function insertAccount(database, address, owner, firstSeenBlock) {
|
|
206
245
|
database
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viem-transport.d.ts","sourceRoot":"","sources":["../src/viem-transport.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,MAAM,CAAC;AAEvD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAO7D"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matches @economicagents/sdk/viem-transport — duplicated here to avoid sdk↔graph pkg cycle.
|
|
3
|
+
*/
|
|
4
|
+
import { http, webSocket } from "viem";
|
|
5
|
+
export function transportFromRpcUrl(rpcUrl) {
|
|
6
|
+
const trimmed = rpcUrl.trim();
|
|
7
|
+
const lower = trimmed.toLowerCase();
|
|
8
|
+
if (lower.startsWith("wss://") || lower.startsWith("ws://")) {
|
|
9
|
+
return webSocket(trimmed);
|
|
10
|
+
}
|
|
11
|
+
return http(trimmed);
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@economicagents/graph",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Economic graph for AEP - transaction graph, payments, credit events, credit scoring, recommendations",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -32,7 +32,9 @@
|
|
|
32
32
|
"aep-graph": "./dist/cli.js"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"
|
|
35
|
+
"commander": "^12.1.0",
|
|
36
|
+
"viem": "^2.47.5",
|
|
37
|
+
"@economicagents/viem-rpc": "0.2.0"
|
|
36
38
|
},
|
|
37
39
|
"optionalDependencies": {
|
|
38
40
|
"better-sqlite3": "^12.1.0"
|