@secondlayer/cli 8.8.0 → 8.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -104,8 +104,8 @@ scoped reads and cannot mint (403). Minted keys inherit your plan's tier.
104
104
 
105
105
  | Command | What it does |
106
106
  |---|---|
107
- | `sl index ft-transfers` / `nft-transfers` / `events --event-type <t>` / `contract-calls` | Decoded L2 layer. Anonymous reads OK; free-tier keys rejected (Build+ for keyed) |
108
- | `sl streams tip` / `events` / `consume` / `reorgs` / `canonical <h>` / `pull` | Raw L1 event firehose. **Requires `SL_API_KEY`** |
107
+ | `sl index ft-transfers` / `nft-transfers` / `events --event-type <t>` / `contract-calls` | Decoded Index layer. Anonymous reads OK; free-tier keys rejected (Build+ for keyed) |
108
+ | `sl streams tip` / `events` / `consume` / `reorgs` / `canonical <h>` / `pull` | Raw chain event firehose. **Requires `SL_API_KEY`** |
109
109
 
110
110
  Reads emit JSON to stdout (`--json` accepted across all read commands); `-o/--output` is a file path, not a format.
111
111
 
package/dist/cli.js CHANGED
@@ -18083,8 +18083,8 @@ var require_pattern = __commonJS((exports) => {
18083
18083
  }
18084
18084
  exports.endsWithSlashGlobStar = endsWithSlashGlobStar;
18085
18085
  function isAffectDepthOfReadingPattern(pattern) {
18086
- const basename = path3.basename(pattern);
18087
- return endsWithSlashGlobStar(pattern) || isStaticPattern(basename);
18086
+ const basename2 = path3.basename(pattern);
18087
+ return endsWithSlashGlobStar(pattern) || isStaticPattern(basename2);
18088
18088
  }
18089
18089
  exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
18090
18090
  function expandPatternsWithBraceExpansion(patterns) {
@@ -31588,8 +31588,8 @@ async function parseInputs(inputs) {
31588
31588
  };
31589
31589
  }
31590
31590
  function deriveContractName(filePath) {
31591
- const basename = path11.basename(filePath, ".clar");
31592
- return basename.replace(/[-_](.)/g, (_2, char) => char.toUpperCase()).replace(/^(.)/, (_2, char) => char.toLowerCase()).replace(/^\d/, "_$&");
31591
+ const basename2 = path11.basename(filePath, ".clar");
31592
+ return basename2.replace(/[-_](.)/g, (_2, char) => char.toUpperCase()).replace(/^(.)/, (_2, char) => char.toLowerCase()).replace(/^\d/, "_$&");
31593
31593
  }
31594
31594
  async function buildConfigFromInputs(parsedInputs, outPath, apiKey, defaultAddress) {
31595
31595
  const contracts = [];
@@ -31811,7 +31811,7 @@ var {
31811
31811
  // package.json
31812
31812
  var package_default = {
31813
31813
  name: "@secondlayer/cli",
31814
- version: "8.8.0",
31814
+ version: "8.10.0",
31815
31815
  repository: {
31816
31816
  type: "git",
31817
31817
  url: "git+https://github.com/ryanwaits/secondlayer.git",
@@ -31860,11 +31860,11 @@ var package_default = {
31860
31860
  dependencies: {
31861
31861
  "@inquirer/prompts": "^8.2.0",
31862
31862
  "@secondlayer/bundler": "^0.3.7",
31863
- "@secondlayer/scaffold": "^1.2.0",
31864
- "@secondlayer/sdk": "^6.21.1",
31865
- "@secondlayer/shared": "^6.30.0",
31863
+ "@secondlayer/scaffold": "^1.3.0",
31864
+ "@secondlayer/sdk": "^6.22.0",
31865
+ "@secondlayer/shared": "^6.32.0",
31866
31866
  "@secondlayer/stacks": "^2.5.1",
31867
- "@secondlayer/subgraphs": "^3.11.0",
31867
+ "@secondlayer/subgraphs": "^3.14.0",
31868
31868
  "@biomejs/js-api": "^0.7.0",
31869
31869
  "@biomejs/wasm-nodejs": "^1.9.0",
31870
31870
  esbuild: "^0.19.0",
@@ -32041,6 +32041,9 @@ async function querySubgraphTableCount(name, table, params = {}) {
32041
32041
  async function getSubgraphGaps(name, opts) {
32042
32042
  return (await getPlatformClient()).subgraphs.gaps(name, opts);
32043
32043
  }
32044
+ async function getContractPrintSchema(contractId) {
32045
+ return (await getPlatformClient()).index.printSchema(contractId);
32046
+ }
32044
32047
  async function getAccountProfile() {
32045
32048
  return httpPlatform("/api/accounts/me");
32046
32049
  }
@@ -34109,9 +34112,13 @@ import {
34109
34112
  watch,
34110
34113
  writeFileSync as writeFileSync4
34111
34114
  } from "node:fs";
34112
- import { dirname as dirname5, join as join7, resolve as resolve2 } from "node:path";
34115
+ import { basename, dirname as dirname5, extname, join as join7, resolve as resolve2 } from "node:path";
34113
34116
  import { fileURLToPath as fileURLToPath2 } from "node:url";
34114
34117
  import { confirm as confirm3 } from "@inquirer/prompts";
34118
+ import {
34119
+ generatePrintPayloadTypes,
34120
+ generatePrintSchemaSubgraph
34121
+ } from "@secondlayer/scaffold";
34115
34122
  import { ByoBreakingChangeError } from "@secondlayer/sdk";
34116
34123
  import { TRAIT_STANDARDS } from "@secondlayer/stacks/clarity";
34117
34124
 
@@ -34875,6 +34882,7 @@ function decodeBuffUtf8(value: unknown): string | null {
34875
34882
 
34876
34883
  // src/commands/subgraphs.ts
34877
34884
  init_api();
34885
+ init_format();
34878
34886
  async function loadSubgraphWithDepCheck(absPath) {
34879
34887
  try {
34880
34888
  return await import(absPath);
@@ -34933,6 +34941,84 @@ function parseVisibilityOption(value) {
34933
34941
  return value;
34934
34942
  throw new Error('--visibility must be "public" or "private"');
34935
34943
  }
34944
+ function collectPinnedPrintSources(sources) {
34945
+ const out = [];
34946
+ for (const [sourceName, raw] of Object.entries(sources ?? {})) {
34947
+ const s = raw;
34948
+ if (s?.type !== "print_event")
34949
+ continue;
34950
+ if (typeof s.contractId !== "string" || s.contractId.length === 0)
34951
+ continue;
34952
+ if (s.trait)
34953
+ continue;
34954
+ out.push({
34955
+ sourceName,
34956
+ contractId: s.contractId,
34957
+ ...typeof s.topic === "string" ? { topic: s.topic } : {}
34958
+ });
34959
+ }
34960
+ return out;
34961
+ }
34962
+ var ORM_ONLY_CODEGEN_FLAGS = {
34963
+ target: "--target",
34964
+ schema: "--schema",
34965
+ env: "--env",
34966
+ modelsOnly: "--models-only"
34967
+ };
34968
+ function ormFlagsConflictingWithPayloads(getOptionValueSource) {
34969
+ return Object.entries(ORM_ONLY_CODEGEN_FLAGS).filter(([key]) => getOptionValueSource(key) === "cli").map(([, flag]) => flag);
34970
+ }
34971
+ async function runPayloadsCodegen(file, output2) {
34972
+ const absPath = resolve2(file);
34973
+ if (!existsSync4(absPath)) {
34974
+ error(`File not found: ${absPath}`);
34975
+ process.exit(1);
34976
+ }
34977
+ const { readFile: readFile3 } = await import("node:fs/promises");
34978
+ const { bundleSubgraphCode } = await import("@secondlayer/bundler");
34979
+ const source = await readFile3(absPath, "utf8");
34980
+ const bundled = await bundleSubgraphCode(source);
34981
+ const pinned = collectPinnedPrintSources(bundled.sources ?? {});
34982
+ if (pinned.length === 0) {
34983
+ error("No pinned print_event sources (contractId set, no trait) — nothing to generate payload types for.");
34984
+ process.exit(1);
34985
+ }
34986
+ const schemas = new Map;
34987
+ for (const contractId of new Set(pinned.map((p) => p.contractId))) {
34988
+ info(`Fetching print schema for ${contractId}...`);
34989
+ try {
34990
+ schemas.set(contractId, await getContractPrintSchema(contractId));
34991
+ } catch (err) {
34992
+ handleApiError(err, "fetch contract print schema");
34993
+ }
34994
+ }
34995
+ const sources = [];
34996
+ for (const p of pinned) {
34997
+ const schema = schemas.get(p.contractId);
34998
+ if (!schema || schema.topics.length === 0) {
34999
+ warn(`No print events observed for ${p.contractId} — skipping source "${p.sourceName}"`);
35000
+ continue;
35001
+ }
35002
+ const topics = p.topic ? schema.topics.filter((t) => t.topic === p.topic) : schema.topics;
35003
+ if (topics.length === 0) {
35004
+ warn(`Topic "${p.topic}" never observed on ${p.contractId} — skipping source "${p.sourceName}"`);
35005
+ continue;
35006
+ }
35007
+ sources.push({
35008
+ sourceName: p.sourceName,
35009
+ contractId: p.contractId,
35010
+ topics
35011
+ });
35012
+ }
35013
+ if (sources.length === 0) {
35014
+ error("No print schemas available for any pinned source. Check the contract ids and that the contracts have emitted print events.");
35015
+ process.exit(1);
35016
+ }
35017
+ const out = generatePrintPayloadTypes({ sources });
35018
+ const outPath = resolve2(output2 ?? join7(dirname5(absPath), `${basename(absPath, extname(absPath))}.payloads.d.ts`));
35019
+ await writeTextFile(outPath, out);
35020
+ success(`Wrote print payload types to ${outPath}`);
35021
+ }
34936
35022
  function parseSubgraphSpecFormat(value) {
34937
35023
  const format = value ?? "openapi";
34938
35024
  if (format === "openapi" || format === "agent" || format === "markdown") {
@@ -35175,11 +35261,37 @@ Grant (first deploy only):`);
35175
35261
  ${yellow("This DROPS all rows in that schema, then rebuilds it. Re-deploy after.")}`);
35176
35262
  console.log(dim("(--force destructive rebuild not yet supported — manual DROP required.)"));
35177
35263
  }
35264
+ function formatDuration(seconds) {
35265
+ if (seconds < 90)
35266
+ return `${Math.max(1, Math.round(seconds))}s`;
35267
+ if (seconds < 5400)
35268
+ return `${Math.round(seconds / 60)}m`;
35269
+ return `${(seconds / 3600).toFixed(1)}h`;
35270
+ }
35178
35271
  function formatSubgraphSync(sync) {
35179
35272
  const targetBlock = sync.targetBlock ?? sync.chainTip;
35180
35273
  const totalBlocks = sync.totalBlocks;
35181
35274
  const processedBlocks = sync.processedBlocks;
35182
35275
  const progress = `${(sync.progress * 100).toFixed(1)}%`;
35276
+ if (sync.queue) {
35277
+ const pos = sync.queue.position != null ? `position ~${sync.queue.position}` : "queued";
35278
+ const est = sync.queue.estimatedStartSeconds != null ? `, est. start ~${formatDuration(sync.queue.estimatedStartSeconds)}` : "";
35279
+ const denom = sync.queue.estimatedEvents != null ? ` · ~${sync.queue.estimatedEvents.toLocaleString()} events to sync` : "";
35280
+ return {
35281
+ line: `queued — ${pos}${est}${denom}`,
35282
+ remainingLabel: "Queue",
35283
+ remaining: sync.queue.position != null ? `~${sync.queue.position} ahead` : "N/A"
35284
+ };
35285
+ }
35286
+ if (sync.estimatedEvents != null && sync.estimatedEvents > 0 && sync.processedEvents != null) {
35287
+ const pct = `${(sync.processedEvents / sync.estimatedEvents * 100).toFixed(1)}%`;
35288
+ const eta = sync.etaSeconds != null ? ` · est ${formatDuration(sync.etaSeconds)}` : "";
35289
+ return {
35290
+ line: `${sync.status} — ${pct} of ~${sync.estimatedEvents.toLocaleString()} events${eta}`,
35291
+ remainingLabel: "Events Remaining",
35292
+ remaining: `${Math.max(0, sync.estimatedEvents - sync.processedEvents).toLocaleString()}`
35293
+ };
35294
+ }
35183
35295
  if (sync.status === "reindexing" || sync.mode === "reindex") {
35184
35296
  if (targetBlock <= 0) {
35185
35297
  return {
@@ -35203,12 +35315,22 @@ function formatSubgraphSync(sync) {
35203
35315
  }
35204
35316
  function registerSubgraphsCommand(program2) {
35205
35317
  const subgraphs = program2.command("subgraphs").description("Manage materialized subgraphs");
35206
- subgraphs.command("create <name>").description("Create a new subgraph definition file").option("--template <slug>", `Foundation Dataset starter (one of: ${SUBGRAPH_TEMPLATE_SLUGS.join(", ")})`).addHelpText("after", `
35318
+ subgraphs.command("create <name>").description("Create a new subgraph definition file").option("--template <slug>", `Starter template (one of: ${SUBGRAPH_TEMPLATE_SLUGS.join(", ")})`).option("--from-contract <contractId>", "Generate sources/schema/handlers from the contract's observed print events (requires network)").option("--table-per-topic", "With --from-contract: one table per print topic instead of a single wide table").addHelpText("after", `
35207
35319
  Examples:
35208
35320
  $ sl subgraphs create my-graph
35209
- $ sl subgraphs create token-balances --template sip-010-balances`).action(async (name, opts) => {
35321
+ $ sl subgraphs create token-balances --template sip-010-balances
35322
+ $ sl subgraphs create sbtc-flows --from-contract SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-registry
35323
+ $ sl subgraphs create sbtc-flows --from-contract SM3....sbtc-registry --table-per-topic`).action(async (name, opts) => {
35324
+ if (opts.fromContract && opts.template) {
35325
+ error("--from-contract and --template are mutually exclusive");
35326
+ process.exit(1);
35327
+ }
35328
+ if (opts.tablePerTopic && !opts.fromContract) {
35329
+ error("--table-per-topic requires --from-contract");
35330
+ process.exit(1);
35331
+ }
35210
35332
  const slug = opts.template ?? "basic";
35211
- if (!SUBGRAPH_TEMPLATE_SLUGS.includes(slug)) {
35333
+ if (!opts.fromContract && !SUBGRAPH_TEMPLATE_SLUGS.includes(slug)) {
35212
35334
  error(`Unknown template "${opts.template}". Available templates:
35213
35335
  ${SUBGRAPH_TEMPLATE_SLUGS.map((s) => ` ${s.padEnd(20)} ${SUBGRAPH_TEMPLATE_DESCRIPTIONS[s]}`).join(`
35214
35336
  `)}`);
@@ -35220,13 +35342,37 @@ ${SUBGRAPH_TEMPLATE_SLUGS.map((s) => ` ${s.padEnd(20)} ${SUBGRAPH_TEMPLATE_DESC
35220
35342
  error(`File already exists: ${filePath}`);
35221
35343
  process.exit(1);
35222
35344
  }
35345
+ let content;
35346
+ if (opts.fromContract) {
35347
+ info(`Fetching print schema for ${opts.fromContract}...`);
35348
+ let schema;
35349
+ try {
35350
+ schema = await getContractPrintSchema(opts.fromContract);
35351
+ } catch (err) {
35352
+ handleApiError(err, "fetch contract print schema");
35353
+ }
35354
+ if (!schema || schema.topics.length === 0) {
35355
+ error(`No print events observed for ${opts.fromContract}.
35356
+ Check the contract id, and that the contract has emitted print events (schemas are inferred from indexed on-chain events).`);
35357
+ process.exit(1);
35358
+ }
35359
+ content = await formatCode(generatePrintSchemaSubgraph({
35360
+ contractId: opts.fromContract,
35361
+ name,
35362
+ topics: schema.topics,
35363
+ tablePerTopic: opts.tablePerTopic
35364
+ }));
35365
+ } else {
35366
+ content = generateSubgraphTemplate(name, slug);
35367
+ }
35223
35368
  if (!existsSync4(dir)) {
35224
35369
  mkdirSync3(dir, { recursive: true });
35225
35370
  }
35226
- const content = generateSubgraphTemplate(name, slug);
35227
35371
  await writeTextFile(filePath, content);
35228
35372
  success(`Created ${filePath}`);
35229
- if (slug !== "basic") {
35373
+ if (opts.fromContract) {
35374
+ info(`Schema inferred from ${opts.fromContract} print events (${opts.tablePerTopic ? "table per topic" : "single wide table"})`);
35375
+ } else if (slug !== "basic") {
35230
35376
  info(`Template: ${slug} — ${SUBGRAPH_TEMPLATE_DESCRIPTIONS[slug]}`);
35231
35377
  }
35232
35378
  info(`Next: sl subgraphs deploy subgraphs/${name}.ts`);
@@ -35292,7 +35438,7 @@ Stopped watching.`);
35292
35438
  });
35293
35439
  await new Promise(() => {});
35294
35440
  });
35295
- 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("--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, options2) => {
35441
+ 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, options2) => {
35296
35442
  try {
35297
35443
  const absPath = resolve2(file);
35298
35444
  const config = await loadConfig();
@@ -35321,7 +35467,10 @@ Stopped watching.`);
35321
35467
  info(`Loading subgraph from ${absPath}`);
35322
35468
  const mod = await loadSubgraphWithDepCheck(absPath);
35323
35469
  const def = mod.default ?? mod;
35324
- const effectiveDef = startBlock === undefined ? def : { ...def, startBlock };
35470
+ const effectiveDef = {
35471
+ ...startBlock === undefined ? def : { ...def, startBlock },
35472
+ ...options2.tipFirst ? { backfillMode: "concurrent" } : {}
35473
+ };
35325
35474
  const { validateSubgraphDefinition } = await import("@secondlayer/subgraphs/validate");
35326
35475
  const validated = validateSubgraphDefinition(effectiveDef);
35327
35476
  if (config.network !== "local") {
@@ -35363,6 +35512,8 @@ Stopped watching.`);
35363
35512
  if (result.start_block_clamped) {
35364
35513
  info(` Free tier indexes forward from deploy (start block ${result.start_block}) — upgrade for genesis backfill.`);
35365
35514
  }
35515
+ for (const w of result.warnings ?? [])
35516
+ warn(w);
35366
35517
  const printDeployFooter = async () => {
35367
35518
  try {
35368
35519
  const { apiUrl } = await resolveAuth();
@@ -35583,7 +35734,16 @@ Synced — exiting watch.`));
35583
35734
  handleApiError(err, "generate subgraph spec");
35584
35735
  }
35585
35736
  });
35586
- subgraphs.command("codegen <file>").description("Generate an ORM schema (Prisma, Drizzle, or Kysely) for a subgraph's tables — point it at your BYO database").option("--target <orm>", "ORM target: prisma | drizzle | kysely", "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) => {
35737
+ subgraphs.command("codegen <file>").description("Generate an ORM schema (Prisma, Drizzle, or Kysely) for a subgraph's tables — point it at your BYO database").option("--target <orm>", "ORM target: prisma | drizzle | kysely", "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("--payloads", "Emit a .d.ts of print payload types for pinned print_event sources, inferred from observed on-chain events (requires network)").option("-o, --output <path>", "Write to a file (defaults to stdout)").action(async (file, options2, command) => {
35738
+ if (options2.payloads) {
35739
+ const conflicts = ormFlagsConflictingWithPayloads((key) => command.getOptionValueSource(key));
35740
+ if (conflicts.length > 0) {
35741
+ error(`--payloads cannot be combined with ${conflicts.join(", ")} — those flags apply to ORM targets only.`);
35742
+ process.exit(1);
35743
+ }
35744
+ await runPayloadsCodegen(file, options2.output);
35745
+ return;
35746
+ }
35587
35747
  try {
35588
35748
  const target = options2.target ?? "prisma";
35589
35749
  if (target !== "prisma" && target !== "drizzle" && target !== "kysely") {
@@ -36228,7 +36388,7 @@ function emitOne(doc, label, json) {
36228
36388
  });
36229
36389
  }
36230
36390
  function registerIndexCommand(program2) {
36231
- const index = program2.command("index").description("Query the decoded Index layer (L2 events + contract calls)");
36391
+ const index = program2.command("index").description("Query the decoded Index layer (events + contract calls)");
36232
36392
  index.command("codegen").description("Generate a typed schema (Prisma, Kysely, Drizzle, or JSON-Schema) for the Index domain tables — point it at your BYO database mirror").option("--target <orm>", "prisma | kysely | drizzle | json-schema", "kysely").option("--schema <name>", "Postgres schema to qualify table names with").option("--tables <list>", "Comma-separated subset of Index tables (default: all)").option("--env <var>", "Prisma datasource url env var", "DATABASE_URL").option("-o, --output <path>", "Write to a file (defaults to stdout)").action(async (o) => {
36233
36393
  try {
36234
36394
  const target = o.target ?? "kysely";
@@ -37934,5 +38094,5 @@ registerAccountCommand(program);
37934
38094
  registerKeysCommand(program);
37935
38095
  program.parse();
37936
38096
 
37937
- //# debugId=B164F591CA98B53C64756E2164756E21
38097
+ //# debugId=D198B46A9AA7918164756E2164756E21
37938
38098
  //# sourceMappingURL=cli.js.map