@secondlayer/cli 3.5.2 → 3.5.4

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
@@ -2530,7 +2530,7 @@ async function request(url, opts) {
2530
2530
  method: opts.method ?? "GET",
2531
2531
  headers,
2532
2532
  body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
2533
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
2533
+ signal: AbortSignal.timeout(opts.timeoutMs ?? REQUEST_TIMEOUT_MS)
2534
2534
  });
2535
2535
  if (!res.ok) {
2536
2536
  let body = {};
@@ -2957,7 +2957,8 @@ function success(message) {
2957
2957
  console.log(green(`✓ ${message}`));
2958
2958
  }
2959
2959
  function error(message) {
2960
- console.error(red(`✗ ${message}`));
2960
+ const text = message.trim() || "Command failed.";
2961
+ console.error(red(`✗ ${text}`));
2961
2962
  }
2962
2963
  function warn(message) {
2963
2964
  console.log(yellow(`⚠ ${message}`));
@@ -2966,17 +2967,17 @@ function info(message) {
2966
2967
  console.log(blue(`ℹ ${message}`));
2967
2968
  }
2968
2969
  function stripAnsi(text) {
2969
- return text.replace(/\x1b\[[0-9;]*m/g, "");
2970
+ return text.replace(ANSI_ESCAPE_PATTERN, "");
2970
2971
  }
2971
2972
  function formatTable(headers, rows) {
2972
2973
  const widths = headers.map((h, i) => {
2973
2974
  const colValues = [h, ...rows.map((r) => r[i] || "")];
2974
2975
  return Math.max(...colValues.map((v) => stripAnsi(v).length));
2975
2976
  });
2976
- const headerRow = headers.map((h, i) => h.padEnd(widths[i])).join(" ");
2977
+ const headerRow = headers.map((h, i) => h.padEnd(widths[i] ?? 0)).join(" ");
2977
2978
  const separator = widths.map((w) => "-".repeat(w)).join(" ");
2978
2979
  const dataRows = rows.map((row) => row.map((cell, i) => {
2979
- const width = widths[i];
2980
+ const width = widths[i] ?? 0;
2980
2981
  const padding = width - stripAnsi(cell).length;
2981
2982
  return cell + " ".repeat(Math.max(0, padding));
2982
2983
  }).join(" "));
@@ -2988,7 +2989,7 @@ function formatKeyValue(pairs) {
2988
2989
  return pairs.map(([key, value]) => `${dim(key.padEnd(maxKeyLen))} ${value}`).join(`
2989
2990
  `);
2990
2991
  }
2991
- var colors;
2992
+ var colors, ANSI_ESCAPE_PATTERN;
2992
2993
  var init_output = __esm(() => {
2993
2994
  colors = {
2994
2995
  reset: "\x1B[0m",
@@ -3001,6 +3002,7 @@ var init_output = __esm(() => {
3001
3002
  dim: "\x1B[2m",
3002
3003
  bold: "\x1B[1m"
3003
3004
  };
3005
+ ANSI_ESCAPE_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
3004
3006
  });
3005
3007
 
3006
3008
  // src/lib/docker.ts
@@ -13464,10 +13466,10 @@ var init_manager = __esm(() => {
13464
13466
  });
13465
13467
 
13466
13468
  // src/services/indexer.ts
13467
- import { dirname as dirname4, resolve as resolve4 } from "node:path";
13469
+ import { dirname as dirname5, resolve as resolve4 } from "node:path";
13468
13470
  async function startIndexer(options2) {
13469
13471
  const port = options2.port ?? 3700;
13470
- const rootDir = dirname4(dirname4(dirname4(dirname4(import.meta.dir))));
13472
+ const rootDir = dirname5(dirname5(dirname5(dirname5(import.meta.dir))));
13471
13473
  const indexerPath = resolve4(rootDir, "packages/indexer/src/index.ts");
13472
13474
  await serviceManager.start(SERVICE_NAME, ["bun", "run", "--watch", indexerPath], {
13473
13475
  port,
@@ -13484,9 +13486,9 @@ var init_indexer = __esm(() => {
13484
13486
  });
13485
13487
 
13486
13488
  // src/services/worker.ts
13487
- import { dirname as dirname5, resolve as resolve5 } from "node:path";
13489
+ import { dirname as dirname6, resolve as resolve5 } from "node:path";
13488
13490
  async function startWorker(options2) {
13489
- const rootDir = dirname5(dirname5(dirname5(dirname5(import.meta.dir))));
13491
+ const rootDir = dirname6(dirname6(dirname6(dirname6(import.meta.dir))));
13490
13492
  const workerPath = resolve5(rootDir, "packages/worker/src/index.ts");
13491
13493
  await serviceManager.start(SERVICE_NAME2, ["bun", "run", "--watch", workerPath], {
13492
13494
  onStdout: options2.onLog,
@@ -13499,10 +13501,10 @@ var init_worker = __esm(() => {
13499
13501
  });
13500
13502
 
13501
13503
  // src/services/api.ts
13502
- import { dirname as dirname6, resolve as resolve6 } from "node:path";
13504
+ import { dirname as dirname7, resolve as resolve6 } from "node:path";
13503
13505
  async function startApi(options2) {
13504
13506
  const port = options2.port ?? 3800;
13505
- const rootDir = dirname6(dirname6(dirname6(dirname6(import.meta.dir))));
13507
+ const rootDir = dirname7(dirname7(dirname7(dirname7(import.meta.dir))));
13506
13508
  const apiPath = resolve6(rootDir, "packages/api/src/index.ts");
13507
13509
  await serviceManager.start(SERVICE_NAME3, ["bun", "run", "--watch", apiPath], {
13508
13510
  port,
@@ -13519,9 +13521,9 @@ var init_api2 = __esm(() => {
13519
13521
  });
13520
13522
 
13521
13523
  // src/services/subgraph-processor.ts
13522
- import { dirname as dirname7, resolve as resolve7 } from "node:path";
13524
+ import { dirname as dirname8, resolve as resolve7 } from "node:path";
13523
13525
  async function startSubgraphProcessor(options2) {
13524
- const rootDir = dirname7(dirname7(dirname7(dirname7(import.meta.dir))));
13526
+ const rootDir = dirname8(dirname8(dirname8(dirname8(import.meta.dir))));
13525
13527
  const servicePath = resolve7(rootDir, "packages/subgraphs/src/service.ts");
13526
13528
  await serviceManager.start(SERVICE_NAME4, ["bun", "run", "--watch", servicePath], {
13527
13529
  env: {
@@ -13557,7 +13559,7 @@ __export(exports_dev_impl, {
13557
13559
  isDevAlreadyRunning: () => isDevAlreadyRunning
13558
13560
  });
13559
13561
  import { mkdirSync as mkdirSync3 } from "node:fs";
13560
- import { dirname as dirname8, join as join7, resolve as resolve8 } from "node:path";
13562
+ import { dirname as dirname9, join as join8, resolve as resolve8 } from "node:path";
13561
13563
  async function isDevAlreadyRunning() {
13562
13564
  if (await isDevRunning()) {
13563
13565
  const running = await getRunningServices();
@@ -13635,7 +13637,7 @@ async function runBackground(options2) {
13635
13637
  startedAt: new Date().toISOString()
13636
13638
  };
13637
13639
  try {
13638
- const packagesDir = dirname8(dirname8(dirname8(dirname8(import.meta.dir))));
13640
+ const packagesDir = dirname9(dirname9(dirname9(dirname9(import.meta.dir))));
13639
13641
  const env = { DATABASE_URL: databaseUrl, DEV_MODE: "true" };
13640
13642
  const apiLogFile = getLogFile("api");
13641
13643
  const apiProc = Bun.spawn(["bun", "run", resolve8(packagesDir, "packages/api/src/index.ts")], {
@@ -13982,7 +13984,7 @@ async function restartDev() {
13982
13984
  await clearDevState();
13983
13985
  await clearLogs();
13984
13986
  const config = await loadConfig();
13985
- const packagesDir = dirname8(dirname8(dirname8(dirname8(import.meta.dir))));
13987
+ const packagesDir = dirname9(dirname9(dirname9(dirname9(import.meta.dir))));
13986
13988
  const env = state.env;
13987
13989
  const newState = {
13988
13990
  services: {},
@@ -14163,7 +14165,7 @@ async function ensureDevPostgres(dataDir) {
14163
14165
  if (check.stdout.toString().trim()) {
14164
14166
  return false;
14165
14167
  }
14166
- const pgDataDir = join7(dataDir, "postgres");
14168
+ const pgDataDir = join8(dataDir, "postgres");
14167
14169
  mkdirSync3(pgDataDir, { recursive: true });
14168
14170
  const stopped = await Bun.$`docker ps -aq -f name=secondlayer-dev-postgres`.quiet().nothrow();
14169
14171
  if (stopped.stdout.toString().trim()) {
@@ -14179,7 +14181,7 @@ async function ensureDevPostgres(dataDir) {
14179
14181
  throw new Error("PostgreSQL failed to start");
14180
14182
  }
14181
14183
  async function runMigrations(databaseUrl) {
14182
- const packagesDir = dirname8(dirname8(dirname8(dirname8(import.meta.dir))));
14184
+ const packagesDir = dirname9(dirname9(dirname9(dirname9(import.meta.dir))));
14183
14185
  const migrateScript = resolve8(packagesDir, "packages/shared/src/db/migrate.ts");
14184
14186
  const result = await Bun.$`DATABASE_URL=${databaseUrl} bun run ${migrateScript}`.quiet().nothrow();
14185
14187
  if (result.exitCode !== 0) {
@@ -21907,7 +21909,7 @@ var init_commands = __esm(() => {
21907
21909
  });
21908
21910
 
21909
21911
  // ../../node_modules/@antfu/ni/dist/shared/ni.B5qNAuoI.mjs
21910
- import path3, { join as join8, dirname as dirname9, resolve as resolve9 } from "node:path";
21912
+ import path3, { join as join9, dirname as dirname10, resolve as resolve9 } from "node:path";
21911
21913
  import process$1 from "node:process";
21912
21914
  import require$$0 from "readline";
21913
21915
  import require$$2 from "events";
@@ -24922,7 +24924,7 @@ function requireLib() {
24922
24924
  return lib;
24923
24925
  hasRequiredLib = 1;
24924
24926
  const { isexe, sync: isexeSync } = requireCjs();
24925
- const { join: join9, delimiter, sep, posix: posix2 } = require$$1$2;
24927
+ const { join: join10, delimiter, sep, posix: posix2 } = require$$1$2;
24926
24928
  const isWindows = process.platform === "win32";
24927
24929
  const rSlash = new RegExp(`[${posix2.sep}${sep === posix2.sep ? "" : sep}]`.replace(/(\\)/g, "\\$1"));
24928
24930
  const rRel = new RegExp(`^\\.${rSlash.source}`);
@@ -24949,7 +24951,7 @@ function requireLib() {
24949
24951
  const getPathPart = (raw, cmd) => {
24950
24952
  const pathPart = /^".*"$/.test(raw) ? raw.slice(1, -1) : raw;
24951
24953
  const prefix = !pathPart && rRel.test(cmd) ? cmd.slice(0, 2) : "";
24952
- return prefix + join9(pathPart, cmd);
24954
+ return prefix + join10(pathPart, cmd);
24953
24955
  };
24954
24956
  const which = async (cmd, opt = {}) => {
24955
24957
  const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
@@ -25098,7 +25100,7 @@ var init_ni_B5qNAuoI = __esm(() => {
25098
25100
  options2 = {};
25099
25101
  libExports = requireLib();
25100
25102
  which = /* @__PURE__ */ getDefaultExportFromCjs(libExports);
25101
- CLI_TEMP_DIR = join8(os4.tmpdir(), "antfu-ni");
25103
+ CLI_TEMP_DIR = join9(os4.tmpdir(), "antfu-ni");
25102
25104
  customRcPath = process$1.env.NI_CONFIG_FILE;
25103
25105
  home = process$1.platform === "win32" ? process$1.env.USERPROFILE : process$1.env.HOME;
25104
25106
  defaultRcPath = path3.join(home || "~/", ".nirc");
@@ -25135,14 +25137,14 @@ function isPlainObject2(value) {
25135
25137
  }
25136
25138
 
25137
25139
  // ../../node_modules/execa/lib/arguments/file-url.js
25138
- import { fileURLToPath as fileURLToPath2 } from "node:url";
25140
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
25139
25141
  var safeNormalizeFileUrl = (file, name) => {
25140
25142
  const fileString = normalizeFileUrl(normalizeDenoExecPath(file));
25141
25143
  if (typeof fileString !== "string") {
25142
25144
  throw new TypeError(`${name} must be a string or a file URL: ${fileString}.`);
25143
25145
  }
25144
25146
  return fileString;
25145
- }, normalizeDenoExecPath = (file) => isDenoExecPath(file) ? file.toString() : file, isDenoExecPath = (file) => typeof file !== "string" && file && Object.getPrototypeOf(file) === String.prototype, normalizeFileUrl = (file) => file instanceof URL ? fileURLToPath2(file) : file;
25147
+ }, normalizeDenoExecPath = (file) => isDenoExecPath(file) ? file.toString() : file, isDenoExecPath = (file) => typeof file !== "string" && file && Object.getPrototypeOf(file) === String.prototype, normalizeFileUrl = (file) => file instanceof URL ? fileURLToPath3(file) : file;
25146
25148
  var init_file_url = () => {};
25147
25149
 
25148
25150
  // ../../node_modules/execa/lib/methods/parameters.js
@@ -26438,9 +26440,9 @@ function pathKey(options3 = {}) {
26438
26440
  import { promisify as promisify4 } from "node:util";
26439
26441
  import { execFile as execFileCallback, execFileSync as execFileSyncOriginal } from "node:child_process";
26440
26442
  import path4 from "node:path";
26441
- import { fileURLToPath as fileURLToPath3 } from "node:url";
26443
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
26442
26444
  function toPath(urlOrPath) {
26443
- return urlOrPath instanceof URL ? fileURLToPath3(urlOrPath) : urlOrPath;
26445
+ return urlOrPath instanceof URL ? fileURLToPath4(urlOrPath) : urlOrPath;
26444
26446
  }
26445
26447
  function traversePathUp(startPath) {
26446
26448
  return {
@@ -28822,7 +28824,7 @@ var init_stdio_option = __esm(() => {
28822
28824
  });
28823
28825
 
28824
28826
  // ../../node_modules/execa/lib/stdio/native.js
28825
- import { readFileSync as readFileSync2 } from "node:fs";
28827
+ import { readFileSync as readFileSync3 } from "node:fs";
28826
28828
  import tty3 from "node:tty";
28827
28829
  var handleNativeStream = ({ stdioItem, stdioItem: { type }, isStdioArray, fdNumber, direction, isSync }) => {
28828
28830
  if (!isStdioArray || type !== "native") {
@@ -28854,7 +28856,7 @@ var handleNativeStream = ({ stdioItem, stdioItem: { type }, isStdioArray, fdNumb
28854
28856
  if (tty3.isatty(targetFdNumber)) {
28855
28857
  throw new TypeError(`The \`${optionName}: ${serializeOptionValue(value)}\` option is invalid: it cannot be a TTY with synchronous methods.`);
28856
28858
  }
28857
- return { type: "uint8Array", value: bufferToUint8Array(readFileSync2(targetFdNumber)), optionName };
28859
+ return { type: "uint8Array", value: bufferToUint8Array(readFileSync3(targetFdNumber)), optionName };
28858
28860
  }, getTargetFdNumber = (value, fdNumber) => {
28859
28861
  if (value === "inherit") {
28860
28862
  return fdNumber;
@@ -29157,7 +29159,7 @@ var init_handle = __esm(() => {
29157
29159
  });
29158
29160
 
29159
29161
  // ../../node_modules/execa/lib/stdio/handle-sync.js
29160
- import { readFileSync as readFileSync3 } from "node:fs";
29162
+ import { readFileSync as readFileSync4 } from "node:fs";
29161
29163
  var handleStdioSync = (options3, verboseInfo) => handleStdio(addPropertiesSync, options3, verboseInfo, true), forbiddenIfSync = ({ type, optionName }) => {
29162
29164
  throwInvalidSyncValue(optionName, TYPE_TO_MESSAGE[type]);
29163
29165
  }, forbiddenNativeIfSync = ({ optionName, value }) => {
@@ -29185,8 +29187,8 @@ var init_handle_sync = __esm(() => {
29185
29187
  addPropertiesSync = {
29186
29188
  input: {
29187
29189
  ...addProperties,
29188
- fileUrl: ({ value }) => ({ contents: [bufferToUint8Array(readFileSync3(value))] }),
29189
- filePath: ({ value: { file } }) => ({ contents: [bufferToUint8Array(readFileSync3(file))] }),
29190
+ fileUrl: ({ value }) => ({ contents: [bufferToUint8Array(readFileSync4(value))] }),
29191
+ filePath: ({ value: { file } }) => ({ contents: [bufferToUint8Array(readFileSync4(file))] }),
29190
29192
  fileNumber: forbiddenIfSync,
29191
29193
  iterable: ({ value }) => ({ contents: [...value] }),
29192
29194
  string: ({ value }) => ({ contents: [value] }),
@@ -29555,7 +29557,7 @@ var init_output2 = __esm(() => {
29555
29557
  });
29556
29558
 
29557
29559
  // ../../node_modules/execa/lib/io/output-sync.js
29558
- import { writeFileSync as writeFileSync2, appendFileSync } from "node:fs";
29560
+ import { writeFileSync as writeFileSync3, appendFileSync } from "node:fs";
29559
29561
  var transformOutputSync = ({ fileDescriptors, syncResult: { output }, options: options3, isMaxBuffer, verboseInfo }) => {
29560
29562
  if (output === null) {
29561
29563
  return { output: Array.from({ length: 3 }) };
@@ -29648,7 +29650,7 @@ var transformOutputSync = ({ fileDescriptors, syncResult: { output }, options: o
29648
29650
  appendFileSync(path9, serializedResult);
29649
29651
  } else {
29650
29652
  outputFiles.add(pathString);
29651
- writeFileSync2(path9, serializedResult);
29653
+ writeFileSync3(path9, serializedResult);
29652
29654
  }
29653
29655
  }
29654
29656
  };
@@ -32437,7 +32439,7 @@ var {
32437
32439
  // package.json
32438
32440
  var package_default = {
32439
32441
  name: "@secondlayer/cli",
32440
- version: "3.5.2",
32442
+ version: "3.5.4",
32441
32443
  description: "CLI for subgraphs and blockchain indexing on Stacks",
32442
32444
  type: "module",
32443
32445
  bin: {
@@ -32480,10 +32482,10 @@ var package_default = {
32480
32482
  dependencies: {
32481
32483
  "@inquirer/prompts": "^8.2.0",
32482
32484
  "@secondlayer/bundler": "^0.3.2",
32483
- "@secondlayer/sdk": "^3.2.0",
32484
- "@secondlayer/shared": "^4.3.2",
32485
+ "@secondlayer/sdk": "^3.2.2",
32486
+ "@secondlayer/shared": "^4.3.4",
32485
32487
  "@secondlayer/stacks": "^2.0.0",
32486
- "@secondlayer/subgraphs": "^1.2.1",
32488
+ "@secondlayer/subgraphs": "^1.3.2",
32487
32489
  "@biomejs/js-api": "^0.7.0",
32488
32490
  "@biomejs/wasm-nodejs": "^1.9.0",
32489
32491
  esbuild: "^0.19.0",
@@ -32562,8 +32564,8 @@ async function backfillSubgraphApi(name, options) {
32562
32564
  async function stopSubgraphApi(name) {
32563
32565
  return (await getTenantClient()).subgraphs.stop(name);
32564
32566
  }
32565
- async function deleteSubgraphApi(name) {
32566
- return (await getTenantClient()).subgraphs.delete(name);
32567
+ async function deleteSubgraphApi(name, options) {
32568
+ return (await getTenantClient()).subgraphs.delete(name, options);
32567
32569
  }
32568
32570
  async function deploySubgraphApi(data) {
32569
32571
  return (await getTenantClient()).subgraphs.deploy(data);
@@ -33194,6 +33196,7 @@ async function createSubscription(name, opts) {
33194
33196
  TASK_ID: `${subgraph}-${table}`
33195
33197
  });
33196
33198
  let signingSecret = null;
33199
+ let provisioningFailed = false;
33197
33200
  if (!opts.skipApi) {
33198
33201
  try {
33199
33202
  if (!sl)
@@ -33211,8 +33214,9 @@ async function createSubscription(name, opts) {
33211
33214
  signingSecret = res.signingSecret;
33212
33215
  success(`Subscription provisioned: ${blue(res.subscription.id)}`);
33213
33216
  } catch (err) {
33217
+ provisioningFailed = true;
33214
33218
  warn(`Subscription provisioning failed: ${err instanceof Error ? err.message : String(err)}`);
33215
- info("Template copied fix auth + run again, or provision via dashboard.");
33219
+ info("Template copied, but the subscription was not created. Provision it in the dashboard, or remove the template directory and rerun this command after fixing the API error.");
33216
33220
  }
33217
33221
  }
33218
33222
  if (signingSecret) {
@@ -33234,6 +33238,10 @@ ${dim(" ")}${signingSecret}`);
33234
33238
  }
33235
33239
  }
33236
33240
  console.log();
33241
+ if (provisioningFailed) {
33242
+ error("Subscription was not created.");
33243
+ process.exit(1);
33244
+ }
33237
33245
  success(`Done. Next:
33238
33246
  cd ${name}
33239
33247
  bun install
@@ -34307,8 +34315,15 @@ async function resyncDatabase(skipConfirm, backfill) {
34307
34315
  }
34308
34316
  }
34309
34317
  // src/commands/subgraphs.ts
34310
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, watch } from "node:fs";
34311
- import { resolve as resolve3 } from "node:path";
34318
+ import {
34319
+ existsSync as existsSync3,
34320
+ mkdirSync as mkdirSync2,
34321
+ readFileSync as readFileSync2,
34322
+ watch,
34323
+ writeFileSync as writeFileSync2
34324
+ } from "node:fs";
34325
+ import { dirname as dirname4, join as join7, resolve as resolve3 } from "node:path";
34326
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
34312
34327
  import { confirm as confirm3 } from "@inquirer/prompts";
34313
34328
 
34314
34329
  // src/generators/subgraph-scaffold.ts
@@ -34583,6 +34598,45 @@ function parseStartBlockOption(value) {
34583
34598
  }
34584
34599
  return parsed;
34585
34600
  }
34601
+ function readCliSubgraphsDependency() {
34602
+ const here = dirname4(fileURLToPath2(import.meta.url));
34603
+ const candidates = [
34604
+ resolve3(here, "..", "..", "package.json"),
34605
+ resolve3(here, "..", "package.json")
34606
+ ];
34607
+ for (const candidate of candidates) {
34608
+ if (!existsSync3(candidate))
34609
+ continue;
34610
+ const pkg = JSON.parse(readFileSync2(candidate, "utf8"));
34611
+ const dep = pkg.dependencies?.["@secondlayer/subgraphs"];
34612
+ if (dep)
34613
+ return dep;
34614
+ }
34615
+ return "^1.3.2";
34616
+ }
34617
+ function ensureScaffoldPackageJson(dir) {
34618
+ const packagePath = join7(dir, "package.json");
34619
+ const subgraphsDep = readCliSubgraphsDependency();
34620
+ if (!existsSync3(packagePath)) {
34621
+ writeFileSync2(packagePath, `${JSON.stringify({
34622
+ type: "module",
34623
+ dependencies: {
34624
+ "@secondlayer/subgraphs": subgraphsDep
34625
+ }
34626
+ }, null, 2)}
34627
+ `, "utf8");
34628
+ return;
34629
+ }
34630
+ const pkg = JSON.parse(readFileSync2(packagePath, "utf8"));
34631
+ if (pkg.dependencies?.["@secondlayer/subgraphs"])
34632
+ return;
34633
+ pkg.dependencies = {
34634
+ ...pkg.dependencies ?? {},
34635
+ "@secondlayer/subgraphs": subgraphsDep
34636
+ };
34637
+ writeFileSync2(packagePath, `${JSON.stringify(pkg, null, 2)}
34638
+ `, "utf8");
34639
+ }
34586
34640
  function createSubgraphDeployPreview(def, options2 = {}) {
34587
34641
  const tableColumns = Object.entries(def.schema).map(([table, schema]) => `${table}: ${Object.keys(schema.columns).join(", ") || "(no columns)"}`);
34588
34642
  return {
@@ -34771,6 +34825,7 @@ Stopped watching.`);
34771
34825
  sources: effectiveDef.sources,
34772
34826
  schema: effectiveDef.schema,
34773
34827
  handlerCode,
34828
+ sourceCode: source,
34774
34829
  ...startBlock !== undefined ? { startBlock } : {}
34775
34830
  });
34776
34831
  if (result.action === "unchanged") {
@@ -34877,6 +34932,7 @@ ${data.length} subgraph(s) total`));
34877
34932
  try {
34878
34933
  const subgraph = await getSubgraphApi(name);
34879
34934
  const rowCounts = Object.entries(subgraph.tables).map(([t, info2]) => `${t}: ${info2.rowCount}`).join(", ") || "N/A";
34935
+ const totalRows = Object.values(subgraph.tables).reduce((sum, info2) => sum + info2.rowCount, 0);
34880
34936
  const errorRate = subgraph.health.totalProcessed > 0 ? `${(subgraph.health.errorRate * 100).toFixed(2)}%` : "N/A";
34881
34937
  const sync = subgraph.sync;
34882
34938
  const syncDisplay = sync ? formatSubgraphSync(sync) : {
@@ -34895,7 +34951,8 @@ ${data.length} subgraph(s) total`));
34895
34951
  ["Integrity", integrity],
34896
34952
  ["Gaps", gapSummary],
34897
34953
  ["Last Block", String(subgraph.lastProcessedBlock)],
34898
- ["Row Count", rowCounts],
34954
+ ["Rows Indexed", totalRows.toLocaleString()],
34955
+ ["Table Rows", rowCounts],
34899
34956
  ["Total Errors", String(subgraph.health.totalErrors)],
34900
34957
  ["Error Rate", errorRate],
34901
34958
  ["Last Error", subgraph.health.lastError ?? "none"],
@@ -35040,9 +35097,9 @@ ${rows.length} row(s)`));
35040
35097
  handleApiError(err, "query subgraph");
35041
35098
  }
35042
35099
  });
35043
- subgraphs.command("delete <name>").description("Delete a subgraph and its data").option("-y, --yes", "Skip confirmation").action(async (name, options2) => {
35100
+ subgraphs.command("delete <name>").description("Delete a subgraph and its data").option("-y, --yes", "Skip confirmation").option("--force", "Cancel active operations and force delete").action(async (name, options2) => {
35044
35101
  try {
35045
- if (!options2.yes) {
35102
+ if (!options2.yes && !options2.force) {
35046
35103
  const { confirm: confirm4 } = await import("@inquirer/prompts");
35047
35104
  const ok = await confirm4({
35048
35105
  message: `Delete subgraph "${name}" and all its data? This cannot be undone.`
@@ -35052,7 +35109,9 @@ ${rows.length} row(s)`));
35052
35109
  return;
35053
35110
  }
35054
35111
  }
35055
- const result = await deleteSubgraphApi(name);
35112
+ const result = await deleteSubgraphApi(name, {
35113
+ force: options2.force
35114
+ });
35056
35115
  success(result.message);
35057
35116
  } catch (err) {
35058
35117
  handleApiError(err, "delete subgraph");
@@ -35080,6 +35139,7 @@ ${rows.length} row(s)`));
35080
35139
  if (!existsSync3(dir))
35081
35140
  mkdirSync2(dir, { recursive: true });
35082
35141
  await writeTextFile(outPath, content);
35142
+ ensureScaffoldPackageJson(dir);
35083
35143
  success(`Created ${outPath}`);
35084
35144
  info(`Next: sl subgraphs deploy ${options2.output}`);
35085
35145
  } catch (err) {
@@ -36039,6 +36099,7 @@ init_output();
36039
36099
  init_project_file();
36040
36100
  init_resolve_tenant();
36041
36101
  import { confirm as confirm5, input as input4, select as select4 } from "@inquirer/prompts";
36102
+ var INSTANCE_CREATE_TIMEOUT_MS = 180000;
36042
36103
  function registerInstanceCommand(program2) {
36043
36104
  const instance = program2.command("instance").description("Manage your dedicated Secondlayer instance");
36044
36105
  instance.command("create").description("Provision a new dedicated instance for the active project").option("--plan <plan>", "Plan: hobby (free) | launch | grow | scale", "hobby").action(async (opts) => {
@@ -36049,14 +36110,22 @@ function registerInstanceCommand(program2) {
36049
36110
  error(`Invalid plan: ${plan} (expected hobby, launch, grow, or scale)`);
36050
36111
  process.exit(1);
36051
36112
  }
36113
+ const spinner = createSpinner("Provisioning your instance (~60s; safe to interrupt — instance will still be created; check `sl instance info`)");
36052
36114
  try {
36053
36115
  const res = await httpPlatform(`/api/projects/${encodeURIComponent(activeSlug)}/instance`, {
36054
36116
  method: "POST",
36055
- body: { plan }
36117
+ body: { plan },
36118
+ timeoutMs: INSTANCE_CREATE_TIMEOUT_MS
36056
36119
  });
36057
- success(`Instance provisioned: ${res.tenant.slug}`);
36120
+ spinner.succeed(`Instance provisioned: ${res.tenant.slug}`);
36058
36121
  printKeyReveal(res.tenant, res.credentials);
36059
36122
  } catch (err) {
36123
+ if (isTimeoutError(err)) {
36124
+ spinner.fail("Provision request timed out after 3 minutes.");
36125
+ error("Provisioning may still finish server-side. Run `sl instance info` to check before retrying.");
36126
+ process.exit(1);
36127
+ }
36128
+ spinner.fail("Provision failed.");
36060
36129
  handleInstanceError(err, "provision instance");
36061
36130
  }
36062
36131
  });
@@ -36135,6 +36204,10 @@ function registerInstanceCommand(program2) {
36135
36204
  }
36136
36205
  const slug = info_.tenant.slug;
36137
36206
  if (!opts.yes) {
36207
+ if (!process.stdin.isTTY) {
36208
+ error(`Refusing to prompt in a non-interactive terminal. Re-run with --yes to delete instance "${slug}".`);
36209
+ process.exit(1);
36210
+ }
36138
36211
  const typed = await input4({
36139
36212
  message: `Type the slug "${slug}" to confirm permanent deletion`,
36140
36213
  validate: (v) => v === slug ? true : "Slug must match exactly"
@@ -36302,6 +36375,43 @@ function printKeyReveal(tenant, creds) {
36302
36375
  console.log(dim("Run `sl subgraphs deploy <file>` to deploy your first subgraph."));
36303
36376
  console.log("");
36304
36377
  }
36378
+ function createSpinner(message) {
36379
+ if (!process.stderr.isTTY) {
36380
+ info(message);
36381
+ return {
36382
+ succeed: success,
36383
+ fail: error
36384
+ };
36385
+ }
36386
+ const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
36387
+ let index = 0;
36388
+ const render = () => {
36389
+ const frame = frames[index % frames.length] ?? frames[0];
36390
+ index += 1;
36391
+ process.stderr.write(`\r${blue(frame)} ${message}`);
36392
+ };
36393
+ const clear = () => {
36394
+ clearInterval(timer3);
36395
+ process.stderr.write("\r\x1B[2K");
36396
+ };
36397
+ const timer3 = setInterval(render, 80);
36398
+ render();
36399
+ return {
36400
+ succeed(message2) {
36401
+ clear();
36402
+ success(message2);
36403
+ },
36404
+ fail(message2) {
36405
+ clear();
36406
+ error(message2);
36407
+ }
36408
+ };
36409
+ }
36410
+ function isTimeoutError(err) {
36411
+ if (!(err instanceof Error))
36412
+ return false;
36413
+ return err.name === "TimeoutError" || err.name === "AbortError" || err.message.toLowerCase().includes("timeout");
36414
+ }
36305
36415
  function warn_box(message) {
36306
36416
  return `${"━".repeat(message.length + 4)}
36307
36417
  ${message}
@@ -36321,10 +36431,10 @@ function handleInstanceError(err, action) {
36321
36431
  error("This project already has an instance. Run `sl instance info` to see it.");
36322
36432
  process.exit(1);
36323
36433
  }
36324
- error(err.message);
36434
+ error(err.message || `Failed to ${action}.`);
36325
36435
  process.exit(1);
36326
36436
  }
36327
- error(`Failed to ${action}: ${err instanceof Error ? err.message : String(err)}`);
36437
+ error(`Failed to ${action}: ${err instanceof Error ? err.message || "Unknown error" : String(err)}`);
36328
36438
  process.exit(1);
36329
36439
  }
36330
36440
  // src/commands/project.ts
@@ -36335,15 +36445,24 @@ init_project_file();
36335
36445
  import { input as input5 } from "@inquirer/prompts";
36336
36446
  function registerProjectCommand(program2) {
36337
36447
  const project = program2.command("project").description("Manage Secondlayer projects");
36338
- project.command("create [name]").description("Create a new project").action(async (nameArg) => {
36448
+ project.command("create [name]").description("Create a new project").option("--slug <slug>", "Project URL identifier").action(async (nameArg, options2 = {}) => {
36339
36449
  const name = nameArg ?? await input5({
36340
36450
  message: "Project name",
36341
36451
  validate: (v) => v.length >= 2 ? true : "Name must be at least 2 characters"
36342
36452
  });
36453
+ const slug = options2.slug ?? slugifyProjectName(name);
36454
+ const validation = validateProjectSlug(slug);
36455
+ if (validation !== true) {
36456
+ error(`${validation}. Pass --slug <slug> to choose one explicitly.`);
36457
+ process.exit(1);
36458
+ }
36343
36459
  try {
36344
- const res = await httpPlatform("/api/projects", { method: "POST", body: { name } });
36345
- success(`Created project ${res.project.name} (${res.project.slug})`);
36346
- const path2 = await writeActiveProject(res.project.slug, process.cwd());
36460
+ const res = await httpPlatform("/api/projects", {
36461
+ method: "POST",
36462
+ body: { name, slug }
36463
+ });
36464
+ success(`Created project ${res.name} (${res.slug})`);
36465
+ const path2 = await writeActiveProject(res.slug, process.cwd());
36347
36466
  info(dim(`Bound to this directory → ${path2}`));
36348
36467
  info(dim("Next: sl instance create --plan launch"));
36349
36468
  } catch (err) {
@@ -36410,6 +36529,18 @@ function handleProjectError(err) {
36410
36529
  error(err instanceof Error ? err.message : String(err));
36411
36530
  process.exit(1);
36412
36531
  }
36532
+ function slugifyProjectName(name) {
36533
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63).replace(/-+$/g, "");
36534
+ }
36535
+ function validateProjectSlug(slug) {
36536
+ if (slug.length < 2 || slug.length > 63) {
36537
+ return "Project slug must be 2-63 characters";
36538
+ }
36539
+ if (!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(slug)) {
36540
+ return "Project slug must use lowercase letters, numbers, and hyphens, and start/end with a letter or number";
36541
+ }
36542
+ return true;
36543
+ }
36413
36544
  // src/cli.ts
36414
36545
  var { version } = package_default;
36415
36546
  program.name("secondlayer").alias("sl").description("SecondLayer CLI — dedicated Stacks indexing + real-time subgraphs").version(version).option("--network <network>", "Override network (local, testnet, mainnet)");
@@ -36451,5 +36582,5 @@ registerLocalCommand(program);
36451
36582
  registerAccountCommand(program);
36452
36583
  program.parse();
36453
36584
 
36454
- //# debugId=8D29D5D4F1C33F0564756E2164756E21
36585
+ //# debugId=96B7A917235EEA4164756E2164756E21
36455
36586
  //# sourceMappingURL=cli.js.map