@better-webhook/cli 3.3.0 → 3.4.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 +25 -15
- package/dist/commands/capture.d.ts +2 -0
- package/dist/commands/capture.js +30 -0
- package/dist/commands/captures.d.ts +2 -0
- package/dist/commands/captures.js +217 -0
- package/dist/commands/dashboard.d.ts +2 -0
- package/dist/commands/dashboard.js +63 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.js +6 -0
- package/dist/commands/replay.d.ts +2 -0
- package/dist/commands/replay.js +140 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +181 -0
- package/dist/commands/templates.d.ts +2 -0
- package/dist/commands/templates.js +285 -0
- package/dist/core/capture-server.d.ts +31 -0
- package/dist/core/capture-server.js +292 -0
- package/dist/core/dashboard-api.d.ts +8 -0
- package/dist/core/dashboard-api.js +271 -0
- package/dist/core/dashboard-server.d.ts +20 -0
- package/dist/core/dashboard-server.js +124 -0
- package/dist/core/executor.d.ts +11 -0
- package/dist/core/executor.js +130 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +5 -0
- package/dist/core/replay-engine.d.ts +18 -0
- package/dist/core/replay-engine.js +208 -0
- package/dist/core/signature.d.ts +24 -0
- package/dist/core/signature.js +205 -0
- package/dist/core/template-manager.d.ts +24 -0
- package/dist/core/template-manager.js +246 -0
- package/dist/index.cjs +27 -1
- package/dist/index.js +26 -1
- package/dist/types/index.d.ts +299 -0
- package/dist/types/index.js +86 -0
- package/package.json +1 -1
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { createHmac } from "crypto";
|
|
2
|
+
export function generateStripeSignature(payload, secret, timestamp) {
|
|
3
|
+
const ts = timestamp || Math.floor(Date.now() / 1000);
|
|
4
|
+
const signedPayload = `${ts}.${payload}`;
|
|
5
|
+
const signature = createHmac("sha256", secret)
|
|
6
|
+
.update(signedPayload)
|
|
7
|
+
.digest("hex");
|
|
8
|
+
return {
|
|
9
|
+
header: "Stripe-Signature",
|
|
10
|
+
value: `t=${ts},v1=${signature}`,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function generateGitHubSignature(payload, secret) {
|
|
14
|
+
const signature = createHmac("sha256", secret).update(payload).digest("hex");
|
|
15
|
+
return {
|
|
16
|
+
header: "X-Hub-Signature-256",
|
|
17
|
+
value: `sha256=${signature}`,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function generateShopifySignature(payload, secret) {
|
|
21
|
+
const signature = createHmac("sha256", secret)
|
|
22
|
+
.update(payload)
|
|
23
|
+
.digest("base64");
|
|
24
|
+
return {
|
|
25
|
+
header: "X-Shopify-Hmac-SHA256",
|
|
26
|
+
value: signature,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function generateTwilioSignature(payload, secret, url) {
|
|
30
|
+
const signatureInput = url + payload;
|
|
31
|
+
const signature = createHmac("sha1", secret)
|
|
32
|
+
.update(signatureInput)
|
|
33
|
+
.digest("base64");
|
|
34
|
+
return {
|
|
35
|
+
header: "X-Twilio-Signature",
|
|
36
|
+
value: signature,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function generateSlackSignature(payload, secret, timestamp) {
|
|
40
|
+
const ts = timestamp || Math.floor(Date.now() / 1000);
|
|
41
|
+
const signatureBaseString = `v0:${ts}:${payload}`;
|
|
42
|
+
const signature = createHmac("sha256", secret)
|
|
43
|
+
.update(signatureBaseString)
|
|
44
|
+
.digest("hex");
|
|
45
|
+
return {
|
|
46
|
+
header: "X-Slack-Signature",
|
|
47
|
+
value: `v0=${signature}`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function generateLinearSignature(payload, secret) {
|
|
51
|
+
const signature = createHmac("sha256", secret).update(payload).digest("hex");
|
|
52
|
+
return {
|
|
53
|
+
header: "Linear-Signature",
|
|
54
|
+
value: signature,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function generateClerkSignature(payload, secret, timestamp, webhookId) {
|
|
58
|
+
const ts = timestamp || Math.floor(Date.now() / 1000);
|
|
59
|
+
const msgId = webhookId || `msg_${Date.now()}`;
|
|
60
|
+
const signedPayload = `${msgId}.${ts}.${payload}`;
|
|
61
|
+
const signature = createHmac("sha256", secret)
|
|
62
|
+
.update(signedPayload)
|
|
63
|
+
.digest("base64");
|
|
64
|
+
return {
|
|
65
|
+
header: "Svix-Signature",
|
|
66
|
+
value: `v1,${signature}`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export function generateSendGridSignature(payload, secret, timestamp) {
|
|
70
|
+
const ts = timestamp || Math.floor(Date.now() / 1000);
|
|
71
|
+
const signedPayload = `${ts}${payload}`;
|
|
72
|
+
const signature = createHmac("sha256", secret)
|
|
73
|
+
.update(signedPayload)
|
|
74
|
+
.digest("base64");
|
|
75
|
+
return {
|
|
76
|
+
header: "X-Twilio-Email-Event-Webhook-Signature",
|
|
77
|
+
value: signature,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function generateRagieSignature(payload, secret) {
|
|
81
|
+
const signature = createHmac("sha256", secret).update(payload).digest("hex");
|
|
82
|
+
return {
|
|
83
|
+
header: "X-Signature",
|
|
84
|
+
value: signature,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export function generateSignature(provider, payload, secret, options) {
|
|
88
|
+
const timestamp = options?.timestamp;
|
|
89
|
+
switch (provider) {
|
|
90
|
+
case "stripe":
|
|
91
|
+
return generateStripeSignature(payload, secret, timestamp);
|
|
92
|
+
case "github":
|
|
93
|
+
return generateGitHubSignature(payload, secret);
|
|
94
|
+
case "shopify":
|
|
95
|
+
return generateShopifySignature(payload, secret);
|
|
96
|
+
case "twilio":
|
|
97
|
+
if (!options?.url) {
|
|
98
|
+
throw new Error("Twilio signature requires URL");
|
|
99
|
+
}
|
|
100
|
+
return generateTwilioSignature(payload, secret, options.url);
|
|
101
|
+
case "slack":
|
|
102
|
+
return generateSlackSignature(payload, secret, timestamp);
|
|
103
|
+
case "linear":
|
|
104
|
+
return generateLinearSignature(payload, secret);
|
|
105
|
+
case "clerk":
|
|
106
|
+
return generateClerkSignature(payload, secret, timestamp, options?.webhookId);
|
|
107
|
+
case "sendgrid":
|
|
108
|
+
return generateSendGridSignature(payload, secret, timestamp);
|
|
109
|
+
case "ragie":
|
|
110
|
+
return generateRagieSignature(payload, secret);
|
|
111
|
+
case "discord":
|
|
112
|
+
case "custom":
|
|
113
|
+
default:
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export function getProviderHeaders(provider, options) {
|
|
118
|
+
const headers = [];
|
|
119
|
+
const timestamp = options?.timestamp || Math.floor(Date.now() / 1000);
|
|
120
|
+
switch (provider) {
|
|
121
|
+
case "stripe":
|
|
122
|
+
headers.push({ key: "Content-Type", value: "application/json" }, {
|
|
123
|
+
key: "User-Agent",
|
|
124
|
+
value: "Stripe/1.0 (+https://stripe.com/docs/webhooks)",
|
|
125
|
+
});
|
|
126
|
+
break;
|
|
127
|
+
case "github":
|
|
128
|
+
headers.push({ key: "Content-Type", value: "application/json" }, { key: "User-Agent", value: "GitHub-Hookshot/better-webhook" }, { key: "X-GitHub-Event", value: options?.event || "push" }, {
|
|
129
|
+
key: "X-GitHub-Delivery",
|
|
130
|
+
value: options?.webhookId || generateDeliveryId(),
|
|
131
|
+
});
|
|
132
|
+
break;
|
|
133
|
+
case "shopify":
|
|
134
|
+
headers.push({ key: "Content-Type", value: "application/json" }, { key: "X-Shopify-Topic", value: options?.event || "orders/create" }, { key: "X-Shopify-Shop-Domain", value: "example.myshopify.com" }, { key: "X-Shopify-API-Version", value: "2024-01" });
|
|
135
|
+
break;
|
|
136
|
+
case "slack":
|
|
137
|
+
headers.push({ key: "Content-Type", value: "application/json" }, { key: "X-Slack-Request-Timestamp", value: String(timestamp) });
|
|
138
|
+
break;
|
|
139
|
+
case "clerk":
|
|
140
|
+
headers.push({ key: "Content-Type", value: "application/json" }, { key: "Svix-Id", value: options?.webhookId || `msg_${Date.now()}` }, { key: "Svix-Timestamp", value: String(timestamp) });
|
|
141
|
+
break;
|
|
142
|
+
case "sendgrid":
|
|
143
|
+
headers.push({ key: "Content-Type", value: "application/json" }, {
|
|
144
|
+
key: "X-Twilio-Email-Event-Webhook-Timestamp",
|
|
145
|
+
value: String(timestamp),
|
|
146
|
+
});
|
|
147
|
+
break;
|
|
148
|
+
case "twilio":
|
|
149
|
+
headers.push({
|
|
150
|
+
key: "Content-Type",
|
|
151
|
+
value: "application/x-www-form-urlencoded",
|
|
152
|
+
});
|
|
153
|
+
break;
|
|
154
|
+
case "linear":
|
|
155
|
+
headers.push({ key: "Content-Type", value: "application/json" }, {
|
|
156
|
+
key: "Linear-Delivery",
|
|
157
|
+
value: options?.webhookId || generateDeliveryId(),
|
|
158
|
+
});
|
|
159
|
+
break;
|
|
160
|
+
case "discord":
|
|
161
|
+
headers.push({ key: "Content-Type", value: "application/json" }, { key: "User-Agent", value: "Discord-Webhook/1.0" });
|
|
162
|
+
break;
|
|
163
|
+
case "ragie":
|
|
164
|
+
headers.push({ key: "Content-Type", value: "application/json" }, {
|
|
165
|
+
key: "X-Ragie-Event",
|
|
166
|
+
value: options?.event || "document_status_updated",
|
|
167
|
+
}, {
|
|
168
|
+
key: "X-Ragie-Delivery",
|
|
169
|
+
value: options?.webhookId || generateDeliveryId(),
|
|
170
|
+
});
|
|
171
|
+
break;
|
|
172
|
+
default:
|
|
173
|
+
headers.push({ key: "Content-Type", value: "application/json" });
|
|
174
|
+
}
|
|
175
|
+
return headers;
|
|
176
|
+
}
|
|
177
|
+
function generateDeliveryId() {
|
|
178
|
+
const chars = "0123456789abcdef";
|
|
179
|
+
let id = "";
|
|
180
|
+
for (let i = 0; i < 36; i++) {
|
|
181
|
+
if (i === 8 || i === 13 || i === 18 || i === 23) {
|
|
182
|
+
id += "-";
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
id += chars[Math.floor(Math.random() * chars.length)];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return id;
|
|
189
|
+
}
|
|
190
|
+
export function verifySignature(provider, payload, signature, secret, options) {
|
|
191
|
+
const generated = generateSignature(provider, payload, secret, options);
|
|
192
|
+
if (!generated) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
const a = generated.value;
|
|
196
|
+
const b = signature;
|
|
197
|
+
if (a.length !== b.length) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
let result = 0;
|
|
201
|
+
for (let i = 0; i < a.length; i++) {
|
|
202
|
+
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
203
|
+
}
|
|
204
|
+
return result === 0;
|
|
205
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type TemplatesIndex, type LocalTemplate, type RemoteTemplate } from "../types/index.js";
|
|
2
|
+
export declare class TemplateManager {
|
|
3
|
+
private baseDir;
|
|
4
|
+
private templatesDir;
|
|
5
|
+
private cacheFile;
|
|
6
|
+
private indexCache;
|
|
7
|
+
constructor(baseDir?: string);
|
|
8
|
+
getTemplatesDir(): string;
|
|
9
|
+
fetchRemoteIndex(forceRefresh?: boolean): Promise<TemplatesIndex>;
|
|
10
|
+
listRemoteTemplates(options?: {
|
|
11
|
+
forceRefresh?: boolean;
|
|
12
|
+
}): Promise<RemoteTemplate[]>;
|
|
13
|
+
downloadTemplate(templateId: string): Promise<LocalTemplate>;
|
|
14
|
+
listLocalTemplates(): LocalTemplate[];
|
|
15
|
+
getLocalTemplate(templateId: string): LocalTemplate | null;
|
|
16
|
+
deleteLocalTemplate(templateId: string): boolean;
|
|
17
|
+
searchTemplates(query: string): Promise<{
|
|
18
|
+
remote: RemoteTemplate[];
|
|
19
|
+
local: LocalTemplate[];
|
|
20
|
+
}>;
|
|
21
|
+
clearCache(): void;
|
|
22
|
+
deleteAllLocalTemplates(): number;
|
|
23
|
+
}
|
|
24
|
+
export declare function getTemplateManager(baseDir?: string): TemplateManager;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { request } from "undici";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmdirSync, unlinkSync, writeFileSync, } from "fs";
|
|
3
|
+
import { join, basename } from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import { TemplatesIndexSchema, WebhookTemplateSchema, } from "../types/index.js";
|
|
6
|
+
const GITHUB_RAW_BASE = "https://raw.githubusercontent.com/endalk200/better-webhook/main";
|
|
7
|
+
const TEMPLATES_INDEX_URL = `${GITHUB_RAW_BASE}/templates/templates.json`;
|
|
8
|
+
export class TemplateManager {
|
|
9
|
+
baseDir;
|
|
10
|
+
templatesDir;
|
|
11
|
+
cacheFile;
|
|
12
|
+
indexCache = null;
|
|
13
|
+
constructor(baseDir) {
|
|
14
|
+
this.baseDir = baseDir || join(homedir(), ".better-webhook");
|
|
15
|
+
this.templatesDir = join(this.baseDir, "templates");
|
|
16
|
+
this.cacheFile = join(this.baseDir, "templates-cache.json");
|
|
17
|
+
if (!existsSync(this.baseDir)) {
|
|
18
|
+
mkdirSync(this.baseDir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
if (!existsSync(this.templatesDir)) {
|
|
21
|
+
mkdirSync(this.templatesDir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
getTemplatesDir() {
|
|
25
|
+
return this.templatesDir;
|
|
26
|
+
}
|
|
27
|
+
async fetchRemoteIndex(forceRefresh = false) {
|
|
28
|
+
if (!forceRefresh && this.indexCache) {
|
|
29
|
+
return this.indexCache;
|
|
30
|
+
}
|
|
31
|
+
if (!forceRefresh && existsSync(this.cacheFile)) {
|
|
32
|
+
try {
|
|
33
|
+
const cached = JSON.parse(readFileSync(this.cacheFile, "utf-8"));
|
|
34
|
+
const cacheAge = Date.now() - (cached.cachedAt || 0);
|
|
35
|
+
if (cacheAge < 3600000) {
|
|
36
|
+
this.indexCache = cached.index;
|
|
37
|
+
return cached.index;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const { statusCode, body } = await request(TEMPLATES_INDEX_URL);
|
|
45
|
+
if (statusCode !== 200) {
|
|
46
|
+
throw new Error(`HTTP ${statusCode}`);
|
|
47
|
+
}
|
|
48
|
+
const text = await body.text();
|
|
49
|
+
const json = JSON.parse(text);
|
|
50
|
+
const index = TemplatesIndexSchema.parse(json);
|
|
51
|
+
this.indexCache = index;
|
|
52
|
+
writeFileSync(this.cacheFile, JSON.stringify({ index, cachedAt: Date.now() }, null, 2));
|
|
53
|
+
return index;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (existsSync(this.cacheFile)) {
|
|
57
|
+
try {
|
|
58
|
+
const cached = JSON.parse(readFileSync(this.cacheFile, "utf-8"));
|
|
59
|
+
if (cached.index) {
|
|
60
|
+
this.indexCache = cached.index;
|
|
61
|
+
return cached.index;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
throw new Error(`Failed to fetch templates index: ${error.message}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async listRemoteTemplates(options) {
|
|
71
|
+
const index = await this.fetchRemoteIndex(!!options?.forceRefresh);
|
|
72
|
+
const localIds = new Set(this.listLocalTemplates().map((t) => t.id));
|
|
73
|
+
return index.templates.map((metadata) => ({
|
|
74
|
+
metadata,
|
|
75
|
+
isDownloaded: localIds.has(metadata.id),
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
async downloadTemplate(templateId) {
|
|
79
|
+
const index = await this.fetchRemoteIndex();
|
|
80
|
+
const templateMeta = index.templates.find((t) => t.id === templateId);
|
|
81
|
+
if (!templateMeta) {
|
|
82
|
+
throw new Error(`Template not found: ${templateId}`);
|
|
83
|
+
}
|
|
84
|
+
const templateUrl = `${GITHUB_RAW_BASE}/templates/${templateMeta.file}`;
|
|
85
|
+
try {
|
|
86
|
+
const { statusCode, body } = await request(templateUrl);
|
|
87
|
+
if (statusCode !== 200) {
|
|
88
|
+
throw new Error(`HTTP ${statusCode}`);
|
|
89
|
+
}
|
|
90
|
+
const text = await body.text();
|
|
91
|
+
const json = JSON.parse(text);
|
|
92
|
+
const template = WebhookTemplateSchema.parse(json);
|
|
93
|
+
const providerDir = join(this.templatesDir, templateMeta.provider);
|
|
94
|
+
if (!existsSync(providerDir)) {
|
|
95
|
+
mkdirSync(providerDir, { recursive: true });
|
|
96
|
+
}
|
|
97
|
+
const fileName = `${templateId}.json`;
|
|
98
|
+
const filePath = join(providerDir, fileName);
|
|
99
|
+
const localTemplate = {
|
|
100
|
+
id: templateId,
|
|
101
|
+
metadata: templateMeta,
|
|
102
|
+
template,
|
|
103
|
+
downloadedAt: new Date().toISOString(),
|
|
104
|
+
filePath,
|
|
105
|
+
};
|
|
106
|
+
const saveData = {
|
|
107
|
+
...template,
|
|
108
|
+
_metadata: {
|
|
109
|
+
...templateMeta,
|
|
110
|
+
downloadedAt: localTemplate.downloadedAt,
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
writeFileSync(filePath, JSON.stringify(saveData, null, 2));
|
|
114
|
+
return localTemplate;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
throw new Error(`Failed to download template ${templateId}: ${error.message}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
listLocalTemplates() {
|
|
121
|
+
const templates = [];
|
|
122
|
+
if (!existsSync(this.templatesDir)) {
|
|
123
|
+
return templates;
|
|
124
|
+
}
|
|
125
|
+
const scanDir = (dir) => {
|
|
126
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
127
|
+
for (const entry of entries) {
|
|
128
|
+
const fullPath = join(dir, entry.name);
|
|
129
|
+
if (entry.isDirectory()) {
|
|
130
|
+
scanDir(fullPath);
|
|
131
|
+
}
|
|
132
|
+
else if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
133
|
+
try {
|
|
134
|
+
const content = JSON.parse(readFileSync(fullPath, "utf-8"));
|
|
135
|
+
const metadata = content._metadata;
|
|
136
|
+
if (metadata) {
|
|
137
|
+
const { _metadata, ...templateData } = content;
|
|
138
|
+
templates.push({
|
|
139
|
+
id: metadata.id,
|
|
140
|
+
metadata,
|
|
141
|
+
template: templateData,
|
|
142
|
+
downloadedAt: metadata.downloadedAt || new Date().toISOString(),
|
|
143
|
+
filePath: fullPath,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
const id = basename(entry.name, ".json");
|
|
148
|
+
templates.push({
|
|
149
|
+
id,
|
|
150
|
+
metadata: {
|
|
151
|
+
id,
|
|
152
|
+
name: id,
|
|
153
|
+
provider: "custom",
|
|
154
|
+
event: "unknown",
|
|
155
|
+
file: entry.name,
|
|
156
|
+
},
|
|
157
|
+
template: content,
|
|
158
|
+
downloadedAt: new Date().toISOString(),
|
|
159
|
+
filePath: fullPath,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
scanDir(this.templatesDir);
|
|
169
|
+
return templates;
|
|
170
|
+
}
|
|
171
|
+
getLocalTemplate(templateId) {
|
|
172
|
+
const templates = this.listLocalTemplates();
|
|
173
|
+
return templates.find((t) => t.id === templateId) || null;
|
|
174
|
+
}
|
|
175
|
+
deleteLocalTemplate(templateId) {
|
|
176
|
+
const template = this.getLocalTemplate(templateId);
|
|
177
|
+
if (!template) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
unlinkSync(template.filePath);
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async searchTemplates(query) {
|
|
189
|
+
const queryLower = query.toLowerCase();
|
|
190
|
+
const remote = await this.listRemoteTemplates();
|
|
191
|
+
const local = this.listLocalTemplates();
|
|
192
|
+
const matchesMeta = (meta) => {
|
|
193
|
+
return (meta.id.toLowerCase().includes(queryLower) ||
|
|
194
|
+
meta.name.toLowerCase().includes(queryLower) ||
|
|
195
|
+
meta.provider.toLowerCase().includes(queryLower) ||
|
|
196
|
+
meta.event.toLowerCase().includes(queryLower) ||
|
|
197
|
+
(meta.description?.toLowerCase().includes(queryLower) ?? false));
|
|
198
|
+
};
|
|
199
|
+
return {
|
|
200
|
+
remote: remote.filter((t) => matchesMeta(t.metadata)),
|
|
201
|
+
local: local.filter((t) => matchesMeta(t.metadata)),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
clearCache() {
|
|
205
|
+
this.indexCache = null;
|
|
206
|
+
if (existsSync(this.cacheFile)) {
|
|
207
|
+
unlinkSync(this.cacheFile);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
deleteAllLocalTemplates() {
|
|
211
|
+
const templates = this.listLocalTemplates();
|
|
212
|
+
let deleted = 0;
|
|
213
|
+
for (const template of templates) {
|
|
214
|
+
try {
|
|
215
|
+
unlinkSync(template.filePath);
|
|
216
|
+
deleted++;
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (existsSync(this.templatesDir)) {
|
|
222
|
+
const entries = readdirSync(this.templatesDir, { withFileTypes: true });
|
|
223
|
+
for (const entry of entries) {
|
|
224
|
+
if (entry.isDirectory()) {
|
|
225
|
+
const dirPath = join(this.templatesDir, entry.name);
|
|
226
|
+
try {
|
|
227
|
+
const contents = readdirSync(dirPath);
|
|
228
|
+
if (contents.length === 0) {
|
|
229
|
+
rmdirSync(dirPath);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return deleted;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
let instance = null;
|
|
241
|
+
export function getTemplateManager(baseDir) {
|
|
242
|
+
if (!instance) {
|
|
243
|
+
instance = new TemplateManager(baseDir);
|
|
244
|
+
}
|
|
245
|
+
return instance;
|
|
246
|
+
}
|
package/dist/index.cjs
CHANGED
|
@@ -25,6 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
|
|
26
26
|
// src/index.ts
|
|
27
27
|
var import_commander7 = require("commander");
|
|
28
|
+
var import_node_module = require("module");
|
|
28
29
|
|
|
29
30
|
// src/commands/templates.ts
|
|
30
31
|
var import_commander = require("commander");
|
|
@@ -745,6 +746,13 @@ function generateSendGridSignature(payload, secret, timestamp) {
|
|
|
745
746
|
value: signature
|
|
746
747
|
};
|
|
747
748
|
}
|
|
749
|
+
function generateRagieSignature(payload, secret) {
|
|
750
|
+
const signature = (0, import_crypto.createHmac)("sha256", secret).update(payload).digest("hex");
|
|
751
|
+
return {
|
|
752
|
+
header: "X-Signature",
|
|
753
|
+
value: signature
|
|
754
|
+
};
|
|
755
|
+
}
|
|
748
756
|
function generateSignature(provider, payload, secret, options) {
|
|
749
757
|
const timestamp = options?.timestamp;
|
|
750
758
|
switch (provider) {
|
|
@@ -772,6 +780,8 @@ function generateSignature(provider, payload, secret, options) {
|
|
|
772
780
|
);
|
|
773
781
|
case "sendgrid":
|
|
774
782
|
return generateSendGridSignature(payload, secret, timestamp);
|
|
783
|
+
case "ragie":
|
|
784
|
+
return generateRagieSignature(payload, secret);
|
|
775
785
|
case "discord":
|
|
776
786
|
case "custom":
|
|
777
787
|
default:
|
|
@@ -853,6 +863,19 @@ function getProviderHeaders(provider, options) {
|
|
|
853
863
|
{ key: "User-Agent", value: "Discord-Webhook/1.0" }
|
|
854
864
|
);
|
|
855
865
|
break;
|
|
866
|
+
case "ragie":
|
|
867
|
+
headers.push(
|
|
868
|
+
{ key: "Content-Type", value: "application/json" },
|
|
869
|
+
{
|
|
870
|
+
key: "X-Ragie-Event",
|
|
871
|
+
value: options?.event || "document_status_updated"
|
|
872
|
+
},
|
|
873
|
+
{
|
|
874
|
+
key: "X-Ragie-Delivery",
|
|
875
|
+
value: options?.webhookId || generateDeliveryId()
|
|
876
|
+
}
|
|
877
|
+
);
|
|
878
|
+
break;
|
|
856
879
|
default:
|
|
857
880
|
headers.push({ key: "Content-Type", value: "application/json" });
|
|
858
881
|
}
|
|
@@ -2586,8 +2609,11 @@ var dashboard = new import_commander6.Command().name("dashboard").description("S
|
|
|
2586
2609
|
});
|
|
2587
2610
|
|
|
2588
2611
|
// src/index.ts
|
|
2612
|
+
var import_meta2 = {};
|
|
2613
|
+
var require2 = (0, import_node_module.createRequire)(import_meta2.url);
|
|
2614
|
+
var packageJson = require2("../package.json");
|
|
2589
2615
|
var program = new import_commander7.Command().name("better-webhook").description(
|
|
2590
2616
|
"Modern CLI for developing, capturing, and replaying webhooks locally"
|
|
2591
|
-
).version(
|
|
2617
|
+
).version(packageJson.version);
|
|
2592
2618
|
program.addCommand(templates).addCommand(run).addCommand(capture).addCommand(captures).addCommand(replay).addCommand(dashboard);
|
|
2593
2619
|
program.parseAsync(process.argv);
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command as Command7 } from "commander";
|
|
5
|
+
import { createRequire } from "module";
|
|
5
6
|
|
|
6
7
|
// src/commands/templates.ts
|
|
7
8
|
import { Command } from "commander";
|
|
@@ -730,6 +731,13 @@ function generateSendGridSignature(payload, secret, timestamp) {
|
|
|
730
731
|
value: signature
|
|
731
732
|
};
|
|
732
733
|
}
|
|
734
|
+
function generateRagieSignature(payload, secret) {
|
|
735
|
+
const signature = createHmac("sha256", secret).update(payload).digest("hex");
|
|
736
|
+
return {
|
|
737
|
+
header: "X-Signature",
|
|
738
|
+
value: signature
|
|
739
|
+
};
|
|
740
|
+
}
|
|
733
741
|
function generateSignature(provider, payload, secret, options) {
|
|
734
742
|
const timestamp = options?.timestamp;
|
|
735
743
|
switch (provider) {
|
|
@@ -757,6 +765,8 @@ function generateSignature(provider, payload, secret, options) {
|
|
|
757
765
|
);
|
|
758
766
|
case "sendgrid":
|
|
759
767
|
return generateSendGridSignature(payload, secret, timestamp);
|
|
768
|
+
case "ragie":
|
|
769
|
+
return generateRagieSignature(payload, secret);
|
|
760
770
|
case "discord":
|
|
761
771
|
case "custom":
|
|
762
772
|
default:
|
|
@@ -838,6 +848,19 @@ function getProviderHeaders(provider, options) {
|
|
|
838
848
|
{ key: "User-Agent", value: "Discord-Webhook/1.0" }
|
|
839
849
|
);
|
|
840
850
|
break;
|
|
851
|
+
case "ragie":
|
|
852
|
+
headers.push(
|
|
853
|
+
{ key: "Content-Type", value: "application/json" },
|
|
854
|
+
{
|
|
855
|
+
key: "X-Ragie-Event",
|
|
856
|
+
value: options?.event || "document_status_updated"
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
key: "X-Ragie-Delivery",
|
|
860
|
+
value: options?.webhookId || generateDeliveryId()
|
|
861
|
+
}
|
|
862
|
+
);
|
|
863
|
+
break;
|
|
841
864
|
default:
|
|
842
865
|
headers.push({ key: "Content-Type", value: "application/json" });
|
|
843
866
|
}
|
|
@@ -2579,8 +2602,10 @@ var dashboard = new Command6().name("dashboard").description("Start the local da
|
|
|
2579
2602
|
});
|
|
2580
2603
|
|
|
2581
2604
|
// src/index.ts
|
|
2605
|
+
var require2 = createRequire(import.meta.url);
|
|
2606
|
+
var packageJson = require2("../package.json");
|
|
2582
2607
|
var program = new Command7().name("better-webhook").description(
|
|
2583
2608
|
"Modern CLI for developing, capturing, and replaying webhooks locally"
|
|
2584
|
-
).version(
|
|
2609
|
+
).version(packageJson.version);
|
|
2585
2610
|
program.addCommand(templates).addCommand(run).addCommand(capture).addCommand(captures).addCommand(replay).addCommand(dashboard);
|
|
2586
2611
|
program.parseAsync(process.argv);
|