@usequeek/app-sdk 0.4.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/CHANGELOG.md +120 -0
- package/LICENSE +21 -0
- package/README.md +210 -0
- package/dist/app-auth.d.ts +139 -0
- package/dist/app-auth.d.ts.map +1 -0
- package/dist/app-auth.js +205 -0
- package/dist/app-auth.js.map +1 -0
- package/dist/background.d.ts +68 -0
- package/dist/background.d.ts.map +1 -0
- package/dist/background.js +78 -0
- package/dist/background.js.map +1 -0
- package/dist/client.d.ts +148 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +269 -0
- package/dist/client.js.map +1 -0
- package/dist/collected.d.ts +103 -0
- package/dist/collected.d.ts.map +1 -0
- package/dist/collected.js +59 -0
- package/dist/collected.js.map +1 -0
- package/dist/crypto.d.ts +6 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +76 -0
- package/dist/crypto.js.map +1 -0
- package/dist/frame.d.ts +58 -0
- package/dist/frame.d.ts.map +1 -0
- package/dist/frame.js +56 -0
- package/dist/frame.js.map +1 -0
- package/dist/handoff.d.ts +113 -0
- package/dist/handoff.d.ts.map +1 -0
- package/dist/handoff.js +29 -0
- package/dist/handoff.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/install-handlers.d.ts +62 -0
- package/dist/install-handlers.d.ts.map +1 -0
- package/dist/install-handlers.js +238 -0
- package/dist/install-handlers.js.map +1 -0
- package/dist/logger.d.ts +27 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +75 -0
- package/dist/logger.js.map +1 -0
- package/dist/merchant-schema.d.ts +13963 -0
- package/dist/merchant-schema.d.ts.map +1 -0
- package/dist/merchant-schema.js +11 -0
- package/dist/merchant-schema.js.map +1 -0
- package/dist/resync.d.ts +80 -0
- package/dist/resync.d.ts.map +1 -0
- package/dist/resync.js +203 -0
- package/dist/resync.js.map +1 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +8 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +66 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +155 -0
- package/dist/session.js.map +1 -0
- package/dist/signatures.d.ts +55 -0
- package/dist/signatures.d.ts.map +1 -0
- package/dist/signatures.js +74 -0
- package/dist/signatures.js.map +1 -0
- package/dist/store.d.ts +278 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +610 -0
- package/dist/store.js.map +1 -0
- package/dist/tokens.d.ts +162 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +411 -0
- package/dist/tokens.js.map +1 -0
- package/dist/webhooks.d.ts +60 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +125 -0
- package/dist/webhooks.js.map +1 -0
- package/openapi/merchant.json +21452 -0
- package/package.json +63 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAQ5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAElD,MAAM,WAAW,oBAAoB,CAAC,KAAK,GAAG,OAAO;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,IAAI,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,KAAK,CAAC;CACb;AAED,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,kBAAkB,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,gBAAgB,CAAC,KAAK,GAAG,OAAO,IAAI,CAC9C,QAAQ,EAAE,oBAAoB,CAAC,KAAK,CAAC,EACrC,OAAO,EAAE,qBAAqB,KAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,iBAAiB,CAAC;IACzB,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC3C,uDAAuD;IACvD,aAAa,CAAC,EAAE,CACd,QAAQ,EAAE,oBAAoB,GAAG,IAAI,EACrC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,KAChE,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAyCD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAiFzE"}
|
package/dist/webhooks.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { MAX_TIMESTAMP_SKEW_SECONDS, verifyQueekSignature, WEBHOOK_ID_HEADER, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_TIMESTAMP_HEADER, } from "./signatures.js";
|
|
3
|
+
/**
|
|
4
|
+
* Receiver for Queek topic deliveries (`DeliverWebhookJob` in
|
|
5
|
+
* queek_backend, one signed POST per installation endpoint).
|
|
6
|
+
*
|
|
7
|
+
* Delivery envelope: `{ id, topic, api_version: "v1", created_at, data }`
|
|
8
|
+
* with `X-Queek-Topic` echoing `topic`. Verification uses the
|
|
9
|
+
* PER-INSTALLATION endpoint secret handed over once inside the install
|
|
10
|
+
* payload (`data.webhook_secret`) — never the app signing secret.
|
|
11
|
+
*
|
|
12
|
+
* Secret lookup: topic payloads are resource renders with no guaranteed
|
|
13
|
+
* installation/vendor id, so the default resolver identifies the
|
|
14
|
+
* installation by trying each stored installation secret against the
|
|
15
|
+
* signature (the signature itself names the sender; HMACs are microseconds,
|
|
16
|
+
* installations per app are few). Apps that carry their own routing hint
|
|
17
|
+
* may pass `resolveSecret` instead.
|
|
18
|
+
*
|
|
19
|
+
* Dedupe: Queek retries a delivery for ~4h until it sees 2xx, so every
|
|
20
|
+
* `webhook-id` is processed AT MOST once — the header id is claimed
|
|
21
|
+
* atomically up front (exactly one concurrent same-id delivery runs the
|
|
22
|
+
* handler) and a repeat answers 200 WITHOUT re-running it. A throwing
|
|
23
|
+
* handler releases the claim and answers 500 so the retry can land.
|
|
24
|
+
* Unknown topics answer 200 too (a topic the app no longer handles must
|
|
25
|
+
* not wedge Queek's retry queue); only auth and malformed bodies are
|
|
26
|
+
* non-2xx.
|
|
27
|
+
*/
|
|
28
|
+
export const QUEEK_TOPIC_HEADER = "X-Queek-Topic";
|
|
29
|
+
async function defaultResolveSecret(store, rawBody, headers, envelope) {
|
|
30
|
+
// Fast path: handoff-shaped payloads that name their installation.
|
|
31
|
+
const data = envelope?.data;
|
|
32
|
+
const named = data !== null && typeof data === "object"
|
|
33
|
+
? data.installation?.id
|
|
34
|
+
: undefined;
|
|
35
|
+
if (typeof named === "string" && named !== "") {
|
|
36
|
+
const found = await store.getInstallation(named);
|
|
37
|
+
const secret = found?.webhookSecret;
|
|
38
|
+
if (found &&
|
|
39
|
+
secret &&
|
|
40
|
+
verifyQueekSignature({ ...headers, body: rawBody, secret }, { skipFreshnessCheck: true })) {
|
|
41
|
+
return { installationId: found.installationId, secret };
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
// General path: the signature identifies the sender.
|
|
46
|
+
const candidates = await store.listWebhookSecrets();
|
|
47
|
+
for (const candidate of candidates) {
|
|
48
|
+
if (verifyQueekSignature({ ...headers, body: rawBody, secret: candidate.secret }, { skipFreshnessCheck: true })) {
|
|
49
|
+
return candidate;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
export function createWebhookHandler(options) {
|
|
55
|
+
const app = new Hono();
|
|
56
|
+
const maxSkew = options.maxSkewSeconds ?? MAX_TIMESTAMP_SKEW_SECONDS;
|
|
57
|
+
app.post("/", async (c) => {
|
|
58
|
+
const rawBody = await c.req.text();
|
|
59
|
+
const id = c.req.header(WEBHOOK_ID_HEADER);
|
|
60
|
+
const timestamp = c.req.header(WEBHOOK_TIMESTAMP_HEADER);
|
|
61
|
+
const signatureHeader = c.req.header(WEBHOOK_SIGNATURE_HEADER);
|
|
62
|
+
if (!id || !timestamp || !signatureHeader) {
|
|
63
|
+
return c.json({ ok: false, error: "missing signature headers" }, 401);
|
|
64
|
+
}
|
|
65
|
+
let envelope = null;
|
|
66
|
+
try {
|
|
67
|
+
const parsed = JSON.parse(rawBody);
|
|
68
|
+
if (typeof parsed === "object" && parsed !== null)
|
|
69
|
+
envelope = parsed;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return c.json({ ok: false, error: "invalid_json" }, 400);
|
|
73
|
+
}
|
|
74
|
+
if (!envelope || typeof envelope.topic !== "string") {
|
|
75
|
+
return c.json({ ok: false, error: "invalid_envelope" }, 400);
|
|
76
|
+
}
|
|
77
|
+
const headers = { id, timestamp, signatureHeader };
|
|
78
|
+
const resolution = options.resolveSecret
|
|
79
|
+
? await options.resolveSecret(envelope, rawBody, headers)
|
|
80
|
+
: await defaultResolveSecret(options.store, rawBody, headers, envelope);
|
|
81
|
+
if (!resolution) {
|
|
82
|
+
return c.json({ ok: false, error: "unknown_installation" }, 401);
|
|
83
|
+
}
|
|
84
|
+
// Freshness is enforced HERE, once, against the resolved secret — the
|
|
85
|
+
// lookup above deliberately skips it so a stale delivery cannot be
|
|
86
|
+
// misattributed before it is rejected.
|
|
87
|
+
const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);
|
|
88
|
+
const ts = Number(timestamp);
|
|
89
|
+
if (!Number.isFinite(ts) || Math.abs(now - ts) > maxSkew) {
|
|
90
|
+
return c.json({ ok: false, error: "stale timestamp" }, 401);
|
|
91
|
+
}
|
|
92
|
+
// Freshness was enforced above; this re-checks the MAC only, so a
|
|
93
|
+
// custom resolver cannot claim an installation without its secret.
|
|
94
|
+
if (!verifyQueekSignature({ ...headers, body: rawBody, secret: resolution.secret }, { skipFreshnessCheck: true })) {
|
|
95
|
+
return c.json({ ok: false, error: "signature mismatch" }, 401);
|
|
96
|
+
}
|
|
97
|
+
const installation = await options.store.getInstallation(resolution.installationId);
|
|
98
|
+
if (!installation) {
|
|
99
|
+
return c.json({ ok: false, error: "unknown_installation" }, 401);
|
|
100
|
+
}
|
|
101
|
+
// Atomic claim on the HEADER id: exactly one concurrent same-id
|
|
102
|
+
// delivery runs the handler; the rest answer deduped.
|
|
103
|
+
if (!(await options.store.claimWebhookId(id))) {
|
|
104
|
+
return c.json({ ok: true, deduped: true });
|
|
105
|
+
}
|
|
106
|
+
const topic = c.req.header(QUEEK_TOPIC_HEADER) ?? envelope.topic;
|
|
107
|
+
const handler = options.handlers[topic];
|
|
108
|
+
if (!handler) {
|
|
109
|
+
// No handler for this topic is NOT a failure: answering non-2xx would
|
|
110
|
+
// retry for hours something the app will never handle. The claim
|
|
111
|
+
// stands as the seen-record.
|
|
112
|
+
return c.json({ ok: true, unhandled: true });
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
await handler(envelope, { installation, topic, eventId: id });
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
await options.store.releaseWebhookId(id);
|
|
119
|
+
return c.json({ ok: false, error: "handler_failed" }, 500);
|
|
120
|
+
}
|
|
121
|
+
return c.json({ ok: true });
|
|
122
|
+
});
|
|
123
|
+
return app;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"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"}
|