@secondlayer/cli 5.6.6 → 5.8.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/dist/cli.js CHANGED
@@ -15419,8 +15419,8 @@ var require_to_regex_range = __commonJS((exports, module) => {
15419
15419
  let relax = String(opts.relaxZeros);
15420
15420
  let shorthand = String(opts.shorthand);
15421
15421
  let capture = String(opts.capture);
15422
- let wrap = String(opts.wrap);
15423
- let cacheKey = min + ":" + max + "=" + relax + shorthand + capture + wrap;
15422
+ let wrap2 = String(opts.wrap);
15423
+ let cacheKey = min + ":" + max + "=" + relax + shorthand + capture + wrap2;
15424
15424
  if (toRegexRange.cache.hasOwnProperty(cacheKey)) {
15425
15425
  return toRegexRange.cache[cacheKey].result;
15426
15426
  }
@@ -15700,9 +15700,9 @@ var require_fill_range = __commonJS((exports, module) => {
15700
15700
  };
15701
15701
  var toRegex = (start, end, options2) => {
15702
15702
  if (Array.isArray(start)) {
15703
- let wrap = options2.wrap === true;
15703
+ let wrap2 = options2.wrap === true;
15704
15704
  let prefix = options2.capture ? "" : "?:";
15705
- return wrap ? `(${prefix}${start.join("|")})` : start.join("|");
15705
+ return wrap2 ? `(${prefix}${start.join("|")})` : start.join("|");
15706
15706
  }
15707
15707
  return toRegexRange(start, end, options2);
15708
15708
  };
@@ -22353,9 +22353,9 @@ function requireLines() {
22353
22353
  }
22354
22354
  function requireWrap() {
22355
22355
  if (hasRequiredWrap)
22356
- return wrap;
22356
+ return wrap2;
22357
22357
  hasRequiredWrap = 1;
22358
- wrap = (msg, opts = {}) => {
22358
+ wrap2 = (msg, opts = {}) => {
22359
22359
  const tab = Number.isSafeInteger(parseInt(opts.margin)) ? new Array(parseInt(opts.margin)).fill(" ").join("") : opts.margin || "";
22360
22360
  const width = opts.width;
22361
22361
  return (msg || "").split(/\r?\n/g).map((line) => line.split(/\s+/g).reduce((arr, w2) => {
@@ -22368,7 +22368,7 @@ function requireWrap() {
22368
22368
  `)).join(`
22369
22369
  `);
22370
22370
  };
22371
- return wrap;
22371
+ return wrap2;
22372
22372
  }
22373
22373
  function requireEntriesToDisplay() {
22374
22374
  if (hasRequiredEntriesToDisplay)
@@ -22660,7 +22660,7 @@ function requireSelect() {
22660
22660
  hasRequiredSelect = 1;
22661
22661
  const color = requireKleur();
22662
22662
  const Prompt = requirePrompt();
22663
- const { style: style2, clear: clear2, figures, wrap: wrap2, entriesToDisplay: entriesToDisplay2 } = requireUtil();
22663
+ const { style: style2, clear: clear2, figures, wrap: wrap3, entriesToDisplay: entriesToDisplay2 } = requireUtil();
22664
22664
  const { cursor } = requireSrc();
22665
22665
 
22666
22666
  class SelectPrompt extends Prompt {
@@ -22791,7 +22791,7 @@ function requireSelect() {
22791
22791
  desc = ` - ${v2.description}`;
22792
22792
  if (prefix.length + title.length + desc.length >= this.out.columns || v2.description.split(/\r?\n/).length > 1) {
22793
22793
  desc = `
22794
- ` + wrap2(v2.description, { margin: 3, width: this.out.columns });
22794
+ ` + wrap3(v2.description, { margin: 3, width: this.out.columns });
22795
22795
  }
22796
22796
  }
22797
22797
  }
@@ -23557,7 +23557,7 @@ function requireMultiselect() {
23557
23557
  const color = requireKleur();
23558
23558
  const { cursor } = requireSrc();
23559
23559
  const Prompt = requirePrompt();
23560
- const { clear: clear2, figures, style: style2, wrap: wrap2, entriesToDisplay: entriesToDisplay2 } = requireUtil();
23560
+ const { clear: clear2, figures, style: style2, wrap: wrap3, entriesToDisplay: entriesToDisplay2 } = requireUtil();
23561
23561
 
23562
23562
  class MultiselectPrompt extends Prompt {
23563
23563
  constructor(opts = {}) {
@@ -23715,7 +23715,7 @@ Instructions:
23715
23715
  desc = ` - ${v2.description}`;
23716
23716
  if (prefix.length + title.length + desc.length >= this.out.columns || v2.description.split(/\r?\n/).length > 1) {
23717
23717
  desc = `
23718
- ` + wrap2(v2.description, { margin: prefix.length, width: this.out.columns });
23718
+ ` + wrap3(v2.description, { margin: prefix.length, width: this.out.columns });
23719
23719
  }
23720
23720
  }
23721
23721
  }
@@ -23788,7 +23788,7 @@ function requireAutocomplete() {
23788
23788
  const color = requireKleur();
23789
23789
  const Prompt = requirePrompt();
23790
23790
  const { erase, cursor } = requireSrc();
23791
- const { style: style2, clear: clear2, figures, wrap: wrap2, entriesToDisplay: entriesToDisplay2 } = requireUtil();
23791
+ const { style: style2, clear: clear2, figures, wrap: wrap3, entriesToDisplay: entriesToDisplay2 } = requireUtil();
23792
23792
  const getVal = (arr, i2) => arr[i2] && (arr[i2].value || arr[i2].title || arr[i2]);
23793
23793
  const getTitle = (arr, i2) => arr[i2] && (arr[i2].title || arr[i2].value || arr[i2]);
23794
23794
  const getIndex = (arr, valOrTitle) => {
@@ -23975,7 +23975,7 @@ function requireAutocomplete() {
23975
23975
  desc = ` - ${v2.description}`;
23976
23976
  if (prefix.length + title.length + desc.length >= this.out.columns || v2.description.split(/\r?\n/).length > 1) {
23977
23977
  desc = `
23978
- ` + wrap2(v2.description, { margin: 3, width: this.out.columns });
23978
+ ` + wrap3(v2.description, { margin: 3, width: this.out.columns });
23979
23979
  }
23980
23980
  }
23981
23981
  return prefix + " " + title + color.gray(desc || "");
@@ -24929,7 +24929,7 @@ async function detect2({ autoInstall, programmatic, cwd } = {}) {
24929
24929
  }
24930
24930
  return agent;
24931
24931
  }
24932
- var ini$1, hasRequiredIni, iniExports, prompts$2, kleur, hasRequiredKleur, action, hasRequiredAction, strip, hasRequiredStrip, src, hasRequiredSrc, clear, hasRequiredClear, figures_1, hasRequiredFigures, style, hasRequiredStyle, lines, hasRequiredLines, wrap, hasRequiredWrap, entriesToDisplay, hasRequiredEntriesToDisplay, util, hasRequiredUtil, prompt, hasRequiredPrompt, text, hasRequiredText, select4, hasRequiredSelect, toggle, hasRequiredToggle, datepart, hasRequiredDatepart, meridiem, hasRequiredMeridiem, day, hasRequiredDay, hours, hasRequiredHours, milliseconds, hasRequiredMilliseconds, minutes, hasRequiredMinutes, month, hasRequiredMonth, seconds, hasRequiredSeconds, year, hasRequiredYear, dateparts, hasRequiredDateparts, date, hasRequiredDate, number, hasRequiredNumber, multiselect, hasRequiredMultiselect, autocomplete, hasRequiredAutocomplete, autocompleteMultiselect, hasRequiredAutocompleteMultiselect, confirm6, hasRequiredConfirm, elements, hasRequiredElements, hasRequiredPrompts$1, lib$1, hasRequiredLib$1, prompts$1, hasRequiredPrompts, promptsExports, prompts, isBrowser, platform, OSC = "\x1B]", BEL = "\x07", SEP = ";", link = (text2, url) => [
24932
+ var ini$1, hasRequiredIni, iniExports, prompts$2, kleur, hasRequiredKleur, action, hasRequiredAction, strip, hasRequiredStrip, src, hasRequiredSrc, clear, hasRequiredClear, figures_1, hasRequiredFigures, style, hasRequiredStyle, lines, hasRequiredLines, wrap2, hasRequiredWrap, entriesToDisplay, hasRequiredEntriesToDisplay, util, hasRequiredUtil, prompt, hasRequiredPrompt, text, hasRequiredText, select4, hasRequiredSelect, toggle, hasRequiredToggle, datepart, hasRequiredDatepart, meridiem, hasRequiredMeridiem, day, hasRequiredDay, hours, hasRequiredHours, milliseconds, hasRequiredMilliseconds, minutes, hasRequiredMinutes, month, hasRequiredMonth, seconds, hasRequiredSeconds, year, hasRequiredYear, dateparts, hasRequiredDateparts, date, hasRequiredDate, number, hasRequiredNumber, multiselect, hasRequiredMultiselect, autocomplete, hasRequiredAutocomplete, autocompleteMultiselect, hasRequiredAutocompleteMultiselect, confirm6, hasRequiredConfirm, elements, hasRequiredElements, hasRequiredPrompts$1, lib$1, hasRequiredLib$1, prompts$1, hasRequiredPrompts, promptsExports, prompts, isBrowser, platform, OSC = "\x1B]", BEL = "\x07", SEP = ";", link = (text2, url) => [
24933
24933
  OSC,
24934
24934
  "8",
24935
24935
  SEP,
@@ -32321,7 +32321,7 @@ var {
32321
32321
  // package.json
32322
32322
  var package_default = {
32323
32323
  name: "@secondlayer/cli",
32324
- version: "5.6.6",
32324
+ version: "5.8.0",
32325
32325
  repository: {
32326
32326
  type: "git",
32327
32327
  url: "git+https://github.com/ryanwaits/secondlayer.git",
@@ -32370,10 +32370,10 @@ var package_default = {
32370
32370
  dependencies: {
32371
32371
  "@inquirer/prompts": "^8.2.0",
32372
32372
  "@secondlayer/bundler": "^0.3.7",
32373
- "@secondlayer/sdk": "^5.0.0",
32374
- "@secondlayer/shared": "^6.8.1",
32375
- "@secondlayer/stacks": "^2.2.1",
32376
- "@secondlayer/subgraphs": "^3.2.1",
32373
+ "@secondlayer/sdk": "^5.6.0",
32374
+ "@secondlayer/shared": "^6.9.0",
32375
+ "@secondlayer/stacks": "^2.3.0",
32376
+ "@secondlayer/subgraphs": "^3.4.0",
32377
32377
  "@biomejs/js-api": "^0.7.0",
32378
32378
  "@biomejs/wasm-nodejs": "^1.9.0",
32379
32379
  esbuild: "^0.19.0",
@@ -34444,6 +34444,7 @@ import { confirm as confirm4 } from "@inquirer/prompts";
34444
34444
 
34445
34445
  // src/generators/subgraph-scaffold.ts
34446
34446
  init_format();
34447
+ import { classifyContract } from "@secondlayer/stacks/clarity";
34447
34448
 
34448
34449
  // src/generators/clarity-to-subgraph.ts
34449
34450
  import {
@@ -34496,65 +34497,121 @@ function mapType(abiType, nullable) {
34496
34497
  function toCamelCase2(str) {
34497
34498
  return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
34498
34499
  }
34499
- async function generateSubgraphScaffold(input2) {
34500
- const { contractId, functions } = input2;
34501
- const contractParts = contractId.split(".");
34502
- const contractName = contractParts[contractParts.length - 1] ?? contractId;
34503
- const subgraphName = input2.subgraphName ?? contractName;
34504
- const publicFunctions = functions.filter((f) => f.access === "public");
34505
- if (publicFunctions.length === 0) {
34506
- throw new Error(`No public functions found in ${contractId}`);
34507
- }
34508
- const sourceEntries = publicFunctions.map((fn) => {
34509
- const sourceName = toCamelCase2(fn.name);
34510
- return ` ${sourceName}: { type: 'contract_call', contractId: '${contractId}', functionName: '${fn.name}' }`;
34511
- });
34512
- const sourcesBlock = sourceEntries.join(`,
34513
- `);
34514
- const tables = publicFunctions.map((fn) => {
34515
- const columns = fn.args.map((arg) => {
34516
- const mapped = clarityTypeToSubgraphColumn(arg.type);
34517
- const nullable = mapped.nullable ? ", nullable: true" : "";
34518
- return ` ${arg.name.replace(/-/g, "_")}: { type: '${mapped.type}'${nullable} }`;
34519
- }).join(`,
34520
- `);
34521
- const tableName = fn.name.replace(/-/g, "_");
34522
- return ` ${tableName}: {
34523
- columns: {
34524
- ${columns || " _placeholder: { type: 'text' }"}
34525
- }
34526
- }`;
34527
- });
34528
- const schemaBlock = tables.join(`,
34529
- `);
34530
- const handlerEntries = publicFunctions.map((fn) => {
34531
- const sourceName = toCamelCase2(fn.name);
34532
- return ` ${sourceName}: async (event, ctx) => {
34533
- // TODO: implement ${fn.name} handler
34534
- // event.args contains the function arguments
34535
- // ctx.insert('${fn.name.replace(/-/g, "_")}', { ... })
34536
- }`;
34537
- });
34538
- const handlersBlock = handlerEntries.join(`,
34539
-
34540
- `);
34541
- const code = `
34500
+ function snake(str) {
34501
+ return str.replace(/-/g, "_");
34502
+ }
34503
+ function wrap(name, sources, schema, handlers) {
34504
+ return `
34542
34505
  import { defineSubgraph } from '@secondlayer/subgraphs';
34543
34506
 
34544
34507
  export default defineSubgraph({
34545
- name: '${subgraphName}',
34508
+ name: '${name}',
34546
34509
  sources: {
34547
- ${sourcesBlock}
34510
+ ${sources}
34548
34511
  },
34549
34512
  schema: {
34550
- ${schemaBlock}
34513
+ ${schema}
34551
34514
  },
34552
34515
  handlers: {
34553
- ${handlersBlock}
34516
+ ${handlers}
34554
34517
  }
34555
34518
  });
34556
34519
  `.trimStart();
34557
- return formatCode(code);
34520
+ }
34521
+ function tokenScaffold(name, source) {
34522
+ const isFt = source.type === "ft_transfer";
34523
+ const scope = source.trait ? `trait: '${source.trait}'` : `assetIdentifier: '${source.assetIdentifier}'`;
34524
+ const cols = isFt ? ` sender: { type: 'principal' },
34525
+ recipient: { type: 'principal' },
34526
+ amount: { type: 'uint' },
34527
+ asset_identifier: { type: 'principal', indexed: true }` : ` sender: { type: 'principal' },
34528
+ recipient: { type: 'principal' },
34529
+ token_id: { type: 'text' },
34530
+ asset_identifier: { type: 'principal', indexed: true }`;
34531
+ const insert = isFt ? `{ sender: event.sender, recipient: event.recipient, amount: event.amount, asset_identifier: event.assetIdentifier }` : `{ sender: event.sender, recipient: event.recipient, token_id: String(event.tokenId), asset_identifier: event.assetIdentifier }`;
34532
+ return wrap(name, ` transfers: { type: '${source.type}', ${scope} }`, ` transfers: {
34533
+ columns: {
34534
+ ${cols}
34535
+ }
34536
+ }`, ` transfers: (event, ctx) => {
34537
+ ctx.insert('transfers', ${insert});
34538
+ }`);
34539
+ }
34540
+ function genericCallsScaffold(name, contractId) {
34541
+ return wrap(name, ` calls: { type: 'contract_call', contractId: '${contractId}' }`, ` calls: {
34542
+ columns: {
34543
+ function_name: { type: 'text', indexed: true },
34544
+ sender: { type: 'principal' },
34545
+ args: { type: 'jsonb' }
34546
+ }
34547
+ }`, ` calls: (event, ctx) => {
34548
+ ctx.insert('calls', { function_name: event.functionName, sender: event.sender, args: { values: event.args } });
34549
+ }`);
34550
+ }
34551
+ function functionsScaffold(name, contractId, abi, fnNames) {
34552
+ const fns = abi.functions.filter((f) => f.access === "public" && fnNames.includes(f.name));
34553
+ if (fns.length === 0) {
34554
+ throw new Error(`none of --functions [${fnNames.join(", ")}] are public functions of ${contractId}`);
34555
+ }
34556
+ const sources = fns.map((f) => ` ${toCamelCase2(f.name)}: { type: 'contract_call', contractId: '${contractId}', functionName: '${f.name}' }`).join(`,
34557
+ `);
34558
+ const schema = fns.map((f) => {
34559
+ const cols = f.args.map((arg) => {
34560
+ const m = clarityTypeToSubgraphColumn(arg.type);
34561
+ return ` ${snake(arg.name)}: { type: '${m.type}'${m.nullable ? ", nullable: true" : ""} }`;
34562
+ }).join(`,
34563
+ `);
34564
+ return ` ${snake(f.name)}: {
34565
+ columns: {
34566
+ ${cols || " sender: { type: 'principal' }"}
34567
+ }
34568
+ }`;
34569
+ }).join(`,
34570
+ `);
34571
+ const handlers = fns.map((f) => {
34572
+ const inserts = f.args.map((arg, i) => {
34573
+ const m = clarityTypeToSubgraphColumn(arg.type);
34574
+ const ts = m.type === "uint" || m.type === "int" ? "bigint" : m.type === "boolean" ? "boolean" : "string";
34575
+ return `${snake(arg.name)}: event.args[${i}] as ${ts}`;
34576
+ }).join(", ");
34577
+ return ` ${toCamelCase2(f.name)}: (event, ctx) => {
34578
+ ctx.insert('${snake(f.name)}', { ${inserts} });
34579
+ }`;
34580
+ }).join(`,
34581
+
34582
+ `);
34583
+ return wrap(name, sources, schema, handlers);
34584
+ }
34585
+ async function generateSubgraphScaffold(input2) {
34586
+ if (input2.trait) {
34587
+ const name2 = input2.subgraphName ?? `${input2.trait}-transfers`;
34588
+ const type = input2.trait === "sip-009" ? "nft_transfer" : "ft_transfer";
34589
+ return formatCode(tokenScaffold(name2, { type, trait: input2.trait }));
34590
+ }
34591
+ const { contractId, abi } = input2;
34592
+ if (!contractId || !abi) {
34593
+ throw new Error("scaffold requires a contractId + abi (or --trait)");
34594
+ }
34595
+ const name = input2.subgraphName ?? contractId.split(".").pop() ?? contractId;
34596
+ if (input2.functions && input2.functions.length > 0) {
34597
+ return formatCode(functionsScaffold(name, contractId, abi, input2.functions));
34598
+ }
34599
+ const standards = classifyContract(abi);
34600
+ if (standards.includes("sip-010")) {
34601
+ const asset = abi.fungible_tokens?.[0]?.name;
34602
+ return formatCode(tokenScaffold(name, {
34603
+ type: "ft_transfer",
34604
+ assetIdentifier: asset ? `${contractId}::${asset}` : contractId
34605
+ }));
34606
+ }
34607
+ if (standards.includes("sip-009")) {
34608
+ const asset = abi.non_fungible_tokens?.[0]?.name;
34609
+ return formatCode(tokenScaffold(name, {
34610
+ type: "nft_transfer",
34611
+ assetIdentifier: asset ? `${contractId}::${asset}` : contractId
34612
+ }));
34613
+ }
34614
+ return formatCode(genericCallsScaffold(name, contractId));
34558
34615
  }
34559
34616
 
34560
34617
  // src/utils/case-conversion.ts
@@ -35475,7 +35532,7 @@ Stopped watching.`);
35475
35532
  });
35476
35533
  await new Promise(() => {});
35477
35534
  });
35478
- subgraphs.command("deploy <file>").description("Deploy a subgraph definition file").option("--start-block <n>", "Override the subgraph definition startBlock for this deploy").option("--dry-run", "Validate and preview deploy without writing changes").option("--preview", "Alias for --dry-run").option("--force", "Skip confirmation prompt for reindex operations").option("--strict", "Run `tsc --noEmit` against the handler before deploy (slower; catches TS type errors)").action(async (file, options2) => {
35535
+ subgraphs.command("deploy <file>").description("Deploy a subgraph definition file").option("--start-block <n>", "Override the subgraph definition startBlock for this deploy").option("--dry-run", "Validate and preview deploy without writing changes").option("--preview", "Alias for --dry-run").option("--force", "Skip confirmation prompt for reindex operations").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)").action(async (file, options2) => {
35479
35536
  try {
35480
35537
  const absPath = resolve2(file);
35481
35538
  const config = await loadConfig();
@@ -35518,6 +35575,9 @@ Stopped watching.`);
35518
35575
  return;
35519
35576
  }
35520
35577
  const deployStartBlock = startBlock ?? effectiveDef.startBlock;
35578
+ if (options2.databaseUrl) {
35579
+ info("BYO data plane: schema + rows will live in your database. The server verifies the connection before deploying.");
35580
+ }
35521
35581
  const result = await deploySubgraphApi({
35522
35582
  name: effectiveDef.name,
35523
35583
  version: undefined,
@@ -35526,7 +35586,8 @@ Stopped watching.`);
35526
35586
  schema: effectiveDef.schema,
35527
35587
  handlerCode,
35528
35588
  sourceCode: source,
35529
- ...deployStartBlock !== undefined ? { startBlock: deployStartBlock } : {}
35589
+ ...deployStartBlock !== undefined ? { startBlock: deployStartBlock } : {},
35590
+ ...options2.databaseUrl ? { databaseUrl: options2.databaseUrl } : {}
35530
35591
  });
35531
35592
  const printDeployFooter = async () => {
35532
35593
  try {
@@ -35772,6 +35833,48 @@ Synced — exiting watch.`));
35772
35833
  process.exit(1);
35773
35834
  }
35774
35835
  });
35836
+ subgraphs.command("codegen <file>").description("Generate an ORM schema (Prisma or Drizzle) for a subgraph's tables — point it at your BYO database").option("--target <orm>", "ORM target: prisma | drizzle", "prisma").option("--schema <name>", "Postgres schema name (defaults to subgraph_<name>)").option("--env <var>", "datasource url env var", "DATABASE_URL").option("--models-only", "Emit only Prisma models (compose via prismaSchemaFolder)").option("-o, --output <path>", "Write to a file (defaults to stdout)").action(async (file, options2) => {
35837
+ try {
35838
+ const target = options2.target ?? "prisma";
35839
+ if (target !== "prisma" && target !== "drizzle") {
35840
+ error(`Unsupported --target "${target}" (supported: prisma, drizzle). For Kysely, run kysely-codegen against your database.`);
35841
+ process.exit(1);
35842
+ }
35843
+ const absPath = resolve2(file);
35844
+ if (!existsSync2(absPath)) {
35845
+ error(`File not found: ${absPath}`);
35846
+ process.exit(1);
35847
+ }
35848
+ const { readFile: readFile3 } = await import("node:fs/promises");
35849
+ const { bundleSubgraphCode } = await import("@secondlayer/bundler");
35850
+ const { generatePrismaSchema, generateDrizzleSchema } = await import("@secondlayer/subgraphs");
35851
+ const source = await readFile3(absPath, "utf8");
35852
+ const bundled = await bundleSubgraphCode(source);
35853
+ const def = {
35854
+ name: bundled.name,
35855
+ version: bundled.version,
35856
+ description: bundled.description,
35857
+ sources: bundled.sources,
35858
+ schema: bundled.schema,
35859
+ handlers: {}
35860
+ };
35861
+ const out = target === "drizzle" ? generateDrizzleSchema(def, { schemaName: options2.schema }) : generatePrismaSchema(def, {
35862
+ schemaName: options2.schema,
35863
+ datasourceEnv: options2.env,
35864
+ modelsOnly: options2.modelsOnly
35865
+ });
35866
+ if (options2.output) {
35867
+ await writeTextFile(resolve2(options2.output), out);
35868
+ success(`Wrote ${target} schema to ${options2.output}`);
35869
+ info("Next: point its datasource at your BYO database, then `prisma generate`.");
35870
+ } else {
35871
+ process.stdout.write(out);
35872
+ }
35873
+ } catch (err) {
35874
+ error(`Failed to generate schema: ${err}`);
35875
+ process.exit(1);
35876
+ }
35877
+ });
35775
35878
  subgraphs.command("reindex <name>").description("Reindex a subgraph from historical blocks (drops + reprocesses)").option("--from <block>", "Start block height").option("--to <block>", "End block height").option("-y, --yes", "Skip confirmation").action(async (name, options2) => {
35776
35879
  try {
35777
35880
  const fromBlock = options2.from ? Number.parseInt(options2.from, 10) : undefined;
@@ -35965,24 +36068,41 @@ ${rows.length} row(s)`));
35965
36068
  handleApiError(err, "delete subgraph");
35966
36069
  }
35967
36070
  });
35968
- subgraphs.command("scaffold <contractAddress>").description("Scaffold a defineSubgraph() file from a contract ABI").option("-o, --output <path>", "Output file path (required)").option("--api-key <key>", "Stacks node API key for direct RPC URLs").option("--no-install", "Skip bun install after writing package.json").action(async (contractAddress, options2) => {
36071
+ subgraphs.command("scaffold [contractAddress]").description("Scaffold a defineSubgraph() file. Detects SIP-010/009 → ft/nft source; --functions for typed call tables; --trait for a trait-scoped source (no contract)").option("-o, --output <path>", "Output file path (required)").option("--api-key <key>", "Stacks node API key for direct RPC URLs").option("--functions <names>", "Comma-separated public functions to index as typed contract_call tables").option("--trait <std>", "Scaffold a trait-scoped source (sip-009|sip-010|sip-013) — no contract needed").option("--no-install", "Skip bun install after writing package.json").action(async (contractAddress, options2) => {
35969
36072
  try {
35970
36073
  if (!options2.output) {
35971
36074
  error("--output <path> is required");
35972
36075
  process.exit(1);
35973
36076
  }
36077
+ const trait = options2.trait;
36078
+ if (trait && !["sip-009", "sip-010", "sip-013"].includes(trait)) {
36079
+ error("--trait must be one of: sip-009, sip-010, sip-013");
36080
+ process.exit(1);
36081
+ }
36082
+ if (!trait && !contractAddress) {
36083
+ error("a <contractAddress> is required (or use --trait)");
36084
+ process.exit(1);
36085
+ }
35974
36086
  const outPath = resolve2(options2.output);
35975
- const network = inferNetwork(contractAddress) ?? "mainnet";
35976
- const apiKey = options2.apiKey ?? process.env.STACKS_NODE_API_KEY ?? process.env.HIRO_API_KEY;
35977
- const client = new StacksApiClient(network, apiKey);
35978
- info(`Fetching ABI for ${contractAddress} via ${client.describeContractInfoSource()}...`);
35979
- const contractInfo = await client.getContractInfo(contractAddress);
35980
- const abi = parseApiResponse(contractInfo);
35981
- info("Generating scaffold...");
35982
- const content = await generateSubgraphScaffold({
35983
- contractId: contractAddress,
35984
- functions: abi.functions
35985
- });
36087
+ let content;
36088
+ if (trait) {
36089
+ info(`Generating trait-scoped scaffold for ${trait}...`);
36090
+ content = await generateSubgraphScaffold({ trait });
36091
+ } else {
36092
+ const address = contractAddress;
36093
+ const network = inferNetwork(address) ?? "mainnet";
36094
+ const apiKey = options2.apiKey ?? process.env.STACKS_NODE_API_KEY ?? process.env.HIRO_API_KEY;
36095
+ const client = new StacksApiClient(network, apiKey);
36096
+ info(`Fetching ABI for ${address} via ${client.describeContractInfoSource()}...`);
36097
+ const contractInfo = await client.getContractInfo(address);
36098
+ const abi = parseApiResponse(contractInfo);
36099
+ info("Generating scaffold...");
36100
+ content = await generateSubgraphScaffold({
36101
+ contractId: address,
36102
+ abi,
36103
+ functions: options2.functions?.split(",").map((f) => f.trim()).filter(Boolean)
36104
+ });
36105
+ }
35986
36106
  const dir = resolve2(outPath, "..");
35987
36107
  if (!existsSync2(dir))
35988
36108
  mkdirSync2(dir, { recursive: true });
@@ -36007,7 +36127,7 @@ ${rows.length} row(s)`));
36007
36127
  process.exit(1);
36008
36128
  }
36009
36129
  });
36010
- subgraphs.command("generate <subgraphName>").description("Generate a typed client for a deployed subgraph").option("-o, --output <path>", "Output file path (required)").action(async (subgraphName, options2) => {
36130
+ subgraphs.command("client <subgraphName>").alias("generate").description("Generate a typed query client for a deployed subgraph").option("-o, --output <path>", "Output file path (required)").action(async (subgraphName, options2) => {
36011
36131
  try {
36012
36132
  if (!options2.output) {
36013
36133
  error("--output <path> is required");
@@ -36353,6 +36473,60 @@ function registerStreamsCommand(program2) {
36353
36473
  }
36354
36474
  });
36355
36475
  }
36476
+ // src/commands/datasets.ts
36477
+ init_output();
36478
+ import { CURSOR_SLUGS, Datasets } from "@secondlayer/sdk";
36479
+ var DEFAULT_BASE_URL2 = "https://api.secondlayer.tools";
36480
+ function client2() {
36481
+ return new Datasets({ baseUrl: process.env.SL_API_URL ?? DEFAULT_BASE_URL2 });
36482
+ }
36483
+ function parseFilters(pairs) {
36484
+ const out = {};
36485
+ for (const pair of pairs ?? []) {
36486
+ const eq = pair.indexOf("=");
36487
+ if (eq <= 0) {
36488
+ throw new Error(`invalid --filter "${pair}" (expected key=value)`);
36489
+ }
36490
+ out[pair.slice(0, eq)] = pair.slice(eq + 1);
36491
+ }
36492
+ return out;
36493
+ }
36494
+ function registerDatasetsCommand(program2) {
36495
+ const datasets = program2.command("datasets").description("Query Foundation Datasets (sBTC, BNS, PoX-4, STX transfers)");
36496
+ datasets.command("list").description("List the dataset catalog + freshness").action(async () => {
36497
+ try {
36498
+ const catalog = await client2().listDatasets();
36499
+ process.stdout.write(`${JSON.stringify(catalog, null, 2)}
36500
+ `);
36501
+ } catch (err) {
36502
+ error(`Failed to list datasets: ${err}`);
36503
+ process.exit(1);
36504
+ }
36505
+ });
36506
+ datasets.command("query <dataset>").description(`Query a cursor-paginated dataset (${Object.keys(CURSOR_SLUGS).join(", ")})`).option("--filter <key=value...>", "Filter as key=value (e.g. --filter stacker=SP… --filter address=SP…)").option("--limit <n>", "Rows per page").option("--cursor <cursor>", "Resume token from a previous next_cursor").option("--json", "Print the full envelope as JSON").action(async (dataset, options2) => {
36507
+ try {
36508
+ const params = parseFilters(options2.filter);
36509
+ if (options2.limit)
36510
+ params.limit = Number.parseInt(options2.limit, 10);
36511
+ if (options2.cursor)
36512
+ params.cursor = options2.cursor;
36513
+ const env = await client2().query(dataset, params);
36514
+ if (options2.json) {
36515
+ process.stdout.write(`${JSON.stringify(env, null, 2)}
36516
+ `);
36517
+ } else {
36518
+ process.stdout.write(`${JSON.stringify(env.rows, null, 2)}
36519
+ `);
36520
+ if (env.next_cursor) {
36521
+ info(`next_cursor: ${env.next_cursor}`);
36522
+ }
36523
+ }
36524
+ } catch (err) {
36525
+ error(`Failed to query dataset: ${err}`);
36526
+ process.exit(1);
36527
+ }
36528
+ });
36529
+ }
36356
36530
  // src/commands/doctor.ts
36357
36531
  init_config();
36358
36532
 
@@ -37203,7 +37377,7 @@ Quickstart:
37203
37377
  $ sl subgraphs deploy subgraphs/my-watcher.ts
37204
37378
  $ sl subgraphs status my-watcher
37205
37379
  `);
37206
- program.command("generate [files...]").aliases(["gen", "codegen"]).description("Generate TypeScript interfaces from Clarity contracts").option("-c, --config <path>", "Path to config file").option("-o, --out <path>", "Output file path (required when using direct files)").option("-k, --api-key <key>", "Stacks node API key for direct RPC URLs").option("-w, --watch", "Watch for changes").action(async (files, options3) => {
37380
+ program.command("generate [files...]").aliases(["gen"]).description("Generate TypeScript interfaces from Clarity contracts").option("-c, --config <path>", "Path to config file").option("-o, --out <path>", "Output file path (required when using direct files)").option("-k, --api-key <key>", "Stacks node API key for direct RPC URLs").option("-w, --watch", "Watch for changes").action(async (files, options3) => {
37207
37381
  const { generate: generate2 } = await Promise.resolve().then(() => (init_generate(), exports_generate));
37208
37382
  await generate2(files, options3);
37209
37383
  });
@@ -37219,6 +37393,7 @@ registerSubgraphsCommand(program);
37219
37393
  registerCreateCommand(program);
37220
37394
  registerSubscriptionsCommand(program);
37221
37395
  registerStreamsCommand(program);
37396
+ registerDatasetsCommand(program);
37222
37397
  registerStatusCommand(program);
37223
37398
  registerStackCommand(program);
37224
37399
  registerDbCommand(program);
@@ -37229,5 +37404,5 @@ registerAccountCommand(program);
37229
37404
  registerBillingCommand(program);
37230
37405
  program.parse();
37231
37406
 
37232
- //# debugId=D5912487DCB1DB8664756E2164756E21
37407
+ //# debugId=BC7D4D440C943F4464756E2164756E21
37233
37408
  //# sourceMappingURL=cli.js.map