@secondlayer/cli 19.4.3 → 20.0.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 +9 -11
- package/dist/cli.js +153 -600
- package/dist/cli.js.map +9 -10
- package/package.json +9 -9
- package/templates/webhooks/cloudflare/.env.example +0 -1
- package/templates/webhooks/cloudflare/README.md +0 -44
- package/templates/webhooks/cloudflare/package.json +0 -15
- package/templates/webhooks/cloudflare/src/index.ts +0 -34
- package/templates/webhooks/cloudflare/tsconfig.json +0 -10
- package/templates/webhooks/cloudflare/wrangler.toml +0 -8
- package/templates/webhooks/inngest/.env.example +0 -2
- package/templates/webhooks/inngest/README.md +0 -29
- package/templates/webhooks/inngest/package.json +0 -18
- package/templates/webhooks/inngest/src/inngest.ts +0 -19
- package/templates/webhooks/inngest/src/server.ts +0 -14
- package/templates/webhooks/inngest/tsconfig.json +0 -11
- package/templates/webhooks/node/.env.example +0 -2
- package/templates/webhooks/node/README.md +0 -36
- package/templates/webhooks/node/package.json +0 -18
- package/templates/webhooks/node/src/index.ts +0 -53
- package/templates/webhooks/node/tsconfig.json +0 -11
- package/templates/webhooks/trigger/.env.example +0 -2
- package/templates/webhooks/trigger/README.md +0 -38
- package/templates/webhooks/trigger/package.json +0 -17
- package/templates/webhooks/trigger/src/tasks/subgraph-event.ts +0 -15
- package/templates/webhooks/trigger/trigger.config.ts +0 -6
- package/templates/webhooks/trigger/tsconfig.json +0 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@secondlayer/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "20.0.1",
|
|
4
4
|
"description": "Run a Stacks index on your own hardware — set it up, restore verified history, and keep it current.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"import": "./dist/index.js"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
|
-
"files": ["dist", "
|
|
24
|
+
"files": ["dist", "README.md"],
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "bunup",
|
|
27
27
|
"dev": "bunup --watch",
|
|
@@ -37,15 +37,15 @@
|
|
|
37
37
|
"@biomejs/wasm-nodejs": "^1.9.0",
|
|
38
38
|
"@dsnp/parquetjs": "~1.8.8",
|
|
39
39
|
"@inquirer/prompts": "^8.2.0",
|
|
40
|
-
"@secondlayer/bundler": "^0.7.
|
|
41
|
-
"@secondlayer/scaffold": "^1.6.
|
|
42
|
-
"@secondlayer/sdk": "^
|
|
43
|
-
"@secondlayer/shared": "^
|
|
40
|
+
"@secondlayer/bundler": "^0.7.1",
|
|
41
|
+
"@secondlayer/scaffold": "^1.6.2",
|
|
42
|
+
"@secondlayer/sdk": "^15.0.0",
|
|
43
|
+
"@secondlayer/shared": "^12.0.0",
|
|
44
44
|
"@secondlayer/stacks": "^6.2.0",
|
|
45
|
-
"@secondlayer/subgraphs": "^
|
|
46
|
-
"@secondlayer/verify": "^0.
|
|
45
|
+
"@secondlayer/subgraphs": "^7.0.1",
|
|
46
|
+
"@secondlayer/verify": "^0.5.0",
|
|
47
47
|
"esbuild": "^0.28.2",
|
|
48
|
-
"smol-toml": "^1.
|
|
48
|
+
"smol-toml": "^1.9.1",
|
|
49
49
|
"zod": "^4.3.6"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
SIGNING_SECRET=
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# {{NAME}} — Cloudflare subscription receiver
|
|
2
|
-
|
|
3
|
-
A [Cloudflare Workflows](https://developers.cloudflare.com/workflows/)
|
|
4
|
-
instance triggered by a Secondlayer subscription via Cloudflare's
|
|
5
|
-
`workflows/instances` REST API. "Workflows" here refers to Cloudflare's
|
|
6
|
-
durable-execution product; it's separate from anything Secondlayer runs.
|
|
7
|
-
|
|
8
|
-
## Run
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
bun install
|
|
12
|
-
bunx wrangler login
|
|
13
|
-
bunx wrangler dev
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
`wrangler dev` spins up a local runtime. Your subscription URL should
|
|
17
|
-
point at Cloudflare's API:
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/workflows/{{NAME}}/instances
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Provision the subscription with bearer auth:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
secondlayer webhooks create {{NAME}} --runtime cloudflare --auth-token <CF_API_TOKEN>
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
You can also patch an existing subscription with
|
|
30
|
-
`secondlayer webhooks update {{NAME}} --auth-token <CF_API_TOKEN>`.
|
|
31
|
-
|
|
32
|
-
The token must have `Workflows: Edit` scope.
|
|
33
|
-
|
|
34
|
-
## Signature verification
|
|
35
|
-
|
|
36
|
-
Cloudflare authenticates the API call with the bearer token. Inside the
|
|
37
|
-
Workflows entrypoint, `event.payload.params._outboxId` is a stable dedup
|
|
38
|
-
key if you want idempotent replay handling.
|
|
39
|
-
|
|
40
|
-
## Deploy
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
bunx wrangler deploy
|
|
44
|
-
```
|
|
@@ -1,15 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
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
|
-
"Cloudflare Workflows receiver — trigger via the `workflows/instances` API.",
|
|
32
|
-
);
|
|
33
|
-
},
|
|
34
|
-
};
|
|
@@ -1,29 +0,0 @@
|
|
|
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 step-level state.
|
|
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
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{NAME}}",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "bunx 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
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
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
|
-
);
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { Hono } from "hono";
|
|
2
|
-
import { serve } from "inngest/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}`);
|
|
@@ -1,36 +0,0 @@
|
|
|
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
|
-
`.env` is written for you at `secondlayer webhooks create` time with the signing
|
|
9
|
-
secret already populated. Lost it: `secondlayer webhooks rotate-secret {{NAME}}`.
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
bun install
|
|
13
|
-
bun run dev
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
The server listens on `:3000`. Your subscription's URL should point at
|
|
17
|
-
`http://<host>:3000/webhook`.
|
|
18
|
-
|
|
19
|
-
## Signature verification
|
|
20
|
-
|
|
21
|
-
Every request is verified before your handler runs. Forged requests return
|
|
22
|
-
`401`. The logic lives in `src/index.ts`; swap `@secondlayer/shared` for the
|
|
23
|
-
[Svix verify library](https://docs.standardwebhooks.com/libraries) if you
|
|
24
|
-
want to decouple from `@secondlayer/shared`:
|
|
25
|
-
|
|
26
|
-
```ts
|
|
27
|
-
import { Webhook } from "standardwebhooks";
|
|
28
|
-
const wh = new Webhook(process.env.SIGNING_SECRET!);
|
|
29
|
-
const event = wh.verify(body, headers);
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Deploy
|
|
33
|
-
|
|
34
|
-
Fly.io, Render, Railway, your own VM — anywhere that can run `bun` works.
|
|
35
|
-
Expose port 3000, set `SIGNING_SECRET`, point the subscription URL at the
|
|
36
|
-
public hostname.
|
|
@@ -1,18 +0,0 @@
|
|
|
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": "^4.2.0",
|
|
12
|
-
"hono": "^4.6.0"
|
|
13
|
-
},
|
|
14
|
-
"devDependencies": {
|
|
15
|
-
"@types/bun": "latest",
|
|
16
|
-
"typescript": "^5"
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
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 `secondlayer webhooks create`.
|
|
8
|
-
* If you lost it: secondlayer webhooks rotate-secret {{NAME}}
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const SIGNING_SECRET = process.env.SIGNING_SECRET;
|
|
12
|
-
if (!SIGNING_SECRET) {
|
|
13
|
-
console.error(
|
|
14
|
-
"SIGNING_SECRET not set. Copy .env.example → .env and fill it in.",
|
|
15
|
-
);
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const app = new Hono();
|
|
20
|
-
|
|
21
|
-
interface SubgraphEvent {
|
|
22
|
-
type: string;
|
|
23
|
-
timestamp: string;
|
|
24
|
-
data: Record<string, unknown>;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
app.post("/webhook", async (c) => {
|
|
28
|
-
const body = await c.req.text();
|
|
29
|
-
const headers: Record<string, string> = {};
|
|
30
|
-
c.req.raw.headers.forEach((v, k) => {
|
|
31
|
-
headers[k.toLowerCase()] = v;
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
if (!verify(body, headers, SIGNING_SECRET)) {
|
|
35
|
-
console.warn("signature verification failed");
|
|
36
|
-
return c.text("unauthorized", 401);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const event = JSON.parse(body) as SubgraphEvent;
|
|
40
|
-
await onEvent(event);
|
|
41
|
-
return c.text("ok", 200);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
async function onEvent(event: SubgraphEvent): Promise<void> {
|
|
45
|
-
// TODO: plug in your business logic. `event.type` is
|
|
46
|
-
// `<subgraph>.<table>.<created|updated|deleted>` (the verb reflects the row
|
|
47
|
-
// op); `event.data` is the row payload.
|
|
48
|
-
console.log("[event]", event.type, event.data);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const PORT = Number.parseInt(process.env.PORT ?? "3000", 10);
|
|
52
|
-
Bun.serve({ port: PORT, fetch: app.fetch });
|
|
53
|
-
console.log(`{{NAME}} listening on :${PORT}`);
|
|
@@ -1,38 +0,0 @@
|
|
|
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
|
-
bunx 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
|
-
Provision the subscription with bearer auth:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
secondlayer webhooks create {{NAME}} --runtime trigger --auth-token tr_secret_abc...
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
You can also patch an existing subscription with
|
|
28
|
-
`secondlayer webhooks update {{NAME}} --auth-token tr_secret_abc...`.
|
|
29
|
-
|
|
30
|
-
## Signature verification
|
|
31
|
-
|
|
32
|
-
Trigger authenticates requests with the bearer token — there's no separate
|
|
33
|
-
HMAC. Your secret IS the auth. Rotate via the Trigger dashboard when
|
|
34
|
-
needed.
|
|
35
|
-
|
|
36
|
-
## Deploy
|
|
37
|
-
|
|
38
|
-
Run `bunx trigger.dev@latest deploy` to push to Trigger Cloud.
|
|
@@ -1,17 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
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
|
-
});
|