@secondlayer/cli 3.3.2 → 3.5.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
|
@@ -8,14 +8,33 @@ bun add -g @secondlayer/cli
|
|
|
8
8
|
sl --version
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
## Quickstart
|
|
11
|
+
## Beta Quickstart
|
|
12
|
+
|
|
13
|
+
Use this path for a first hosted demo. The CLI stores your login session,
|
|
14
|
+
binds the current directory to a project, provisions a dedicated tenant, then
|
|
15
|
+
deploys and queries a subgraph through short-lived tenant credentials.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bun add -g @secondlayer/cli
|
|
19
|
+
|
|
20
|
+
sl login
|
|
21
|
+
sl project create my-app
|
|
22
|
+
sl project use my-app
|
|
23
|
+
sl instance create --plan hobby
|
|
24
|
+
|
|
25
|
+
sl subgraphs scaffold SP1234ABCD.my-contract -o subgraphs/my-contract.ts
|
|
26
|
+
sl subgraphs deploy subgraphs/my-contract.ts --start-block <recent-block>
|
|
27
|
+
sl subgraphs query my-contract <table> --sort _block_height --order desc
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then wire a receiver:
|
|
12
31
|
|
|
13
32
|
```bash
|
|
14
|
-
sl
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
33
|
+
sl create subscription my-hook \
|
|
34
|
+
--runtime node \
|
|
35
|
+
--subgraph my-contract \
|
|
36
|
+
--table <table> \
|
|
37
|
+
--url https://<receiver-host>/webhook
|
|
19
38
|
```
|
|
20
39
|
|
|
21
40
|
## Command surface
|
|
@@ -49,7 +68,7 @@ One instance per project. The platform API spawns a dedicated `sl-pg-{slug}`,
|
|
|
49
68
|
|
|
50
69
|
| Command | What it does |
|
|
51
70
|
|---|---|
|
|
52
|
-
| `sl instance create --plan <launch\|grow\|scale>` | Provision containers. Boxed reveal of `serviceKey` + `anonKey` (shown once). |
|
|
71
|
+
| `sl instance create --plan <hobby\|launch\|grow\|scale>` | Provision containers. Boxed reveal of `serviceKey` + `anonKey` (shown once). |
|
|
53
72
|
| `sl instance info` | Plan, status, resource usage |
|
|
54
73
|
| `sl instance resize --plan <...>` | Recreate containers with new CPU/memory (~30s downtime) |
|
|
55
74
|
| `sl instance suspend` / `resume` | Stop/start containers, volume preserved |
|
|
@@ -63,7 +82,7 @@ One instance per project. The platform API spawns a dedicated `sl-pg-{slug}`,
|
|
|
63
82
|
|
|
64
83
|
| Command | What it does |
|
|
65
84
|
|---|---|
|
|
66
|
-
| `sl create subscription <name> --runtime <inngest\|trigger\|cloudflare\|node> [--filter key=value]` | Scaffold a receiver project wired to a new subscription. Copies the runtime template into `./<name>/`, provisions through the active project/instance, supports scalar filters, and wires the signing secret so the dev server starts consuming events immediately. |
|
|
85
|
+
| `sl create subscription <name> --runtime <inngest\|trigger\|cloudflare\|node> [--auth-token <token>] [--filter key=value]` | Scaffold a receiver project wired to a new subscription. Copies the runtime template into `./<name>/`, provisions through the active project/instance, supports scalar filters and bearer auth, and wires the signing secret so the dev server starts consuming events immediately. |
|
|
67
86
|
|
|
68
87
|
### Subscriptions (tenant-scoped)
|
|
69
88
|
|
|
@@ -76,7 +95,7 @@ active project/instance the same way as `sl subgraphs ...`; `SL_API_URL` and
|
|
|
76
95
|
|---|---|
|
|
77
96
|
| `sl subscriptions list` | List subscriptions with status, target table, format, and last success |
|
|
78
97
|
| `sl subscriptions get <id\|name>` | Show full config, filter, retry/circuit state |
|
|
79
|
-
| `sl subscriptions update <id\|name> --url <url> [--filter key.gte=value]` | Patch URL, filter, format, runtime, retry, timeout, concurrency |
|
|
98
|
+
| `sl subscriptions update <id\|name> --url <url> [--auth-token <token>] [--filter key.gte=value]` | Patch URL, bearer auth, filter, format, runtime, retry, timeout, concurrency |
|
|
80
99
|
| `sl subscriptions pause/resume <id\|name>` | Stop or restart delivery |
|
|
81
100
|
| `sl subscriptions rotate-secret <id\|name>` | Rotate signing secret and print the new value once |
|
|
82
101
|
| `sl subscriptions deliveries <id\|name>` | Last 100 delivery attempts |
|
|
@@ -99,7 +118,7 @@ invocation. No long-lived key on disk.
|
|
|
99
118
|
| Command | What it does |
|
|
100
119
|
|---|---|
|
|
101
120
|
| `sl subgraphs new <name>` | Scaffold a subgraph definition file |
|
|
102
|
-
| `sl subgraphs deploy <file
|
|
121
|
+
| `sl subgraphs deploy <file> [--start-block <n>]` | Deploy to the active instance; `--start-block` overrides the definition start block for that deploy |
|
|
103
122
|
| `sl subgraphs dev <file>` | Watch + hot-redeploy |
|
|
104
123
|
| `sl subgraphs list` | List deployed subgraphs |
|
|
105
124
|
| `sl subgraphs status <name>` | Indexing progress, row counts, gaps |
|
package/dist/cli.js
CHANGED
|
@@ -2529,7 +2529,8 @@ async function request(url, opts) {
|
|
|
2529
2529
|
const res = await fetch(url, {
|
|
2530
2530
|
method: opts.method ?? "GET",
|
|
2531
2531
|
headers,
|
|
2532
|
-
body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined
|
|
2532
|
+
body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
|
|
2533
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
2533
2534
|
});
|
|
2534
2535
|
if (!res.ok) {
|
|
2535
2536
|
let body = {};
|
|
@@ -2559,7 +2560,7 @@ async function httpPlatform(path2, opts = {}) {
|
|
|
2559
2560
|
async function httpPlatformAnon(path2, opts = {}) {
|
|
2560
2561
|
return request(`${PLATFORM_API_URL}${path2}`, opts);
|
|
2561
2562
|
}
|
|
2562
|
-
var CliHttpError, PLATFORM_API_URL;
|
|
2563
|
+
var CliHttpError, PLATFORM_API_URL, REQUEST_TIMEOUT_MS = 30000;
|
|
2563
2564
|
var init_http = __esm(() => {
|
|
2564
2565
|
init_session();
|
|
2565
2566
|
CliHttpError = class CliHttpError extends Error {
|
|
@@ -12996,7 +12997,7 @@ class StacksApiClient {
|
|
|
12996
12997
|
constructor(network = "mainnet", apiKey, apiUrl, slApiUrl) {
|
|
12997
12998
|
this.useProxy = !apiUrl && network !== "devnet";
|
|
12998
12999
|
if (this.useProxy) {
|
|
12999
|
-
this.baseUrl = slApiUrl || "";
|
|
13000
|
+
this.baseUrl = slApiUrl?.replace(/\/$/, "") || "";
|
|
13000
13001
|
this.headers = {};
|
|
13001
13002
|
} else {
|
|
13002
13003
|
this.baseUrl = apiUrl || process.env.STACKS_NODE_RPC_URL || "http://localhost:3999";
|
|
@@ -13007,24 +13008,31 @@ class StacksApiClient {
|
|
|
13007
13008
|
if (!this.useProxy || this.baseUrl)
|
|
13008
13009
|
return;
|
|
13009
13010
|
const { apiUrl, ephemeralKey } = await resolveActiveTenant();
|
|
13010
|
-
this.baseUrl = apiUrl;
|
|
13011
|
+
this.baseUrl = apiUrl.replace(/\/$/, "");
|
|
13011
13012
|
this.headers = { authorization: `Bearer ${ephemeralKey}` };
|
|
13012
13013
|
}
|
|
13014
|
+
describeContractInfoSource() {
|
|
13015
|
+
if (this.useProxy)
|
|
13016
|
+
return "Secondlayer node";
|
|
13017
|
+
return `Stacks node RPC at ${this.baseUrl}`;
|
|
13018
|
+
}
|
|
13013
13019
|
async fetchWithErrorHandling(url, resourceType, resourceId) {
|
|
13014
13020
|
try {
|
|
13015
|
-
const response2 = await
|
|
13021
|
+
const response2 = await contractFetch(url, {
|
|
13016
13022
|
headers: this.headers,
|
|
13017
13023
|
responseType: "json"
|
|
13018
13024
|
});
|
|
13019
13025
|
return response2.body;
|
|
13020
13026
|
} catch (error2) {
|
|
13021
|
-
|
|
13027
|
+
const statusCode = typeof error2 === "object" && error2 !== null && "response" in error2 && typeof error2.response === "object" && error2.response !== null && "statusCode" in error2.response ? error2.response.statusCode : undefined;
|
|
13028
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
13029
|
+
if (statusCode === 401) {
|
|
13022
13030
|
throw new Error("Authentication required. Run: secondlayer auth login");
|
|
13023
13031
|
}
|
|
13024
|
-
if (
|
|
13032
|
+
if (statusCode === 404) {
|
|
13025
13033
|
throw new Error(`${resourceType} not found: ${resourceId}`);
|
|
13026
13034
|
}
|
|
13027
|
-
throw new Error(`Failed to fetch ${resourceType.toLowerCase()}: ${
|
|
13035
|
+
throw new Error(`Failed to fetch ${resourceType.toLowerCase()}: ${message}`);
|
|
13028
13036
|
}
|
|
13029
13037
|
}
|
|
13030
13038
|
async getContractInfo(contractId) {
|
|
@@ -13045,17 +13053,16 @@ class StacksApiClient {
|
|
|
13045
13053
|
return data.source;
|
|
13046
13054
|
}
|
|
13047
13055
|
}
|
|
13048
|
-
var
|
|
13056
|
+
var contractFetch;
|
|
13049
13057
|
var init_api = __esm(() => {
|
|
13050
13058
|
init_source3();
|
|
13051
13059
|
init_resolve_tenant();
|
|
13052
|
-
|
|
13053
|
-
timeout: { request:
|
|
13060
|
+
contractFetch = source_default2.extend({
|
|
13061
|
+
timeout: { request: 15000 },
|
|
13054
13062
|
retry: {
|
|
13055
|
-
limit:
|
|
13063
|
+
limit: 0,
|
|
13056
13064
|
methods: ["GET", "POST"],
|
|
13057
|
-
statusCodes: [408, 429, 500, 502, 503, 504]
|
|
13058
|
-
calculateDelay: ({ attemptCount }) => attemptCount * 1000
|
|
13065
|
+
statusCodes: [408, 429, 500, 502, 503, 504]
|
|
13059
13066
|
}
|
|
13060
13067
|
});
|
|
13061
13068
|
});
|
|
@@ -32224,7 +32231,7 @@ Examples:`));
|
|
|
32224
32231
|
No .clar files or contract addresses matched the provided inputs`));
|
|
32225
32232
|
process.exit(1);
|
|
32226
32233
|
}
|
|
32227
|
-
const apiKey = options3.apiKey || process.env.HIRO_API_KEY;
|
|
32234
|
+
const apiKey = options3.apiKey || process.env.STACKS_NODE_API_KEY || process.env.HIRO_API_KEY;
|
|
32228
32235
|
config = await buildConfigFromInputs(parsedInputs, options3.out, apiKey);
|
|
32229
32236
|
} else {
|
|
32230
32237
|
config = await loadConfig2(options3.config);
|
|
@@ -32430,7 +32437,7 @@ var {
|
|
|
32430
32437
|
// package.json
|
|
32431
32438
|
var package_default = {
|
|
32432
32439
|
name: "@secondlayer/cli",
|
|
32433
|
-
version: "3.
|
|
32440
|
+
version: "3.5.0",
|
|
32434
32441
|
description: "CLI for subgraphs and blockchain indexing on Stacks",
|
|
32435
32442
|
type: "module",
|
|
32436
32443
|
bin: {
|
|
@@ -32472,11 +32479,11 @@ var package_default = {
|
|
|
32472
32479
|
license: "MIT",
|
|
32473
32480
|
dependencies: {
|
|
32474
32481
|
"@inquirer/prompts": "^8.2.0",
|
|
32475
|
-
"@secondlayer/bundler": "^0.3.
|
|
32476
|
-
"@secondlayer/sdk": "^3.
|
|
32477
|
-
"@secondlayer/shared": "^4.
|
|
32482
|
+
"@secondlayer/bundler": "^0.3.2",
|
|
32483
|
+
"@secondlayer/sdk": "^3.2.0",
|
|
32484
|
+
"@secondlayer/shared": "^4.2.0",
|
|
32478
32485
|
"@secondlayer/stacks": "^2.0.0",
|
|
32479
|
-
"@secondlayer/subgraphs": "^1.2.
|
|
32486
|
+
"@secondlayer/subgraphs": "^1.2.1",
|
|
32480
32487
|
"@biomejs/js-api": "^0.7.0",
|
|
32481
32488
|
"@biomejs/wasm-nodejs": "^1.9.0",
|
|
32482
32489
|
esbuild: "^0.19.0",
|
|
@@ -33138,11 +33145,22 @@ async function promptFor(_name, opts) {
|
|
|
33138
33145
|
});
|
|
33139
33146
|
return { runtime, subgraph, table, url };
|
|
33140
33147
|
}
|
|
33148
|
+
function buildSubscriptionAuthConfig(authToken) {
|
|
33149
|
+
if (authToken === undefined)
|
|
33150
|
+
return;
|
|
33151
|
+
const token = authToken.trim();
|
|
33152
|
+
if (token.length === 0) {
|
|
33153
|
+
throw new Error("--auth-token must not be empty");
|
|
33154
|
+
}
|
|
33155
|
+
return { authType: "bearer", token };
|
|
33156
|
+
}
|
|
33141
33157
|
async function createSubscription(name, opts) {
|
|
33142
33158
|
const { runtime, subgraph, table, url } = await promptFor(name, opts);
|
|
33143
33159
|
let filter;
|
|
33160
|
+
let authConfig;
|
|
33144
33161
|
try {
|
|
33145
33162
|
filter = parseSubscriptionFilter(opts.filter);
|
|
33163
|
+
authConfig = buildSubscriptionAuthConfig(opts.authToken);
|
|
33146
33164
|
} catch (err) {
|
|
33147
33165
|
error(err instanceof Error ? err.message : String(err));
|
|
33148
33166
|
process.exit(1);
|
|
@@ -33187,7 +33205,8 @@ async function createSubscription(name, opts) {
|
|
|
33187
33205
|
url,
|
|
33188
33206
|
format: FORMAT_BY_RUNTIME[runtime],
|
|
33189
33207
|
runtime,
|
|
33190
|
-
...filter ? { filter } : {}
|
|
33208
|
+
...filter ? { filter } : {},
|
|
33209
|
+
...authConfig ? { authConfig } : {}
|
|
33191
33210
|
});
|
|
33192
33211
|
signingSecret = res.signingSecret;
|
|
33193
33212
|
success(`Subscription provisioned: ${blue(res.subscription.id)}`);
|
|
@@ -33253,7 +33272,7 @@ async function getSubscriptionClient(opts) {
|
|
|
33253
33272
|
}
|
|
33254
33273
|
function registerCreateCommand(program2) {
|
|
33255
33274
|
const create = program2.command("create").description("Scaffold new resources (subscription receivers, etc.)");
|
|
33256
|
-
create.command("subscription <name>").description("Scaffold a subscription receiver for a runtime").option("-r, --runtime <runtime>", "inngest | trigger | cloudflare | node").option("-s, --subgraph <name>", "Subgraph to subscribe to").option("-t, --table <name>", "Table to subscribe to").option("-u, --url <url>", "Webhook URL").option("--filter <kv...>", "Filter as key=value (supports .eq/.neq/.gt/.gte/.lt/.lte suffixes)").option("--service-key <key>", "SL_SERVICE_KEY override").option("--base-url <url>", "SL_API_URL override").option("--skip-api", "Copy template only, don't call the API").action(async (name, options) => {
|
|
33275
|
+
create.command("subscription <name>").description("Scaffold a subscription receiver for a runtime").option("-r, --runtime <runtime>", "inngest | trigger | cloudflare | node").option("-s, --subgraph <name>", "Subgraph to subscribe to").option("-t, --table <name>", "Table to subscribe to").option("-u, --url <url>", "Webhook URL").option("--auth-token <token>", "Bearer token for receiver API auth").option("--filter <kv...>", "Filter as key=value (supports .eq/.neq/.gt/.gte/.lt/.lte suffixes)").option("--service-key <key>", "SL_SERVICE_KEY override").option("--base-url <url>", "SL_API_URL override").option("--skip-api", "Copy template only, don't call the API").action(async (name, options) => {
|
|
33257
33276
|
await createSubscription(name, options);
|
|
33258
33277
|
});
|
|
33259
33278
|
}
|
|
@@ -33412,6 +33431,9 @@ function buildUpdatePatch(options) {
|
|
|
33412
33431
|
patch.name = options.name;
|
|
33413
33432
|
if (options.url)
|
|
33414
33433
|
patch.url = options.url;
|
|
33434
|
+
const authConfig = buildSubscriptionAuthConfig(options.authToken);
|
|
33435
|
+
if (authConfig)
|
|
33436
|
+
patch.authConfig = authConfig;
|
|
33415
33437
|
if (options.format) {
|
|
33416
33438
|
patch.format = options.format;
|
|
33417
33439
|
}
|
|
@@ -33722,7 +33744,7 @@ ${data.length} subscription(s) total`));
|
|
|
33722
33744
|
handleApiError(err, "get subscription");
|
|
33723
33745
|
}
|
|
33724
33746
|
});
|
|
33725
|
-
commonOptions(subscriptions.command("update <idOrName>").description("Update subscription config").option("--name <name>", "Rename subscription").option("--url <url>", "Webhook URL").option("--format <format>", "standard-webhooks | inngest | trigger | cloudflare | cloudevents | raw").option("--runtime <runtime>", "inngest | trigger | cloudflare | node | none").option("--filter <kv...>", "Filter as key=value (supports .eq/.neq/.gt/.gte/.lt/.lte suffixes)").option("--clear-filter", "Replace filter with {}").option("--max-retries <n>", "Maximum delivery retries").option("--timeout-ms <n>", "Delivery timeout in milliseconds").option("--concurrency <n>", "Per-subscription delivery concurrency").option("--json", "Output as JSON")).action(async (idOrName, options) => {
|
|
33747
|
+
commonOptions(subscriptions.command("update <idOrName>").description("Update subscription config").option("--name <name>", "Rename subscription").option("--url <url>", "Webhook URL").option("--auth-token <token>", "Set bearer token auth config").option("--format <format>", "standard-webhooks | inngest | trigger | cloudflare | cloudevents | raw").option("--runtime <runtime>", "inngest | trigger | cloudflare | node | none").option("--filter <kv...>", "Filter as key=value (supports .eq/.neq/.gt/.gte/.lt/.lte suffixes)").option("--clear-filter", "Replace filter with {}").option("--max-retries <n>", "Maximum delivery retries").option("--timeout-ms <n>", "Delivery timeout in milliseconds").option("--concurrency <n>", "Per-subscription delivery concurrency").option("--json", "Output as JSON")).action(async (idOrName, options) => {
|
|
33726
33748
|
try {
|
|
33727
33749
|
const client = await getSubscriptionClient(options);
|
|
33728
33750
|
const patch = buildUpdatePatch(options);
|
|
@@ -34548,6 +34570,61 @@ export default defineSubgraph({
|
|
|
34548
34570
|
|
|
34549
34571
|
// src/commands/subgraphs.ts
|
|
34550
34572
|
init_api();
|
|
34573
|
+
function parseStartBlockOption(value) {
|
|
34574
|
+
if (value === undefined)
|
|
34575
|
+
return;
|
|
34576
|
+
const trimmed = value.trim();
|
|
34577
|
+
if (!/^(0|[1-9]\d*)$/.test(trimmed)) {
|
|
34578
|
+
throw new Error("--start-block must be a nonnegative integer");
|
|
34579
|
+
}
|
|
34580
|
+
const parsed = Number(trimmed);
|
|
34581
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
34582
|
+
throw new Error("--start-block must be a safe integer");
|
|
34583
|
+
}
|
|
34584
|
+
return parsed;
|
|
34585
|
+
}
|
|
34586
|
+
function createSubgraphDeployPreview(def, options2 = {}) {
|
|
34587
|
+
const tableColumns = Object.entries(def.schema).map(([table, schema]) => `${table}: ${Object.keys(schema.columns).join(", ") || "(no columns)"}`);
|
|
34588
|
+
return {
|
|
34589
|
+
name: def.name,
|
|
34590
|
+
version: def.version ?? "(auto)",
|
|
34591
|
+
description: def.description ?? "",
|
|
34592
|
+
startBlock: String(def.startBlock ?? 1),
|
|
34593
|
+
sources: Object.keys(def.sources).join(", ") || "(none)",
|
|
34594
|
+
handlers: Object.keys(def.handlers).join(", ") || "(none)",
|
|
34595
|
+
tables: Object.keys(def.schema).join(", ") || "(none)",
|
|
34596
|
+
tableColumns,
|
|
34597
|
+
...options2.bundleBytes !== undefined ? { bundleSize: `${options2.bundleBytes} bytes` } : {}
|
|
34598
|
+
};
|
|
34599
|
+
}
|
|
34600
|
+
function printSubgraphDeployPreview(preview, context) {
|
|
34601
|
+
success("Subgraph deploy dry run passed");
|
|
34602
|
+
const pairs = [
|
|
34603
|
+
["File", context.file],
|
|
34604
|
+
["Network", context.network],
|
|
34605
|
+
["Name", preview.name],
|
|
34606
|
+
["Version", preview.version],
|
|
34607
|
+
["Start Block", preview.startBlock],
|
|
34608
|
+
["Sources", preview.sources],
|
|
34609
|
+
["Handlers", preview.handlers],
|
|
34610
|
+
["Tables", preview.tables]
|
|
34611
|
+
];
|
|
34612
|
+
if (preview.bundleSize)
|
|
34613
|
+
pairs.push(["Bundle Size", preview.bundleSize]);
|
|
34614
|
+
console.log(formatKeyValue(pairs));
|
|
34615
|
+
if (preview.description) {
|
|
34616
|
+
console.log(`
|
|
34617
|
+
${dim("Description")} ${preview.description}`);
|
|
34618
|
+
}
|
|
34619
|
+
if (preview.tableColumns.length > 0) {
|
|
34620
|
+
console.log(`
|
|
34621
|
+
${dim("Columns")}`);
|
|
34622
|
+
for (const line of preview.tableColumns)
|
|
34623
|
+
console.log(` ${line}`);
|
|
34624
|
+
}
|
|
34625
|
+
const deployTarget = context.bundled ? "tenant API" : "local database";
|
|
34626
|
+
info(`Dry run only. No ${deployTarget} changes were made.`);
|
|
34627
|
+
}
|
|
34551
34628
|
function registerSubgraphsCommand(program2) {
|
|
34552
34629
|
const subgraphs = program2.command("subgraphs").description("Manage materialized subgraphs");
|
|
34553
34630
|
subgraphs.command("new <name>").description("Scaffold a new subgraph definition file").action(async (name) => {
|
|
@@ -34626,34 +34703,54 @@ Stopped watching.`);
|
|
|
34626
34703
|
});
|
|
34627
34704
|
await new Promise(() => {});
|
|
34628
34705
|
});
|
|
34629
|
-
subgraphs.command("deploy <file>").description("Deploy a subgraph definition file").option("--version <semver>", "Explicit version (default: auto-increment patch)").option("--force", "Skip confirmation prompt for reindex operations").action(async (file, options2) => {
|
|
34706
|
+
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) => {
|
|
34630
34707
|
try {
|
|
34631
34708
|
const absPath = resolve3(file);
|
|
34632
34709
|
const config = await loadConfig();
|
|
34710
|
+
const dryRun = options2.dryRun || options2.preview;
|
|
34711
|
+
const startBlock = parseStartBlockOption(options2.startBlock);
|
|
34712
|
+
if (startBlock !== undefined) {
|
|
34713
|
+
warn(`--start-block ${startBlock} overrides the definition's startBlock for this deploy.`);
|
|
34714
|
+
}
|
|
34633
34715
|
info(`Loading subgraph from ${absPath}`);
|
|
34634
34716
|
const mod = await import(absPath);
|
|
34635
34717
|
const def = mod.default ?? mod;
|
|
34718
|
+
const effectiveDef = startBlock === undefined ? def : { ...def, startBlock };
|
|
34636
34719
|
const { validateSubgraphDefinition } = await import("@secondlayer/subgraphs/validate");
|
|
34637
|
-
validateSubgraphDefinition(
|
|
34720
|
+
const validated = validateSubgraphDefinition(effectiveDef);
|
|
34638
34721
|
if (config.network !== "local") {
|
|
34639
|
-
info(
|
|
34722
|
+
info(`${dryRun ? "Bundling for remote deploy dry run" : "Bundling for remote deploy"} (${config.network})...`);
|
|
34640
34723
|
const { readFile: readFile4 } = await import("node:fs/promises");
|
|
34641
34724
|
const source = await readFile4(absPath, "utf8");
|
|
34642
34725
|
const { bundleSubgraphCode } = await import("@secondlayer/bundler");
|
|
34643
34726
|
const bundled = await bundleSubgraphCode(source);
|
|
34644
34727
|
const handlerCode = bundled.handlerCode;
|
|
34728
|
+
if (dryRun) {
|
|
34729
|
+
printSubgraphDeployPreview(createSubgraphDeployPreview({
|
|
34730
|
+
...validated,
|
|
34731
|
+
version: options2.version ?? validated.version
|
|
34732
|
+
}, {
|
|
34733
|
+
bundleBytes: Buffer.byteLength(handlerCode, "utf8")
|
|
34734
|
+
}), {
|
|
34735
|
+
network: config.network,
|
|
34736
|
+
file: absPath,
|
|
34737
|
+
bundled: true
|
|
34738
|
+
});
|
|
34739
|
+
return;
|
|
34740
|
+
}
|
|
34645
34741
|
const result = await deploySubgraphApi({
|
|
34646
|
-
name:
|
|
34742
|
+
name: effectiveDef.name,
|
|
34647
34743
|
version: options2.version,
|
|
34648
|
-
description:
|
|
34649
|
-
sources:
|
|
34650
|
-
schema:
|
|
34651
|
-
handlerCode
|
|
34744
|
+
description: effectiveDef.description,
|
|
34745
|
+
sources: effectiveDef.sources,
|
|
34746
|
+
schema: effectiveDef.schema,
|
|
34747
|
+
handlerCode,
|
|
34748
|
+
...startBlock !== undefined ? { startBlock } : {}
|
|
34652
34749
|
});
|
|
34653
34750
|
if (result.action === "unchanged") {
|
|
34654
|
-
info(`Subgraph "${
|
|
34751
|
+
info(`Subgraph "${effectiveDef.name}" is up to date (v${result.version} — no changes)`);
|
|
34655
34752
|
} else if (result.action === "created") {
|
|
34656
|
-
success(`Subgraph "${
|
|
34753
|
+
success(`Subgraph "${effectiveDef.name}" created → v${result.version}`);
|
|
34657
34754
|
} else if (result.action === "reindexed") {
|
|
34658
34755
|
if (result.diff) {
|
|
34659
34756
|
const { addedTables, addedColumns, breakingChanges } = result.diff;
|
|
@@ -34675,7 +34772,7 @@ Stopped watching.`);
|
|
|
34675
34772
|
info("Aborted.");
|
|
34676
34773
|
process.exit(0);
|
|
34677
34774
|
}
|
|
34678
|
-
success(`Subgraph "${
|
|
34775
|
+
success(`Subgraph "${effectiveDef.name}" updated → v${result.version} (reindexing)`);
|
|
34679
34776
|
} else {
|
|
34680
34777
|
if (result.diff) {
|
|
34681
34778
|
const { addedTables, addedColumns } = result.diff;
|
|
@@ -34685,23 +34782,35 @@ Stopped watching.`);
|
|
|
34685
34782
|
info(` + columns: ${t}.${cols.join(", ")}`);
|
|
34686
34783
|
}
|
|
34687
34784
|
}
|
|
34688
|
-
success(`Subgraph "${
|
|
34785
|
+
success(`Subgraph "${effectiveDef.name}" updated → v${result.version}`);
|
|
34689
34786
|
}
|
|
34690
34787
|
} else {
|
|
34788
|
+
if (dryRun) {
|
|
34789
|
+
printSubgraphDeployPreview(createSubgraphDeployPreview({
|
|
34790
|
+
...validated,
|
|
34791
|
+
version: options2.version ?? validated.version
|
|
34792
|
+
}), {
|
|
34793
|
+
network: config.network,
|
|
34794
|
+
file: absPath,
|
|
34795
|
+
bundled: false
|
|
34796
|
+
});
|
|
34797
|
+
return;
|
|
34798
|
+
}
|
|
34691
34799
|
const { deploySchema } = await import("@secondlayer/subgraphs");
|
|
34692
34800
|
const { getDb: getDb2, closeDb } = await import("@secondlayer/shared/db");
|
|
34693
34801
|
const db = getDb2();
|
|
34694
|
-
const result = await deploySchema(db,
|
|
34695
|
-
version: options2.version
|
|
34802
|
+
const result = await deploySchema(db, effectiveDef, absPath, {
|
|
34803
|
+
version: options2.version,
|
|
34804
|
+
forceReindex: startBlock !== undefined
|
|
34696
34805
|
});
|
|
34697
34806
|
if (result.action === "unchanged") {
|
|
34698
|
-
info(`Subgraph "${
|
|
34807
|
+
info(`Subgraph "${effectiveDef.name}" is up to date (v${result.version} — no changes)`);
|
|
34699
34808
|
} else if (result.action === "created") {
|
|
34700
|
-
success(`Subgraph "${
|
|
34809
|
+
success(`Subgraph "${effectiveDef.name}" created → v${result.version}`);
|
|
34701
34810
|
} else if (result.action === "reindexed") {
|
|
34702
|
-
success(`Subgraph "${
|
|
34811
|
+
success(`Subgraph "${effectiveDef.name}" updated → v${result.version} (reindexing)`);
|
|
34703
34812
|
} else {
|
|
34704
|
-
success(`Subgraph "${
|
|
34813
|
+
success(`Subgraph "${effectiveDef.name}" updated → v${result.version}`);
|
|
34705
34814
|
}
|
|
34706
34815
|
await closeDb();
|
|
34707
34816
|
}
|
|
@@ -34921,7 +35030,7 @@ ${rows.length} row(s)`));
|
|
|
34921
35030
|
handleApiError(err, "delete subgraph");
|
|
34922
35031
|
}
|
|
34923
35032
|
});
|
|
34924
|
-
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>", "
|
|
35033
|
+
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").action(async (contractAddress, options2) => {
|
|
34925
35034
|
try {
|
|
34926
35035
|
if (!options2.output) {
|
|
34927
35036
|
error("--output <path> is required");
|
|
@@ -34929,9 +35038,9 @@ ${rows.length} row(s)`));
|
|
|
34929
35038
|
}
|
|
34930
35039
|
const outPath = resolve3(options2.output);
|
|
34931
35040
|
const network = inferNetwork(contractAddress) ?? "mainnet";
|
|
34932
|
-
const apiKey = options2.apiKey ?? process.env.HIRO_API_KEY;
|
|
34933
|
-
info(`Fetching ABI for ${contractAddress}...`);
|
|
35041
|
+
const apiKey = options2.apiKey ?? process.env.STACKS_NODE_API_KEY ?? process.env.HIRO_API_KEY;
|
|
34934
35042
|
const client = new StacksApiClient(network, apiKey);
|
|
35043
|
+
info(`Fetching ABI for ${contractAddress} via ${client.describeContractInfoSource()}...`);
|
|
34935
35044
|
const contractInfo = await client.getContractInfo(contractAddress);
|
|
34936
35045
|
const abi = parseApiResponse(contractInfo);
|
|
34937
35046
|
info("Generating scaffold...");
|
|
@@ -36289,7 +36398,7 @@ Quickstart:
|
|
|
36289
36398
|
$ sl instance create --plan launch # Provision a dedicated instance
|
|
36290
36399
|
$ sl subgraphs deploy ./x.ts # Deploy a subgraph — targets your instance
|
|
36291
36400
|
`);
|
|
36292
|
-
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>", "
|
|
36401
|
+
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) => {
|
|
36293
36402
|
const { generate: generate2 } = await Promise.resolve().then(() => (init_generate(), exports_generate));
|
|
36294
36403
|
await generate2(files, options3);
|
|
36295
36404
|
});
|
|
@@ -36314,5 +36423,5 @@ registerLocalCommand(program);
|
|
|
36314
36423
|
registerAccountCommand(program);
|
|
36315
36424
|
program.parse();
|
|
36316
36425
|
|
|
36317
|
-
//# debugId=
|
|
36426
|
+
//# debugId=02FDB52BEFC9082464756E2164756E21
|
|
36318
36427
|
//# sourceMappingURL=cli.js.map
|