@secondlayer/cli 9.1.0 → 9.3.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.
Files changed (3) hide show
  1. package/dist/cli.js +451 -177
  2. package/dist/cli.js.map +6 -5
  3. package/package.json +15 -5
package/dist/cli.js CHANGED
@@ -28910,7 +28910,7 @@ export default defineSubgraph({
28910
28910
  `;
28911
28911
  }
28912
28912
  function sip010Balances(name) {
28913
- return `import { defineSubgraph } from "@secondlayer/subgraphs";
28913
+ return `import { defineSchema, defineSubgraph, readContractAt, type TypedSubgraphContext } from "@secondlayer/subgraphs";
28914
28914
 
28915
28915
  /**
28916
28916
  * Track SIP-010 token balances per (asset_identifier, holder).
@@ -28919,10 +28919,59 @@ function sip010Balances(name) {
28919
28919
  * token by adding \`assetIdentifier: "SP...token::token-name"\` to each
28920
28920
  * source filter.
28921
28921
  *
28922
+ * Balances are moved with \`ctx.increment\`, which applies a DELTA in one
28923
+ * atomic statement. That matters: a read-modify-write (findOne → compute →
28924
+ * upsert) loses concurrent updates to the same row, and it is not replay-safe
28925
+ * under \`backfillMode: "concurrent"\`. Deltas commute, so order doesn't
28926
+ * matter and a reorg rewind reverses cleanly via the journal.
28927
+ *
28928
+ * Token metadata (decimals/symbol) comes from the token contract itself via
28929
+ * \`readContractAt\`, so \`amount\` stops being a bare integer nobody can
28930
+ * render. The read is pinned to the block being processed — a handler stays a
28931
+ * pure function of its block — and \`cache: "contract-constant"\` says these
28932
+ * two values can never change for a given token, so each is fetched ONCE
28933
+ * instead of once per block.
28934
+ *
28922
28935
  * Query examples once deployed:
28923
28936
  * GET /v1/subgraphs/${name}/balances?_search=SP1...
28924
28937
  * GET /v1/subgraphs/${name}/balances?holder=SP1...
28938
+ * GET /v1/subgraphs/${name}/tokens
28925
28939
  */
28940
+
28941
+ /** The two SIP-010 read-only functions this subgraph calls. */
28942
+ const SIP010_ABI = {
28943
+ functions: [
28944
+ { name: "get-decimals", access: "read-only", args: [], outputs: { response: { ok: "uint128", error: "uint128" } } },
28945
+ { name: "get-symbol", access: "read-only", args: [], outputs: { response: { ok: { "string-ascii": { length: 32 } }, error: "uint128" } } },
28946
+ ],
28947
+ } as const;
28948
+
28949
+ /** Tokens already labelled in this process — metadata is immutable, so one
28950
+ * resolution per token is enough even across a backfill. */
28951
+ const labelled = new Set<string>();
28952
+
28953
+ // Hoisted so the helper below can be typed against it.
28954
+ const schema = defineSchema({
28955
+ balances: {
28956
+ columns: {
28957
+ asset_identifier: { type: "text", indexed: true, search: true },
28958
+ holder: { type: "principal", indexed: true, search: true },
28959
+ amount: { type: "uint" },
28960
+ },
28961
+ // increment() upserts on this key.
28962
+ uniqueKeys: [["asset_identifier", "holder"]],
28963
+ },
28964
+ tokens: {
28965
+ columns: {
28966
+ asset_identifier: { type: "text", indexed: true },
28967
+ contract_id: { type: "text" },
28968
+ symbol: { type: "text" },
28969
+ decimals: { type: "uint" },
28970
+ },
28971
+ uniqueKeys: [["asset_identifier"]],
28972
+ },
28973
+ });
28974
+
28926
28975
  export default defineSubgraph({
28927
28976
  name: "${name}",
28928
28977
  version: "1.0.0",
@@ -28934,55 +28983,46 @@ export default defineSubgraph({
28934
28983
  burn: { type: "ft_burn" },
28935
28984
  },
28936
28985
 
28937
- schema: {
28938
- balances: {
28939
- columns: {
28940
- asset_identifier: { type: "text", indexed: true, search: true },
28941
- holder: { type: "principal", indexed: true, search: true },
28942
- amount: { type: "uint" },
28943
- },
28944
- uniqueKeys: [["asset_identifier", "holder"]],
28945
- },
28946
- },
28986
+ schema,
28947
28987
 
28948
28988
  handlers: {
28949
28989
  transfer: async (event, ctx) => {
28990
+ await labelToken(event.assetIdentifier, ctx);
28950
28991
  const amount = BigInt(event.amount ?? 0);
28951
28992
  if (event.sender) {
28952
- await adjust(ctx, event.assetIdentifier, event.sender, -amount);
28993
+ ctx.increment("balances", { asset_identifier: event.assetIdentifier, holder: event.sender }, { amount: -amount });
28953
28994
  }
28954
28995
  if (event.recipient) {
28955
- await adjust(ctx, event.assetIdentifier, event.recipient, amount);
28996
+ ctx.increment("balances", { asset_identifier: event.assetIdentifier, holder: event.recipient }, { amount });
28956
28997
  }
28957
28998
  },
28958
- mint: async (event, ctx) => {
28999
+ mint: (event, ctx) => {
28959
29000
  if (event.recipient) {
28960
- await adjust(ctx, event.assetIdentifier, event.recipient, BigInt(event.amount ?? 0));
29001
+ ctx.increment("balances", { asset_identifier: event.assetIdentifier, holder: event.recipient }, { amount: BigInt(event.amount ?? 0) });
28961
29002
  }
28962
29003
  },
28963
- burn: async (event, ctx) => {
29004
+ burn: (event, ctx) => {
28964
29005
  if (event.sender) {
28965
- await adjust(ctx, event.assetIdentifier, event.sender, -BigInt(event.amount ?? 0));
29006
+ ctx.increment("balances", { asset_identifier: event.assetIdentifier, holder: event.sender }, { amount: -BigInt(event.amount ?? 0) });
28966
29007
  }
28967
29008
  },
28968
29009
  },
28969
29010
  });
28970
29011
 
28971
- async function adjust(
28972
- // biome-ignore lint/suspicious/noExplicitAny: subgraph runtime ctx shape
28973
- ctx: any,
28974
- assetIdentifier: string,
28975
- holder: string,
28976
- delta: bigint,
28977
- ): Promise<void> {
28978
- const existing = await ctx.findOne("balances", { asset_identifier: assetIdentifier, holder });
28979
- const current = existing ? BigInt(existing.amount) : 0n;
28980
- const next = current + delta;
28981
- await ctx.upsert(
28982
- "balances",
28983
- { asset_identifier: assetIdentifier, holder },
28984
- { asset_identifier: assetIdentifier, holder, amount: next },
28985
- );
29012
+ /** Resolve a token's symbol + decimals from its contract, once. */
29013
+ async function labelToken(
29014
+ assetIdentifier: string | undefined,
29015
+ ctx: TypedSubgraphContext<typeof schema>,
29016
+ ) {
29017
+ if (!assetIdentifier || labelled.has(assetIdentifier)) return;
29018
+ labelled.add(assetIdentifier);
29019
+ const contractId = assetIdentifier.split("::")[0];
29020
+ const token = readContractAt(ctx, contractId, SIP010_ABI, { cache: "contract-constant" });
29021
+ const [decimals, symbol] = await Promise.all([
29022
+ token.read.getDecimals({}),
29023
+ token.read.getSymbol({}),
29024
+ ]);
29025
+ ctx.upsert("tokens", { asset_identifier: assetIdentifier }, { asset_identifier: assetIdentifier, contract_id: contractId, symbol, decimals });
28986
29026
  }
28987
29027
  `;
28988
29028
  }
@@ -29206,6 +29246,223 @@ function deriveBaseUrl(apiUrl) {
29206
29246
  }
29207
29247
  }
29208
29248
 
29249
+ // src/commands/subgraph-test.ts
29250
+ var exports_subgraph_test = {};
29251
+ __export(exports_subgraph_test, {
29252
+ runSubgraphTest: () => runSubgraphTest
29253
+ });
29254
+ import { createHash } from "node:crypto";
29255
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
29256
+ import { dirname as dirname3, resolve as resolve2 } from "node:path";
29257
+ import { Index } from "@secondlayer/sdk";
29258
+ function filterHashOf(sources) {
29259
+ const stable = JSON.stringify(Object.fromEntries(Object.entries(sources).sort(([a4], [b2]) => a4.localeCompare(b2))), (_k, v2) => typeof v2 === "bigint" ? `${v2}n` : v2);
29260
+ return createHash("sha256").update(stable).digest("hex").slice(0, 16);
29261
+ }
29262
+ function cassettePath(name) {
29263
+ return resolve2(CASSETTE_DIR, `${name}.json`);
29264
+ }
29265
+ function loadCassette(name, filterHash) {
29266
+ const path11 = cassettePath(name);
29267
+ if (!existsSync2(path11))
29268
+ return null;
29269
+ let parsed;
29270
+ try {
29271
+ parsed = JSON.parse(readFileSync3(path11, "utf8"));
29272
+ } catch {
29273
+ return { stale: "unreadable" };
29274
+ }
29275
+ if (parsed.decoderVersion !== DECODER_VERSION) {
29276
+ return { stale: `recorded by decoder v${parsed.decoderVersion}` };
29277
+ }
29278
+ if (parsed.filterHash !== filterHash) {
29279
+ return { stale: "sources changed since it was recorded" };
29280
+ }
29281
+ return parsed;
29282
+ }
29283
+ function eventTypeFor(filter) {
29284
+ if (filter.type === "print_event")
29285
+ return "print";
29286
+ if (filter.type === "contract_call" || filter.type === "contract_deploy") {
29287
+ return null;
29288
+ }
29289
+ return filter.type;
29290
+ }
29291
+ async function runSubgraphTest(file, options3) {
29292
+ const absPath = resolve2(file);
29293
+ if (!existsSync2(absPath)) {
29294
+ error(`File not found: ${absPath}`);
29295
+ process.exit(1);
29296
+ }
29297
+ const { readFile: readFile3 } = await import("node:fs/promises");
29298
+ const { bundleSubgraphCode } = await import("@secondlayer/bundler");
29299
+ const { createTestContext, buildEvent } = await import("@secondlayer/subgraphs/testing");
29300
+ const source = await readFile3(absPath, "utf8");
29301
+ const bundled = await bundleSubgraphCode(source);
29302
+ const sources = bundled.sources ?? {};
29303
+ const filterHash = filterHashOf(sources);
29304
+ const mod = await import(`${absPath}?t=${Date.now()}`);
29305
+ const def = mod.default;
29306
+ if (!def?.handlers || !def.schema) {
29307
+ error("File must default-export a defineSubgraph() definition with handlers and schema.");
29308
+ process.exit(1);
29309
+ }
29310
+ const cached = loadCassette(bundled.name, filterHash);
29311
+ if (cached && "stale" in cached) {
29312
+ warn(`Cassette discarded (${cached.stale}) — re-recording.`);
29313
+ }
29314
+ const usable = cached && !("stale" in cached) ? cached : null;
29315
+ let events;
29316
+ let fromHeight;
29317
+ let toHeight;
29318
+ if (usable && options3.offline !== false && !options3.from) {
29319
+ info(`Replaying cassette: blocks ${usable.fromHeight}–${usable.toHeight}, recorded ${usable.recordedAt}`);
29320
+ events = usable.events;
29321
+ fromHeight = usable.fromHeight;
29322
+ toHeight = usable.toHeight;
29323
+ } else {
29324
+ if (options3.offline) {
29325
+ error("No usable cassette for --offline. Record one first: sl subgraphs test <file> --from <h> --to <h>");
29326
+ process.exit(1);
29327
+ }
29328
+ if (!options3.from) {
29329
+ error("--from <height> is required for the first run (Index reads are metered). Later runs replay the cassette.");
29330
+ process.exit(1);
29331
+ }
29332
+ fromHeight = Number(options3.from);
29333
+ toHeight = options3.to ? Number(options3.to) : fromHeight + 100;
29334
+ if (!Number.isInteger(fromHeight) || !Number.isInteger(toHeight)) {
29335
+ error("--from/--to must be integers.");
29336
+ process.exit(1);
29337
+ }
29338
+ const limit = options3.limit ? Number(options3.limit) : 500;
29339
+ const index = new Index;
29340
+ events = {};
29341
+ for (const [name, filter] of Object.entries(sources)) {
29342
+ const eventType = eventTypeFor(filter);
29343
+ if (eventType === null) {
29344
+ warn(`source "${name}" (${filter.type}) is not readable from index.events — skipped.`);
29345
+ continue;
29346
+ }
29347
+ info(`Fetching ${name} (${eventType}) blocks ${fromHeight}–${toHeight}…`);
29348
+ try {
29349
+ const envelope = await index.events.list({
29350
+ eventType,
29351
+ ...filter.contractId ? {
29352
+ contractId: Array.isArray(filter.contractId) ? filter.contractId : filter.contractId
29353
+ } : {},
29354
+ fromHeight,
29355
+ toHeight,
29356
+ limit
29357
+ });
29358
+ events[name] = envelope.events;
29359
+ } catch (err) {
29360
+ const e2 = err;
29361
+ if (e2?.status === 402) {
29362
+ const details = e2.body?.details;
29363
+ error(`Blocks ${fromHeight}–${toHeight} are below the free read window.${details?.oldest_seekable_height ? ` Try --from ${details.oldest_seekable_height}, or add a paid API key (SL_API_KEY) to test against older history.` : " Add a paid API key (SL_API_KEY) to test against older history."}`);
29364
+ process.exit(1);
29365
+ }
29366
+ throw err;
29367
+ }
29368
+ }
29369
+ if (options3.record !== false) {
29370
+ const cassette = {
29371
+ decoderVersion: DECODER_VERSION,
29372
+ filterHash,
29373
+ subgraph: bundled.name,
29374
+ fromHeight,
29375
+ toHeight,
29376
+ recordedAt: new Date().toISOString(),
29377
+ events
29378
+ };
29379
+ const path11 = cassettePath(bundled.name);
29380
+ mkdirSync(dirname3(path11), { recursive: true });
29381
+ writeFileSync2(path11, `${JSON.stringify(cassette, (_k, v2) => typeof v2 === "bigint" ? `${v2}n` : v2, 2)}
29382
+ `);
29383
+ info(`Recorded cassette → ${path11}`);
29384
+ }
29385
+ }
29386
+ const ctx = createTestContext(def.schema);
29387
+ let applied = 0;
29388
+ let failed = 0;
29389
+ const failures = [];
29390
+ for (const [name, rows] of Object.entries(events)) {
29391
+ const handler = def.handlers[name] ?? def.handlers["*"];
29392
+ if (typeof handler !== "function") {
29393
+ warn(`source "${name}": no handler — skipped.`);
29394
+ continue;
29395
+ }
29396
+ const filter = sources[name];
29397
+ for (const row of rows) {
29398
+ const payload = toHandlerPayload(filter, row);
29399
+ if (filter?.type === "print_event" && filter.topic && payload.topic !== filter.topic) {
29400
+ continue;
29401
+ }
29402
+ try {
29403
+ await handler(buildEvent(filter, payload), ctx);
29404
+ applied++;
29405
+ } catch (err) {
29406
+ failed++;
29407
+ if (failures.length < 5) {
29408
+ failures.push(` ${name} @ ${row.cursor}: ${err instanceof Error ? err.message : String(err)}`);
29409
+ }
29410
+ }
29411
+ }
29412
+ }
29413
+ info("");
29414
+ const tables = Object.keys(def.schema);
29415
+ let totalRows = 0;
29416
+ for (const table of tables) {
29417
+ const rows = await ctx.rows(table);
29418
+ totalRows += rows.length;
29419
+ info(` ${table.padEnd(24)} ${String(rows.length).padStart(6)} rows`);
29420
+ const sample = rows[0];
29421
+ if (sample) {
29422
+ const preview = Object.entries(sample).filter(([k2]) => !k2.startsWith("_")).slice(0, 4).map(([k2, v2]) => `${k2}=${String(v2).slice(0, 24)}`).join(" ");
29423
+ info(` ${"".padEnd(24)} ${preview}`);
29424
+ }
29425
+ }
29426
+ info("");
29427
+ if (failed > 0) {
29428
+ warn(`${failed} handler error${failed === 1 ? "" : "s"}:`);
29429
+ for (const f2 of failures)
29430
+ warn(f2);
29431
+ }
29432
+ const fetched = Object.values(events).reduce((n3, r2) => n3 + r2.length, 0);
29433
+ if (fetched === 0) {
29434
+ warn(`No events matched these sources in blocks ${fromHeight}–${toHeight}. Widen the range (--to) or check the source filters.`);
29435
+ return;
29436
+ }
29437
+ if (totalRows === 0) {
29438
+ error(`${applied} event${applied === 1 ? "" : "s"} applied, but NO rows were written — the shape of the field-mapping bug that shipped a 0-row subgraph. Check your handler's field names against the payload.`);
29439
+ process.exit(1);
29440
+ }
29441
+ success(`${applied} event${applied === 1 ? "" : "s"} → ${totalRows} row${totalRows === 1 ? "" : "s"} across ${tables.length} table${tables.length === 1 ? "" : "s"} (blocks ${fromHeight}–${toHeight})`);
29442
+ }
29443
+ function toHandlerPayload(_filter, row) {
29444
+ if (row.event_type === "print") {
29445
+ const payload = row.payload;
29446
+ return {
29447
+ contractId: row.contract_id ?? "",
29448
+ topic: payload?.topic ?? "",
29449
+ data: payload?.value ?? {}
29450
+ };
29451
+ }
29452
+ const r2 = row;
29453
+ return {
29454
+ ...r2.sender !== undefined ? { sender: r2.sender } : {},
29455
+ ...r2.recipient !== undefined ? { recipient: r2.recipient } : {},
29456
+ ...r2.amount !== undefined ? { amount: BigInt(String(r2.amount)) } : {},
29457
+ ...r2.asset_identifier !== undefined ? { assetIdentifier: r2.asset_identifier } : {},
29458
+ ...r2.value !== undefined ? { tokenId: r2.value } : {}
29459
+ };
29460
+ }
29461
+ var CASSETTE_DIR = "cassettes", DECODER_VERSION = 1;
29462
+ var init_subgraph_test = __esm(() => {
29463
+ init_output();
29464
+ });
29465
+
29209
29466
  // src/commands/subgraphs.ts
29210
29467
  var exports_subgraphs = {};
29211
29468
  __export(exports_subgraphs, {
@@ -29224,13 +29481,13 @@ __export(exports_subgraphs, {
29224
29481
  });
29225
29482
  import { spawn as spawn2 } from "node:child_process";
29226
29483
  import {
29227
- existsSync as existsSync2,
29228
- mkdirSync,
29229
- readFileSync as readFileSync3,
29484
+ existsSync as existsSync3,
29485
+ mkdirSync as mkdirSync2,
29486
+ readFileSync as readFileSync4,
29230
29487
  watch,
29231
- writeFileSync as writeFileSync2
29488
+ writeFileSync as writeFileSync3
29232
29489
  } from "node:fs";
29233
- import { basename, dirname as dirname3, extname, join as join4, resolve as resolve2 } from "node:path";
29490
+ import { basename, dirname as dirname4, extname, join as join4, resolve as resolve3 } from "node:path";
29234
29491
  import { fileURLToPath as fileURLToPath3 } from "node:url";
29235
29492
  import { confirm as confirm3 } from "@inquirer/prompts";
29236
29493
  import {
@@ -29319,8 +29576,8 @@ function ormFlagsConflictingWithPayloads(getOptionValueSource) {
29319
29576
  return Object.entries(ORM_ONLY_CODEGEN_FLAGS).filter(([key]) => getOptionValueSource(key) === "cli").map(([, flag]) => flag);
29320
29577
  }
29321
29578
  async function runPayloadsCodegen(file, output2) {
29322
- const absPath = resolve2(file);
29323
- if (!existsSync2(absPath)) {
29579
+ const absPath = resolve3(file);
29580
+ if (!existsSync3(absPath)) {
29324
29581
  error(`File not found: ${absPath}`);
29325
29582
  process.exit(1);
29326
29583
  }
@@ -29365,7 +29622,7 @@ async function runPayloadsCodegen(file, output2) {
29365
29622
  process.exit(1);
29366
29623
  }
29367
29624
  const out = generatePrintPayloadTypes({ sources });
29368
- const outPath = resolve2(output2 ?? join4(dirname3(absPath), `${basename(absPath, extname(absPath))}.payloads.d.ts`));
29625
+ const outPath = resolve3(output2 ?? join4(dirname4(absPath), `${basename(absPath, extname(absPath))}.payloads.d.ts`));
29369
29626
  await writeTextFile(outPath, out);
29370
29627
  success(`Wrote print payload types to ${outPath}`);
29371
29628
  }
@@ -29390,10 +29647,10 @@ async function writeOrPrintSubgraphSpec(spec, format2, output2) {
29390
29647
  process.stdout.write(text2);
29391
29648
  return;
29392
29649
  }
29393
- const outPath = resolve2(output2);
29394
- const dir = resolve2(outPath, "..");
29395
- if (!existsSync2(dir))
29396
- mkdirSync(dir, { recursive: true });
29650
+ const outPath = resolve3(output2);
29651
+ const dir = resolve3(outPath, "..");
29652
+ if (!existsSync3(dir))
29653
+ mkdirSync2(dir, { recursive: true });
29397
29654
  await writeTextFile(outPath, text2);
29398
29655
  success(`Created ${outPath}`);
29399
29656
  }
@@ -29484,15 +29741,15 @@ function createLocalSubgraphDetail(input2) {
29484
29741
  };
29485
29742
  }
29486
29743
  function readCliSubgraphsDependency() {
29487
- const here = dirname3(fileURLToPath3(import.meta.url));
29744
+ const here = dirname4(fileURLToPath3(import.meta.url));
29488
29745
  const candidates = [
29489
- resolve2(here, "..", "..", "package.json"),
29490
- resolve2(here, "..", "package.json")
29746
+ resolve3(here, "..", "..", "package.json"),
29747
+ resolve3(here, "..", "package.json")
29491
29748
  ];
29492
29749
  for (const candidate of candidates) {
29493
- if (!existsSync2(candidate))
29750
+ if (!existsSync3(candidate))
29494
29751
  continue;
29495
- const pkg = JSON.parse(readFileSync3(candidate, "utf8"));
29752
+ const pkg = JSON.parse(readFileSync4(candidate, "utf8"));
29496
29753
  const dep = pkg.dependencies?.["@secondlayer/subgraphs"];
29497
29754
  if (dep)
29498
29755
  return dep;
@@ -29502,8 +29759,8 @@ function readCliSubgraphsDependency() {
29502
29759
  function ensureScaffoldPackageJson(dir) {
29503
29760
  const packagePath = join4(dir, "package.json");
29504
29761
  const subgraphsDep = readCliSubgraphsDependency();
29505
- if (!existsSync2(packagePath)) {
29506
- writeFileSync2(packagePath, `${JSON.stringify({
29762
+ if (!existsSync3(packagePath)) {
29763
+ writeFileSync3(packagePath, `${JSON.stringify({
29507
29764
  type: "module",
29508
29765
  dependencies: {
29509
29766
  "@secondlayer/subgraphs": subgraphsDep
@@ -29512,14 +29769,14 @@ function ensureScaffoldPackageJson(dir) {
29512
29769
  `, "utf8");
29513
29770
  return;
29514
29771
  }
29515
- const pkg = JSON.parse(readFileSync3(packagePath, "utf8"));
29772
+ const pkg = JSON.parse(readFileSync4(packagePath, "utf8"));
29516
29773
  if (pkg.dependencies?.["@secondlayer/subgraphs"])
29517
29774
  return;
29518
29775
  pkg.dependencies = {
29519
29776
  ...pkg.dependencies ?? {},
29520
29777
  "@secondlayer/subgraphs": subgraphsDep
29521
29778
  };
29522
- writeFileSync2(packagePath, `${JSON.stringify(pkg, null, 2)}
29779
+ writeFileSync3(packagePath, `${JSON.stringify(pkg, null, 2)}
29523
29780
  `, "utf8");
29524
29781
  }
29525
29782
  async function runBunInstall(dir) {
@@ -29687,9 +29944,9 @@ ${SUBGRAPH_TEMPLATE_SLUGS.map((s2) => ` ${s2.padEnd(20)} ${SUBGRAPH_TEMPLATE_DE
29687
29944
  `)}`);
29688
29945
  process.exit(1);
29689
29946
  }
29690
- const dir = resolve2("subgraphs");
29691
- const filePath = resolve2(dir, `${name}.ts`);
29692
- if (existsSync2(filePath)) {
29947
+ const dir = resolve3("subgraphs");
29948
+ const filePath = resolve3(dir, `${name}.ts`);
29949
+ if (existsSync3(filePath)) {
29693
29950
  error(`File already exists: ${filePath}`);
29694
29951
  process.exit(1);
29695
29952
  }
@@ -29717,8 +29974,8 @@ Check the contract id, and that the contract has emitted print events (schemas a
29717
29974
  } else {
29718
29975
  content = generateSubgraphTemplate(name, slug);
29719
29976
  }
29720
- if (!existsSync2(dir)) {
29721
- mkdirSync(dir, { recursive: true });
29977
+ if (!existsSync3(dir)) {
29978
+ mkdirSync2(dir, { recursive: true });
29722
29979
  }
29723
29980
  await writeTextFile(filePath, content);
29724
29981
  success(`Created ${filePath}`);
@@ -29731,8 +29988,8 @@ Check the contract id, and that the contract has emitted print events (schemas a
29731
29988
  });
29732
29989
  subgraphs.command("dev <file>").description("Watch a subgraph file and auto-redeploy on change").action(async (file) => {
29733
29990
  await requireLocalNetwork();
29734
- const absPath = resolve2(file);
29735
- if (!existsSync2(absPath)) {
29991
+ const absPath = resolve3(file);
29992
+ if (!existsSync3(absPath)) {
29736
29993
  error(`File not found: ${absPath}`);
29737
29994
  process.exit(1);
29738
29995
  }
@@ -29792,7 +30049,7 @@ Stopped watching.`);
29792
30049
  });
29793
30050
  subgraphs.command("deploy <file>").alias("update").description("Deploy a subgraph definition file (alias: update — deploy is create-or-update)").option("--start-block <n>", "Override the subgraph definition startBlock for this deploy").option("--tip-first", "Go live at chain tip immediately; history backfills in the background (requires order-tolerant handlers — commutative or insert-only writes)").option("--dry-run", "Validate and preview deploy without writing changes").option("-y, --yes", "Skip the reindex confirmation prompt").option("--database-url <url>", "BYO data plane: write the subgraph's schema/rows to your own Postgres. With --dry-run, prints the DDL + grant script and verifies the connection.").option("--strict", "Run `tsc --noEmit` against the handler before deploy (slower; catches TS type errors)").option("--visibility <visibility>", "Read visibility: public (anon /v1 reads, global name claim) or private (your key only). Defaults: managed → public, BYO → private.").action(async (file, options3) => {
29794
30051
  try {
29795
- const absPath = resolve2(file);
30052
+ const absPath = resolve3(file);
29796
30053
  const config = await loadConfig2();
29797
30054
  if (config.network !== "local") {
29798
30055
  await requireAuth();
@@ -30093,8 +30350,8 @@ Synced — exiting watch.`));
30093
30350
  try {
30094
30351
  const format2 = parseSubgraphSpecFormat(options3.format);
30095
30352
  const specOptions = options3.server ? { serverUrl: options3.server } : undefined;
30096
- const absPath = resolve2(nameOrFile);
30097
- if (nameOrFile.endsWith(".ts") && !existsSync2(absPath)) {
30353
+ const absPath = resolve3(nameOrFile);
30354
+ if (nameOrFile.endsWith(".ts") && !existsSync3(absPath)) {
30098
30355
  error(`File not found: ${absPath}`);
30099
30356
  process.exit(1);
30100
30357
  }
@@ -30113,6 +30370,13 @@ Synced — exiting watch.`));
30113
30370
  deprecatedCodegenNotice("sl subgraphs codegen", options3.payloads ? "sl codegen prints" : "sl codegen subgraph");
30114
30371
  await runSubgraphSchemaCodegen(file, options3, (key) => command.getOptionValueSource(key));
30115
30372
  });
30373
+ subgraphs.command("test <file>").description("Run a subgraph's handlers against real chain data without deploying").option("--from <height>", "Start block height (required on first run)").option("--to <height>", "End block height (defaults to --from + 100)").option("--offline", "Replay the recorded cassette; never hit the network").option("--no-record", "Don't write a cassette").option("--limit <n>", "Max events per source", "500").addHelpText("after", `
30374
+ Examples:
30375
+ $ sl subgraphs test subgraphs/bns-names.ts --from 167484 --to 167600
30376
+ $ sl subgraphs test subgraphs/bns-names.ts --offline`).action(async (file, options3) => {
30377
+ const { runSubgraphTest: runSubgraphTest2 } = await Promise.resolve().then(() => (init_subgraph_test(), exports_subgraph_test));
30378
+ await runSubgraphTest2(file, options3);
30379
+ });
30116
30380
  subgraphs.command("reindex <name>").description("Reindex a subgraph from historical blocks (drops + reprocesses)").option("--from-block <n>", "Start block height").option("--to-block <n>", "End block height").option("-y, --yes", "Skip confirmation").addHelpText("after", `
30117
30381
  Examples:
30118
30382
  $ sl subgraphs reindex my-graph -y
@@ -30372,7 +30636,7 @@ Examples:
30372
30636
  error("a <contractAddress> is required (or use --trait)");
30373
30637
  process.exit(1);
30374
30638
  }
30375
- const outPath = resolve2(options3.output);
30639
+ const outPath = resolve3(options3.output);
30376
30640
  let content;
30377
30641
  if (trait) {
30378
30642
  info(`Generating trait-scoped scaffold for ${trait}...`);
@@ -30392,9 +30656,9 @@ Examples:
30392
30656
  functions: options3.functions?.split(",").map((f2) => f2.trim()).filter(Boolean)
30393
30657
  });
30394
30658
  }
30395
- const dir = resolve2(outPath, "..");
30396
- if (!existsSync2(dir))
30397
- mkdirSync(dir, { recursive: true });
30659
+ const dir = resolve3(outPath, "..");
30660
+ if (!existsSync3(dir))
30661
+ mkdirSync2(dir, { recursive: true });
30398
30662
  await writeTextFile(outPath, content);
30399
30663
  ensureScaffoldPackageJson(dir);
30400
30664
  success(`Created ${outPath}`);
@@ -30427,14 +30691,14 @@ async function runSubgraphClientCodegen(subgraphName, options3) {
30427
30691
  error("--output <path> is required");
30428
30692
  process.exit(1);
30429
30693
  }
30430
- const outPath = resolve2(options3.output);
30694
+ const outPath = resolve3(options3.output);
30431
30695
  info(`Fetching subgraph metadata for "${subgraphName}"...`);
30432
30696
  const subgraphDetail = await getSubgraphApi(subgraphName);
30433
30697
  info("Generating typed client...");
30434
30698
  const content = await generateSubgraphConsumer(subgraphName, subgraphDetail);
30435
- const dir = resolve2(outPath, "..");
30436
- if (!existsSync2(dir))
30437
- mkdirSync(dir, { recursive: true });
30699
+ const dir = resolve3(outPath, "..");
30700
+ if (!existsSync3(dir))
30701
+ mkdirSync2(dir, { recursive: true });
30438
30702
  await writeTextFile(outPath, content);
30439
30703
  success(`Created ${outPath}`);
30440
30704
  } catch (err) {
@@ -30457,8 +30721,8 @@ async function runSubgraphSchemaCodegen(file, options3, optionSource, defaultTar
30457
30721
  error(`Unsupported --target "${target}" (supported: prisma, drizzle, kysely).`);
30458
30722
  process.exit(1);
30459
30723
  }
30460
- const absPath = resolve2(file);
30461
- if (!existsSync2(absPath)) {
30724
+ const absPath = resolve3(file);
30725
+ if (!existsSync3(absPath)) {
30462
30726
  error(`File not found: ${absPath}`);
30463
30727
  process.exit(1);
30464
30728
  }
@@ -30485,7 +30749,7 @@ async function runSubgraphSchemaCodegen(file, options3, optionSource, defaultTar
30485
30749
  modelsOnly: options3.modelsOnly
30486
30750
  });
30487
30751
  if (options3.output) {
30488
- await writeTextFile(resolve2(options3.output), out);
30752
+ await writeTextFile(resolve3(options3.output), out);
30489
30753
  success(`Wrote ${target} schema to ${options3.output}`);
30490
30754
  const next = target === "prisma" ? "point its datasource at your BYO database, then `prisma generate`." : target === "drizzle" ? "point your Drizzle connection at your BYO database (treat tables read-only)." : "import the `DB` type into `new Kysely<DB>()` against your BYO database.";
30491
30755
  info(`Next: ${next}`);
@@ -30527,10 +30791,10 @@ __export(exports_index_api, {
30527
30791
  runIndexCodegen: () => runIndexCodegen,
30528
30792
  registerIndexCommand: () => registerIndexCommand
30529
30793
  });
30530
- import { resolve as resolve3 } from "node:path";
30531
- import { Index } from "@secondlayer/sdk";
30794
+ import { resolve as resolve4 } from "node:path";
30795
+ import { Index as Index2 } from "@secondlayer/sdk";
30532
30796
  function client() {
30533
- return new Index({
30797
+ return new Index2({
30534
30798
  baseUrl: process.env.SL_API_URL ?? DEFAULT_BASE_URL,
30535
30799
  apiKey: resolveEnvKey()
30536
30800
  });
@@ -30719,7 +30983,7 @@ async function runIndexCodegen(o3) {
30719
30983
  datasourceEnv: o3.env
30720
30984
  });
30721
30985
  if (o3.output) {
30722
- await writeTextFile(resolve3(o3.output), out);
30986
+ await writeTextFile(resolve4(o3.output), out);
30723
30987
  success(`Wrote ${target} Index schema to ${o3.output}`);
30724
30988
  } else {
30725
30989
  process.stdout.write(out);
@@ -30831,9 +31095,9 @@ var init_query = __esm(() => {
30831
31095
  CLOSE = {};
30832
31096
  Query = class Query extends Promise {
30833
31097
  constructor(strings, args, handler, canceller, options3 = {}) {
30834
- let resolve4, reject;
31098
+ let resolve5, reject;
30835
31099
  super((a4, b2) => {
30836
- resolve4 = a4;
31100
+ resolve5 = a4;
30837
31101
  reject = b2;
30838
31102
  });
30839
31103
  this.tagged = Array.isArray(strings.raw);
@@ -30844,7 +31108,7 @@ var init_query = __esm(() => {
30844
31108
  this.options = options3;
30845
31109
  this.state = null;
30846
31110
  this.statement = null;
30847
- this.resolve = (x2) => (this.active = false, resolve4(x2));
31111
+ this.resolve = (x2) => (this.active = false, resolve5(x2));
30848
31112
  this.reject = (x2) => (this.active = false, reject(x2));
30849
31113
  this.active = false;
30850
31114
  this.cancelled = null;
@@ -30892,12 +31156,12 @@ var init_query = __esm(() => {
30892
31156
  if (this.executed && !this.active)
30893
31157
  return { done: true };
30894
31158
  prev && prev();
30895
- const promise = new Promise((resolve4, reject) => {
31159
+ const promise = new Promise((resolve5, reject) => {
30896
31160
  this.cursorFn = (value) => {
30897
- resolve4({ value, done: false });
31161
+ resolve5({ value, done: false });
30898
31162
  return new Promise((r2) => prev = r2);
30899
31163
  };
30900
- this.resolve = () => (this.active = false, resolve4({ done: true }));
31164
+ this.resolve = () => (this.active = false, resolve5({ done: true }));
30901
31165
  this.reject = (x2) => (this.active = false, reject(x2));
30902
31166
  });
30903
31167
  this.execute();
@@ -31451,12 +31715,12 @@ function Connection(options3, queues = {}, { onopen = noop4, onend = noop4, oncl
31451
31715
  x2.on("drain", drain);
31452
31716
  return x2;
31453
31717
  }
31454
- async function cancel({ pid, secret }, resolve4, reject) {
31718
+ async function cancel({ pid, secret }, resolve5, reject) {
31455
31719
  try {
31456
31720
  cancelMessage = bytes_default().i32(16).i32(80877102).i32(pid).i32(secret).end(16);
31457
31721
  await connect();
31458
31722
  socket.once("error", reject);
31459
- socket.once("close", resolve4);
31723
+ socket.once("close", resolve5);
31460
31724
  } catch (error3) {
31461
31725
  reject(error3);
31462
31726
  }
@@ -32329,7 +32593,7 @@ var noop5 = () => {};
32329
32593
  // ../../node_modules/postgres/src/large.js
32330
32594
  import Stream2 from "stream";
32331
32595
  function largeObject(sql, oid, mode = 131072 | 262144) {
32332
- return new Promise(async (resolve4, reject) => {
32596
+ return new Promise(async (resolve5, reject) => {
32333
32597
  await sql.begin(async (sql2) => {
32334
32598
  let finish;
32335
32599
  !oid && ([{ oid }] = await sql2`select lo_creat(-1) as oid`);
@@ -32355,7 +32619,7 @@ function largeObject(sql, oid, mode = 131072 | 262144) {
32355
32619
  ) seek
32356
32620
  `
32357
32621
  };
32358
- resolve4(lo);
32622
+ resolve5(lo);
32359
32623
  return new Promise(async (r2) => finish = r2);
32360
32624
  async function readable2({
32361
32625
  highWaterMark = 2048 * 8,
@@ -32515,8 +32779,8 @@ function Postgres(a4, b3) {
32515
32779
  }
32516
32780
  async function reserve() {
32517
32781
  const queue = queue_default();
32518
- const c4 = open.length ? open.shift() : await new Promise((resolve4, reject) => {
32519
- const query = { reserve: resolve4, reject };
32782
+ const c4 = open.length ? open.shift() : await new Promise((resolve5, reject) => {
32783
+ const query = { reserve: resolve5, reject };
32520
32784
  queries.push(query);
32521
32785
  closed.length && connect(closed.shift(), query);
32522
32786
  });
@@ -32553,9 +32817,9 @@ function Postgres(a4, b3) {
32553
32817
  let uncaughtError, result;
32554
32818
  name && await sql2`savepoint ${sql2(name)}`;
32555
32819
  try {
32556
- result = await new Promise((resolve4, reject) => {
32820
+ result = await new Promise((resolve5, reject) => {
32557
32821
  const x2 = fn2(sql2);
32558
- Promise.resolve(Array.isArray(x2) ? Promise.all(x2) : x2).then(resolve4, reject);
32822
+ Promise.resolve(Array.isArray(x2) ? Promise.all(x2) : x2).then(resolve5, reject);
32559
32823
  });
32560
32824
  if (uncaughtError)
32561
32825
  throw uncaughtError;
@@ -32612,8 +32876,8 @@ function Postgres(a4, b3) {
32612
32876
  return c4.execute(query) ? move(c4, busy) : move(c4, full);
32613
32877
  }
32614
32878
  function cancel(query) {
32615
- return new Promise((resolve4, reject) => {
32616
- query.state ? query.active ? connection_default(options3).cancel(query.state, resolve4, reject) : query.cancelled = { resolve: resolve4, reject } : (queries.remove(query), query.cancelled = true, query.reject(Errors.generic("57014", "canceling statement due to user request")), resolve4());
32879
+ return new Promise((resolve5, reject) => {
32880
+ query.state ? query.active ? connection_default(options3).cancel(query.state, resolve5, reject) : query.cancelled = { resolve: resolve5, reject } : (queries.remove(query), query.cancelled = true, query.reject(Errors.generic("57014", "canceling statement due to user request")), resolve5());
32617
32881
  });
32618
32882
  }
32619
32883
  async function end({ timeout = null } = {}) {
@@ -32629,11 +32893,11 @@ function Postgres(a4, b3) {
32629
32893
  async function close() {
32630
32894
  await Promise.all(connections.map((c4) => c4.end()));
32631
32895
  }
32632
- async function destroy(resolve4) {
32896
+ async function destroy(resolve5) {
32633
32897
  await Promise.all(connections.map((c4) => c4.terminate()));
32634
32898
  while (queries.length)
32635
32899
  queries.shift().reject(Errors.connection("CONNECTION_DESTROYED", options3));
32636
- resolve4();
32900
+ resolve5();
32637
32901
  }
32638
32902
  function connect(c4, query) {
32639
32903
  move(c4, connecting);
@@ -33273,11 +33537,11 @@ var init_manager = __esm(() => {
33273
33537
  });
33274
33538
 
33275
33539
  // src/services/indexer.ts
33276
- import { dirname as dirname8, resolve as resolve5 } from "node:path";
33540
+ import { dirname as dirname9, resolve as resolve6 } from "node:path";
33277
33541
  async function startIndexer(options3) {
33278
33542
  const port = options3.port ?? 3700;
33279
- const rootDir = dirname8(dirname8(dirname8(dirname8(import.meta.dir))));
33280
- const indexerPath = resolve5(rootDir, "packages/indexer/src/index.ts");
33543
+ const rootDir = dirname9(dirname9(dirname9(dirname9(import.meta.dir))));
33544
+ const indexerPath = resolve6(rootDir, "packages/indexer/src/index.ts");
33281
33545
  await serviceManager.start(SERVICE_NAME, ["bun", "run", "--watch", indexerPath], {
33282
33546
  port,
33283
33547
  env: {
@@ -33293,10 +33557,10 @@ var init_indexer = __esm(() => {
33293
33557
  });
33294
33558
 
33295
33559
  // src/services/worker.ts
33296
- import { dirname as dirname9, resolve as resolve6 } from "node:path";
33560
+ import { dirname as dirname10, resolve as resolve7 } from "node:path";
33297
33561
  async function startWorker(options3) {
33298
- const rootDir = dirname9(dirname9(dirname9(dirname9(import.meta.dir))));
33299
- const workerPath = resolve6(rootDir, "packages/worker/src/index.ts");
33562
+ const rootDir = dirname10(dirname10(dirname10(dirname10(import.meta.dir))));
33563
+ const workerPath = resolve7(rootDir, "packages/worker/src/index.ts");
33300
33564
  await serviceManager.start(SERVICE_NAME2, ["bun", "run", "--watch", workerPath], {
33301
33565
  onStdout: options3.onLog,
33302
33566
  onStderr: options3.onLog
@@ -33308,11 +33572,11 @@ var init_worker = __esm(() => {
33308
33572
  });
33309
33573
 
33310
33574
  // src/services/api.ts
33311
- import { dirname as dirname10, resolve as resolve7 } from "node:path";
33575
+ import { dirname as dirname11, resolve as resolve8 } from "node:path";
33312
33576
  async function startApi(options3) {
33313
33577
  const port = options3.port ?? 3800;
33314
- const rootDir = dirname10(dirname10(dirname10(dirname10(import.meta.dir))));
33315
- const apiPath = resolve7(rootDir, "packages/api/src/index.ts");
33578
+ const rootDir = dirname11(dirname11(dirname11(dirname11(import.meta.dir))));
33579
+ const apiPath = resolve8(rootDir, "packages/api/src/index.ts");
33316
33580
  await serviceManager.start(SERVICE_NAME3, ["bun", "run", "--watch", apiPath], {
33317
33581
  port,
33318
33582
  env: {
@@ -33328,10 +33592,10 @@ var init_api2 = __esm(() => {
33328
33592
  });
33329
33593
 
33330
33594
  // src/services/subgraph-processor.ts
33331
- import { dirname as dirname11, resolve as resolve8 } from "node:path";
33595
+ import { dirname as dirname12, resolve as resolve9 } from "node:path";
33332
33596
  async function startSubgraphProcessor(options3) {
33333
- const rootDir = dirname11(dirname11(dirname11(dirname11(import.meta.dir))));
33334
- const servicePath = resolve8(rootDir, "packages/subgraphs/src/service.ts");
33597
+ const rootDir = dirname12(dirname12(dirname12(dirname12(import.meta.dir))));
33598
+ const servicePath = resolve9(rootDir, "packages/subgraphs/src/service.ts");
33335
33599
  await serviceManager.start(SERVICE_NAME4, ["bun", "run", "--watch", servicePath], {
33336
33600
  env: {
33337
33601
  SUBGRAPH_CONCURRENCY: String(options3.concurrency ?? 5)
@@ -33365,8 +33629,8 @@ __export(exports_dev_impl, {
33365
33629
  restartDev: () => restartDev,
33366
33630
  isDevAlreadyRunning: () => isDevAlreadyRunning
33367
33631
  });
33368
- import { mkdirSync as mkdirSync4 } from "node:fs";
33369
- import { dirname as dirname12, join as join11, resolve as resolve9 } from "node:path";
33632
+ import { mkdirSync as mkdirSync5 } from "node:fs";
33633
+ import { dirname as dirname13, join as join11, resolve as resolve10 } from "node:path";
33370
33634
  async function isDevAlreadyRunning() {
33371
33635
  if (await isDevRunning()) {
33372
33636
  const running = await getRunningServices();
@@ -33444,10 +33708,10 @@ async function runBackground(options3) {
33444
33708
  startedAt: new Date().toISOString()
33445
33709
  };
33446
33710
  try {
33447
- const packagesDir = dirname12(dirname12(dirname12(dirname12(import.meta.dir))));
33711
+ const packagesDir = dirname13(dirname13(dirname13(dirname13(import.meta.dir))));
33448
33712
  const env2 = { DATABASE_URL: databaseUrl, DEV_MODE: "true" };
33449
33713
  const apiLogFile = getLogFile("api");
33450
- const apiProc = Bun.spawn(["bun", "run", resolve9(packagesDir, "packages/api/src/index.ts")], {
33714
+ const apiProc = Bun.spawn(["bun", "run", resolve10(packagesDir, "packages/api/src/index.ts")], {
33451
33715
  env: { ...process.env, ...env2, PORT: String(apiPort) },
33452
33716
  stdout: Bun.file(apiLogFile),
33453
33717
  stderr: Bun.file(apiLogFile)
@@ -33460,7 +33724,7 @@ async function runBackground(options3) {
33460
33724
  };
33461
33725
  console.log(green(" ✓ API"), dim(`http://localhost:${apiPort}`));
33462
33726
  const indexerLogFile = getLogFile("indexer");
33463
- const indexerProc = Bun.spawn(["bun", "run", resolve9(packagesDir, "packages/indexer/src/index.ts")], {
33727
+ const indexerProc = Bun.spawn(["bun", "run", resolve10(packagesDir, "packages/indexer/src/index.ts")], {
33464
33728
  env: { ...process.env, ...env2, PORT: String(indexerPort) },
33465
33729
  stdout: Bun.file(indexerLogFile),
33466
33730
  stderr: Bun.file(indexerLogFile)
@@ -33474,7 +33738,7 @@ async function runBackground(options3) {
33474
33738
  console.log(green(" ✓ Indexer"), dim(`http://localhost:${indexerPort}`));
33475
33739
  if (options3.worker) {
33476
33740
  const workerLogFile = getLogFile("worker");
33477
- const workerProc = Bun.spawn(["bun", "run", resolve9(packagesDir, "packages/worker/src/index.ts")], {
33741
+ const workerProc = Bun.spawn(["bun", "run", resolve10(packagesDir, "packages/worker/src/index.ts")], {
33478
33742
  env: { ...process.env, ...env2 },
33479
33743
  stdout: Bun.file(workerLogFile),
33480
33744
  stderr: Bun.file(workerLogFile)
@@ -33492,7 +33756,7 @@ async function runBackground(options3) {
33492
33756
  const subgraphsProc = Bun.spawn([
33493
33757
  "bun",
33494
33758
  "run",
33495
- resolve9(packagesDir, "packages/subgraphs/src/service.ts")
33759
+ resolve10(packagesDir, "packages/subgraphs/src/service.ts")
33496
33760
  ], {
33497
33761
  env: { ...process.env, ...env2 },
33498
33762
  stdout: Bun.file(subgraphsLogFile),
@@ -33794,7 +34058,7 @@ async function restartDev() {
33794
34058
  await clearDevState();
33795
34059
  await clearLogs();
33796
34060
  const config = await loadConfig2();
33797
- const packagesDir = dirname12(dirname12(dirname12(dirname12(import.meta.dir))));
34061
+ const packagesDir = dirname13(dirname13(dirname13(dirname13(import.meta.dir))));
33798
34062
  const env2 = state.env;
33799
34063
  const newState = {
33800
34064
  services: {},
@@ -33804,7 +34068,7 @@ async function restartDev() {
33804
34068
  };
33805
34069
  const apiPort = config.ports.api;
33806
34070
  const apiLogFile = getLogFile("api");
33807
- const apiProc = Bun.spawn(["bun", "run", resolve9(packagesDir, "packages/api/src/index.ts")], {
34071
+ const apiProc = Bun.spawn(["bun", "run", resolve10(packagesDir, "packages/api/src/index.ts")], {
33808
34072
  env: { ...process.env, ...env2, PORT: String(apiPort) },
33809
34073
  stdout: Bun.file(apiLogFile),
33810
34074
  stderr: Bun.file(apiLogFile)
@@ -33818,7 +34082,7 @@ async function restartDev() {
33818
34082
  console.log(green(" ✓ API"), dim(`http://localhost:${apiPort}`));
33819
34083
  const indexerPort = config.ports.indexer;
33820
34084
  const indexerLogFile = getLogFile("indexer");
33821
- const indexerProc = Bun.spawn(["bun", "run", resolve9(packagesDir, "packages/indexer/src/index.ts")], {
34085
+ const indexerProc = Bun.spawn(["bun", "run", resolve10(packagesDir, "packages/indexer/src/index.ts")], {
33822
34086
  env: { ...process.env, ...env2, PORT: String(indexerPort) },
33823
34087
  stdout: Bun.file(indexerLogFile),
33824
34088
  stderr: Bun.file(indexerLogFile)
@@ -33832,7 +34096,7 @@ async function restartDev() {
33832
34096
  console.log(green(" ✓ Indexer"), dim(`http://localhost:${indexerPort}`));
33833
34097
  if (state.services.worker) {
33834
34098
  const workerLogFile = getLogFile("worker");
33835
- const workerProc = Bun.spawn(["bun", "run", resolve9(packagesDir, "packages/worker/src/index.ts")], {
34099
+ const workerProc = Bun.spawn(["bun", "run", resolve10(packagesDir, "packages/worker/src/index.ts")], {
33836
34100
  env: { ...process.env, ...env2 },
33837
34101
  stdout: Bun.file(workerLogFile),
33838
34102
  stderr: Bun.file(workerLogFile)
@@ -33847,7 +34111,7 @@ async function restartDev() {
33847
34111
  }
33848
34112
  if (state.services.subgraphs) {
33849
34113
  const subgraphsLogFile = getLogFile("subgraphs");
33850
- const subgraphsProc = Bun.spawn(["bun", "run", resolve9(packagesDir, "packages/subgraphs/src/service.ts")], {
34114
+ const subgraphsProc = Bun.spawn(["bun", "run", resolve10(packagesDir, "packages/subgraphs/src/service.ts")], {
33851
34115
  env: { ...process.env, ...env2 },
33852
34116
  stdout: Bun.file(subgraphsLogFile),
33853
34117
  stderr: Bun.file(subgraphsLogFile)
@@ -33976,7 +34240,7 @@ async function ensureDevPostgres(dataDir) {
33976
34240
  return false;
33977
34241
  }
33978
34242
  const pgDataDir = join11(dataDir, "postgres");
33979
- mkdirSync4(pgDataDir, { recursive: true });
34243
+ mkdirSync5(pgDataDir, { recursive: true });
33980
34244
  const stopped = await Bun.$`docker ps -aq -f name=secondlayer-dev-postgres`.quiet().nothrow();
33981
34245
  if (stopped.stdout.toString().trim()) {
33982
34246
  await Bun.$`docker rm secondlayer-dev-postgres`.quiet().nothrow();
@@ -33991,8 +34255,8 @@ async function ensureDevPostgres(dataDir) {
33991
34255
  throw new Error("PostgreSQL failed to start");
33992
34256
  }
33993
34257
  async function runMigrations(databaseUrl) {
33994
- const packagesDir = dirname12(dirname12(dirname12(dirname12(import.meta.dir))));
33995
- const migrateScript = resolve9(packagesDir, "packages/shared/src/db/migrate.ts");
34258
+ const packagesDir = dirname13(dirname13(dirname13(dirname13(import.meta.dir))));
34259
+ const migrateScript = resolve10(packagesDir, "packages/shared/src/db/migrate.ts");
33996
34260
  const result = await Bun.$`DATABASE_URL=${databaseUrl} bun run ${migrateScript}`.quiet().nothrow();
33997
34261
  if (result.exitCode !== 0) {
33998
34262
  throw new Error(`Migration failed: ${result.stderr.toString()}`);
@@ -34475,7 +34739,7 @@ var {
34475
34739
  // package.json
34476
34740
  var package_default = {
34477
34741
  name: "@secondlayer/cli",
34478
- version: "9.1.0",
34742
+ version: "9.3.0",
34479
34743
  repository: {
34480
34744
  type: "git",
34481
34745
  url: "git+https://github.com/ryanwaits/secondlayer.git",
@@ -34500,7 +34764,11 @@ var package_default = {
34500
34764
  import: "./dist/plugins/index.js"
34501
34765
  }
34502
34766
  },
34503
- files: ["dist", "templates", "README.md"],
34767
+ files: [
34768
+ "dist",
34769
+ "templates",
34770
+ "README.md"
34771
+ ],
34504
34772
  scripts: {
34505
34773
  build: "bunup",
34506
34774
  dev: "bunup --watch",
@@ -34508,7 +34776,13 @@ var package_default = {
34508
34776
  typecheck: "tsc --noEmit",
34509
34777
  prepublishOnly: "bun run build"
34510
34778
  },
34511
- keywords: ["stacks", "clarity", "blockchain", "smart-contracts", "cli"],
34779
+ keywords: [
34780
+ "stacks",
34781
+ "clarity",
34782
+ "blockchain",
34783
+ "smart-contracts",
34784
+ "cli"
34785
+ ],
34512
34786
  author: "",
34513
34787
  license: "MIT",
34514
34788
  dependencies: {
@@ -34517,8 +34791,8 @@ var package_default = {
34517
34791
  "@secondlayer/scaffold": "^1.5.0",
34518
34792
  "@secondlayer/sdk": "^6.36.0",
34519
34793
  "@secondlayer/shared": "^7.0.3",
34520
- "@secondlayer/stacks": "^2.21.0",
34521
- "@secondlayer/subgraphs": "^3.21.0",
34794
+ "@secondlayer/stacks": "^2.22.0",
34795
+ "@secondlayer/subgraphs": "^3.23.0",
34522
34796
  "@biomejs/js-api": "^0.7.0",
34523
34797
  "@biomejs/wasm-nodejs": "^1.9.0",
34524
34798
  esbuild: "^0.27.4",
@@ -35213,14 +35487,14 @@ init_output();
35213
35487
  init_resolve_auth();
35214
35488
  import {
35215
35489
  copyFileSync,
35216
- existsSync as existsSync3,
35217
- mkdirSync as mkdirSync2,
35218
- readFileSync as readFileSync4,
35490
+ existsSync as existsSync4,
35491
+ mkdirSync as mkdirSync3,
35492
+ readFileSync as readFileSync5,
35219
35493
  readdirSync,
35220
35494
  statSync as statSync2,
35221
- writeFileSync as writeFileSync3
35495
+ writeFileSync as writeFileSync4
35222
35496
  } from "node:fs";
35223
- import { dirname as dirname4, join as join6, relative, resolve as resolve4 } from "node:path";
35497
+ import { dirname as dirname5, join as join6, relative, resolve as resolve5 } from "node:path";
35224
35498
  import { fileURLToPath as fileURLToPath4 } from "node:url";
35225
35499
  import { input as input2, select as select3 } from "@inquirer/prompts";
35226
35500
  import { SecondLayer as SecondLayer3 } from "@secondlayer/sdk";
@@ -35235,17 +35509,17 @@ var FORMAT_BY_RUNTIME = {
35235
35509
  node: "standard-webhooks"
35236
35510
  };
35237
35511
  function templatesRoot() {
35238
- const here = dirname4(fileURLToPath4(import.meta.url));
35239
- const candidateDist = resolve4(here, "..", "templates", "subscriptions");
35240
- if (existsSync3(candidateDist))
35512
+ const here = dirname5(fileURLToPath4(import.meta.url));
35513
+ const candidateDist = resolve5(here, "..", "templates", "subscriptions");
35514
+ if (existsSync4(candidateDist))
35241
35515
  return candidateDist;
35242
- return resolve4(here, "..", "..", "templates", "subscriptions");
35516
+ return resolve5(here, "..", "..", "templates", "subscriptions");
35243
35517
  }
35244
35518
  function copyTemplate(src2, dest, vars) {
35245
- if (!existsSync3(src2)) {
35519
+ if (!existsSync4(src2)) {
35246
35520
  throw new Error(`template dir missing: ${src2}`);
35247
35521
  }
35248
- mkdirSync2(dest, { recursive: true });
35522
+ mkdirSync3(dest, { recursive: true });
35249
35523
  const entries2 = readdirSync(src2);
35250
35524
  for (const entry of entries2) {
35251
35525
  const from = join6(src2, entry);
@@ -35254,9 +35528,9 @@ function copyTemplate(src2, dest, vars) {
35254
35528
  if (st.isDirectory()) {
35255
35529
  copyTemplate(from, to, vars);
35256
35530
  } else {
35257
- const raw = readFileSync4(from, "utf8");
35531
+ const raw = readFileSync5(from, "utf8");
35258
35532
  const rendered = raw.replace(/\{\{([A-Z_]+)\}\}/g, (match, key) => vars[key] ?? match);
35259
- writeFileSync3(to, rendered);
35533
+ writeFileSync4(to, rendered);
35260
35534
  }
35261
35535
  }
35262
35536
  }
@@ -35265,7 +35539,7 @@ function parseTriggersInput(opts) {
35265
35539
  if (opts.triggersFile) {
35266
35540
  let parsed;
35267
35541
  try {
35268
- parsed = JSON.parse(readFileSync4(opts.triggersFile, "utf8"));
35542
+ parsed = JSON.parse(readFileSync5(opts.triggersFile, "utf8"));
35269
35543
  } catch (err) {
35270
35544
  throw new Error(`Could not read/parse --triggers-file ${opts.triggersFile}: ${err instanceof Error ? err.message : String(err)}`);
35271
35545
  }
@@ -35350,9 +35624,9 @@ async function createSubscription(name, opts) {
35350
35624
  }
35351
35625
  }
35352
35626
  const eventName = `${subgraph}.${table}.created`;
35353
- const targetDir = resolve4(process.cwd(), name);
35627
+ const targetDir = resolve5(process.cwd(), name);
35354
35628
  if (!opts.scaffold === false) {
35355
- if (existsSync3(targetDir)) {
35629
+ if (existsSync4(targetDir)) {
35356
35630
  error(`Directory already exists: ${relative(process.cwd(), targetDir)}`);
35357
35631
  process.exit(1);
35358
35632
  }
@@ -35403,15 +35677,15 @@ async function createSubscription(name, opts) {
35403
35677
  if (signingSecret && !opts.scaffold === false) {
35404
35678
  const envTarget = join6(targetDir, ".env");
35405
35679
  const envExample = join6(targetDir, ".env.example");
35406
- if (existsSync3(envExample) && !existsSync3(envTarget)) {
35680
+ if (existsSync4(envExample) && !existsSync4(envTarget)) {
35407
35681
  copyFileSync(envExample, envTarget);
35408
35682
  }
35409
- if (!existsSync3(envTarget)) {
35410
- writeFileSync3(envTarget, `SIGNING_SECRET=${signingSecret}
35683
+ if (!existsSync4(envTarget)) {
35684
+ writeFileSync4(envTarget, `SIGNING_SECRET=${signingSecret}
35411
35685
  `);
35412
35686
  } else {
35413
- const cur = readFileSync4(envTarget, "utf8");
35414
- writeFileSync3(envTarget, cur.match(/^SIGNING_SECRET=/m) ? cur.replace(/^SIGNING_SECRET=.*/m, `SIGNING_SECRET=${signingSecret}`) : `${cur}
35687
+ const cur = readFileSync5(envTarget, "utf8");
35688
+ writeFileSync4(envTarget, cur.match(/^SIGNING_SECRET=/m) ? cur.replace(/^SIGNING_SECRET=.*/m, `SIGNING_SECRET=${signingSecret}`) : `${cur}
35415
35689
  SIGNING_SECRET=${signingSecret}
35416
35690
  `);
35417
35691
  }
@@ -36178,8 +36452,8 @@ POST result (client-side, not logged):`));
36178
36452
  }
36179
36453
  // src/commands/devnet.ts
36180
36454
  import { spawnSync as spawnSync2 } from "node:child_process";
36181
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "node:fs";
36182
- import { dirname as dirname6, join as join8 } from "node:path";
36455
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6 } from "node:fs";
36456
+ import { dirname as dirname7, join as join8 } from "node:path";
36183
36457
 
36184
36458
  // src/lib/devnet-compose.ts
36185
36459
  var DEFAULT_IMAGE_OWNER = "ryanwaits";
@@ -36321,24 +36595,24 @@ volumes:
36321
36595
  }
36322
36596
 
36323
36597
  // src/lib/devnet-config.ts
36324
- import { existsSync as existsSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
36325
- import { dirname as dirname5, join as join7, parse as parsePath } from "node:path";
36598
+ import { existsSync as existsSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
36599
+ import { dirname as dirname6, join as join7, parse as parsePath } from "node:path";
36326
36600
  import { parse as parseToml } from "smol-toml";
36327
36601
  var DEFAULT_INDEXER_OBSERVER = "host.docker.internal:3700";
36328
36602
  function findClarinetProject(startDir) {
36329
36603
  let dir = startDir;
36330
36604
  const { root } = parsePath(dir);
36331
36605
  while (true) {
36332
- if (existsSync4(join7(dir, "Clarinet.toml")))
36606
+ if (existsSync5(join7(dir, "Clarinet.toml")))
36333
36607
  return dir;
36334
36608
  if (dir === root)
36335
36609
  return null;
36336
- dir = dirname5(dir);
36610
+ dir = dirname6(dir);
36337
36611
  }
36338
36612
  }
36339
36613
  function ensureEventObserver(devnetTomlPath, endpoint = DEFAULT_INDEXER_OBSERVER) {
36340
- const exists = existsSync4(devnetTomlPath);
36341
- const original = exists ? readFileSync5(devnetTomlPath, "utf8") : "";
36614
+ const exists = existsSync5(devnetTomlPath);
36615
+ const original = exists ? readFileSync6(devnetTomlPath, "utf8") : "";
36342
36616
  if (original.trim()) {
36343
36617
  const parsed = parseToml(original);
36344
36618
  const observers = parsed.devnet?.stacks_node_events_observers;
@@ -36352,7 +36626,7 @@ function ensureEventObserver(devnetTomlPath, endpoint = DEFAULT_INDEXER_OBSERVER
36352
36626
  const at = (arrayStart.index ?? 0) + arrayStart[0].length;
36353
36627
  const next = original.slice(at).trimStart().startsWith("]") ? entry : `${entry}, `;
36354
36628
  const patched = original.slice(0, at) + next + original.slice(at);
36355
- writeFileSync4(devnetTomlPath, patched);
36629
+ writeFileSync5(devnetTomlPath, patched);
36356
36630
  return { status: "added" };
36357
36631
  }
36358
36632
  const header = original.match(/^\[devnet\]\s*$/m);
@@ -36360,7 +36634,7 @@ function ensureEventObserver(devnetTomlPath, endpoint = DEFAULT_INDEXER_OBSERVER
36360
36634
  const at = (header.index ?? 0) + header[0].length;
36361
36635
  const patched = `${original.slice(0, at)}
36362
36636
  stacks_node_events_observers = [${entry}]${original.slice(at)}`;
36363
- writeFileSync4(devnetTomlPath, patched);
36637
+ writeFileSync5(devnetTomlPath, patched);
36364
36638
  return { status: "added" };
36365
36639
  }
36366
36640
  const prefix = original && !original.endsWith(`
@@ -36370,7 +36644,7 @@ stacks_node_events_observers = [${entry}]${original.slice(at)}`;
36370
36644
  ` : ""}[devnet]
36371
36645
  stacks_node_events_observers = [${entry}]
36372
36646
  `;
36373
- writeFileSync4(devnetTomlPath, block);
36647
+ writeFileSync5(devnetTomlPath, block);
36374
36648
  return { status: exists ? "added" : "created" };
36375
36649
  }
36376
36650
 
@@ -36420,7 +36694,7 @@ function registerDevnetCommand(program2) {
36420
36694
  }
36421
36695
  function resolveProject(explicit) {
36422
36696
  if (explicit) {
36423
- if (!existsSync5(join8(explicit, "Clarinet.toml"))) {
36697
+ if (!existsSync6(join8(explicit, "Clarinet.toml"))) {
36424
36698
  error(`No Clarinet.toml in ${explicit}`);
36425
36699
  process.exit(1);
36426
36700
  }
@@ -36437,7 +36711,7 @@ function resolveProject(explicit) {
36437
36711
  async function connect(options3) {
36438
36712
  const project = resolveProject(options3.project);
36439
36713
  const devnetToml = join8(project, "settings", "Devnet.toml");
36440
- mkdirSync3(dirname6(devnetToml), { recursive: true });
36714
+ mkdirSync4(dirname7(devnetToml), { recursive: true });
36441
36715
  const result = ensureEventObserver(devnetToml, DEFAULT_INDEXER_OBSERVER);
36442
36716
  console.log(`${green("✓")} found Clarinet.toml`);
36443
36717
  if (result.status === "present") {
@@ -36447,8 +36721,8 @@ async function connect(options3) {
36447
36721
  console.log(dim(` + stacks_node_events_observers = ["${DEFAULT_INDEXER_OBSERVER}"]`));
36448
36722
  }
36449
36723
  const composePath = join8(project, COMPOSE_REL);
36450
- mkdirSync3(dirname6(composePath), { recursive: true });
36451
- writeFileSync5(composePath, buildDevnetCompose({ owner: options3.owner, imageTag: options3.imageTag }));
36724
+ mkdirSync4(dirname7(composePath), { recursive: true });
36725
+ writeFileSync6(composePath, buildDevnetCompose({ owner: options3.owner, imageTag: options3.imageTag }));
36452
36726
  console.log(`${green("✓")} wrote ${COMPOSE_REL}`);
36453
36727
  if (!options3.up) {
36454
36728
  console.log(dim(`
@@ -36477,7 +36751,7 @@ Stop with: sl devnet down`));
36477
36751
  async function down(options3) {
36478
36752
  const project = resolveProject(options3.project);
36479
36753
  const composePath = join8(project, COMPOSE_REL);
36480
- if (!existsSync5(composePath)) {
36754
+ if (!existsSync6(composePath)) {
36481
36755
  error(`No ${COMPOSE_REL} in ${project} — nothing to stop. Run \`sl devnet connect\` first.`);
36482
36756
  process.exit(1);
36483
36757
  }
@@ -36492,7 +36766,7 @@ async function down(options3) {
36492
36766
  async function logs(service, options3) {
36493
36767
  const project = resolveProject(options3.project);
36494
36768
  const composePath = join8(project, COMPOSE_REL);
36495
- if (!existsSync5(composePath)) {
36769
+ if (!existsSync6(composePath)) {
36496
36770
  error(`No ${COMPOSE_REL} in ${project} — run \`sl devnet connect\` first.`);
36497
36771
  process.exit(1);
36498
36772
  }
@@ -36617,7 +36891,7 @@ init_subgraphs2();
36617
36891
  init_output();
36618
36892
  init_resolve_auth();
36619
36893
  import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
36620
- import { dirname as dirname7, join as join9 } from "node:path";
36894
+ import { dirname as dirname8, join as join9 } from "node:path";
36621
36895
  import { createStreamsClient } from "@secondlayer/sdk";
36622
36896
  import { STREAMS_EVENT_TYPES } from "@secondlayer/shared";
36623
36897
  var DEFAULT_BASE_URL3 = "https://api.secondlayer.tools";
@@ -36794,7 +37068,7 @@ Examples:
36794
37068
  for (const file of files) {
36795
37069
  const bytes = await dumps.download(file);
36796
37070
  const dest = join9(options3.to, file.path);
36797
- await mkdir3(dirname7(dest), { recursive: true });
37071
+ await mkdir3(dirname8(dest), { recursive: true });
36798
37072
  await writeFile3(dest, bytes);
36799
37073
  process.stderr.write(`# ${file.path} (${file.row_count} rows, ${bytes.byteLength} bytes)
36800
37074
  `);
@@ -38080,10 +38354,10 @@ init_http();
38080
38354
  init_output();
38081
38355
 
38082
38356
  // src/lib/project-file.ts
38083
- import { existsSync as existsSync6 } from "node:fs";
38357
+ import { existsSync as existsSync7 } from "node:fs";
38084
38358
  import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4 } from "node:fs/promises";
38085
38359
  import { homedir as homedir5 } from "node:os";
38086
- import { dirname as dirname13, join as join12, parse as parsePath2, resolve as resolve10 } from "node:path";
38360
+ import { dirname as dirname14, join as join12, parse as parsePath2, resolve as resolve11 } from "node:path";
38087
38361
  import { z as z4 } from "zod";
38088
38362
  var ProjectFileSchema = z4.object({
38089
38363
  slug: z4.string().min(1)
@@ -38091,23 +38365,23 @@ var ProjectFileSchema = z4.object({
38091
38365
  var FILENAME = "project";
38092
38366
  var DIRNAME = ".secondlayer";
38093
38367
  async function readActiveProject(cwd, globalDefault) {
38094
- let dir = resolve10(cwd);
38095
- const home2 = resolve10(homedir5());
38368
+ let dir = resolve11(cwd);
38369
+ const home2 = resolve11(homedir5());
38096
38370
  const fsRoot = parsePath2(dir).root;
38097
38371
  while (true) {
38098
38372
  const candidate = join12(dir, DIRNAME, FILENAME);
38099
- if (existsSync6(candidate)) {
38373
+ if (existsSync7(candidate)) {
38100
38374
  try {
38101
38375
  const raw = await readFile3(candidate, "utf8");
38102
38376
  const parsed = ProjectFileSchema.parse(JSON.parse(raw));
38103
38377
  return { slug: parsed.slug, resolvedFrom: candidate };
38104
38378
  } catch {}
38105
38379
  }
38106
- if (existsSync6(join12(dir, ".git")))
38380
+ if (existsSync7(join12(dir, ".git")))
38107
38381
  break;
38108
38382
  if (dir === home2 || dir === fsRoot)
38109
38383
  break;
38110
- const parent = dirname13(dir);
38384
+ const parent = dirname14(dir);
38111
38385
  if (parent === dir)
38112
38386
  break;
38113
38387
  dir = parent;
@@ -38121,7 +38395,7 @@ async function readActiveProject(cwd, globalDefault) {
38121
38395
  return null;
38122
38396
  }
38123
38397
  async function writeActiveProject(slug, cwd) {
38124
- const dir = join12(resolve10(cwd), DIRNAME);
38398
+ const dir = join12(resolve11(cwd), DIRNAME);
38125
38399
  await mkdir4(dir, { recursive: true });
38126
38400
  const file = join12(dir, FILENAME);
38127
38401
  await writeFile4(file, `${JSON.stringify({ slug }, null, 2)}
@@ -38345,5 +38619,5 @@ registerAccountCommand(program);
38345
38619
  registerKeysCommand(program);
38346
38620
  program.parse();
38347
38621
 
38348
- //# debugId=D8E1B996905C6BA564756E2164756E21
38622
+ //# debugId=083CEA7A9489EF8064756E2164756E21
38349
38623
  //# sourceMappingURL=cli.js.map