@better-webhook/cli 3.4.4 → 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 +35 -0
- package/dist/dashboard/assets/index-CxrRCNTh.css +1 -0
- package/dist/dashboard/assets/index-Dlqdzwyc.js +23 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/index.cjs +330 -15
- package/dist/index.js +330 -15
- package/package.json +4 -3
- package/dist/commands/capture.d.ts +0 -2
- package/dist/commands/capture.js +0 -30
- package/dist/commands/captures.d.ts +0 -2
- package/dist/commands/captures.js +0 -217
- package/dist/commands/dashboard.d.ts +0 -2
- package/dist/commands/dashboard.js +0 -65
- package/dist/commands/index.d.ts +0 -6
- package/dist/commands/index.js +0 -6
- package/dist/commands/replay.d.ts +0 -2
- package/dist/commands/replay.js +0 -140
- package/dist/commands/run.d.ts +0 -2
- package/dist/commands/run.js +0 -181
- package/dist/commands/templates.d.ts +0 -2
- package/dist/commands/templates.js +0 -285
- package/dist/core/capture-server.d.ts +0 -31
- package/dist/core/capture-server.js +0 -298
- package/dist/core/dashboard-api.d.ts +0 -8
- package/dist/core/dashboard-api.js +0 -271
- package/dist/core/dashboard-server.d.ts +0 -20
- package/dist/core/dashboard-server.js +0 -124
- package/dist/core/executor.d.ts +0 -11
- package/dist/core/executor.js +0 -130
- package/dist/core/index.d.ts +0 -5
- package/dist/core/index.js +0 -5
- package/dist/core/replay-engine.d.ts +0 -18
- package/dist/core/replay-engine.js +0 -208
- package/dist/core/signature.d.ts +0 -24
- package/dist/core/signature.js +0 -199
- package/dist/core/template-manager.d.ts +0 -24
- package/dist/core/template-manager.js +0 -246
- package/dist/dashboard/assets/index-BSfTbn4Y.js +0 -23
- package/dist/dashboard/assets/index-zDTVdss_.css +0 -1
- package/dist/types/index.d.ts +0 -299
- package/dist/types/index.js +0 -86
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync, unlinkSync } from "fs";
|
|
2
|
-
import { join } from "path";
|
|
3
|
-
import { homedir } from "os";
|
|
4
|
-
import { executeWebhook } from "./executor.js";
|
|
5
|
-
export class ReplayEngine {
|
|
6
|
-
capturesDir;
|
|
7
|
-
constructor(capturesDir) {
|
|
8
|
-
this.capturesDir =
|
|
9
|
-
capturesDir || join(homedir(), ".better-webhook", "captures");
|
|
10
|
-
}
|
|
11
|
-
getCapturesDir() {
|
|
12
|
-
return this.capturesDir;
|
|
13
|
-
}
|
|
14
|
-
listCaptures(limit = 100) {
|
|
15
|
-
if (!existsSync(this.capturesDir)) {
|
|
16
|
-
return [];
|
|
17
|
-
}
|
|
18
|
-
const files = readdirSync(this.capturesDir)
|
|
19
|
-
.filter((f) => f.endsWith(".json"))
|
|
20
|
-
.sort()
|
|
21
|
-
.reverse()
|
|
22
|
-
.slice(0, limit);
|
|
23
|
-
const captures = [];
|
|
24
|
-
for (const file of files) {
|
|
25
|
-
try {
|
|
26
|
-
const content = readFileSync(join(this.capturesDir, file), "utf-8");
|
|
27
|
-
const capture = JSON.parse(content);
|
|
28
|
-
captures.push({ file, capture });
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return captures;
|
|
34
|
-
}
|
|
35
|
-
getCapture(captureId) {
|
|
36
|
-
const captures = this.listCaptures(1000);
|
|
37
|
-
let found = captures.find((c) => c.capture.id === captureId);
|
|
38
|
-
if (found)
|
|
39
|
-
return found;
|
|
40
|
-
found = captures.find((c) => c.file.includes(captureId));
|
|
41
|
-
if (found)
|
|
42
|
-
return found;
|
|
43
|
-
found = captures.find((c) => c.capture.id.startsWith(captureId));
|
|
44
|
-
return found || null;
|
|
45
|
-
}
|
|
46
|
-
async replay(captureId, options) {
|
|
47
|
-
const captureFile = this.getCapture(captureId);
|
|
48
|
-
if (!captureFile) {
|
|
49
|
-
throw new Error(`Capture not found: ${captureId}`);
|
|
50
|
-
}
|
|
51
|
-
const { capture } = captureFile;
|
|
52
|
-
const headers = [];
|
|
53
|
-
const skipHeaders = [
|
|
54
|
-
"host",
|
|
55
|
-
"content-length",
|
|
56
|
-
"connection",
|
|
57
|
-
"accept-encoding",
|
|
58
|
-
];
|
|
59
|
-
for (const [key, value] of Object.entries(capture.headers)) {
|
|
60
|
-
if (!skipHeaders.includes(key.toLowerCase())) {
|
|
61
|
-
const headerValue = Array.isArray(value) ? value.join(", ") : value;
|
|
62
|
-
if (headerValue) {
|
|
63
|
-
headers.push({ key, value: headerValue });
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
if (options.headers) {
|
|
68
|
-
for (const h of options.headers) {
|
|
69
|
-
const existingIdx = headers.findIndex((eh) => eh.key.toLowerCase() === h.key.toLowerCase());
|
|
70
|
-
if (existingIdx >= 0) {
|
|
71
|
-
headers[existingIdx] = h;
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
headers.push(h);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
const body = capture.rawBody || capture.body;
|
|
79
|
-
return executeWebhook({
|
|
80
|
-
url: options.targetUrl,
|
|
81
|
-
method: options.method || capture.method,
|
|
82
|
-
headers,
|
|
83
|
-
body,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
captureToTemplate(captureId, options) {
|
|
87
|
-
const captureFile = this.getCapture(captureId);
|
|
88
|
-
if (!captureFile) {
|
|
89
|
-
throw new Error(`Capture not found: ${captureId}`);
|
|
90
|
-
}
|
|
91
|
-
const { capture } = captureFile;
|
|
92
|
-
const skipHeaders = [
|
|
93
|
-
"host",
|
|
94
|
-
"content-length",
|
|
95
|
-
"connection",
|
|
96
|
-
"accept-encoding",
|
|
97
|
-
"stripe-signature",
|
|
98
|
-
"x-hub-signature-256",
|
|
99
|
-
"x-hub-signature",
|
|
100
|
-
"x-shopify-hmac-sha256",
|
|
101
|
-
"x-twilio-signature",
|
|
102
|
-
"x-slack-signature",
|
|
103
|
-
"svix-signature",
|
|
104
|
-
"linear-signature",
|
|
105
|
-
];
|
|
106
|
-
const headers = [];
|
|
107
|
-
for (const [key, value] of Object.entries(capture.headers)) {
|
|
108
|
-
if (!skipHeaders.includes(key.toLowerCase())) {
|
|
109
|
-
const headerValue = Array.isArray(value) ? value.join(", ") : value;
|
|
110
|
-
if (headerValue) {
|
|
111
|
-
headers.push({ key, value: headerValue });
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
let body;
|
|
116
|
-
if (capture.body) {
|
|
117
|
-
body = capture.body;
|
|
118
|
-
}
|
|
119
|
-
else if (capture.rawBody) {
|
|
120
|
-
try {
|
|
121
|
-
body = JSON.parse(capture.rawBody);
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
body = capture.rawBody;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return {
|
|
128
|
-
url: options?.url || `http://localhost:3000${capture.path}`,
|
|
129
|
-
method: capture.method,
|
|
130
|
-
headers,
|
|
131
|
-
body,
|
|
132
|
-
provider: capture.provider,
|
|
133
|
-
description: `Captured ${capture.provider || "webhook"} at ${capture.timestamp}`,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
getCaptureSummary(captureId) {
|
|
137
|
-
const captureFile = this.getCapture(captureId);
|
|
138
|
-
if (!captureFile) {
|
|
139
|
-
return "Capture not found";
|
|
140
|
-
}
|
|
141
|
-
const { capture } = captureFile;
|
|
142
|
-
const lines = [];
|
|
143
|
-
lines.push(`ID: ${capture.id}`);
|
|
144
|
-
lines.push(`Timestamp: ${new Date(capture.timestamp).toLocaleString()}`);
|
|
145
|
-
lines.push(`Method: ${capture.method}`);
|
|
146
|
-
lines.push(`Path: ${capture.path}`);
|
|
147
|
-
if (capture.provider) {
|
|
148
|
-
lines.push(`Provider: ${capture.provider}`);
|
|
149
|
-
}
|
|
150
|
-
lines.push(`Content-Type: ${capture.contentType || "unknown"}`);
|
|
151
|
-
lines.push(`Body Size: ${capture.contentLength || 0} bytes`);
|
|
152
|
-
const headerCount = Object.keys(capture.headers).length;
|
|
153
|
-
lines.push(`Headers: ${headerCount}`);
|
|
154
|
-
return lines.join("\n");
|
|
155
|
-
}
|
|
156
|
-
searchCaptures(query) {
|
|
157
|
-
const queryLower = query.toLowerCase();
|
|
158
|
-
const captures = this.listCaptures(1000);
|
|
159
|
-
return captures.filter((c) => {
|
|
160
|
-
const { capture } = c;
|
|
161
|
-
return (capture.id.toLowerCase().includes(queryLower) ||
|
|
162
|
-
capture.path.toLowerCase().includes(queryLower) ||
|
|
163
|
-
capture.method.toLowerCase().includes(queryLower) ||
|
|
164
|
-
capture.provider?.toLowerCase().includes(queryLower) ||
|
|
165
|
-
c.file.toLowerCase().includes(queryLower));
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
getCapturesByProvider(provider) {
|
|
169
|
-
const captures = this.listCaptures(1000);
|
|
170
|
-
return captures.filter((c) => c.capture.provider === provider);
|
|
171
|
-
}
|
|
172
|
-
deleteCapture(captureId) {
|
|
173
|
-
const captureFile = this.getCapture(captureId);
|
|
174
|
-
if (!captureFile) {
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
177
|
-
try {
|
|
178
|
-
unlinkSync(join(this.capturesDir, captureFile.file));
|
|
179
|
-
return true;
|
|
180
|
-
}
|
|
181
|
-
catch {
|
|
182
|
-
return false;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
deleteAllCaptures() {
|
|
186
|
-
if (!existsSync(this.capturesDir)) {
|
|
187
|
-
return 0;
|
|
188
|
-
}
|
|
189
|
-
const files = readdirSync(this.capturesDir).filter((f) => f.endsWith(".json"));
|
|
190
|
-
let deleted = 0;
|
|
191
|
-
for (const file of files) {
|
|
192
|
-
try {
|
|
193
|
-
unlinkSync(join(this.capturesDir, file));
|
|
194
|
-
deleted++;
|
|
195
|
-
}
|
|
196
|
-
catch {
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
return deleted;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
let instance = null;
|
|
203
|
-
export function getReplayEngine(capturesDir) {
|
|
204
|
-
if (!instance) {
|
|
205
|
-
instance = new ReplayEngine(capturesDir);
|
|
206
|
-
}
|
|
207
|
-
return instance;
|
|
208
|
-
}
|
package/dist/core/signature.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { WebhookProvider, GeneratedSignature, HeaderEntry } from "../types/index.js";
|
|
2
|
-
export declare function generateStripeSignature(payload: string, secret: string, timestamp?: number): GeneratedSignature;
|
|
3
|
-
export declare function generateGitHubSignature(payload: string, secret: string): GeneratedSignature;
|
|
4
|
-
export declare function generateShopifySignature(payload: string, secret: string): GeneratedSignature;
|
|
5
|
-
export declare function generateTwilioSignature(payload: string, secret: string, url: string): GeneratedSignature;
|
|
6
|
-
export declare function generateSlackSignature(payload: string, secret: string, timestamp?: number): GeneratedSignature;
|
|
7
|
-
export declare function generateLinearSignature(payload: string, secret: string): GeneratedSignature;
|
|
8
|
-
export declare function generateClerkSignature(payload: string, secret: string, timestamp?: number, webhookId?: string): GeneratedSignature;
|
|
9
|
-
export declare function generateSendGridSignature(payload: string, secret: string, timestamp?: number): GeneratedSignature;
|
|
10
|
-
export declare function generateRagieSignature(payload: string, secret: string): GeneratedSignature;
|
|
11
|
-
export declare function generateSignature(provider: WebhookProvider, payload: string, secret: string, options?: {
|
|
12
|
-
timestamp?: number;
|
|
13
|
-
url?: string;
|
|
14
|
-
webhookId?: string;
|
|
15
|
-
}): GeneratedSignature | null;
|
|
16
|
-
export declare function getProviderHeaders(provider: WebhookProvider, options?: {
|
|
17
|
-
timestamp?: number;
|
|
18
|
-
webhookId?: string;
|
|
19
|
-
event?: string;
|
|
20
|
-
}): HeaderEntry[];
|
|
21
|
-
export declare function verifySignature(provider: WebhookProvider, payload: string, signature: string, secret: string, options?: {
|
|
22
|
-
timestamp?: number;
|
|
23
|
-
url?: string;
|
|
24
|
-
}): boolean;
|
package/dist/core/signature.js
DELETED
|
@@ -1,199 +0,0 @@
|
|
|
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
|
-
break;
|
|
166
|
-
default:
|
|
167
|
-
headers.push({ key: "Content-Type", value: "application/json" });
|
|
168
|
-
}
|
|
169
|
-
return headers;
|
|
170
|
-
}
|
|
171
|
-
function generateDeliveryId() {
|
|
172
|
-
const chars = "0123456789abcdef";
|
|
173
|
-
let id = "";
|
|
174
|
-
for (let i = 0; i < 36; i++) {
|
|
175
|
-
if (i === 8 || i === 13 || i === 18 || i === 23) {
|
|
176
|
-
id += "-";
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
id += chars[Math.floor(Math.random() * chars.length)];
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
return id;
|
|
183
|
-
}
|
|
184
|
-
export function verifySignature(provider, payload, signature, secret, options) {
|
|
185
|
-
const generated = generateSignature(provider, payload, secret, options);
|
|
186
|
-
if (!generated) {
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
const a = generated.value;
|
|
190
|
-
const b = signature;
|
|
191
|
-
if (a.length !== b.length) {
|
|
192
|
-
return false;
|
|
193
|
-
}
|
|
194
|
-
let result = 0;
|
|
195
|
-
for (let i = 0; i < a.length; i++) {
|
|
196
|
-
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
197
|
-
}
|
|
198
|
-
return result === 0;
|
|
199
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
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;
|