@secondlayer/cli 5.3.0 → 5.4.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 +161 -20
- package/dist/cli.js.map +7 -6
- package/package.json +3 -3
- package/templates/subscriptions/cloudflare/.env.example +1 -0
- package/templates/subscriptions/inngest/.env.example +2 -0
- package/templates/subscriptions/inngest/package.json +1 -1
- package/templates/subscriptions/node/README.md +3 -1
- package/templates/subscriptions/trigger/.env.example +2 -0
package/dist/cli.js
CHANGED
|
@@ -32321,7 +32321,7 @@ var {
|
|
|
32321
32321
|
// package.json
|
|
32322
32322
|
var package_default = {
|
|
32323
32323
|
name: "@secondlayer/cli",
|
|
32324
|
-
version: "5.
|
|
32324
|
+
version: "5.4.0",
|
|
32325
32325
|
description: "CLI for subgraphs and blockchain indexing on Stacks",
|
|
32326
32326
|
type: "module",
|
|
32327
32327
|
bin: {
|
|
@@ -32365,9 +32365,9 @@ var package_default = {
|
|
|
32365
32365
|
"@inquirer/prompts": "^8.2.0",
|
|
32366
32366
|
"@secondlayer/bundler": "^0.3.5",
|
|
32367
32367
|
"@secondlayer/sdk": "^3.6.0",
|
|
32368
|
-
"@secondlayer/shared": "^6.4.
|
|
32368
|
+
"@secondlayer/shared": "^6.4.1",
|
|
32369
32369
|
"@secondlayer/stacks": "^2.2.0",
|
|
32370
|
-
"@secondlayer/subgraphs": "^2.0.
|
|
32370
|
+
"@secondlayer/subgraphs": "^2.0.2",
|
|
32371
32371
|
"@biomejs/js-api": "^0.7.0",
|
|
32372
32372
|
"@biomejs/wasm-nodejs": "^1.9.0",
|
|
32373
32373
|
esbuild: "^0.19.0",
|
|
@@ -33095,6 +33095,21 @@ async function validateSubscriptionTargetFromApi(client, input) {
|
|
|
33095
33095
|
}
|
|
33096
33096
|
}
|
|
33097
33097
|
|
|
33098
|
+
// src/utils/urls.ts
|
|
33099
|
+
function deriveBaseUrl(apiUrl) {
|
|
33100
|
+
const override = process.env.SL_DASHBOARD_URL?.trim();
|
|
33101
|
+
if (override)
|
|
33102
|
+
return override.replace(/\/$/, "");
|
|
33103
|
+
try {
|
|
33104
|
+
const url = new URL(apiUrl);
|
|
33105
|
+
url.hostname = url.hostname.replace(/^api\./, "");
|
|
33106
|
+
url.pathname = "/";
|
|
33107
|
+
return url.toString().replace(/\/$/, "");
|
|
33108
|
+
} catch {
|
|
33109
|
+
return apiUrl.replace(/\/$/, "");
|
|
33110
|
+
}
|
|
33111
|
+
}
|
|
33112
|
+
|
|
33098
33113
|
// src/commands/create.ts
|
|
33099
33114
|
var RUNTIMES = ["inngest", "trigger", "cloudflare", "node"];
|
|
33100
33115
|
var FORMAT_BY_RUNTIME = {
|
|
@@ -33158,6 +33173,10 @@ function buildSubscriptionAuthConfig(authToken) {
|
|
|
33158
33173
|
return { authType: "bearer", token };
|
|
33159
33174
|
}
|
|
33160
33175
|
async function createSubscription(name, opts) {
|
|
33176
|
+
if (opts.runtime && !RUNTIMES.includes(opts.runtime)) {
|
|
33177
|
+
error(`Unknown --runtime "${opts.runtime}". Valid: ${RUNTIMES.join(", ")}`);
|
|
33178
|
+
process.exit(1);
|
|
33179
|
+
}
|
|
33161
33180
|
const { runtime, subgraph, table, url } = await promptFor(name, opts);
|
|
33162
33181
|
let filter;
|
|
33163
33182
|
let authConfig;
|
|
@@ -33220,31 +33239,52 @@ async function createSubscription(name, opts) {
|
|
|
33220
33239
|
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.");
|
|
33221
33240
|
}
|
|
33222
33241
|
}
|
|
33242
|
+
let subscriptionId;
|
|
33243
|
+
let subscriptionStatus;
|
|
33244
|
+
if (sl) {
|
|
33245
|
+
try {
|
|
33246
|
+
const list = await sl.subscriptions.list();
|
|
33247
|
+
const rows = list.data ?? [];
|
|
33248
|
+
const created = rows.find((s) => s.name === name);
|
|
33249
|
+
subscriptionId = created?.id;
|
|
33250
|
+
subscriptionStatus = created?.status;
|
|
33251
|
+
} catch {}
|
|
33252
|
+
}
|
|
33223
33253
|
if (signingSecret) {
|
|
33224
33254
|
const envTarget = join4(targetDir, ".env");
|
|
33225
33255
|
const envExample = join4(targetDir, ".env.example");
|
|
33226
33256
|
if (existsSync(envExample) && !existsSync(envTarget)) {
|
|
33227
33257
|
copyFileSync(envExample, envTarget);
|
|
33228
33258
|
}
|
|
33229
|
-
if (existsSync(envTarget)) {
|
|
33259
|
+
if (!existsSync(envTarget)) {
|
|
33260
|
+
writeFileSync(envTarget, `SIGNING_SECRET=${signingSecret}
|
|
33261
|
+
`);
|
|
33262
|
+
} else {
|
|
33230
33263
|
const cur = readFileSync(envTarget, "utf8");
|
|
33231
|
-
|
|
33232
|
-
writeFileSync(envTarget, next.includes("SIGNING_SECRET=") ? next : `${cur}
|
|
33264
|
+
writeFileSync(envTarget, cur.match(/^SIGNING_SECRET=/m) ? cur.replace(/^SIGNING_SECRET=.*/m, `SIGNING_SECRET=${signingSecret}`) : `${cur}
|
|
33233
33265
|
SIGNING_SECRET=${signingSecret}
|
|
33234
33266
|
`);
|
|
33235
|
-
success(`Signing secret written to ${relative(process.cwd(), envTarget)}`);
|
|
33236
|
-
} else {
|
|
33237
|
-
info(`Signing secret (copy this — won't be shown again):
|
|
33238
|
-
${dim(" ")}${signingSecret}`);
|
|
33239
33267
|
}
|
|
33268
|
+
success(`Signing secret written to ${relative(process.cwd(), envTarget)}`);
|
|
33240
33269
|
}
|
|
33241
33270
|
console.log();
|
|
33242
33271
|
if (provisioningFailed) {
|
|
33243
33272
|
error("Subscription was not created.");
|
|
33244
33273
|
process.exit(1);
|
|
33245
33274
|
}
|
|
33275
|
+
let dashboardLine = "";
|
|
33276
|
+
try {
|
|
33277
|
+
const { apiUrl } = await resolveActiveTenant();
|
|
33278
|
+
const base = deriveBaseUrl(apiUrl);
|
|
33279
|
+
dashboardLine = subscriptionId ? `Dashboard: ${base}/platform/subgraphs/${subgraph}/subscriptions/${subscriptionId}
|
|
33280
|
+
` : `Dashboard: ${base}/platform/subgraphs/${subgraph}/subscriptions
|
|
33281
|
+
`;
|
|
33282
|
+
} catch {}
|
|
33283
|
+
const pausedLine = subscriptionStatus === "paused" ? `Subscription is paused. Resume:
|
|
33284
|
+
sl subscriptions resume ${name}
|
|
33285
|
+
` : "";
|
|
33246
33286
|
success(`Done. Next:
|
|
33247
|
-
cd ${name}
|
|
33287
|
+
${dashboardLine}${pausedLine}cd ${name}
|
|
33248
33288
|
bun install
|
|
33249
33289
|
bun run dev`);
|
|
33250
33290
|
}
|
|
@@ -33485,6 +33525,7 @@ function printSubscriptionDetail(sub) {
|
|
|
33485
33525
|
["Circuit Opened", sub.circuitOpenedAt ?? "none"],
|
|
33486
33526
|
["Last Error", sub.lastError ?? "none"],
|
|
33487
33527
|
["Max Retries", String(sub.maxRetries)],
|
|
33528
|
+
["Backoff", "30s → 2m → 10m → 1h → 6h → 24h → 72h"],
|
|
33488
33529
|
["Timeout", `${sub.timeoutMs}ms`],
|
|
33489
33530
|
["Concurrency", String(sub.concurrency)],
|
|
33490
33531
|
["Created", sub.createdAt],
|
|
@@ -33791,7 +33832,22 @@ ${data.length} subscription(s) total`));
|
|
|
33791
33832
|
commonOptions(subscriptions.command("delete <idOrName>").description("Delete a subscription").option("-y, --yes", "Skip confirmation").option("--json", "Output as JSON")).action(async (idOrName, options) => {
|
|
33792
33833
|
try {
|
|
33793
33834
|
const client = await getSubscriptionClient(options);
|
|
33794
|
-
|
|
33835
|
+
let resolved = null;
|
|
33836
|
+
try {
|
|
33837
|
+
resolved = await resolveSubscriptionRef(client, idOrName);
|
|
33838
|
+
} catch (err) {
|
|
33839
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
33840
|
+
const status = err?.status;
|
|
33841
|
+
if (status === 404 || /not found/i.test(msg)) {
|
|
33842
|
+
if (options.json)
|
|
33843
|
+
printJson({ deleted: false, reason: "not_found" });
|
|
33844
|
+
else
|
|
33845
|
+
info(`Subscription "${idOrName}" not found (already deleted?)`);
|
|
33846
|
+
return;
|
|
33847
|
+
}
|
|
33848
|
+
throw err;
|
|
33849
|
+
}
|
|
33850
|
+
const { id, detail } = resolved;
|
|
33795
33851
|
const ok = await confirmOrExit(`Delete subscription "${detail.name}"? Pending outbox rows will be removed.`, options.yes);
|
|
33796
33852
|
if (!ok)
|
|
33797
33853
|
return;
|
|
@@ -34971,6 +35027,45 @@ function decodeBuffUtf8(value: unknown): string | null {
|
|
|
34971
35027
|
|
|
34972
35028
|
// src/commands/subgraphs.ts
|
|
34973
35029
|
init_api();
|
|
35030
|
+
init_resolve_tenant();
|
|
35031
|
+
async function loadSubgraphWithDepCheck(absPath) {
|
|
35032
|
+
try {
|
|
35033
|
+
return await import(absPath);
|
|
35034
|
+
} catch (err) {
|
|
35035
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
35036
|
+
const code = err?.code;
|
|
35037
|
+
const missingSdk = (code === "ERR_MODULE_NOT_FOUND" || msg.includes("ERR_MODULE_NOT_FOUND")) && msg.includes("@secondlayer/subgraphs");
|
|
35038
|
+
if (!missingSdk)
|
|
35039
|
+
throw err;
|
|
35040
|
+
warn("Missing dependency: @secondlayer/subgraphs");
|
|
35041
|
+
const install = await confirm3({
|
|
35042
|
+
message: "Install with `bun add @secondlayer/subgraphs`?",
|
|
35043
|
+
default: true
|
|
35044
|
+
});
|
|
35045
|
+
if (!install)
|
|
35046
|
+
throw err;
|
|
35047
|
+
await new Promise((res, rej) => {
|
|
35048
|
+
const child = spawn("bun", ["add", "@secondlayer/subgraphs"], {
|
|
35049
|
+
stdio: "inherit"
|
|
35050
|
+
});
|
|
35051
|
+
child.on("error", rej);
|
|
35052
|
+
child.on("exit", (c) => c === 0 ? res() : rej(new Error(`bun add exit ${c}`)));
|
|
35053
|
+
});
|
|
35054
|
+
return await import(absPath);
|
|
35055
|
+
}
|
|
35056
|
+
}
|
|
35057
|
+
async function typecheckHandler(absPath) {
|
|
35058
|
+
await new Promise((res, rej) => {
|
|
35059
|
+
const child = spawn("bunx", ["tsc", "--noEmit", "--allowJs", "--target", "es2022", absPath], { stdio: "inherit" });
|
|
35060
|
+
child.on("error", (err) => rej(new Error(`Failed to run tsc — install typescript (\`bun add -d typescript\`) or drop --strict. (${err.message})`)));
|
|
35061
|
+
child.on("exit", (code) => {
|
|
35062
|
+
if (code === 0)
|
|
35063
|
+
res();
|
|
35064
|
+
else
|
|
35065
|
+
rej(new Error(`Type-check failed (tsc exit ${code})`));
|
|
35066
|
+
});
|
|
35067
|
+
});
|
|
35068
|
+
}
|
|
34974
35069
|
function parseStartBlockOption(value) {
|
|
34975
35070
|
if (value === undefined)
|
|
34976
35071
|
return;
|
|
@@ -35290,7 +35385,7 @@ Stopped watching.`);
|
|
|
35290
35385
|
});
|
|
35291
35386
|
await new Promise(() => {});
|
|
35292
35387
|
});
|
|
35293
|
-
subgraphs.command("deploy <file>").description("Deploy a subgraph definition file").option("--version <semver>", "Explicit version (default: auto-increment patch)").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").action(async (file, options2) => {
|
|
35388
|
+
subgraphs.command("deploy <file>").description("Deploy a subgraph definition file").option("--version <semver>", "Explicit version (default: auto-increment patch)").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) => {
|
|
35294
35389
|
try {
|
|
35295
35390
|
const absPath = resolve2(file);
|
|
35296
35391
|
const config = await loadConfig();
|
|
@@ -35302,8 +35397,12 @@ Stopped watching.`);
|
|
|
35302
35397
|
if (startBlock !== undefined) {
|
|
35303
35398
|
warn(`--start-block ${startBlock} overrides the definition's startBlock for this deploy.`);
|
|
35304
35399
|
}
|
|
35400
|
+
if (options2.strict) {
|
|
35401
|
+
info("Type-checking handler (tsc --noEmit)...");
|
|
35402
|
+
await typecheckHandler(absPath);
|
|
35403
|
+
}
|
|
35305
35404
|
info(`Loading subgraph from ${absPath}`);
|
|
35306
|
-
const mod = await
|
|
35405
|
+
const mod = await loadSubgraphWithDepCheck(absPath);
|
|
35307
35406
|
const def = mod.default ?? mod;
|
|
35308
35407
|
const effectiveDef = startBlock === undefined ? def : { ...def, startBlock };
|
|
35309
35408
|
const { validateSubgraphDefinition } = await import("@secondlayer/subgraphs/validate");
|
|
@@ -35338,10 +35437,23 @@ Stopped watching.`);
|
|
|
35338
35437
|
sourceCode: source,
|
|
35339
35438
|
...startBlock !== undefined ? { startBlock } : {}
|
|
35340
35439
|
});
|
|
35440
|
+
const printDeployFooter = async () => {
|
|
35441
|
+
try {
|
|
35442
|
+
const { apiUrl } = await resolveActiveTenant();
|
|
35443
|
+
const baseUrl = deriveBaseUrl(apiUrl);
|
|
35444
|
+
const firstTable = Object.keys(effectiveDef.schema ?? {})[0];
|
|
35445
|
+
info(` Dashboard: ${baseUrl}/platform/subgraphs/${effectiveDef.name}`);
|
|
35446
|
+
if (firstTable) {
|
|
35447
|
+
info(` REST: ${apiUrl}/api/subgraphs/${effectiveDef.name}/${firstTable}`);
|
|
35448
|
+
}
|
|
35449
|
+
info(` Watch: sl subgraphs status ${effectiveDef.name}`);
|
|
35450
|
+
} catch {}
|
|
35451
|
+
};
|
|
35341
35452
|
if (result.action === "unchanged") {
|
|
35342
35453
|
info(`Subgraph "${effectiveDef.name}" is up to date (v${result.version} — no changes)`);
|
|
35343
35454
|
} else if (result.action === "created") {
|
|
35344
35455
|
success(`Subgraph "${effectiveDef.name}" created → v${result.version}`);
|
|
35456
|
+
await printDeployFooter();
|
|
35345
35457
|
} else if (result.action === "reindexed") {
|
|
35346
35458
|
if (result.diff) {
|
|
35347
35459
|
const { addedTables, addedColumns, breakingChanges } = result.diff;
|
|
@@ -35364,6 +35476,7 @@ Stopped watching.`);
|
|
|
35364
35476
|
process.exit(0);
|
|
35365
35477
|
}
|
|
35366
35478
|
success(`Subgraph "${effectiveDef.name}" updated → v${result.version} (reindexing)`);
|
|
35479
|
+
await printDeployFooter();
|
|
35367
35480
|
} else {
|
|
35368
35481
|
if (result.diff) {
|
|
35369
35482
|
const { addedTables, addedColumns } = result.diff;
|
|
@@ -35374,6 +35487,7 @@ Stopped watching.`);
|
|
|
35374
35487
|
}
|
|
35375
35488
|
}
|
|
35376
35489
|
success(`Subgraph "${effectiveDef.name}" updated → v${result.version}`);
|
|
35490
|
+
await printDeployFooter();
|
|
35377
35491
|
}
|
|
35378
35492
|
} else {
|
|
35379
35493
|
if (dryRun) {
|
|
@@ -35438,8 +35552,8 @@ ${data.length} subgraph(s) total`));
|
|
|
35438
35552
|
handleApiError(err, "list subgraphs");
|
|
35439
35553
|
}
|
|
35440
35554
|
});
|
|
35441
|
-
subgraphs.command("status <name>").description("Show detailed subgraph status").action(async (name) => {
|
|
35442
|
-
|
|
35555
|
+
subgraphs.command("status <name>").description("Show detailed subgraph status").option("-w, --watch", "Refresh every 2s until synced or Ctrl-C").action(async (name, options2) => {
|
|
35556
|
+
const renderOnce = async () => {
|
|
35443
35557
|
const subgraph = await getSubgraphApi(name);
|
|
35444
35558
|
const rowCounts = Object.entries(subgraph.tables).map(([t, info2]) => `${t}: ${info2.rowCount}`).join(", ") || "N/A";
|
|
35445
35559
|
const totalRows = Object.values(subgraph.tables).reduce((sum, info2) => sum + info2.rowCount, 0);
|
|
@@ -35482,6 +35596,23 @@ Table endpoints:`));
|
|
|
35482
35596
|
console.log(dim(` ${info2.endpoint}`));
|
|
35483
35597
|
}
|
|
35484
35598
|
}
|
|
35599
|
+
return subgraph;
|
|
35600
|
+
};
|
|
35601
|
+
try {
|
|
35602
|
+
if (!options2.watch) {
|
|
35603
|
+
await renderOnce();
|
|
35604
|
+
return;
|
|
35605
|
+
}
|
|
35606
|
+
while (true) {
|
|
35607
|
+
process.stdout.write("\x1Bc");
|
|
35608
|
+
const sg = await renderOnce();
|
|
35609
|
+
if (sg && sg.status === "synced") {
|
|
35610
|
+
console.log(dim(`
|
|
35611
|
+
Synced — exiting watch.`));
|
|
35612
|
+
return;
|
|
35613
|
+
}
|
|
35614
|
+
await new Promise((res) => setTimeout(res, 2000));
|
|
35615
|
+
}
|
|
35485
35616
|
} catch (err) {
|
|
35486
35617
|
handleApiError(err, "get subgraph status");
|
|
35487
35618
|
}
|
|
@@ -35671,9 +35802,19 @@ ${rows.length} row(s)`));
|
|
|
35671
35802
|
try {
|
|
35672
35803
|
if (!options2.yes && !options2.force) {
|
|
35673
35804
|
const { confirm: confirm4 } = await import("@inquirer/prompts");
|
|
35674
|
-
|
|
35675
|
-
|
|
35676
|
-
|
|
35805
|
+
let ok = false;
|
|
35806
|
+
try {
|
|
35807
|
+
ok = await confirm4({
|
|
35808
|
+
message: `Delete subgraph "${name}" and all its data? This cannot be undone.`
|
|
35809
|
+
});
|
|
35810
|
+
} catch (promptErr) {
|
|
35811
|
+
const m = promptErr instanceof Error ? promptErr.message : String(promptErr);
|
|
35812
|
+
if (m.includes("ExitPromptError") || m.includes("force closed")) {
|
|
35813
|
+
error("Interactive prompt unavailable. Re-run with -y to skip confirmation.");
|
|
35814
|
+
process.exit(1);
|
|
35815
|
+
}
|
|
35816
|
+
throw promptErr;
|
|
35817
|
+
}
|
|
35677
35818
|
if (!ok) {
|
|
35678
35819
|
info("Cancelled");
|
|
35679
35820
|
return;
|
|
@@ -36955,5 +37096,5 @@ registerAccountCommand(program);
|
|
|
36955
37096
|
registerBillingCommand(program);
|
|
36956
37097
|
program.parse();
|
|
36957
37098
|
|
|
36958
|
-
//# debugId=
|
|
37099
|
+
//# debugId=7D07B6242D81B6A564756E2164756E21
|
|
36959
37100
|
//# sourceMappingURL=cli.js.map
|