@secondlayer/cli 3.1.2-alpha.0 → 3.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secondlayer/cli",
3
- "version": "3.1.2-alpha.0",
3
+ "version": "3.2.0",
4
4
  "description": "CLI for subgraphs and blockchain indexing on Stacks",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,11 +42,11 @@
42
42
  "license": "MIT",
43
43
  "dependencies": {
44
44
  "@inquirer/prompts": "^8.2.0",
45
- "@secondlayer/bundler": "^0.3.1-alpha.0",
46
- "@secondlayer/sdk": "^3.0.0-alpha.0",
47
- "@secondlayer/shared": "^3.0.0-alpha.0",
48
- "@secondlayer/stacks": "^1.0.0-alpha.0",
49
- "@secondlayer/subgraphs": "^1.0.0-alpha.0",
45
+ "@secondlayer/bundler": "^0.3.1",
46
+ "@secondlayer/sdk": "^3.0.0",
47
+ "@secondlayer/shared": "^3.0.0",
48
+ "@secondlayer/stacks": "^1.0.0",
49
+ "@secondlayer/subgraphs": "^1.0.0",
50
50
  "@biomejs/js-api": "^0.7.0",
51
51
  "@biomejs/wasm-nodejs": "^1.9.0",
52
52
  "esbuild": "^0.19.0",
@@ -0,0 +1,39 @@
1
+ # {{NAME}} — Cloudflare Workflows subscription receiver
2
+
3
+ A Cloudflare Workflow triggered by a Secondlayer subscription via the
4
+ `workflows/instances` REST API.
5
+
6
+ ## Run
7
+
8
+ ```bash
9
+ bun install
10
+ npx wrangler login
11
+ npx wrangler dev
12
+ ```
13
+
14
+ `wrangler dev` spins up a local runtime. Your subscription URL should
15
+ point at Cloudflare's API:
16
+
17
+ ```
18
+ https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/workflows/{{NAME}}/instances
19
+ ```
20
+
21
+ With `auth_config`:
22
+
23
+ ```json
24
+ { "authType": "bearer", "token": "<CF_API_TOKEN>" }
25
+ ```
26
+
27
+ The token must have `Workflows: Edit` scope.
28
+
29
+ ## Signature verification
30
+
31
+ Cloudflare authenticates the API call with the bearer token. Inside your
32
+ workflow the `event.payload.params._outboxId` is a stable dedup key if
33
+ you want idempotent replay handling.
34
+
35
+ ## Deploy
36
+
37
+ ```bash
38
+ npx wrangler deploy
39
+ ```
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "{{NAME}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "wrangler dev",
7
+ "deploy": "wrangler deploy",
8
+ "types": "wrangler types"
9
+ },
10
+ "devDependencies": {
11
+ "@cloudflare/workers-types": "^4.20250101.0",
12
+ "typescript": "^5",
13
+ "wrangler": "^4.0.0"
14
+ }
15
+ }
@@ -0,0 +1,34 @@
1
+ import {
2
+ WorkflowEntrypoint,
3
+ type WorkflowEvent,
4
+ type WorkflowStep,
5
+ } from "cloudflare:workers";
6
+
7
+ interface Params {
8
+ _type: string;
9
+ _outboxId: string;
10
+ [k: string]: unknown;
11
+ }
12
+
13
+ export class OnSubgraphEvent extends WorkflowEntrypoint<Env, Params> {
14
+ async run(event: WorkflowEvent<Params>, step: WorkflowStep): Promise<void> {
15
+ const params = event.payload;
16
+ await step.do("handle", async () => {
17
+ console.log("[subgraph event]", params._type, params);
18
+ // TODO: plug in your business logic.
19
+ return { ok: true };
20
+ });
21
+ }
22
+ }
23
+
24
+ interface Env {
25
+ MY_WORKFLOW: Workflow<Params>;
26
+ }
27
+
28
+ export default {
29
+ async fetch(_req: Request): Promise<Response> {
30
+ return new Response(
31
+ "This worker is a Workflow receiver — trigger it via the Cloudflare API.",
32
+ );
33
+ },
34
+ };
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "skipLibCheck": true,
8
+ "types": ["@cloudflare/workers-types"]
9
+ }
10
+ }
@@ -0,0 +1,8 @@
1
+ name = "{{NAME}}"
2
+ main = "src/index.ts"
3
+ compatibility_date = "2025-01-01"
4
+
5
+ [[workflows]]
6
+ name = "{{NAME}}"
7
+ binding = "MY_WORKFLOW"
8
+ class_name = "OnSubgraphEvent"
@@ -0,0 +1,29 @@
1
+ # {{NAME}} — Inngest subscription receiver
2
+
3
+ Inngest Dev Server with a function wired to `{{EVENT_NAME}}`. Events come
4
+ from your Secondlayer subscription — Inngest handles retries, concurrency,
5
+ and durable execution.
6
+
7
+ ## Run
8
+
9
+ ```bash
10
+ bun install
11
+ bun run dev
12
+ ```
13
+
14
+ This starts the Inngest dev UI at `http://localhost:8288`. Point your
15
+ subscription URL at the Inngest event endpoint shown in the dev UI
16
+ (typically `http://localhost:8288/e/{eventKey}` for local, or the hosted
17
+ `https://inn.gs/e/{eventKey}` endpoint for cloud).
18
+
19
+ ## Signature verification
20
+
21
+ Inngest uses its own event key auth — there's no HMAC to verify. The event
22
+ key in the URL IS the auth token. For self-hosted Inngest add an ingress
23
+ allowlist so only the Secondlayer emitter can post.
24
+
25
+ ## Deploy
26
+
27
+ - **Inngest Cloud**: create an app at https://inngest.com, copy the event
28
+ key, and set it in the subscription URL: `https://inn.gs/e/{EVENT_KEY}`.
29
+ - **Self-host**: see https://www.inngest.com/docs/self-hosting
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "{{NAME}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "npx inngest-cli@latest dev",
8
+ "start": "bun --hot src/server.ts"
9
+ },
10
+ "dependencies": {
11
+ "hono": "^4.6.0",
12
+ "inngest": "^3.27.0"
13
+ },
14
+ "devDependencies": {
15
+ "@types/bun": "latest",
16
+ "typescript": "^5"
17
+ }
18
+ }
@@ -0,0 +1,19 @@
1
+ import { Inngest } from "inngest";
2
+
3
+ export const inngest = new Inngest({
4
+ id: "{{NAME}}",
5
+ });
6
+
7
+ export const onSubgraphEvent = inngest.createFunction(
8
+ { id: "on-subgraph-event", name: "On Subgraph Event" },
9
+ { event: "{{EVENT_NAME}}" },
10
+ async ({ event, step }) => {
11
+ // `event.data` is the row payload delivered by Secondlayer.
12
+ const row = event.data as Record<string, unknown>;
13
+ await step.run("handle", async () => {
14
+ console.log("[subgraph event]", event.name, row);
15
+ // TODO: plug in your business logic — AI SDK, HTTP, DB writes, etc.
16
+ return { ok: true };
17
+ });
18
+ },
19
+ );
@@ -0,0 +1,14 @@
1
+ import { serve } from "inngest/hono";
2
+ import { Hono } from "hono";
3
+ import { inngest, onSubgraphEvent } from "./inngest.ts";
4
+
5
+ const app = new Hono();
6
+ app.on(
7
+ ["GET", "POST", "PUT"],
8
+ "/api/inngest",
9
+ serve({ client: inngest, functions: [onSubgraphEvent] }),
10
+ );
11
+
12
+ const PORT = Number.parseInt(process.env.PORT ?? "3000", 10);
13
+ Bun.serve({ port: PORT, fetch: app.fetch });
14
+ console.log(`{{NAME}} (Inngest host) listening on :${PORT}`);
@@ -0,0 +1,11 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "types": ["bun-types"]
10
+ }
11
+ }
@@ -0,0 +1,2 @@
1
+ SIGNING_SECRET=
2
+ PORT=3000
@@ -0,0 +1,34 @@
1
+ # {{NAME}} — Node.js subscription receiver
2
+
3
+ Hono HTTP server that verifies Standard Webhooks signatures and routes events
4
+ to your business logic. No framework lock-in.
5
+
6
+ ## Run
7
+
8
+ ```bash
9
+ bun install
10
+ cp .env.example .env # paste the signingSecret you copied on create
11
+ bun run dev
12
+ ```
13
+
14
+ The server listens on `:3000`. Your subscription's URL should point at
15
+ `http://<host>:3000/webhook`.
16
+
17
+ ## Signature verification
18
+
19
+ Every request is verified before your handler runs. Forged requests return
20
+ `401`. The logic lives in `src/index.ts`; swap `@secondlayer/shared` for the
21
+ [Svix verify library](https://docs.standardwebhooks.com/libraries) if you
22
+ want to decouple from `@secondlayer/shared`:
23
+
24
+ ```ts
25
+ import { Webhook } from "standardwebhooks";
26
+ const wh = new Webhook(process.env.SIGNING_SECRET!);
27
+ const event = wh.verify(body, headers);
28
+ ```
29
+
30
+ ## Deploy
31
+
32
+ Fly.io, Render, Railway, your own VM — anywhere that can run `bun` works.
33
+ Expose port 3000, set `SIGNING_SECRET`, point the subscription URL at the
34
+ public hostname.
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "{{NAME}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "bun --hot src/index.ts",
8
+ "start": "bun src/index.ts"
9
+ },
10
+ "dependencies": {
11
+ "@secondlayer/shared": "^3.0.0-beta.0",
12
+ "hono": "^4.6.0"
13
+ },
14
+ "devDependencies": {
15
+ "@types/bun": "latest",
16
+ "typescript": "^5"
17
+ }
18
+ }
@@ -0,0 +1,50 @@
1
+ import { verify } from "@secondlayer/shared/crypto/standard-webhooks";
2
+ import { Hono } from "hono";
3
+
4
+ /**
5
+ * Minimal Standard Webhooks receiver.
6
+ *
7
+ * The signing secret was shown ONCE when you ran `sl create subscription`.
8
+ * If you lost it, rotate it from the dashboard: `/subscriptions/<id>` → rotate.
9
+ */
10
+
11
+ const SIGNING_SECRET = process.env.SIGNING_SECRET;
12
+ if (!SIGNING_SECRET) {
13
+ console.error("SIGNING_SECRET not set. Copy .env.example → .env and fill it in.");
14
+ process.exit(1);
15
+ }
16
+
17
+ const app = new Hono();
18
+
19
+ interface SubgraphEvent {
20
+ type: string;
21
+ timestamp: string;
22
+ data: Record<string, unknown>;
23
+ }
24
+
25
+ app.post("/webhook", async (c) => {
26
+ const body = await c.req.text();
27
+ const headers: Record<string, string> = {};
28
+ c.req.raw.headers.forEach((v, k) => {
29
+ headers[k.toLowerCase()] = v;
30
+ });
31
+
32
+ if (!verify(body, headers, SIGNING_SECRET)) {
33
+ console.warn("signature verification failed");
34
+ return c.text("unauthorized", 401);
35
+ }
36
+
37
+ const event = JSON.parse(body) as SubgraphEvent;
38
+ await onEvent(event);
39
+ return c.text("ok", 200);
40
+ });
41
+
42
+ async function onEvent(event: SubgraphEvent): Promise<void> {
43
+ // TODO: plug in your business logic. `event.type` is
44
+ // `<subgraph>.<table>.created`; `event.data` is the row payload.
45
+ console.log("[event]", event.type, event.data);
46
+ }
47
+
48
+ const PORT = Number.parseInt(process.env.PORT ?? "3000", 10);
49
+ Bun.serve({ port: PORT, fetch: app.fetch });
50
+ console.log(`{{NAME}} listening on :${PORT}`);
@@ -0,0 +1,11 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "types": ["bun-types"]
10
+ }
11
+ }
@@ -0,0 +1,35 @@
1
+ # {{NAME}} — Trigger.dev subscription receiver
2
+
3
+ A Trigger.dev v3 task that receives subgraph events via the
4
+ `{task}/trigger` HTTP API.
5
+
6
+ ## Run
7
+
8
+ ```bash
9
+ bun install
10
+ npx trigger.dev@latest init # follow prompts
11
+ bun run dev
12
+ ```
13
+
14
+ The Trigger CLI provisions a project and shows the task endpoint URL. Your
15
+ Secondlayer subscription URL should point at:
16
+
17
+ ```
18
+ https://api.trigger.dev/api/v1/tasks/{{TASK_ID}}/trigger
19
+ ```
20
+
21
+ Set the `TRIGGER_SECRET_KEY` on the subscription via `auth_config`:
22
+
23
+ ```json
24
+ { "authType": "bearer", "token": "tr_secret_abc..." }
25
+ ```
26
+
27
+ ## Signature verification
28
+
29
+ Trigger authenticates requests with the bearer token — there's no separate
30
+ HMAC. Your secret IS the auth. Rotate via the Trigger dashboard when
31
+ needed.
32
+
33
+ ## Deploy
34
+
35
+ Run `npx trigger.dev deploy` to push to Trigger Cloud.
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "{{NAME}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "npx trigger.dev@latest dev",
8
+ "deploy": "npx trigger.dev@latest deploy"
9
+ },
10
+ "dependencies": {
11
+ "@trigger.dev/sdk": "^3.3.0"
12
+ },
13
+ "devDependencies": {
14
+ "@types/bun": "latest",
15
+ "typescript": "^5"
16
+ }
17
+ }
@@ -0,0 +1,15 @@
1
+ import { task } from "@trigger.dev/sdk/v3";
2
+
3
+ /**
4
+ * Task triggered by a Secondlayer subscription. The Secondlayer emitter
5
+ * POSTs to this task's HTTP endpoint; Trigger.dev handles durable
6
+ * execution, retries, and concurrency.
7
+ */
8
+ export const onSubgraphEvent = task({
9
+ id: "{{TASK_ID}}",
10
+ run: async (payload: Record<string, unknown>, { ctx: _ctx }) => {
11
+ console.log("[subgraph event]", payload);
12
+ // TODO: your business logic — AI SDK, HTTP, DB, chain broadcasts.
13
+ return { ok: true };
14
+ },
15
+ });
@@ -0,0 +1,6 @@
1
+ import { defineConfig } from "@trigger.dev/sdk/v3";
2
+
3
+ export default defineConfig({
4
+ project: "{{NAME}}",
5
+ dirs: ["./src/tasks"],
6
+ });
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true
9
+ }
10
+ }