@secondlayer/cli 3.5.6 → 3.6.1

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
@@ -25,6 +25,7 @@ sl instance create --plan hobby
25
25
  sl subgraphs scaffold SP1234ABCD.my-contract -o subgraphs/my-contract.ts
26
26
  sl subgraphs deploy subgraphs/my-contract.ts --start-block <recent-block>
27
27
  sl subgraphs query my-contract <table> --sort _block_height --order desc
28
+ sl subgraphs spec my-contract --format openapi -o openapi.json
28
29
  ```
29
30
 
30
31
  `sl subgraphs scaffold` writes the definition file, creates or updates
@@ -128,6 +129,8 @@ invocation. No long-lived key on disk.
128
129
  | `sl subgraphs list` | List deployed subgraphs |
129
130
  | `sl subgraphs status <name>` | Indexing progress, row counts, gaps |
130
131
  | `sl subgraphs query <name> <table>` | Query a subgraph table with filters, sort, pagination |
132
+ | `sl subgraphs spec <name> [--format openapi\|agent\|markdown] [-o <path>] [--server <url>]` | Export generated API docs for a deployed subgraph |
133
+ | `sl subgraphs inspect <file> [--format openapi\|agent\|markdown] [-o <path>] [--server <url>]` | Generate API docs from a local `defineSubgraph()` file before deploy |
131
134
  | `sl subgraphs reindex <name>` | Drop + re-process from the tip backwards |
132
135
  | `sl subgraphs backfill <name>` | Fill a specific block range |
133
136
  | `sl subgraphs stop <name>` | Pause processing |
@@ -136,6 +139,32 @@ invocation. No long-lived key on disk.
136
139
  | `sl subgraphs scaffold <SP...::contract> [-o <path>] [--no-install]` | Generate a starter subgraph from a deployed contract, write/amend `package.json`, and install dependencies unless skipped |
137
140
  | `sl subgraphs generate <name>` | Regenerate TS types for an existing subgraph |
138
141
 
142
+ #### Subgraph API specs
143
+
144
+ Use `spec` when the subgraph is already deployed to the active project
145
+ instance. Use `inspect` when you want the same documentation contract from a
146
+ local TypeScript definition before deploy.
147
+
148
+ ```bash
149
+ # Deployed subgraph docs from the active project instance
150
+ sl subgraphs spec token-transfers --format openapi -o openapi.json
151
+ sl subgraphs spec token-transfers --format agent
152
+ sl subgraphs spec token-transfers --format markdown -o API.md
153
+
154
+ # Local pre-deploy docs from a defineSubgraph() file
155
+ sl subgraphs inspect subgraphs/token-transfers.ts --format agent
156
+ sl subgraphs inspect subgraphs/token-transfers.ts --format openapi \
157
+ --server https://<your-slug>.secondlayer.tools
158
+ ```
159
+
160
+ Formats:
161
+
162
+ | Format | Use |
163
+ |---|---|
164
+ | `openapi` | OpenAPI 3.1 JSON for docs tooling, client generators, and API catalogs |
165
+ | `agent` | Compact JSON schema for agents that need table, column, filter, and endpoint context |
166
+ | `markdown` | Human-readable API reference for docs pages or repository docs |
167
+
139
168
  ### Local dev + OSS
140
169
 
141
170
  | Command | What it does |
package/dist/cli.js CHANGED
@@ -32439,7 +32439,7 @@ var {
32439
32439
  // package.json
32440
32440
  var package_default = {
32441
32441
  name: "@secondlayer/cli",
32442
- version: "3.5.6",
32442
+ version: "3.6.1",
32443
32443
  description: "CLI for subgraphs and blockchain indexing on Stacks",
32444
32444
  type: "module",
32445
32445
  bin: {
@@ -32482,8 +32482,8 @@ var package_default = {
32482
32482
  dependencies: {
32483
32483
  "@inquirer/prompts": "^8.2.0",
32484
32484
  "@secondlayer/bundler": "^0.3.2",
32485
- "@secondlayer/sdk": "^3.2.2",
32486
- "@secondlayer/shared": "^4.3.4",
32485
+ "@secondlayer/sdk": "^3.3.0",
32486
+ "@secondlayer/shared": "^4.4.0",
32487
32487
  "@secondlayer/stacks": "^2.0.0",
32488
32488
  "@secondlayer/subgraphs": "^1.3.2",
32489
32489
  "@biomejs/js-api": "^0.7.0",
@@ -32555,6 +32555,15 @@ async function listSubgraphsApi() {
32555
32555
  async function getSubgraphApi(name) {
32556
32556
  return (await getTenantClient()).subgraphs.get(name);
32557
32557
  }
32558
+ async function getSubgraphOpenApi(name, options) {
32559
+ return (await getTenantClient()).subgraphs.openapi(name, options);
32560
+ }
32561
+ async function getSubgraphAgentSchema(name, options) {
32562
+ return (await getTenantClient()).subgraphs.schema(name, options);
32563
+ }
32564
+ async function getSubgraphMarkdown(name, options) {
32565
+ return (await getTenantClient()).subgraphs.markdown(name, options);
32566
+ }
32558
32567
  async function reindexSubgraphApi(name, options) {
32559
32568
  return (await getTenantClient()).subgraphs.reindex(name, options);
32560
32569
  }
@@ -34599,6 +34608,91 @@ function parseStartBlockOption(value) {
34599
34608
  }
34600
34609
  return parsed;
34601
34610
  }
34611
+ function parseSubgraphSpecFormat(value) {
34612
+ const format = value ?? "openapi";
34613
+ if (format === "openapi" || format === "agent" || format === "markdown") {
34614
+ return format;
34615
+ }
34616
+ throw new Error("--format must be one of: openapi, agent, markdown");
34617
+ }
34618
+ function formatSubgraphSpecOutput(spec, format) {
34619
+ if (typeof spec === "string")
34620
+ return spec;
34621
+ if (format === "markdown")
34622
+ return String(spec);
34623
+ return `${JSON.stringify(spec, null, 2)}
34624
+ `;
34625
+ }
34626
+ async function writeOrPrintSubgraphSpec(spec, format, output) {
34627
+ const text = formatSubgraphSpecOutput(spec, format);
34628
+ if (!output) {
34629
+ process.stdout.write(text);
34630
+ return;
34631
+ }
34632
+ const outPath = resolve3(output);
34633
+ const dir = resolve3(outPath, "..");
34634
+ if (!existsSync3(dir))
34635
+ mkdirSync2(dir, { recursive: true });
34636
+ await writeTextFile(outPath, text);
34637
+ success(`Created ${outPath}`);
34638
+ }
34639
+ function createLocalSubgraphDetail(input2) {
34640
+ const tables = {};
34641
+ for (const [tableName, rawTable] of Object.entries(input2.schema)) {
34642
+ const table = rawTable;
34643
+ const columns = {};
34644
+ for (const [columnName, column] of Object.entries(table.columns ?? {})) {
34645
+ columns[columnName] = {
34646
+ type: column.type ?? "text",
34647
+ ...column.nullable && { nullable: true },
34648
+ ...column.indexed && { indexed: true },
34649
+ ...column.search && { searchable: true },
34650
+ ...column.default !== undefined && { default: column.default }
34651
+ };
34652
+ }
34653
+ columns._id = { type: "serial" };
34654
+ columns._block_height = { type: "bigint" };
34655
+ columns._tx_id = { type: "text" };
34656
+ columns._created_at = { type: "timestamp" };
34657
+ tables[tableName] = {
34658
+ endpoint: `/subgraphs/${input2.name}/${tableName}`,
34659
+ columns,
34660
+ rowCount: 0,
34661
+ example: `/subgraphs/${input2.name}/${tableName}?_sort=_block_height&_order=desc&_limit=10`,
34662
+ ...table.indexes && { indexes: table.indexes },
34663
+ ...table.uniqueKeys && { uniqueKeys: table.uniqueKeys }
34664
+ };
34665
+ }
34666
+ return {
34667
+ name: input2.name,
34668
+ version: input2.version ?? "0.0.0",
34669
+ schemaHash: input2.schemaHash,
34670
+ status: "local",
34671
+ lastProcessedBlock: 0,
34672
+ ...input2.description && { description: input2.description },
34673
+ sources: input2.sources,
34674
+ health: {
34675
+ totalProcessed: 0,
34676
+ totalErrors: 0,
34677
+ errorRate: 0,
34678
+ lastError: null,
34679
+ lastErrorAt: null
34680
+ },
34681
+ sync: {
34682
+ status: "synced",
34683
+ startBlock: 0,
34684
+ lastProcessedBlock: 0,
34685
+ chainTip: 0,
34686
+ blocksRemaining: 0,
34687
+ progress: 1,
34688
+ gaps: { count: 0, totalMissingBlocks: 0, ranges: [] },
34689
+ integrity: "complete"
34690
+ },
34691
+ tables,
34692
+ createdAt: new Date(0).toISOString(),
34693
+ updatedAt: new Date(0).toISOString()
34694
+ };
34695
+ }
34602
34696
  function readCliSubgraphsDependency() {
34603
34697
  const here = dirname4(fileURLToPath2(import.meta.url));
34604
34698
  const candidates = [
@@ -35003,6 +35097,66 @@ Table endpoints:`));
35003
35097
  handleApiError(err, "get subgraph status");
35004
35098
  }
35005
35099
  });
35100
+ subgraphs.command("spec <name>").description("Output API documentation for a deployed subgraph").option("--format <format>", "Output format: openapi, agent, markdown", "openapi").option("-o, --output <path>", "Write output to a file").option("--server <url>", "Override the server URL in generated docs").action(async (name, options2) => {
35101
+ try {
35102
+ const format = parseSubgraphSpecFormat(options2.format);
35103
+ const specOptions = options2.server ? { serverUrl: options2.server } : undefined;
35104
+ const spec = format === "openapi" ? await getSubgraphOpenApi(name, specOptions) : format === "agent" ? await getSubgraphAgentSchema(name, specOptions) : await getSubgraphMarkdown(name, specOptions);
35105
+ await writeOrPrintSubgraphSpec(spec, format, options2.output);
35106
+ } catch (err) {
35107
+ if (err instanceof Error && err.message.startsWith("--format")) {
35108
+ error(err.message);
35109
+ process.exit(1);
35110
+ }
35111
+ handleApiError(err, "generate subgraph spec");
35112
+ }
35113
+ });
35114
+ subgraphs.command("inspect <file>").description("Output API documentation for a local subgraph file").option("--format <format>", "Output format: openapi, agent, markdown", "agent").option("-o, --output <path>", "Write output to a file").option("--server <url>", "Override the server URL in generated docs").action(async (file, options2) => {
35115
+ try {
35116
+ const format = parseSubgraphSpecFormat(options2.format);
35117
+ const absPath = resolve3(file);
35118
+ if (!existsSync3(absPath)) {
35119
+ error(`File not found: ${absPath}`);
35120
+ process.exit(1);
35121
+ }
35122
+ const { readFile: readFile4 } = await import("node:fs/promises");
35123
+ const { bundleSubgraphCode } = await import("@secondlayer/bundler");
35124
+ const { generateSubgraphSQL } = await import("@secondlayer/subgraphs");
35125
+ const {
35126
+ generateSubgraphAgentSchema,
35127
+ generateSubgraphMarkdown,
35128
+ generateSubgraphOpenApi
35129
+ } = await import("@secondlayer/shared/subgraphs/spec");
35130
+ const source = await readFile4(absPath, "utf8");
35131
+ const bundled = await bundleSubgraphCode(source);
35132
+ const { hash } = generateSubgraphSQL({
35133
+ name: bundled.name,
35134
+ version: bundled.version,
35135
+ description: bundled.description,
35136
+ sources: bundled.sources,
35137
+ schema: bundled.schema,
35138
+ handlers: {}
35139
+ });
35140
+ const detail = createLocalSubgraphDetail({
35141
+ name: bundled.name,
35142
+ version: bundled.version,
35143
+ description: bundled.description,
35144
+ sources: bundled.sources,
35145
+ schema: bundled.schema,
35146
+ schemaHash: hash
35147
+ });
35148
+ const specOptions = options2.server ? { serverUrl: options2.server } : undefined;
35149
+ const spec = format === "openapi" ? generateSubgraphOpenApi(detail, specOptions) : format === "agent" ? generateSubgraphAgentSchema(detail, specOptions) : generateSubgraphMarkdown(detail, specOptions);
35150
+ await writeOrPrintSubgraphSpec(spec, format, options2.output);
35151
+ } catch (err) {
35152
+ if (err instanceof Error && err.message.startsWith("--format")) {
35153
+ error(err.message);
35154
+ process.exit(1);
35155
+ }
35156
+ error(`Failed to inspect subgraph: ${err}`);
35157
+ process.exit(1);
35158
+ }
35159
+ });
35006
35160
  subgraphs.command("reindex <name>").description("Reindex a subgraph from historical blocks").option("--from <block>", "Start block height").option("--to <block>", "End block height").action(async (name, options2) => {
35007
35161
  try {
35008
35162
  info(`Reindexing subgraph "${name}"...`);
@@ -36487,6 +36641,11 @@ function handleInstanceError(err, action) {
36487
36641
  error("This project already has an instance. Run `sl instance info` to see it.");
36488
36642
  process.exit(1);
36489
36643
  }
36644
+ if (err.code === "PROVISIONER_REJECTED" || err.code === "INSTANCE_RECORD_FAILED" || err.code === "INSTANCE_PROVISION_FAILED") {
36645
+ error(err.message || `Failed to ${action}.`);
36646
+ error("Run: sl instance info before retrying.");
36647
+ process.exit(1);
36648
+ }
36490
36649
  error(err.message || `Failed to ${action}.`);
36491
36650
  process.exit(1);
36492
36651
  }
@@ -36638,5 +36797,5 @@ registerLocalCommand(program);
36638
36797
  registerAccountCommand(program);
36639
36798
  program.parse();
36640
36799
 
36641
- //# debugId=CA129D1D7E8ADE9A64756E2164756E21
36800
+ //# debugId=EA9BC913F2D4968D64756E2164756E21
36642
36801
  //# sourceMappingURL=cli.js.map