@secondlayer/subgraphs 6.3.6 → 6.4.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.
@@ -440,7 +440,7 @@ class SubgraphContext {
440
440
  async readRows(table, where, limit) {
441
441
  const qualifiedTable = `"${this.pgSchemaName}"."${table}"`;
442
442
  const { clause } = buildWhereClause(where);
443
- const query = `SELECT * FROM ${qualifiedTable} WHERE ${clause}${limit !== undefined ? ` LIMIT ${limit}` : ""}`;
443
+ const query = `SELECT * FROM ${qualifiedTable} WHERE ${clause} ORDER BY "_id"${limit !== undefined ? ` LIMIT ${limit}` : ""}`;
444
444
  const { rows } = await sql.raw(query).execute(this.db);
445
445
  return rows.map((r) => this.coerceRow(table, r));
446
446
  }
@@ -1254,6 +1254,478 @@ var COLUMN_TYPES = new Set([
1254
1254
  "jsonb"
1255
1255
  ]);
1256
1256
 
1257
+ // src/runtime/realm.ts
1258
+ import vm from "node:vm";
1259
+
1260
+ // src/verification.ts
1261
+ import { createHash as createHash2 } from "node:crypto";
1262
+ // package.json
1263
+ var package_default = {
1264
+ name: "@secondlayer/subgraphs",
1265
+ version: "6.4.0",
1266
+ description: "Describe the Stacks data you want as one TypeScript file; it fills itself and survives reorgs.",
1267
+ license: "MIT",
1268
+ repository: {
1269
+ type: "git",
1270
+ url: "git+https://github.com/ryanwaits/secondlayer.git",
1271
+ directory: "packages/subgraphs"
1272
+ },
1273
+ homepage: "https://github.com/ryanwaits/secondlayer/tree/main/packages/subgraphs#readme",
1274
+ type: "module",
1275
+ main: "./dist/index.js",
1276
+ types: "./dist/index.d.ts",
1277
+ exports: {
1278
+ ".": {
1279
+ types: "./dist/index.d.ts",
1280
+ import: "./dist/index.js"
1281
+ },
1282
+ "./types": {
1283
+ types: "./dist/types.d.ts",
1284
+ import: "./dist/types.js"
1285
+ },
1286
+ "./schema": {
1287
+ types: "./dist/schema/index.d.ts",
1288
+ import: "./dist/schema/index.js"
1289
+ },
1290
+ "./validate": {
1291
+ types: "./dist/validate.d.ts",
1292
+ import: "./dist/validate.js"
1293
+ },
1294
+ "./verification": {
1295
+ types: "./dist/verification.d.ts",
1296
+ import: "./dist/verification.js"
1297
+ },
1298
+ "./runtime/replay": {
1299
+ types: "./dist/runtime/replay.d.ts",
1300
+ import: "./dist/runtime/replay.js"
1301
+ },
1302
+ "./runtime/emitter": {
1303
+ types: "./dist/runtime/emitter.d.ts",
1304
+ import: "./dist/runtime/emitter.js"
1305
+ },
1306
+ "./testing": {
1307
+ types: "./dist/testing/index.d.ts",
1308
+ import: "./dist/testing/index.js"
1309
+ }
1310
+ },
1311
+ files: ["dist"],
1312
+ scripts: {
1313
+ build: "bunup",
1314
+ dev: "bunup --watch",
1315
+ test: "bun test",
1316
+ typecheck: "tsc --noEmit",
1317
+ prepublishOnly: "bun run build"
1318
+ },
1319
+ dependencies: {
1320
+ "@secondlayer/shared": "^11.22.0",
1321
+ "@secondlayer/stacks": "^6.1.0",
1322
+ esbuild: "^0.28.2",
1323
+ kysely: "0.28.17",
1324
+ zod: "^4.3.6"
1325
+ },
1326
+ devDependencies: {
1327
+ "@types/bun": "latest",
1328
+ typescript: "^5"
1329
+ },
1330
+ publishConfig: {
1331
+ access: "public"
1332
+ }
1333
+ };
1334
+
1335
+ // src/verification.ts
1336
+ var SUBGRAPHS_RUNTIME = `@secondlayer/subgraphs@${package_default.version}`;
1337
+ var FORBIDDEN_GLOBALS = {
1338
+ Date: "wall-clock time differs per run",
1339
+ performance: "timers differ per run",
1340
+ process: "host environment differs per run",
1341
+ fetch: "network reads are not chain inputs",
1342
+ crypto: "randomness differs per run",
1343
+ setTimeout: "scheduling order differs per run",
1344
+ setInterval: "scheduling order differs per run",
1345
+ setImmediate: "scheduling order differs per run",
1346
+ clearTimeout: "scheduling order differs per run",
1347
+ clearInterval: "scheduling order differs per run",
1348
+ queueMicrotask: "scheduling order differs per run",
1349
+ WeakRef: "garbage collection timing differs per run",
1350
+ FinalizationRegistry: "garbage collection timing differs per run",
1351
+ Atomics: "shared memory is not single-threaded",
1352
+ SharedArrayBuffer: "shared memory is not single-threaded",
1353
+ Intl: "locale and time zone data differ per host",
1354
+ Temporal: "locale and time zone data differ per host",
1355
+ eval: "generated code hides inputs from the scan",
1356
+ WebAssembly: "compiled code hides inputs from the scan",
1357
+ ShadowRealm: "a nested realm escapes the allow-list",
1358
+ require: "imports must be bundled into the handler"
1359
+ };
1360
+ var ALLOWED_GLOBALS = [
1361
+ "undefined",
1362
+ "NaN",
1363
+ "Infinity",
1364
+ "globalThis",
1365
+ "isNaN",
1366
+ "isFinite",
1367
+ "parseInt",
1368
+ "parseFloat",
1369
+ "decodeURI",
1370
+ "decodeURIComponent",
1371
+ "encodeURI",
1372
+ "encodeURIComponent",
1373
+ "escape",
1374
+ "unescape",
1375
+ "Object",
1376
+ "Function",
1377
+ "Array",
1378
+ "Number",
1379
+ "Boolean",
1380
+ "String",
1381
+ "Symbol",
1382
+ "BigInt",
1383
+ "Math",
1384
+ "JSON",
1385
+ "Reflect",
1386
+ "Proxy",
1387
+ "Promise",
1388
+ "Map",
1389
+ "Set",
1390
+ "WeakMap",
1391
+ "WeakSet",
1392
+ "RegExp",
1393
+ "Iterator",
1394
+ "DisposableStack",
1395
+ "AsyncDisposableStack",
1396
+ "Error",
1397
+ "AggregateError",
1398
+ "SuppressedError",
1399
+ "EvalError",
1400
+ "RangeError",
1401
+ "ReferenceError",
1402
+ "SyntaxError",
1403
+ "TypeError",
1404
+ "URIError",
1405
+ "ArrayBuffer",
1406
+ "DataView",
1407
+ "Int8Array",
1408
+ "Uint8Array",
1409
+ "Uint8ClampedArray",
1410
+ "Int16Array",
1411
+ "Uint16Array",
1412
+ "Int32Array",
1413
+ "Uint32Array",
1414
+ "Float16Array",
1415
+ "Float32Array",
1416
+ "Float64Array",
1417
+ "BigInt64Array",
1418
+ "BigUint64Array",
1419
+ "TextEncoder",
1420
+ "TextDecoder",
1421
+ "atob",
1422
+ "btoa",
1423
+ "console"
1424
+ ];
1425
+ var ALLOWED_MATH = [
1426
+ "abs",
1427
+ "min",
1428
+ "max",
1429
+ "floor",
1430
+ "ceil",
1431
+ "trunc",
1432
+ "sign",
1433
+ "round",
1434
+ "E",
1435
+ "LN10",
1436
+ "LN2",
1437
+ "LOG10E",
1438
+ "LOG2E",
1439
+ "PI",
1440
+ "SQRT1_2",
1441
+ "SQRT2"
1442
+ ];
1443
+ var LOCALE_METHODS = [
1444
+ "toLocaleString",
1445
+ "toLocaleDateString",
1446
+ "toLocaleTimeString",
1447
+ "toLocaleUpperCase",
1448
+ "toLocaleLowerCase",
1449
+ "localeCompare"
1450
+ ];
1451
+ function formatFinding(f) {
1452
+ return `${f.file}:${f.line}:${f.column} ${f.name}: ${f.reason}`;
1453
+ }
1454
+ var STATE_SOURCE_TYPES = new Set([
1455
+ "var_set",
1456
+ "map_set",
1457
+ "map_insert",
1458
+ "map_delete"
1459
+ ]);
1460
+ var LEVEL_RANK = { state: 0, events: 1, none: 2 };
1461
+ var MAX_LISTED_FINDINGS = 10;
1462
+ function deriveVerification(def, findings = []) {
1463
+ let level = "state";
1464
+ const raise = (to) => {
1465
+ if (LEVEL_RANK[to] > LEVEL_RANK[level])
1466
+ level = to;
1467
+ };
1468
+ const reasons = [];
1469
+ for (const [name, source] of Object.entries(def.sources ?? {})) {
1470
+ const filter = source;
1471
+ if (filter.trait) {
1472
+ raise("none");
1473
+ reasons.push(`source "${name}": trait scope needs a proven contract registry`);
1474
+ } else if (!STATE_SOURCE_TYPES.has(filter.type)) {
1475
+ raise("events");
1476
+ reasons.push(`${filter.type} source "${name}" needs event proofs`);
1477
+ }
1478
+ }
1479
+ if (def.backfillMode === "concurrent") {
1480
+ raise("none");
1481
+ reasons.push("backfillMode concurrent: tip-first order is not chain order");
1482
+ }
1483
+ if (findings.some((f) => f.kind === "needs-events"))
1484
+ raise("events");
1485
+ for (const f of findings.slice(0, MAX_LISTED_FINDINGS)) {
1486
+ reasons.push(f.kind === "needs-events" ? `${formatFinding(f)} (needs event proofs)` : formatFinding(f));
1487
+ }
1488
+ if (findings.length > MAX_LISTED_FINDINGS) {
1489
+ reasons.push(`…and ${findings.length - MAX_LISTED_FINDINGS} more handler findings`);
1490
+ }
1491
+ const writesMapInsert = Object.values(def.sources ?? {}).some((s) => s.type === "map_insert");
1492
+ return {
1493
+ level,
1494
+ reasons,
1495
+ unproven: reasons.length === 0 ? [
1496
+ "tx attribution of writes: event.tx, _tx_id (needs event proofs)",
1497
+ "intermediate writes within a block (needs event proofs)",
1498
+ ...writesMapInsert ? ["map_insert vs map_set (needs event proofs)"] : []
1499
+ ] : []
1500
+ };
1501
+ }
1502
+ function isDeterminismViolation(v) {
1503
+ return v.level === "state" && v.reasons.length > 0;
1504
+ }
1505
+ function sha256(input) {
1506
+ return createHash2("sha256").update(input).digest("hex");
1507
+ }
1508
+ function computePin(input) {
1509
+ const canonical = JSON.stringify({
1510
+ handlerHash: sha256(input.handlerCode),
1511
+ network: input.network,
1512
+ runtime: input.runtime ?? SUBGRAPHS_RUNTIME,
1513
+ schemaHash: input.schemaHash,
1514
+ startBlock: input.startBlock ?? null
1515
+ });
1516
+ return sha256(canonical);
1517
+ }
1518
+
1519
+ // src/runtime/realm.ts
1520
+ class NondeterminismError extends Error {
1521
+ name = "NondeterminismError";
1522
+ }
1523
+
1524
+ class HandlerReadError extends Error {
1525
+ name = "HandlerReadError";
1526
+ }
1527
+ var HOST_GLOBALS = [
1528
+ "ArrayBuffer",
1529
+ "DataView",
1530
+ "Int8Array",
1531
+ "Uint8Array",
1532
+ "Uint8ClampedArray",
1533
+ "Int16Array",
1534
+ "Uint16Array",
1535
+ "Int32Array",
1536
+ "Uint32Array",
1537
+ "Float16Array",
1538
+ "Float32Array",
1539
+ "Float64Array",
1540
+ "BigInt64Array",
1541
+ "BigUint64Array",
1542
+ "TextEncoder",
1543
+ "TextDecoder",
1544
+ "atob",
1545
+ "btoa"
1546
+ ];
1547
+ function forbid(name, reason) {
1548
+ return () => {
1549
+ throw new NondeterminismError(`${name}: ${reason}`);
1550
+ };
1551
+ }
1552
+ function createRealm() {
1553
+ const host = globalThis;
1554
+ const sandbox = Object.create(null);
1555
+ for (const name of HOST_GLOBALS) {
1556
+ if (host[name] !== undefined)
1557
+ sandbox[name] = host[name];
1558
+ }
1559
+ const context = vm.createContext(sandbox, {
1560
+ name: "subgraph-handler",
1561
+ codeGeneration: { strings: false, wasm: false }
1562
+ });
1563
+ const g = vm.runInContext("globalThis", context);
1564
+ const allowed = new Set(ALLOWED_GLOBALS);
1565
+ for (const name of Object.getOwnPropertyNames(g)) {
1566
+ if (!allowed.has(name))
1567
+ delete g[name];
1568
+ }
1569
+ for (const [name, reason] of Object.entries(FORBIDDEN_GLOBALS)) {
1570
+ Object.defineProperty(g, name, { get: forbid(name, reason) });
1571
+ }
1572
+ const realmMath = g.Math;
1573
+ const safeMath = {};
1574
+ const allowedMath = new Set(ALLOWED_MATH);
1575
+ for (const key of Object.getOwnPropertyNames(realmMath)) {
1576
+ if (allowedMath.has(key))
1577
+ safeMath[key] = realmMath[key];
1578
+ else
1579
+ Object.defineProperty(safeMath, key, {
1580
+ get: forbid(`Math.${key}`, key === "random" ? "randomness differs per run" : "result is engine-approximated")
1581
+ });
1582
+ }
1583
+ Object.defineProperty(g, "Math", { value: Object.freeze(safeMath) });
1584
+ for (const ctor of ["String", "Number", "BigInt", "Array", "Object"]) {
1585
+ const proto = g[ctor].prototype;
1586
+ for (const method of LOCALE_METHODS) {
1587
+ if (Object.prototype.hasOwnProperty.call(proto, method)) {
1588
+ Object.defineProperty(proto, method, {
1589
+ value: forbid(method, "output depends on the host's locale data")
1590
+ });
1591
+ }
1592
+ }
1593
+ }
1594
+ Object.preventExtensions(g);
1595
+ return context;
1596
+ }
1597
+ function checkRowValues(where, values) {
1598
+ for (const [col, value] of Object.entries(values)) {
1599
+ checkValue(value, `${where}.${col}`);
1600
+ }
1601
+ }
1602
+ function checkValue(value, path) {
1603
+ switch (typeof value) {
1604
+ case "number":
1605
+ if (!Number.isSafeInteger(value)) {
1606
+ throw new TypeError(`${path}: ${value} is not a safe integer; verifiable subgraphs store bigint or integers only`);
1607
+ }
1608
+ return;
1609
+ case "function":
1610
+ case "symbol":
1611
+ throw new TypeError(`${path}: a ${typeof value} is not a row value`);
1612
+ case "object":
1613
+ if (value === null || ArrayBuffer.isView(value))
1614
+ return;
1615
+ if (Array.isArray(value)) {
1616
+ value.forEach((v, i) => checkValue(v, `${path}[${i}]`));
1617
+ return;
1618
+ }
1619
+ for (const [k, v] of Object.entries(value))
1620
+ checkValue(v, `${path}.${k}`);
1621
+ return;
1622
+ default:
1623
+ return;
1624
+ }
1625
+ }
1626
+ function deterministicContext(ctx, schema) {
1627
+ let pendingReads = 0;
1628
+ const knownTable = (table) => {
1629
+ if (!Object.prototype.hasOwnProperty.call(schema, table)) {
1630
+ throw new Error(`Table "${table}" not found in subgraph schema`);
1631
+ }
1632
+ };
1633
+ const guard = (op) => {
1634
+ if (pendingReads > 0) {
1635
+ throw new NondeterminismError(`ctx.${op} while a ctx read is pending: concurrent ctx calls order writes by database latency; await each read before the next call`);
1636
+ }
1637
+ };
1638
+ const read = async (op, run) => {
1639
+ guard(op);
1640
+ pendingReads++;
1641
+ try {
1642
+ return await run();
1643
+ } catch (err) {
1644
+ if (err instanceof NondeterminismError)
1645
+ throw err;
1646
+ throw new HandlerReadError(`ctx.${op} failed: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
1647
+ } finally {
1648
+ pendingReads--;
1649
+ }
1650
+ };
1651
+ return {
1652
+ get block() {
1653
+ return ctx.block;
1654
+ },
1655
+ get tx() {
1656
+ return ctx.tx;
1657
+ },
1658
+ get client() {
1659
+ throw new NondeterminismError("ctx.client: contract reads need re-execution proofs");
1660
+ },
1661
+ insert(table, row) {
1662
+ guard("insert");
1663
+ checkRowValues(`insert ${table}`, row);
1664
+ ctx.insert(table, row);
1665
+ },
1666
+ update(table, where, set) {
1667
+ guard("update");
1668
+ checkRowValues(`update ${table}`, where);
1669
+ checkRowValues(`update ${table}`, set);
1670
+ ctx.update(table, where, set);
1671
+ },
1672
+ upsert(table, key, row) {
1673
+ guard("upsert");
1674
+ checkRowValues(`upsert ${table}`, key);
1675
+ checkRowValues(`upsert ${table}`, row);
1676
+ ctx.upsert(table, key, row);
1677
+ },
1678
+ delete(table, where) {
1679
+ guard("delete");
1680
+ ctx.delete(table, where);
1681
+ },
1682
+ increment(table, key, deltas) {
1683
+ guard("increment");
1684
+ checkRowValues(`increment ${table}`, key);
1685
+ checkRowValues(`increment ${table}`, deltas);
1686
+ ctx.increment(table, key, deltas);
1687
+ },
1688
+ findOne: (table, where) => {
1689
+ knownTable(table);
1690
+ return read("findOne", () => ctx.findOne(table, where));
1691
+ },
1692
+ findMany: (table, ...rest) => {
1693
+ knownTable(table);
1694
+ return read("findMany", () => ctx.findMany(table, ...rest));
1695
+ }
1696
+ };
1697
+ }
1698
+ async function loadDeterministicDefinition(handlerCode) {
1699
+ const { transform } = await import("esbuild");
1700
+ const { code } = await transform(handlerCode, {
1701
+ format: "cjs",
1702
+ loader: "js",
1703
+ logLevel: "silent"
1704
+ });
1705
+ const context = createRealm();
1706
+ const evaluate = vm.runInContext(`(function (module, exports) {
1707
+ ${code}
1708
+ })`, context, { filename: "handler.js" });
1709
+ const mod = { exports: {} };
1710
+ evaluate(mod, mod.exports);
1711
+ const def = mod.exports.default ?? mod.exports;
1712
+ const sources = structuredClone(def.sources);
1713
+ const schema = structuredClone(def.schema);
1714
+ const facades = new WeakMap;
1715
+ const handlers = {};
1716
+ for (const [name, handler] of Object.entries(def.handlers ?? {})) {
1717
+ handlers[name] = (event, ctx) => {
1718
+ let facade = facades.get(ctx);
1719
+ if (!facade) {
1720
+ facade = deterministicContext(ctx, schema);
1721
+ facades.set(ctx, facade);
1722
+ }
1723
+ return handler(event, facade);
1724
+ };
1725
+ }
1726
+ return { ...def, sources, schema, handlers };
1727
+ }
1728
+
1257
1729
  // src/runtime/source-matcher.ts
1258
1730
  var patternCache = new Map;
1259
1731
  function matchAny(value, pattern) {
@@ -2285,5 +2757,5 @@ export {
2285
2757
  toHandlerPayload
2286
2758
  };
2287
2759
 
2288
- //# debugId=73BD3E2068CDEA0464756E2164756E21
2760
+ //# debugId=124EC2B77481A62F64756E2164756E21
2289
2761
  //# sourceMappingURL=index.js.map