@usequeek/app-sdk 0.4.0 → 0.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/CHANGELOG.md +30 -0
- package/README.md +126 -10
- package/dist/delivery.d.ts +48 -0
- package/dist/delivery.d.ts.map +1 -0
- package/dist/delivery.js +46 -0
- package/dist/delivery.js.map +1 -0
- package/dist/hono.d.ts +19 -0
- package/dist/hono.d.ts.map +1 -0
- package/dist/hono.js +27 -0
- package/dist/hono.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/install-handlers.d.ts +23 -5
- package/dist/install-handlers.d.ts.map +1 -1
- package/dist/install-handlers.js +191 -156
- package/dist/install-handlers.js.map +1 -1
- package/dist/webhooks.d.ts +22 -4
- package/dist/webhooks.d.ts.map +1 -1
- package/dist/webhooks.js +94 -72
- package/dist/webhooks.js.map +1 -1
- package/package.json +10 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@usequeek/app-sdk` are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
4
4
|
|
|
5
|
+
## [0.5.0] - 2026-09-29
|
|
6
|
+
|
|
7
|
+
Framework-agnostic core: the install handoff and webhook receivers no longer require Hono.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **BREAKING:** `createInstallHandlers` and `createWebhookHandler` moved from the package root to
|
|
12
|
+
the `@usequeek/app-sdk/hono` subpath (thin wrappers over the new core — same options objects,
|
|
13
|
+
same behaviour, same errors/status codes). Migration:
|
|
14
|
+
`import { createInstallHandlers } from "@usequeek/app-sdk/hono"`. `hono` is now an optional
|
|
15
|
+
peer (`peerDependenciesMeta: { hono: { optional: true } }`), so non-Hono apps install zero
|
|
16
|
+
framework code. `./server` keeps working as today.
|
|
17
|
+
- The root entry is framework-free in three thin layers, each a wrapper over the one below:
|
|
18
|
+
layer 1 `handleInstallDelivery(input, options)` / `handleWebhookDelivery(input, options)` take
|
|
19
|
+
untouched body bytes + headers (`Record` or `Headers`, case-insensitive, array-tolerant) plus
|
|
20
|
+
method/path where the install endpoints genuinely need them, and return plain
|
|
21
|
+
`{ status, body }` — zero request/response types, so whatever request object each framework
|
|
22
|
+
supports works; layer 2 `handleInstallRequest` / `handleWebhookRequest` adapt the Web-standard
|
|
23
|
+
`Request` → `Response` onto layer 1; layer 3 `@usequeek/app-sdk/hono` adapts Hono onto layer 2.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- `delivery.ts`: `CoreDelivery` / `InstallDelivery` / `DeliveryResult` / `CoreHeaders` plus
|
|
28
|
+
`readHeader`, `decodeBody`, `toResponse`.
|
|
29
|
+
- `hono.ts` behind the `./hono` export: the Hono wrappers.
|
|
30
|
+
- `scripts/verify-package.mjs` now proves the split in two sandboxes: the root entry plus
|
|
31
|
+
`./server` work with NO `hono` installed and NO framework at all (layer 1 is called directly
|
|
32
|
+
with a `Buffer` + plain headers; importing `/hono` there fails — the proof the root carries
|
|
33
|
+
zero framework imports), while `/hono` works with it.
|
|
34
|
+
|
|
5
35
|
## [0.2.0] - 2026-09-25
|
|
6
36
|
|
|
7
37
|
GitHub-style app credentials (slice S1 of `app-credentials-and-databases`, built against the
|
package/README.md
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
# @usequeek/app-sdk
|
|
2
2
|
|
|
3
|
-
The SDK for building a Queek app on the public Merchant API and signed webhooks.
|
|
3
|
+
The SDK for building a Queek app on the public Merchant API and signed webhooks. Framework-agnostic Web-standard handlers for the install handoff and topic webhooks (`Request` in, `Response` out — use them from Next.js route handlers, Express, or any runtime), a typed Merchant API client (types generated from the live contract), GitHub-style app credentials (one asymmetric key per app, short-lived per-installation tokens minted on demand), resync recovery, and an encrypted installation store (SQLite for local/test, Postgres in production). Optional Hono wrappers live under `@usequeek/app-sdk/hono`.
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
npm i @usequeek/app-sdk
|
|
6
|
+
npm i @usequeek/app-sdk pg
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
Requires Node `>=22.14`. `
|
|
9
|
+
Requires Node `>=22.14`. `pg` is a regular dependency (the production store). `hono` is an optional peer — install it (`npm i hono`) only if you use the Hono wrappers.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
The shape follows [`@shopify/shopify-api`](https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/README.md): the core "doesn't rely on any specific framework, so you can include it alongside your preferred stack" (runtime differences are covered by adapters such as `@shopify/shopify-api/adapters/node`), and framework integrations are separate packages in the [shopify-app-js monorepo](https://github.com/Shopify/shopify-app-js) (e.g. `@shopify/shopify-app-express` and `@shopify/shopify-app-remix` build on `@shopify/shopify-api`).
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## Example (any framework)
|
|
14
|
+
|
|
15
|
+
A minimal app with plain Web-standard handlers — no framework import:
|
|
14
16
|
|
|
15
17
|
```ts
|
|
16
18
|
import {
|
|
17
19
|
createAppTokenProvider,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
handleInstallRequest,
|
|
21
|
+
handleWebhookRequest,
|
|
20
22
|
loadAppCredential,
|
|
21
23
|
SqliteInstallationStore,
|
|
22
24
|
} from "@usequeek/app-sdk";
|
|
23
|
-
import { Hono } from "hono";
|
|
24
25
|
|
|
25
26
|
const store = new SqliteInstallationStore({
|
|
26
27
|
path: "./data/installations.db",
|
|
@@ -30,6 +31,118 @@ const tokens = createAppTokenProvider({
|
|
|
30
31
|
credential: loadAppCredential({ appSlug: "hello" }), // APP_SLUG/APP_KEY_ID/APP_PRIVATE_KEY
|
|
31
32
|
store,
|
|
32
33
|
});
|
|
34
|
+
const installOptions = { appSecret: process.env.QUEEK_APP_SECRET!, store };
|
|
35
|
+
const webhookOptions = {
|
|
36
|
+
store,
|
|
37
|
+
handlers: {
|
|
38
|
+
"orders/updated": async (envelope, context) => {
|
|
39
|
+
console.log("order update:", context.installation.storePid, envelope.data);
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Next.js App Router — one route file per handoff path (`handleInstallRequest` routes on the request URL's trailing `install` / `uninstall` / `settings` segment, so one shared options object serves all three):
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
// app/api/install/route.ts (and uninstall/route.ts, settings/route.ts likewise)
|
|
49
|
+
import { installOptions } from "./options";
|
|
50
|
+
|
|
51
|
+
export async function POST(request: Request) {
|
|
52
|
+
return handleInstallRequest(request, installOptions);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// app/api/webhooks/route.ts
|
|
56
|
+
import { webhookOptions } from "./options";
|
|
57
|
+
|
|
58
|
+
export async function POST(request: Request) {
|
|
59
|
+
return handleWebhookRequest(request, webhookOptions);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Express (no new package)
|
|
64
|
+
|
|
65
|
+
Express hands you whatever request object it supports — pass its raw body bytes and headers straight to the layer-1 core (no `Request` construction needed). Keep the untouched bytes Queek signed with the built-in `express.raw` parser:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import express from "express";
|
|
69
|
+
import {
|
|
70
|
+
type DeliveryResult,
|
|
71
|
+
handleInstallDelivery,
|
|
72
|
+
handleWebhookDelivery,
|
|
73
|
+
} from "@usequeek/app-sdk";
|
|
74
|
+
|
|
75
|
+
const app = express();
|
|
76
|
+
app.use("/api", express.raw({ type: "application/json" }));
|
|
77
|
+
|
|
78
|
+
function reply(res: express.Response, result: DeliveryResult) {
|
|
79
|
+
res.status(result.status).json(result.body);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
for (const route of ["install", "uninstall", "settings"]) {
|
|
83
|
+
app.post(`/api/${route}`, async (req, res) => {
|
|
84
|
+
reply(
|
|
85
|
+
res,
|
|
86
|
+
await handleInstallDelivery(
|
|
87
|
+
{ rawBody: req.body as Buffer, headers: req.headers, method: req.method, path: req.path },
|
|
88
|
+
installOptions,
|
|
89
|
+
),
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
app.post("/api/webhooks", async (req, res) => {
|
|
94
|
+
reply(
|
|
95
|
+
res,
|
|
96
|
+
await handleWebhookDelivery({ rawBody: req.body as Buffer, headers: req.headers }, webhookOptions),
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Fastify (no new package)
|
|
102
|
+
|
|
103
|
+
Same idea — keep the body raw with `addContentTypeParser`, then call the core directly:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import Fastify from "fastify";
|
|
107
|
+
import { handleWebhookDelivery } from "@usequeek/app-sdk";
|
|
108
|
+
|
|
109
|
+
const fastify = Fastify();
|
|
110
|
+
fastify.addContentTypeParser("application/json", { parseAs: "buffer" }, (_req, body, done) => {
|
|
111
|
+
done(null, body);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
fastify.post("/api/webhooks", async (req, reply) => {
|
|
115
|
+
const result = await handleWebhookDelivery(
|
|
116
|
+
{ rawBody: req.body as Buffer, headers: req.headers },
|
|
117
|
+
webhookOptions,
|
|
118
|
+
);
|
|
119
|
+
return reply.status(result.status).send(result.body);
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Why the raw body matters
|
|
124
|
+
|
|
125
|
+
Signature verification covers the exact bytes Queek sent (`{id}.{timestamp}.{body}`), not the parsed JSON value — so the SDK takes the untouched body bytes at every layer. A parsed-then-restringified body has different bytes (spacing, key order) and will NOT verify. A string `rawBody` is used verbatim, so only pass a string when it is already the exact UTF-8 text — otherwise prefer `Buffer`/`Uint8Array` straight from `express.raw`, Fastify's `parseAs: "buffer"`, or `await request.arrayBuffer()`. This is Stripe's model (`stripe.webhooks.constructEvent(rawBody, sigHeader, secret)`): [their docs](https://docs.stripe.com/webhooks) put it bluntly — "Stripe requires the raw body of the request to perform signature verification… Any manipulation to the raw body of the request causes the verification to fail." In practice: Express needs `express.raw(...)` (never `express.json()`) on webhook routes, Fastify needs `addContentTypeParser` with `parseAs: "buffer"`, and in Next.js you read `await request.arrayBuffer()` (the SDK's layer 2 already does) rather than `await request.json()`.
|
|
126
|
+
|
|
127
|
+
### Body-size posture
|
|
128
|
+
|
|
129
|
+
The SDK itself imposes no maximum body size (`arrayBuffer` / `TextDecoder` / `JSON.parse` are unbounded, as in 0.4.0) — the cap belongs to the framework parser in front of it, which already rejects oversized bodies before the SDK ever sees them. Queek deliveries are small JSON payloads, so the framework defaults are plenty; if you set them explicitly, `express.raw({ type: "application/json", limit: "1mb" })` and Fastify's default `bodyLimit` of 1 MiB are the recommended ceilings. No SDK option is needed unless Queek ever ships large deliveries.
|
|
130
|
+
|
|
131
|
+
(If you already run Hono on Node, `@hono/node-server`'s `getRequestListener` bridges serving for you — but the adapters above need no extra dependency.)
|
|
132
|
+
|
|
133
|
+
### Hono
|
|
134
|
+
|
|
135
|
+
Prefer Hono? The thin wrappers under `@usequeek/app-sdk/hono` (same options, same behaviour, same errors/status codes) mount the same core:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import { SqliteInstallationStore } from "@usequeek/app-sdk";
|
|
139
|
+
import { createInstallHandlers, createWebhookHandler } from "@usequeek/app-sdk/hono";
|
|
140
|
+
import { Hono } from "hono";
|
|
141
|
+
|
|
142
|
+
const store = new SqliteInstallationStore({
|
|
143
|
+
path: "./data/installations.db",
|
|
144
|
+
storeKey: process.env.APP_ENCRYPTION_KEY!,
|
|
145
|
+
});
|
|
33
146
|
|
|
34
147
|
const app = new Hono();
|
|
35
148
|
app.get("/health", (c) => c.json({ ok: true }));
|
|
@@ -49,6 +162,8 @@ app.route(
|
|
|
49
162
|
export default app;
|
|
50
163
|
```
|
|
51
164
|
|
|
165
|
+
> Migrating from 0.4.x: `import { createInstallHandlers } from "@usequeek/app-sdk/hono"` — the creators moved out of the root entry so non-Hono apps never install `hono`. The options objects are unchanged.
|
|
166
|
+
|
|
52
167
|
Every Merchant API call goes through `createInstallationClient({ installationId, apiBase, tokens })`,
|
|
53
168
|
which resolves the installation's token via `acquireToken()` and sends it as `X-Client-Key`.
|
|
54
169
|
|
|
@@ -148,9 +263,10 @@ retries (~4 h) are gone; resync cannot backfill them. Full runbook: `docs/deploy
|
|
|
148
263
|
429 `resync_cooldown` → skip + record; other 429 → backoff + retry) → drop tokens →
|
|
149
264
|
purge absent except known-pending. Connectivity scope only.
|
|
150
265
|
- **verify** (`signatures.ts`): `verifyQueekSignature` — Standard Webhooks verification (`webhook-id`, `webhook-timestamp`, `webhook-signature` over `{id}.{timestamp}.{body}`, keyed by the decoded `whsec_…` bytes), with timestamp-skew enforcement.
|
|
151
|
-
- **
|
|
266
|
+
- **delivery core** (`delivery.ts`): `CoreDelivery` (`rawBody` + `headers`) / `InstallDelivery` (+ `method`/`path`) / `DeliveryResult` (`{ status, body }`) / `CoreHeaders`, plus `readHeader` (case-insensitive, array-tolerant), `decodeBody`, and `toResponse`. Zero request/response types.
|
|
267
|
+
- **install handlers** (`install-handlers.ts`): layer 1 `handleInstallDelivery(input, { appSecret, store, onInstall?, onUninstall?, onSettings? })` serves the signed install/uninstall/settings handoff from raw bytes + headers (routes on the path's trailing segment); layer 2 `handleInstallRequest(request, …)` adapts `Request` → `Response` onto it. Defaults persist the installation (encrypted) in the store; a redelivered install for an existing installation merges idempotently (`saveResyncedInstallation`). The Hono wrapper `createInstallHandlers` lives under `@usequeek/app-sdk/hono` (`hono.ts`).
|
|
152
268
|
- **client** (`client.ts`): `createQueekClient({ apiBase, apiKey })` — the low-level typed fetch client over the Merchant API (`X-Client-Key`), with `Idempotency-Key` on writes, typed `QueekApiError`s, and 429 retry helpers. Types come from `openapi/merchant.json`, the committed snapshot of the live contract. Prefer `createInstallationClient` in apps.
|
|
153
|
-
- **webhooks** (`webhooks.ts`): `
|
|
269
|
+
- **webhooks** (`webhooks.ts`): layer 1 `handleWebhookDelivery(input, { store, handlers })` verifies each delivery against the installation's endpoint secret, dedupes on `webhook-id`, and dispatches `topic → handler` at most once; layer 2 `handleWebhookRequest(request, …)` adapts `Request` → `Response` onto it. The Hono wrapper `createWebhookHandler` lives under `@usequeek/app-sdk/hono` (`hono.ts`).
|
|
154
270
|
- **store** (`store.ts`): `SqliteInstallationStore` (local/dev/test) and `PostgresInstallationStore`
|
|
155
271
|
(`pg`, pool max 2, advisory-locked schema + `schema_version` row so two containers boot
|
|
156
272
|
safely) — installations encrypted at rest (AES-GCM via `APP_ENCRYPTION_KEY`), plus the
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared plumbing for the framework-free delivery core (layer 1).
|
|
3
|
+
*
|
|
4
|
+
* Layer 1 functions take plain data — untouched body bytes plus headers —
|
|
5
|
+
* and return plain `{ status, body }` results. They reference zero
|
|
6
|
+
* request/response types (no `Request`, no framework), so any framework
|
|
7
|
+
* that can hand over the raw body bytes can use them. Layer 2
|
|
8
|
+
* (`handleInstallRequest` / `handleWebhookRequest`) adapts the Web
|
|
9
|
+
* standard onto this core; `@usequeek/app-sdk/hono` adapts Hono onto
|
|
10
|
+
* layer 2.
|
|
11
|
+
*
|
|
12
|
+
* The raw-body rule (Stripe's model:
|
|
13
|
+
* `stripe.webhooks.constructEvent(rawBody, sigHeader, secret)` —
|
|
14
|
+
* https://docs.stripe.com/webhooks: "Stripe requires the raw body of the
|
|
15
|
+
* request to perform signature verification… Any manipulation to the raw
|
|
16
|
+
* body of the request causes the verification to fail"): `rawBody` must be
|
|
17
|
+
* the exact bytes Queek signed. Never pass a parsed-then-restringified
|
|
18
|
+
* body — JSON re-serialization changes bytes (spacing, key order) and the
|
|
19
|
+
* signature will not verify.
|
|
20
|
+
*/
|
|
21
|
+
/** Headers as any framework holds them: a `Headers` instance, or a plain object (Express-style: lowercased keys, array values allowed). */
|
|
22
|
+
export type CoreHeaders = Record<string, string | string[] | undefined> | Headers;
|
|
23
|
+
/** The framework-free delivery input both core handlers take. */
|
|
24
|
+
export interface CoreDelivery {
|
|
25
|
+
/** The untouched body bytes Queek signed (string is used verbatim; binary is UTF-8-decoded). */
|
|
26
|
+
rawBody: Uint8Array | Buffer | string;
|
|
27
|
+
headers: CoreHeaders;
|
|
28
|
+
}
|
|
29
|
+
/** Layer-1 input for the install handoff, which genuinely needs routing. */
|
|
30
|
+
export interface InstallDelivery extends CoreDelivery {
|
|
31
|
+
/** Defaults to `"POST"`. Anything else answers 405. */
|
|
32
|
+
method?: string;
|
|
33
|
+
/** Request path (`/install`, …) or full URL — routes on the trailing `install` / `uninstall` / `settings` segment. */
|
|
34
|
+
path?: string;
|
|
35
|
+
}
|
|
36
|
+
/** What the layer-1 core returns: an HTTP status plus a JSON-serializable body. */
|
|
37
|
+
export interface DeliveryResult {
|
|
38
|
+
status: number;
|
|
39
|
+
body: unknown;
|
|
40
|
+
headers?: Record<string, string>;
|
|
41
|
+
}
|
|
42
|
+
/** Case-insensitive header lookup across `Headers` and plain (possibly lowercased, possibly array-valued) objects. */
|
|
43
|
+
export declare function readHeader(headers: CoreHeaders, name: string): string | undefined;
|
|
44
|
+
/** Decode untouched body bytes to the exact string Queek signed. */
|
|
45
|
+
export declare function decodeBody(rawBody: Uint8Array | Buffer | string): string;
|
|
46
|
+
/** Build the layer-2 `Response` from a layer-1 result. */
|
|
47
|
+
export declare function toResponse(result: DeliveryResult): Response;
|
|
48
|
+
//# sourceMappingURL=delivery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,2IAA2I;AAC3I,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC;AAElF,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,gGAAgG;IAChG,OAAO,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;IACtC,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,4EAA4E;AAC5E,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACnD,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sHAAsH;IACtH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,mFAAmF;AACnF,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,sHAAsH;AACtH,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAYjF;AAED,oEAAoE;AACpE,wBAAgB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAGxE;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,QAAQ,CAE3D"}
|
package/dist/delivery.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared plumbing for the framework-free delivery core (layer 1).
|
|
3
|
+
*
|
|
4
|
+
* Layer 1 functions take plain data — untouched body bytes plus headers —
|
|
5
|
+
* and return plain `{ status, body }` results. They reference zero
|
|
6
|
+
* request/response types (no `Request`, no framework), so any framework
|
|
7
|
+
* that can hand over the raw body bytes can use them. Layer 2
|
|
8
|
+
* (`handleInstallRequest` / `handleWebhookRequest`) adapts the Web
|
|
9
|
+
* standard onto this core; `@usequeek/app-sdk/hono` adapts Hono onto
|
|
10
|
+
* layer 2.
|
|
11
|
+
*
|
|
12
|
+
* The raw-body rule (Stripe's model:
|
|
13
|
+
* `stripe.webhooks.constructEvent(rawBody, sigHeader, secret)` —
|
|
14
|
+
* https://docs.stripe.com/webhooks: "Stripe requires the raw body of the
|
|
15
|
+
* request to perform signature verification… Any manipulation to the raw
|
|
16
|
+
* body of the request causes the verification to fail"): `rawBody` must be
|
|
17
|
+
* the exact bytes Queek signed. Never pass a parsed-then-restringified
|
|
18
|
+
* body — JSON re-serialization changes bytes (spacing, key order) and the
|
|
19
|
+
* signature will not verify.
|
|
20
|
+
*/
|
|
21
|
+
/** Case-insensitive header lookup across `Headers` and plain (possibly lowercased, possibly array-valued) objects. */
|
|
22
|
+
export function readHeader(headers, name) {
|
|
23
|
+
if (headers instanceof Headers) {
|
|
24
|
+
return headers.get(name) ?? undefined;
|
|
25
|
+
}
|
|
26
|
+
const want = name.toLowerCase();
|
|
27
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
28
|
+
if (key.toLowerCase() === want) {
|
|
29
|
+
if (value === undefined)
|
|
30
|
+
return undefined;
|
|
31
|
+
return Array.isArray(value) ? value[0] : value;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
/** Decode untouched body bytes to the exact string Queek signed. */
|
|
37
|
+
export function decodeBody(rawBody) {
|
|
38
|
+
if (typeof rawBody === "string")
|
|
39
|
+
return rawBody;
|
|
40
|
+
return new TextDecoder("utf-8").decode(rawBody);
|
|
41
|
+
}
|
|
42
|
+
/** Build the layer-2 `Response` from a layer-1 result. */
|
|
43
|
+
export function toResponse(result) {
|
|
44
|
+
return Response.json(result.body, { status: result.status, headers: result.headers });
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=delivery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery.js","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA2BH,sHAAsH;AACtH,MAAM,UAAU,UAAU,CAAC,OAAoB,EAAE,IAAY;IAC3D,IAAI,OAAO,YAAY,OAAO,EAAE,CAAC;QAC/B,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC;IACxC,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC;YAC/B,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,SAAS,CAAC;YAC1C,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACjD,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,UAAU,CAAC,OAAqC;IAC9D,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,OAAO,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAClD,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,UAAU,CAAC,MAAsB;IAC/C,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;AACxF,CAAC"}
|
package/dist/hono.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional Hono integration for `@usequeek/app-sdk`.
|
|
3
|
+
*
|
|
4
|
+
* Import from `@usequeek/app-sdk/hono` (requires the optional `hono` peer):
|
|
5
|
+
* `createInstallHandlers` and `createWebhookHandler` are thin wrappers
|
|
6
|
+
* built ONLY on the layer-2 Web-standard handlers (`handleInstallRequest`
|
|
7
|
+
* / `handleWebhookRequest` in the root entry) — same options objects, same
|
|
8
|
+
* behaviour, same errors/status codes.
|
|
9
|
+
*/
|
|
10
|
+
import { Hono } from "hono";
|
|
11
|
+
import { type InstallHandlerOptions } from "./install-handlers.js";
|
|
12
|
+
import { type WebhookHandlerOptions } from "./webhooks.js";
|
|
13
|
+
export type { InstallCallbacks, InstallHandlerOptions } from "./install-handlers.js";
|
|
14
|
+
export type { QueekWebhookEnvelope, SecretResolution, WebhookHandlerContext, WebhookHandlerFn, WebhookHandlerOptions, } from "./webhooks.js";
|
|
15
|
+
/** Mount the signed install/uninstall/settings handoff (`POST /install`, `POST /uninstall`, `POST /settings`). */
|
|
16
|
+
export declare function createInstallHandlers(options: InstallHandlerOptions): Hono;
|
|
17
|
+
/** Mount the signed topic-delivery receiver (`POST /`). */
|
|
18
|
+
export declare function createWebhookHandler(options: WebhookHandlerOptions): Hono;
|
|
19
|
+
//# sourceMappingURL=hono.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../src/hono.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAwB,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACzF,OAAO,EAAwB,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEjF,YAAY,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACrF,YAAY,EACV,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AAEvB,kHAAkH;AAClH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAM1E;AAED,2DAA2D;AAC3D,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAIzE"}
|
package/dist/hono.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional Hono integration for `@usequeek/app-sdk`.
|
|
3
|
+
*
|
|
4
|
+
* Import from `@usequeek/app-sdk/hono` (requires the optional `hono` peer):
|
|
5
|
+
* `createInstallHandlers` and `createWebhookHandler` are thin wrappers
|
|
6
|
+
* built ONLY on the layer-2 Web-standard handlers (`handleInstallRequest`
|
|
7
|
+
* / `handleWebhookRequest` in the root entry) — same options objects, same
|
|
8
|
+
* behaviour, same errors/status codes.
|
|
9
|
+
*/
|
|
10
|
+
import { Hono } from "hono";
|
|
11
|
+
import { handleInstallRequest } from "./install-handlers.js";
|
|
12
|
+
import { handleWebhookRequest } from "./webhooks.js";
|
|
13
|
+
/** Mount the signed install/uninstall/settings handoff (`POST /install`, `POST /uninstall`, `POST /settings`). */
|
|
14
|
+
export function createInstallHandlers(options) {
|
|
15
|
+
const app = new Hono();
|
|
16
|
+
app.post("/install", (c) => handleInstallRequest(c.req.raw, options));
|
|
17
|
+
app.post("/uninstall", (c) => handleInstallRequest(c.req.raw, options));
|
|
18
|
+
app.post("/settings", (c) => handleInstallRequest(c.req.raw, options));
|
|
19
|
+
return app;
|
|
20
|
+
}
|
|
21
|
+
/** Mount the signed topic-delivery receiver (`POST /`). */
|
|
22
|
+
export function createWebhookHandler(options) {
|
|
23
|
+
const app = new Hono();
|
|
24
|
+
app.post("/", (c) => handleWebhookRequest(c.req.raw, options));
|
|
25
|
+
return app;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=hono.js.map
|
package/dist/hono.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../src/hono.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,oBAAoB,EAA8B,MAAM,uBAAuB,CAAC;AACzF,OAAO,EAAE,oBAAoB,EAA8B,MAAM,eAAe,CAAC;AAWjF,kHAAkH;AAClH,MAAM,UAAU,qBAAqB,CAAC,OAA8B;IAClE,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;IACtE,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;IACxE,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;IACvE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,oBAAoB,CAAC,OAA8B;IACjE,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/D,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,13 +13,14 @@ export { backgroundTaskCount, type CatchupFailure, type CatchupInstallation, typ
|
|
|
13
13
|
export { apiHostsFromEnv, createQueekClient, DEFAULT_API_HOSTS, devApiHostsFromEnv, InvalidApiBaseError, MERCHANT_API_PATH, type MerchantPaths, newIdempotencyKey, type OperationResponse, QueekApiError, type QueekClient, type QueekClientOptions, type QueekErrorDetails, queekApiErrorFromResponse, type RequestOptions, type RetryOptions, resolveApiBase, type StoreProfile, } from "./client.js";
|
|
14
14
|
export { type AlertBody, type AlertSeverity, type CollectedDefinition, type CollectedDefinitionCreate, type CollectedDefinitionUpdate, type CollectedField, collectedDefinitions, createRecord, type MerchantAlert, type SetupNotice, type SetupNoticeBody, type SetupNoticeItem, type SetupNoticeStatus, type SuccessEnvelope, sendAlert, setSetupNotice, } from "./collected.js";
|
|
15
15
|
export { decryptSecret, encryptSecret, parseStoreKey, storeKeyFingerprint, } from "./crypto.js";
|
|
16
|
+
export { type CoreDelivery, type CoreHeaders, decodeBody, type DeliveryResult, type InstallDelivery, readHeader, } from "./delivery.js";
|
|
16
17
|
export { APP_SOURCE, type AppInboundMessage, type AppOutboundMessage, DASHBOARD_SOURCE, type FrameBridgeOptions, listenToDashboard, sendReady, } from "./frame.js";
|
|
17
18
|
export { type HandoffEnvelope, type HandoffEnvelopeAny, type HandoffInstallationRef, type HandoffStore, INSTALL_EVENT, type InstallData, type InstallEnvelope, RESYNC_EVENT, type ResyncEnvelope, SETTINGS_EVENT, type SettingsData, type SettingsEnvelope, UNINSTALL_EVENT, type UninstallData, type UninstallEnvelope, } from "./handoff.js";
|
|
18
|
-
export { buildInstallationRecord,
|
|
19
|
+
export { buildInstallationRecord, handleInstallDelivery, handleInstallRequest, type InstallCallbacks, type InstallHandlerOptions, saveResyncedInstallation, } from "./install-handlers.js";
|
|
19
20
|
export { createLogger, type LogFields, type Logger, type LoggerOptions, type LogLevel, REDACTED, } from "./logger.js";
|
|
20
21
|
export { type ResyncListItem, type ResyncOptions, type ResyncResult, resyncFromQueek, } from "./resync.js";
|
|
21
22
|
export { MAX_TIMESTAMP_SKEW_SECONDS, SECRET_PREFIX, type SignatureFailure, type SignatureInput, secretKeyBytes, signQueekPayload, type VerifyOptions, verifyQueekSignature, verifyQueekSignatureDetailed, WEBHOOK_ID_HEADER, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_TIMESTAMP_HEADER, } from "./signatures.js";
|
|
22
23
|
export { createInstallationStore, createPostgresPool, INSTALLATION_SCHEMA_VERSION, type InstallationRecord, type InstallationStore, type InstallationStoreSelector, PostgresInstallationStore, type PostgresStoreOptions, SqliteInstallationStore, type SqliteStoreOptions, type WebhookSecretCandidate, } from "./store.js";
|
|
23
24
|
export { APP_API_PATH, AppTokenProvider, type AppTokens, createAppTokenProvider, createInstallationClient, type InstallationClientOptions, mintPath, resolveAppApiBase, type TokenProviderOptions, } from "./tokens.js";
|
|
24
|
-
export {
|
|
25
|
+
export { handleWebhookDelivery, handleWebhookRequest, QUEEK_TOPIC_HEADER, type QueekWebhookEnvelope, type SecretResolution, type WebhookHandlerContext, type WebhookHandlerFn, type WebhookHandlerOptions, } from "./webhooks.js";
|
|
25
26
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -13,13 +13,14 @@ export { backgroundTaskCount, detach, runInstallationCatchup, } from "./backgrou
|
|
|
13
13
|
export { apiHostsFromEnv, createQueekClient, DEFAULT_API_HOSTS, devApiHostsFromEnv, InvalidApiBaseError, MERCHANT_API_PATH, newIdempotencyKey, QueekApiError, queekApiErrorFromResponse, resolveApiBase, } from "./client.js";
|
|
14
14
|
export { collectedDefinitions, createRecord, sendAlert, setSetupNotice, } from "./collected.js";
|
|
15
15
|
export { decryptSecret, encryptSecret, parseStoreKey, storeKeyFingerprint, } from "./crypto.js";
|
|
16
|
+
export { decodeBody, readHeader, } from "./delivery.js";
|
|
16
17
|
export { APP_SOURCE, DASHBOARD_SOURCE, listenToDashboard, sendReady, } from "./frame.js";
|
|
17
18
|
export { INSTALL_EVENT, RESYNC_EVENT, SETTINGS_EVENT, UNINSTALL_EVENT, } from "./handoff.js";
|
|
18
|
-
export { buildInstallationRecord,
|
|
19
|
+
export { buildInstallationRecord, handleInstallDelivery, handleInstallRequest, saveResyncedInstallation, } from "./install-handlers.js";
|
|
19
20
|
export { createLogger, REDACTED, } from "./logger.js";
|
|
20
21
|
export { resyncFromQueek, } from "./resync.js";
|
|
21
22
|
export { MAX_TIMESTAMP_SKEW_SECONDS, SECRET_PREFIX, secretKeyBytes, signQueekPayload, verifyQueekSignature, verifyQueekSignatureDetailed, WEBHOOK_ID_HEADER, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_TIMESTAMP_HEADER, } from "./signatures.js";
|
|
22
23
|
export { createInstallationStore, createPostgresPool, INSTALLATION_SCHEMA_VERSION, PostgresInstallationStore, SqliteInstallationStore, } from "./store.js";
|
|
23
24
|
export { APP_API_PATH, AppTokenProvider, createAppTokenProvider, createInstallationClient, mintPath, resolveAppApiBase, } from "./tokens.js";
|
|
24
|
-
export {
|
|
25
|
+
export { handleWebhookDelivery, handleWebhookRequest, QUEEK_TOPIC_HEADER, } from "./webhooks.js";
|
|
25
26
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,6BAA6B,EAC7B,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,EAEtB,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,yBAAyB,EACzB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EAEnB,UAAU,EACV,2BAA2B,EAC3B,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,mBAAmB,EAMnB,MAAM,EACN,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EAEjB,iBAAiB,EAEjB,aAAa,EAIb,yBAAyB,EAGzB,cAAc,GAEf,MAAM,aAAa,CAAC;AACrB,OAAO,EAOL,oBAAoB,EACpB,YAAY,EAOZ,SAAS,EACT,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,UAAU,EAGV,gBAAgB,EAEhB,iBAAiB,EACjB,SAAS,GACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAKL,aAAa,EAGb,YAAY,EAEZ,cAAc,EAGd,eAAe,GAGhB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,6BAA6B,EAC7B,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,EAEtB,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,yBAAyB,EACzB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EAEnB,UAAU,EACV,2BAA2B,EAC3B,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,mBAAmB,EAMnB,MAAM,EACN,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EAEjB,iBAAiB,EAEjB,aAAa,EAIb,yBAAyB,EAGzB,cAAc,GAEf,MAAM,aAAa,CAAC;AACrB,OAAO,EAOL,oBAAoB,EACpB,YAAY,EAOZ,SAAS,EACT,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAGL,UAAU,EAGV,UAAU,GACX,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,UAAU,EAGV,gBAAgB,EAEhB,iBAAiB,EACjB,SAAS,GACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAKL,aAAa,EAGb,YAAY,EAEZ,cAAc,EAGd,eAAe,GAGhB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EAGpB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,YAAY,EAKZ,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EAIL,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,0BAA0B,EAC1B,aAAa,EAGb,cAAc,EACd,gBAAgB,EAEhB,oBAAoB,EACpB,4BAA4B,EAC5B,iBAAiB,EACjB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,2BAA2B,EAI3B,yBAAyB,EAEzB,uBAAuB,GAGxB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAEhB,sBAAsB,EACtB,wBAAwB,EAExB,QAAQ,EACR,iBAAiB,GAElB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,GAMnB,MAAM,eAAe,CAAC"}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { Hono } from "hono";
|
|
2
1
|
import { type InstallData, type InstallEnvelope, type SettingsEnvelope, type UninstallEnvelope } from "./handoff.js";
|
|
2
|
+
import { type DeliveryResult, type InstallDelivery } from "./delivery.js";
|
|
3
3
|
import type { InstallationRecord, InstallationStore } from "./store.js";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* (`AppInstallService::deliver()` in queek_backend)
|
|
5
|
+
* Framework-agnostic handlers for Queek's signed server-to-server handoff
|
|
6
|
+
* (`AppInstallService::deliver()` in queek_backend) in three thin layers:
|
|
7
|
+
* layer 1 `handleInstallDelivery(input, options)` takes plain data
|
|
8
|
+
* (untouched body bytes + headers, plus method/path for routing) and
|
|
9
|
+
* returns a plain `{ status, body }` result — zero request/response types,
|
|
10
|
+
* so whatever request object each framework supports works; layer 2
|
|
11
|
+
* `handleInstallRequest(request, options)` adapts the Web standard onto
|
|
12
|
+
* layer 1; layer 3 (`@usequeek/app-sdk/hono`) adapts Hono onto layer 2.
|
|
7
13
|
*
|
|
8
14
|
* Every handoff — install, uninstall, settings, resync — carries the
|
|
9
15
|
* Standard Webhooks headers signed with the APP signing secret (`whsec_…`,
|
|
@@ -15,7 +21,7 @@ import type { InstallationRecord, InstallationStore } from "./store.js";
|
|
|
15
21
|
* `AppInstallService::resyncPayload()`) redelivers the install-shaped data
|
|
16
22
|
* — rotated `webhook_secret`, non-secret settings, and recovery copies of
|
|
17
23
|
* `proxy_secret` / `embed_secret` / `app_id` — to the app's `settings_url`,
|
|
18
|
-
* falling back to `install_url`. Both
|
|
24
|
+
* falling back to `install_url`. Both `install` and `settings` accept it
|
|
19
25
|
* with the same signature/verification rules and apply it via the resync
|
|
20
26
|
* merge (`saveResyncedInstallation`: refreshes secrets/settings/scopes,
|
|
21
27
|
* keeps `installedAt` + the cached token, keeps stored values the resync
|
|
@@ -58,5 +64,17 @@ export declare function buildInstallationRecord(data: InstallData, nowIso?: stri
|
|
|
58
64
|
* tokens explicitly when it wants fresh ones).
|
|
59
65
|
*/
|
|
60
66
|
export declare function saveResyncedInstallation(existing: InstallationRecord, data: InstallData, nowIso?: string): InstallationRecord;
|
|
61
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Layer 1: handle one signed install-handoff delivery (`install`,
|
|
69
|
+
* `uninstall`, or `settings`, taken from the input path's last segment)
|
|
70
|
+
* from plain data — no `Request`, no framework. Same options, same
|
|
71
|
+
* behaviour, same errors/status codes at every layer.
|
|
72
|
+
*/
|
|
73
|
+
export declare function handleInstallDelivery(input: InstallDelivery, options: InstallHandlerOptions): Promise<DeliveryResult>;
|
|
74
|
+
/**
|
|
75
|
+
* Layer 2: the Web-standard wrapper, built ONLY on layer 1 — reads
|
|
76
|
+
* `await request.arrayBuffer()` + headers, calls the core, builds the
|
|
77
|
+
* `Response`.
|
|
78
|
+
*/
|
|
79
|
+
export declare function handleInstallRequest(request: Request, options: InstallHandlerOptions): Promise<Response>;
|
|
62
80
|
//# sourceMappingURL=install-handlers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-handlers.d.ts","sourceRoot":"","sources":["../src/install-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"install-handlers.d.ts","sourceRoot":"","sources":["../src/install-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,eAAe,EAIpB,KAAK,gBAAgB,EAErB,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,eAAe,EAGrB,MAAM,eAAe,CAAC;AAQvB,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,0DAA0D;IAC1D,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,yEAAyE;IACzE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC7D,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,iBAAiB,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,WAAW,EACjB,MAAM,GAAE,MAAiC,GACxC,kBAAkB,CAEpB;AAiCD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,kBAAkB,EAC5B,IAAI,EAAE,WAAW,EACjB,MAAM,GAAE,MAAiC,GACxC,kBAAkB,CAYpB;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,cAAc,CAAC,CAUzB;AAUD;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,QAAQ,CAAC,CAYnB"}
|