@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
package/dist/frame.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framed-page handshake client (S4 stage 2): the browser-safe half of the
|
|
3
|
+
* dashboard bridge. Carries NO secret and performs NO verification — it only
|
|
4
|
+
* shapes and addresses messages. The dashboard (queek-merchant) enforces
|
|
5
|
+
* origin+source binding and mints tokens; the app backend verifies them via
|
|
6
|
+
* `./server` (`verifySessionToken`).
|
|
7
|
+
*
|
|
8
|
+
* DOM-free by design (structural minimal types): the app passes its own
|
|
9
|
+
* window/parent in, so this module needs no DOM lib and stays import-safe
|
|
10
|
+
* in edge runtimes.
|
|
11
|
+
*/
|
|
12
|
+
export const DASHBOARD_SOURCE = "queek-merchant";
|
|
13
|
+
export const APP_SOURCE = "queek-app";
|
|
14
|
+
function isInbound(data) {
|
|
15
|
+
if (!data || typeof data !== "object") {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
const message = data;
|
|
19
|
+
if (message.source !== DASHBOARD_SOURCE || typeof message.type !== "string") {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (message.type === "token") {
|
|
23
|
+
return typeof message.token === "string" && message.token.length > 0;
|
|
24
|
+
}
|
|
25
|
+
return message.type === "resize-ack";
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Listen for dashboard messages, accepting only the exact dashboard
|
|
29
|
+
* origin. Returns an unsubscribe function. Never touches any secret.
|
|
30
|
+
*/
|
|
31
|
+
export function listenToDashboard(options) {
|
|
32
|
+
const { dashboardOrigin, onToken, onResizeAck } = options;
|
|
33
|
+
const target = options.target;
|
|
34
|
+
const onMessage = (event) => {
|
|
35
|
+
if (event.origin !== dashboardOrigin) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (!isInbound(event.data)) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (event.data.type === "token") {
|
|
42
|
+
onToken?.(event.data.token);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
onResizeAck?.();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
target?.addEventListener("message", onMessage);
|
|
49
|
+
return () => target?.removeEventListener("message", onMessage);
|
|
50
|
+
}
|
|
51
|
+
/** Announce readiness to the exact dashboard origin — never `"*"`. */
|
|
52
|
+
export function sendReady(dashboardOrigin, target) {
|
|
53
|
+
const message = { source: APP_SOURCE, type: "ready" };
|
|
54
|
+
target?.postMessage(message, dashboardOrigin);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=frame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.js","sourceRoot":"","sources":["../src/frame.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,gBAAgB,CAAC;AACjD,MAAM,CAAC,MAAM,UAAU,GAAG,WAAW,CAAC;AAiCtC,SAAS,SAAS,CAAC,IAAa;IAC9B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,OAAO,GAAG,IAA+B,CAAC;IAChD,IAAI,OAAO,CAAC,MAAM,KAAK,gBAAgB,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC5E,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC7B,OAAO,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,OAAO,CAAC,IAAI,KAAK,YAAY,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAA2B;IAC3D,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IAC1D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9B,MAAM,SAAS,GAAG,CAAC,KAAiB,EAAE,EAAE;QACtC,IAAI,KAAK,CAAC,MAAM,KAAK,eAAe,EAAE,CAAC;YACrC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,OAAO;QACT,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAChC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;aAAM,CAAC;YACN,WAAW,EAAE,EAAE,CAAC;QAClB,CAAC;IACH,CAAC,CAAC;IACF,MAAM,EAAE,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAC/C,OAAO,GAAG,EAAE,CAAC,MAAM,EAAE,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AACjE,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,SAAS,CAAC,eAAuB,EAAE,MAAwB;IACzE,MAAM,OAAO,GAAuB,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAC1E,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed payloads for Queek's server-to-server install handoff.
|
|
3
|
+
*
|
|
4
|
+
* Source of truth: queek_backend `App\Services\Apps\AppInstallService`
|
|
5
|
+
* (`installPayload()`, `uninstallPayload()`, `settingsPayload()`,
|
|
6
|
+
* `handoffBody()`). The signed body envelope is:
|
|
7
|
+
*
|
|
8
|
+
* { id, type, api_version: "v1", created_at, data }
|
|
9
|
+
*
|
|
10
|
+
* `type` is `app/installed` | `app/uninstalled` | `app/settings_updated`
|
|
11
|
+
* | `app/resync`. The handoff POST carries the Standard Webhooks headers
|
|
12
|
+
* (`webhook-id`, `webhook-timestamp`, `webhook-signature`) signed with the
|
|
13
|
+
* APP's signing secret (`whsec_…`, minted once at `app:register`), NOT the
|
|
14
|
+
* per-installation webhook secret. The per-installation `webhook_secret`
|
|
15
|
+
* arrives INSIDE the install payload (`data.webhook_secret`) — once — so
|
|
16
|
+
* the app can verify the topic events it is about to receive.
|
|
17
|
+
*
|
|
18
|
+
* `app/resync` is the installation resync handoff (backend
|
|
19
|
+
* `AppInstallService::resyncPayload()`, delivered to the app's
|
|
20
|
+
* `settings_url` falling back to `install_url`): the install-shaped data
|
|
21
|
+
* plus the resync-only `secret_rotated` flag, over the same signed channel
|
|
22
|
+
* with the same verification rules. Both the `/install` and `/settings`
|
|
23
|
+
* handlers accept it.
|
|
24
|
+
*/
|
|
25
|
+
export declare const INSTALL_EVENT = "app/installed";
|
|
26
|
+
export declare const UNINSTALL_EVENT = "app/uninstalled";
|
|
27
|
+
export declare const SETTINGS_EVENT = "app/settings_updated";
|
|
28
|
+
export declare const RESYNC_EVENT = "app/resync";
|
|
29
|
+
export interface HandoffInstallationRef {
|
|
30
|
+
id: string;
|
|
31
|
+
p_id: string;
|
|
32
|
+
}
|
|
33
|
+
export interface HandoffStore {
|
|
34
|
+
id: string;
|
|
35
|
+
p_id: string | null;
|
|
36
|
+
name: string;
|
|
37
|
+
is_test: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface InstallData {
|
|
40
|
+
installation: HandoffInstallationRef;
|
|
41
|
+
store: HandoffStore;
|
|
42
|
+
/** The Merchant API base for this store (e.g. https://api.usequeek.com/api/v1/merchant). */
|
|
43
|
+
api_base: string;
|
|
44
|
+
/**
|
|
45
|
+
* No store-callable credential crosses the handoff any more (S1, SDK
|
|
46
|
+
* 0.2.0): the app mints short-lived installation tokens with its
|
|
47
|
+
* asymmetric app key (`acquireToken()`) and caches them encrypted in its
|
|
48
|
+
* own database. The same envelope redelivers `webhook_secret` + the
|
|
49
|
+
* non-secret settings snapshot on resync.
|
|
50
|
+
*/
|
|
51
|
+
scopes: string[];
|
|
52
|
+
settings: Record<string, unknown>;
|
|
53
|
+
/** The installation endpoint's `whsec_…` secret, handed over ONCE per rotation. */
|
|
54
|
+
webhook_secret: string | null;
|
|
55
|
+
/**
|
|
56
|
+
* The installation's `whsec_…` proxy secret (backend `proxy_secret`),
|
|
57
|
+
* handed over ONCE per install/resync over the signed channel. The
|
|
58
|
+
* booking app signs slot-claims with it; core verifies the HMAC
|
|
59
|
+
* against the same installation secret. Null for apps without a
|
|
60
|
+
* proxy — the key's absence is the signal. Source of truth:
|
|
61
|
+
* queek_backend `App\Services\Apps\AppInstallService::installPayload()`.
|
|
62
|
+
*/
|
|
63
|
+
proxy_secret: string | null;
|
|
64
|
+
/**
|
|
65
|
+
* The installation's `embsec_…` embed secret (backend `embed_secret`),
|
|
66
|
+
* handed over on install/resync: the HS256 key of the dashboard session
|
|
67
|
+
* tokens its embedded merchant page receives (verify with
|
|
68
|
+
* `@usequeek/app-sdk/server` `verifySessionToken`). Null for apps
|
|
69
|
+
* without a merchant page.
|
|
70
|
+
*/
|
|
71
|
+
embed_secret?: string | null;
|
|
72
|
+
/**
|
|
73
|
+
* The app's own id, exactly as the session token signs it (`app_id`
|
|
74
|
+
* claim) — the verifier binds it. Source of truth: queek_backend
|
|
75
|
+
* `AppInstallService::installPayload()` / `resyncPayload()`.
|
|
76
|
+
*/
|
|
77
|
+
app_id?: string | null;
|
|
78
|
+
webhook_url: string | null;
|
|
79
|
+
webhook_topics: string[];
|
|
80
|
+
/**
|
|
81
|
+
* Resync-only (backend `resyncPayload()`): whether the delivery rotated
|
|
82
|
+
* the endpoint secret (`webhook_secret` holds the new secret when true,
|
|
83
|
+
* null when the installation has no webhook endpoint). Absent on install.
|
|
84
|
+
*/
|
|
85
|
+
secret_rotated?: boolean | null;
|
|
86
|
+
}
|
|
87
|
+
export interface UninstallData {
|
|
88
|
+
installation: HandoffInstallationRef;
|
|
89
|
+
store: HandoffStore;
|
|
90
|
+
}
|
|
91
|
+
export interface SettingsData {
|
|
92
|
+
installation: HandoffInstallationRef;
|
|
93
|
+
store: HandoffStore | null;
|
|
94
|
+
settings: Record<string, unknown>;
|
|
95
|
+
}
|
|
96
|
+
export interface HandoffEnvelope<TType extends string, TData> {
|
|
97
|
+
id: string;
|
|
98
|
+
type: TType;
|
|
99
|
+
api_version: "v1";
|
|
100
|
+
created_at: string;
|
|
101
|
+
data: TData;
|
|
102
|
+
}
|
|
103
|
+
export type InstallEnvelope = HandoffEnvelope<typeof INSTALL_EVENT, InstallData>;
|
|
104
|
+
export type UninstallEnvelope = HandoffEnvelope<typeof UNINSTALL_EVENT, UninstallData>;
|
|
105
|
+
export type SettingsEnvelope = HandoffEnvelope<typeof SETTINGS_EVENT, SettingsData>;
|
|
106
|
+
/**
|
|
107
|
+
* The installation resync handoff: the install-shaped data (plus the
|
|
108
|
+
* resync-only `secret_rotated` flag) under `type: app/resync`. Handled by
|
|
109
|
+
* the install AND settings handlers via the resync merge path.
|
|
110
|
+
*/
|
|
111
|
+
export type ResyncEnvelope = HandoffEnvelope<typeof RESYNC_EVENT, InstallData>;
|
|
112
|
+
export type HandoffEnvelopeAny = InstallEnvelope | UninstallEnvelope | SettingsEnvelope | ResyncEnvelope;
|
|
113
|
+
//# sourceMappingURL=handoff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoff.d.ts","sourceRoot":"","sources":["../src/handoff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAC7C,eAAO,MAAM,eAAe,oBAAoB,CAAC;AACjD,eAAO,MAAM,cAAc,yBAAyB,CAAC;AACrD,eAAO,MAAM,YAAY,eAAe,CAAC;AAEzC,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,sBAAsB,CAAC;IACrC,KAAK,EAAE,YAAY,CAAC;IACpB,4FAA4F;IAC5F,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,mFAAmF;IACnF,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;;;;;OAOG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,sBAAsB,CAAC;IACrC,KAAK,EAAE,YAAY,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,sBAAsB,CAAC;IACrC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,eAAe,CAAC,KAAK,SAAS,MAAM,EAAE,KAAK;IAC1D,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,KAAK,CAAC;IACZ,WAAW,EAAE,IAAI,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,KAAK,CAAC;CACb;AAED,MAAM,MAAM,eAAe,GAAG,eAAe,CAAC,OAAO,aAAa,EAAE,WAAW,CAAC,CAAC;AACjF,MAAM,MAAM,iBAAiB,GAAG,eAAe,CAAC,OAAO,eAAe,EAAE,aAAa,CAAC,CAAC;AACvF,MAAM,MAAM,gBAAgB,GAAG,eAAe,CAAC,OAAO,cAAc,EAAE,YAAY,CAAC,CAAC;AACpF;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC,OAAO,YAAY,EAAE,WAAW,CAAC,CAAC;AAE/E,MAAM,MAAM,kBAAkB,GAAG,eAAe,GAAG,iBAAiB,GAAG,gBAAgB,GAAG,cAAc,CAAC"}
|
package/dist/handoff.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed payloads for Queek's server-to-server install handoff.
|
|
3
|
+
*
|
|
4
|
+
* Source of truth: queek_backend `App\Services\Apps\AppInstallService`
|
|
5
|
+
* (`installPayload()`, `uninstallPayload()`, `settingsPayload()`,
|
|
6
|
+
* `handoffBody()`). The signed body envelope is:
|
|
7
|
+
*
|
|
8
|
+
* { id, type, api_version: "v1", created_at, data }
|
|
9
|
+
*
|
|
10
|
+
* `type` is `app/installed` | `app/uninstalled` | `app/settings_updated`
|
|
11
|
+
* | `app/resync`. The handoff POST carries the Standard Webhooks headers
|
|
12
|
+
* (`webhook-id`, `webhook-timestamp`, `webhook-signature`) signed with the
|
|
13
|
+
* APP's signing secret (`whsec_…`, minted once at `app:register`), NOT the
|
|
14
|
+
* per-installation webhook secret. The per-installation `webhook_secret`
|
|
15
|
+
* arrives INSIDE the install payload (`data.webhook_secret`) — once — so
|
|
16
|
+
* the app can verify the topic events it is about to receive.
|
|
17
|
+
*
|
|
18
|
+
* `app/resync` is the installation resync handoff (backend
|
|
19
|
+
* `AppInstallService::resyncPayload()`, delivered to the app's
|
|
20
|
+
* `settings_url` falling back to `install_url`): the install-shaped data
|
|
21
|
+
* plus the resync-only `secret_rotated` flag, over the same signed channel
|
|
22
|
+
* with the same verification rules. Both the `/install` and `/settings`
|
|
23
|
+
* handlers accept it.
|
|
24
|
+
*/
|
|
25
|
+
export const INSTALL_EVENT = "app/installed";
|
|
26
|
+
export const UNINSTALL_EVENT = "app/uninstalled";
|
|
27
|
+
export const SETTINGS_EVENT = "app/settings_updated";
|
|
28
|
+
export const RESYNC_EVENT = "app/resync";
|
|
29
|
+
//# sourceMappingURL=handoff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoff.js","sourceRoot":"","sources":["../src/handoff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAC7C,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAC;AACjD,MAAM,CAAC,MAAM,cAAc,GAAG,sBAAsB,CAAC;AACrD,MAAM,CAAC,MAAM,YAAY,GAAG,YAAY,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@usequeek/app-sdk` — the shared kit every Queek app is built from.
|
|
3
|
+
*
|
|
4
|
+
* Queek speaks Standard Webhooks (`webhook-id`, `webhook-timestamp`,
|
|
5
|
+
* `webhook-signature` over `{id}.{timestamp}.{raw body}`, keyed by the
|
|
6
|
+
* decoded `whsec_…` bytes) on two channels: the install handoff (signed
|
|
7
|
+
* with the APP secret) and topic deliveries (signed with the
|
|
8
|
+
* PER-INSTALLATION endpoint secret). The Merchant API takes the
|
|
9
|
+
* installation credential as `X-Client-Key`.
|
|
10
|
+
*/
|
|
11
|
+
export { API_KEY_EXPIRED_CODE, API_KEY_REVOKED_CODE, APP_INSTALLATION_GONE_CODE, APP_INSTALLATION_PENDING_CODE, APP_JWT_SKEW_SECONDS, APP_JWT_TTL_SECONDS, APP_TOKEN_REVOKED_CODE, type AppCredential, AppMintHaltedError, INVALID_CLIENT_CODE, INVALID_CLIENT_KEY_CODE, InvalidAppCredentialError, isAppTokenRevoked, isInstallationGone, isInstallationPending, isInvalidClient, isResyncCooldown, isTokenRefusal, loadAppCredential, MAX_MINT_ATTEMPTS, MINT_BACKOFF_BASE_MS, MINT_BACKOFF_MAX_MS, RESYNC_COOLDOWN_CODE, RETRY_JITTER_MAX_MS, type SignAppJwtOptions, signAppJwt, TOKEN_VALIDITY_SKEW_SECONDS, TOO_MANY_REQUESTS_CODE, UnknownInstallationError, } from "./app-auth.js";
|
|
12
|
+
export { backgroundTaskCount, type CatchupFailure, type CatchupInstallation, type CatchupOptions, type CatchupResult, type DetachOptions, detach, runInstallationCatchup, } from "./background.js";
|
|
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
|
+
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
|
+
export { decryptSecret, encryptSecret, parseStoreKey, storeKeyFingerprint, } from "./crypto.js";
|
|
16
|
+
export { APP_SOURCE, type AppInboundMessage, type AppOutboundMessage, DASHBOARD_SOURCE, type FrameBridgeOptions, listenToDashboard, sendReady, } from "./frame.js";
|
|
17
|
+
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, createInstallHandlers, type InstallCallbacks, type InstallHandlerOptions, saveResyncedInstallation, } from "./install-handlers.js";
|
|
19
|
+
export { createLogger, type LogFields, type Logger, type LoggerOptions, type LogLevel, REDACTED, } from "./logger.js";
|
|
20
|
+
export { type ResyncListItem, type ResyncOptions, type ResyncResult, resyncFromQueek, } from "./resync.js";
|
|
21
|
+
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
|
+
export { createInstallationStore, createPostgresPool, INSTALLATION_SCHEMA_VERSION, type InstallationRecord, type InstallationStore, type InstallationStoreSelector, PostgresInstallationStore, type PostgresStoreOptions, SqliteInstallationStore, type SqliteStoreOptions, type WebhookSecretCandidate, } from "./store.js";
|
|
23
|
+
export { APP_API_PATH, AppTokenProvider, type AppTokens, createAppTokenProvider, createInstallationClient, type InstallationClientOptions, mintPath, resolveAppApiBase, type TokenProviderOptions, } from "./tokens.js";
|
|
24
|
+
export { createWebhookHandler, QUEEK_TOPIC_HEADER, type QueekWebhookEnvelope, type SecretResolution, type WebhookHandlerContext, type WebhookHandlerFn, type WebhookHandlerOptions, } from "./webhooks.js";
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@usequeek/app-sdk` — the shared kit every Queek app is built from.
|
|
3
|
+
*
|
|
4
|
+
* Queek speaks Standard Webhooks (`webhook-id`, `webhook-timestamp`,
|
|
5
|
+
* `webhook-signature` over `{id}.{timestamp}.{raw body}`, keyed by the
|
|
6
|
+
* decoded `whsec_…` bytes) on two channels: the install handoff (signed
|
|
7
|
+
* with the APP secret) and topic deliveries (signed with the
|
|
8
|
+
* PER-INSTALLATION endpoint secret). The Merchant API takes the
|
|
9
|
+
* installation credential as `X-Client-Key`.
|
|
10
|
+
*/
|
|
11
|
+
export { API_KEY_EXPIRED_CODE, API_KEY_REVOKED_CODE, APP_INSTALLATION_GONE_CODE, APP_INSTALLATION_PENDING_CODE, APP_JWT_SKEW_SECONDS, APP_JWT_TTL_SECONDS, APP_TOKEN_REVOKED_CODE, AppMintHaltedError, INVALID_CLIENT_CODE, INVALID_CLIENT_KEY_CODE, InvalidAppCredentialError, isAppTokenRevoked, isInstallationGone, isInstallationPending, isInvalidClient, isResyncCooldown, isTokenRefusal, loadAppCredential, MAX_MINT_ATTEMPTS, MINT_BACKOFF_BASE_MS, MINT_BACKOFF_MAX_MS, RESYNC_COOLDOWN_CODE, RETRY_JITTER_MAX_MS, signAppJwt, TOKEN_VALIDITY_SKEW_SECONDS, TOO_MANY_REQUESTS_CODE, UnknownInstallationError, } from "./app-auth.js";
|
|
12
|
+
export { backgroundTaskCount, detach, runInstallationCatchup, } from "./background.js";
|
|
13
|
+
export { apiHostsFromEnv, createQueekClient, DEFAULT_API_HOSTS, devApiHostsFromEnv, InvalidApiBaseError, MERCHANT_API_PATH, newIdempotencyKey, QueekApiError, queekApiErrorFromResponse, resolveApiBase, } from "./client.js";
|
|
14
|
+
export { collectedDefinitions, createRecord, sendAlert, setSetupNotice, } from "./collected.js";
|
|
15
|
+
export { decryptSecret, encryptSecret, parseStoreKey, storeKeyFingerprint, } from "./crypto.js";
|
|
16
|
+
export { APP_SOURCE, DASHBOARD_SOURCE, listenToDashboard, sendReady, } from "./frame.js";
|
|
17
|
+
export { INSTALL_EVENT, RESYNC_EVENT, SETTINGS_EVENT, UNINSTALL_EVENT, } from "./handoff.js";
|
|
18
|
+
export { buildInstallationRecord, createInstallHandlers, saveResyncedInstallation, } from "./install-handlers.js";
|
|
19
|
+
export { createLogger, REDACTED, } from "./logger.js";
|
|
20
|
+
export { resyncFromQueek, } from "./resync.js";
|
|
21
|
+
export { MAX_TIMESTAMP_SKEW_SECONDS, SECRET_PREFIX, secretKeyBytes, signQueekPayload, verifyQueekSignature, verifyQueekSignatureDetailed, WEBHOOK_ID_HEADER, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_TIMESTAMP_HEADER, } from "./signatures.js";
|
|
22
|
+
export { createInstallationStore, createPostgresPool, INSTALLATION_SCHEMA_VERSION, PostgresInstallationStore, SqliteInstallationStore, } from "./store.js";
|
|
23
|
+
export { APP_API_PATH, AppTokenProvider, createAppTokenProvider, createInstallationClient, mintPath, resolveAppApiBase, } from "./tokens.js";
|
|
24
|
+
export { createWebhookHandler, QUEEK_TOPIC_HEADER, } from "./webhooks.js";
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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,EAGrB,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,oBAAoB,EACpB,kBAAkB,GAMnB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { type InstallData, type InstallEnvelope, type SettingsEnvelope, type UninstallEnvelope } from "./handoff.js";
|
|
3
|
+
import type { InstallationRecord, InstallationStore } from "./store.js";
|
|
4
|
+
/**
|
|
5
|
+
* Hono handlers for Queek's signed server-to-server handoff
|
|
6
|
+
* (`AppInstallService::deliver()` in queek_backend).
|
|
7
|
+
*
|
|
8
|
+
* Every handoff — install, uninstall, settings, resync — carries the
|
|
9
|
+
* Standard Webhooks headers signed with the APP signing secret (`whsec_…`,
|
|
10
|
+
* minted once at `app:register`). The app answers 2xx only after the
|
|
11
|
+
* installation is durably stored; anything else makes Queek revoke the
|
|
12
|
+
* just-minted key and mark the install failed (retry = a fresh install).
|
|
13
|
+
*
|
|
14
|
+
* The resync handoff (`type: app/resync`, backend
|
|
15
|
+
* `AppInstallService::resyncPayload()`) redelivers the install-shaped data
|
|
16
|
+
* — rotated `webhook_secret`, non-secret settings, and recovery copies of
|
|
17
|
+
* `proxy_secret` / `embed_secret` / `app_id` — to the app's `settings_url`,
|
|
18
|
+
* falling back to `install_url`. Both `/install` and `/settings` accept it
|
|
19
|
+
* with the same signature/verification rules and apply it via the resync
|
|
20
|
+
* merge (`saveResyncedInstallation`: refreshes secrets/settings/scopes,
|
|
21
|
+
* keeps `installedAt` + the cached token, keeps stored values the resync
|
|
22
|
+
* omits).
|
|
23
|
+
*
|
|
24
|
+
* Order per request: verify signature (freshness included) → parse the
|
|
25
|
+
* typed payload → atomically claim the header `webhook-id` (a claimed id
|
|
26
|
+
* answers 409: exactly one same-id delivery runs the callback) → run the
|
|
27
|
+
* callback → 2xx. A callback that throws (e.g. the store write failed)
|
|
28
|
+
* RELEASES the claim and answers non-2xx, so a Queek retry can still land.
|
|
29
|
+
* The claim and the check are the SAME header id — never the body id.
|
|
30
|
+
*/
|
|
31
|
+
export interface InstallCallbacks {
|
|
32
|
+
/** Defaults to persisting the installation (encrypted) in `store`. */
|
|
33
|
+
onInstall?: (envelope: InstallEnvelope) => Promise<void>;
|
|
34
|
+
/** Defaults to deleting the installation from `store`. */
|
|
35
|
+
onUninstall?: (envelope: UninstallEnvelope) => Promise<void>;
|
|
36
|
+
/** Defaults to merging the new settings into the stored installation. */
|
|
37
|
+
onSettings?: (envelope: SettingsEnvelope) => Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export interface InstallHandlerOptions extends InstallCallbacks {
|
|
40
|
+
/** The app signing secret (`whsec_…`) from Queek registration. */
|
|
41
|
+
appSecret: string;
|
|
42
|
+
store: InstallationStore;
|
|
43
|
+
nowSeconds?: number;
|
|
44
|
+
maxSkewSeconds?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build the storable installation record from install `data`. Timestamps
|
|
48
|
+
* default to now. Exported so apps with a custom `onInstall` (e.g. to
|
|
49
|
+
* proof-call the Merchant API before answering 2xx) persist exactly what
|
|
50
|
+
* the default handler would — never a divergent mapping.
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildInstallationRecord(data: InstallData, nowIso?: string): InstallationRecord;
|
|
53
|
+
/**
|
|
54
|
+
* Merge a re-delivered install envelope (resync) into an existing row:
|
|
55
|
+
* refresh the ref fields, scopes, settings and webhook secret, but keep
|
|
56
|
+
* the original `installedAt` and the cached installation token (a secret
|
|
57
|
+
* rotation does not invalidate minted tokens; `resyncFromQueek` drops
|
|
58
|
+
* tokens explicitly when it wants fresh ones).
|
|
59
|
+
*/
|
|
60
|
+
export declare function saveResyncedInstallation(existing: InstallationRecord, data: InstallData, nowIso?: string): InstallationRecord;
|
|
61
|
+
export declare function createInstallHandlers(options: InstallHandlerOptions): Hono;
|
|
62
|
+
//# sourceMappingURL=install-handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-handlers.d.ts","sourceRoot":"","sources":["../src/install-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,eAAe,EAIpB,KAAK,gBAAgB,EAErB,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AAQtB,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;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,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAyM1E"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { INSTALL_EVENT, RESYNC_EVENT, SETTINGS_EVENT, UNINSTALL_EVENT, } from "./handoff.js";
|
|
3
|
+
import { MAX_TIMESTAMP_SKEW_SECONDS, verifyQueekSignatureDetailed, WEBHOOK_ID_HEADER, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_TIMESTAMP_HEADER, } from "./signatures.js";
|
|
4
|
+
/**
|
|
5
|
+
* Build the storable installation record from install `data`. Timestamps
|
|
6
|
+
* default to now. Exported so apps with a custom `onInstall` (e.g. to
|
|
7
|
+
* proof-call the Merchant API before answering 2xx) persist exactly what
|
|
8
|
+
* the default handler would — never a divergent mapping.
|
|
9
|
+
*/
|
|
10
|
+
export function buildInstallationRecord(data, nowIso = new Date().toISOString()) {
|
|
11
|
+
return installationRecordFromInstall(data, nowIso);
|
|
12
|
+
}
|
|
13
|
+
function installationRecordFromInstall(data, nowIso) {
|
|
14
|
+
return {
|
|
15
|
+
installationId: data.installation.id,
|
|
16
|
+
installationPid: data.installation.p_id,
|
|
17
|
+
vendorId: data.store.id,
|
|
18
|
+
storePid: data.store.p_id,
|
|
19
|
+
storeName: data.store.name,
|
|
20
|
+
apiBase: data.api_base,
|
|
21
|
+
// No credential crosses the handoff (S1): a fresh row caches no token
|
|
22
|
+
// (the first call mints one); see `saveResyncedInstallation` for the
|
|
23
|
+
// existing-row path, which keeps the cached token.
|
|
24
|
+
token: null,
|
|
25
|
+
tokenExpiresAt: null,
|
|
26
|
+
tokenKid: null,
|
|
27
|
+
// A handoff (install or resync redelivery) proves Queek holds the
|
|
28
|
+
// installation: fresh rows start unflagged, and a redelivery for an
|
|
29
|
+
// existing row clears a stale pending mark (seen active).
|
|
30
|
+
pending: false,
|
|
31
|
+
scopes: data.scopes,
|
|
32
|
+
settings: data.settings,
|
|
33
|
+
webhookSecret: data.webhook_secret,
|
|
34
|
+
proxySecret: data.proxy_secret,
|
|
35
|
+
embedSecret: data.embed_secret ?? null,
|
|
36
|
+
appId: data.app_id ?? null,
|
|
37
|
+
webhookUrl: data.webhook_url,
|
|
38
|
+
webhookTopics: data.webhook_topics,
|
|
39
|
+
installedAt: nowIso,
|
|
40
|
+
updatedAt: nowIso,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Merge a re-delivered install envelope (resync) into an existing row:
|
|
45
|
+
* refresh the ref fields, scopes, settings and webhook secret, but keep
|
|
46
|
+
* the original `installedAt` and the cached installation token (a secret
|
|
47
|
+
* rotation does not invalidate minted tokens; `resyncFromQueek` drops
|
|
48
|
+
* tokens explicitly when it wants fresh ones).
|
|
49
|
+
*/
|
|
50
|
+
export function saveResyncedInstallation(existing, data, nowIso = new Date().toISOString()) {
|
|
51
|
+
const fresh = installationRecordFromInstall(data, nowIso);
|
|
52
|
+
return {
|
|
53
|
+
...fresh,
|
|
54
|
+
// A handoff without the key (older payloads) keeps what is stored.
|
|
55
|
+
embedSecret: data.embed_secret === undefined ? (existing.embedSecret ?? null) : fresh.embedSecret,
|
|
56
|
+
appId: data.app_id === undefined ? (existing.appId ?? null) : fresh.appId,
|
|
57
|
+
installedAt: existing.installedAt,
|
|
58
|
+
token: existing.token,
|
|
59
|
+
tokenExpiresAt: existing.tokenExpiresAt,
|
|
60
|
+
tokenKid: existing.tokenKid,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export function createInstallHandlers(options) {
|
|
64
|
+
const app = new Hono();
|
|
65
|
+
const maxSkew = options.maxSkewSeconds ?? MAX_TIMESTAMP_SKEW_SECONDS;
|
|
66
|
+
async function guard(id, timestamp, signatureHeader, rawBody) {
|
|
67
|
+
if (!id || !timestamp || !signatureHeader) {
|
|
68
|
+
return { ok: false, status: 401, reason: "missing signature headers" };
|
|
69
|
+
}
|
|
70
|
+
const checked = verifyQueekSignatureDetailed({ id, timestamp, body: rawBody, signatureHeader, secret: options.appSecret }, { nowSeconds: options.nowSeconds, maxSkewSeconds: maxSkew });
|
|
71
|
+
if (!checked.ok) {
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
status: 401,
|
|
75
|
+
reason: checked.reason === "stale_timestamp" ? "stale timestamp" : "signature mismatch",
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return { ok: true, id };
|
|
79
|
+
}
|
|
80
|
+
function parseEnvelope(rawBody) {
|
|
81
|
+
try {
|
|
82
|
+
const parsed = JSON.parse(rawBody);
|
|
83
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
84
|
+
return null;
|
|
85
|
+
return parsed;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function isInstallPayload(data) {
|
|
92
|
+
return (!!data &&
|
|
93
|
+
typeof data === "object" &&
|
|
94
|
+
typeof data.installation?.id === "string" &&
|
|
95
|
+
typeof data.api_base === "string");
|
|
96
|
+
}
|
|
97
|
+
/** Default resync apply: merge into the existing row (keeping
|
|
98
|
+
* `installedAt`, the cached token and stored values the resync omits) or
|
|
99
|
+
* store a fresh row when nothing is stored (post-outage recovery). */
|
|
100
|
+
async function applyResync(data) {
|
|
101
|
+
const existing = await options.store.getInstallation(data.installation.id);
|
|
102
|
+
await options.store.saveInstallation(existing
|
|
103
|
+
? saveResyncedInstallation(existing, data, new Date().toISOString())
|
|
104
|
+
: installationRecordFromInstall(data, new Date().toISOString()));
|
|
105
|
+
}
|
|
106
|
+
app.post("/install", async (c) => {
|
|
107
|
+
const rawBody = await c.req.text();
|
|
108
|
+
const checked = await guard(c.req.header(WEBHOOK_ID_HEADER), c.req.header(WEBHOOK_TIMESTAMP_HEADER), c.req.header(WEBHOOK_SIGNATURE_HEADER), rawBody);
|
|
109
|
+
if (!checked.ok)
|
|
110
|
+
return c.json({ ok: false, error: checked.reason }, checked.status);
|
|
111
|
+
const envelope = parseEnvelope(rawBody);
|
|
112
|
+
if (!envelope)
|
|
113
|
+
return c.json({ ok: false, error: "invalid_envelope" }, 400);
|
|
114
|
+
// The platform resync handoff redelivers the install-shaped data under
|
|
115
|
+
// `type: app/resync` (same signed channel, same verification): it lands
|
|
116
|
+
// here as well as on `/settings` (the backend aims at `settings_url`
|
|
117
|
+
// first, falling back to `install_url`).
|
|
118
|
+
if (envelope.type !== INSTALL_EVENT && envelope.type !== RESYNC_EVENT) {
|
|
119
|
+
return c.json({ ok: false, error: "unexpected event type" }, 400);
|
|
120
|
+
}
|
|
121
|
+
if (!isInstallPayload(envelope.data)) {
|
|
122
|
+
return c.json({ ok: false, error: "invalid install payload" }, 400);
|
|
123
|
+
}
|
|
124
|
+
// Atomic claim on the HEADER id (never the body id): exactly one
|
|
125
|
+
// same-id delivery runs the callback.
|
|
126
|
+
if (!(await options.store.claimWebhookId(checked.id))) {
|
|
127
|
+
return c.json({ ok: false, error: "duplicate delivery" }, 409);
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
if (options.onInstall) {
|
|
131
|
+
// A resync envelope is install-shaped, so the install callback
|
|
132
|
+
// handles it (apps merge via `saveResyncedInstallation` there).
|
|
133
|
+
await options.onInstall(envelope);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// A resync redelivers the install envelope for an EXISTING
|
|
137
|
+
// installation: merge idempotently (keep `installedAt` + the cached
|
|
138
|
+
// token) instead of resetting the row.
|
|
139
|
+
await applyResync(envelope.data);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Non-2xx on purpose: Queek revokes the key and the merchant retries
|
|
144
|
+
// as a fresh install. The claim is released so the retry can land.
|
|
145
|
+
await options.store.releaseWebhookId(checked.id);
|
|
146
|
+
return c.json({ ok: false, error: "install_failed" }, 500);
|
|
147
|
+
}
|
|
148
|
+
return c.json({ ok: true });
|
|
149
|
+
});
|
|
150
|
+
app.post("/uninstall", async (c) => {
|
|
151
|
+
const rawBody = await c.req.text();
|
|
152
|
+
const checked = await guard(c.req.header(WEBHOOK_ID_HEADER), c.req.header(WEBHOOK_TIMESTAMP_HEADER), c.req.header(WEBHOOK_SIGNATURE_HEADER), rawBody);
|
|
153
|
+
if (!checked.ok)
|
|
154
|
+
return c.json({ ok: false, error: checked.reason }, checked.status);
|
|
155
|
+
const envelope = parseEnvelope(rawBody);
|
|
156
|
+
if (!envelope)
|
|
157
|
+
return c.json({ ok: false, error: "invalid_envelope" }, 400);
|
|
158
|
+
if (envelope.type !== UNINSTALL_EVENT) {
|
|
159
|
+
return c.json({ ok: false, error: "unexpected event type" }, 400);
|
|
160
|
+
}
|
|
161
|
+
if (!(await options.store.claimWebhookId(checked.id))) {
|
|
162
|
+
return c.json({ ok: false, error: "duplicate delivery" }, 409);
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
if (options.onUninstall) {
|
|
166
|
+
await options.onUninstall(envelope);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
await options.store.deleteInstallation(envelope.data.installation.id);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
await options.store.releaseWebhookId(checked.id);
|
|
174
|
+
return c.json({ ok: false, error: "uninstall_failed" }, 500);
|
|
175
|
+
}
|
|
176
|
+
return c.json({ ok: true });
|
|
177
|
+
});
|
|
178
|
+
app.post("/settings", async (c) => {
|
|
179
|
+
const rawBody = await c.req.text();
|
|
180
|
+
const checked = await guard(c.req.header(WEBHOOK_ID_HEADER), c.req.header(WEBHOOK_TIMESTAMP_HEADER), c.req.header(WEBHOOK_SIGNATURE_HEADER), rawBody);
|
|
181
|
+
if (!checked.ok)
|
|
182
|
+
return c.json({ ok: false, error: checked.reason }, checked.status);
|
|
183
|
+
const envelope = parseEnvelope(rawBody);
|
|
184
|
+
if (!envelope)
|
|
185
|
+
return c.json({ ok: false, error: "invalid_envelope" }, 400);
|
|
186
|
+
// The platform resync handoff (`type: app/resync`) is delivered to the
|
|
187
|
+
// app's `settings_url` first: it lands here with the install-shaped
|
|
188
|
+
// data, so it takes the resync merge — never the settings callback,
|
|
189
|
+
// whose envelope shape does not fit.
|
|
190
|
+
if (envelope.type !== SETTINGS_EVENT && envelope.type !== RESYNC_EVENT) {
|
|
191
|
+
return c.json({ ok: false, error: "unexpected event type" }, 400);
|
|
192
|
+
}
|
|
193
|
+
if (envelope.type === RESYNC_EVENT) {
|
|
194
|
+
if (!isInstallPayload(envelope.data)) {
|
|
195
|
+
return c.json({ ok: false, error: "invalid install payload" }, 400);
|
|
196
|
+
}
|
|
197
|
+
if (!(await options.store.claimWebhookId(checked.id))) {
|
|
198
|
+
return c.json({ ok: false, error: "duplicate delivery" }, 409);
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
await applyResync(envelope.data);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
await options.store.releaseWebhookId(checked.id);
|
|
205
|
+
return c.json({ ok: false, error: "settings_failed" }, 500);
|
|
206
|
+
}
|
|
207
|
+
return c.json({ ok: true });
|
|
208
|
+
}
|
|
209
|
+
if (!(await options.store.claimWebhookId(checked.id))) {
|
|
210
|
+
return c.json({ ok: false, error: "duplicate delivery" }, 409);
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
if (options.onSettings) {
|
|
214
|
+
await options.onSettings(envelope);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
const data = envelope.data;
|
|
218
|
+
const existing = await options.store.getInstallation(data.installation.id);
|
|
219
|
+
if (!existing) {
|
|
220
|
+
await options.store.releaseWebhookId(checked.id);
|
|
221
|
+
return c.json({ ok: false, error: "unknown_installation" }, 404);
|
|
222
|
+
}
|
|
223
|
+
await options.store.saveInstallation({
|
|
224
|
+
...existing,
|
|
225
|
+
settings: data.settings,
|
|
226
|
+
updatedAt: new Date().toISOString(),
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
await options.store.releaseWebhookId(checked.id);
|
|
232
|
+
return c.json({ ok: false, error: "settings_failed" }, 500);
|
|
233
|
+
}
|
|
234
|
+
return c.json({ ok: true });
|
|
235
|
+
});
|
|
236
|
+
return app;
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=install-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-handlers.js","sourceRoot":"","sources":["../src/install-handlers.ts"],"names":[],"mappings":"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"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ONE structured logger for every Queek app: JSON lines on stdout (info and
|
|
3
|
+
* below) / stderr (warn and above), with secret redaction baked in — never
|
|
4
|
+
* opt-in per call site.
|
|
5
|
+
*
|
|
6
|
+
* Redacted: any field whose name looks like key/secret/token/password/auth/
|
|
7
|
+
* credential/session, and any string shaped like an `sk_`/`pk_` key, a
|
|
8
|
+
* `whsec_` secret, a `Bearer …` token, a bare RS256 JWT (`eyJ….….…`), or a
|
|
9
|
+
* PEM private-key block — in field values AND in the message. What is left
|
|
10
|
+
* in the logs is ids and p_ids, which is all an operator needs.
|
|
11
|
+
*/
|
|
12
|
+
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
13
|
+
export type LogFields = Record<string, unknown>;
|
|
14
|
+
export interface LoggerOptions {
|
|
15
|
+
service: string;
|
|
16
|
+
level?: LogLevel;
|
|
17
|
+
sink?: (line: string, level: LogLevel) => void;
|
|
18
|
+
}
|
|
19
|
+
export declare const REDACTED = "[redacted]";
|
|
20
|
+
export interface Logger {
|
|
21
|
+
debug(message: string, fields?: LogFields): void;
|
|
22
|
+
info(message: string, fields?: LogFields): void;
|
|
23
|
+
warn(message: string, fields?: LogFields): void;
|
|
24
|
+
error(message: string, fields?: LogFields): void;
|
|
25
|
+
}
|
|
26
|
+
export declare function createLogger(options: LoggerOptions): Logger;
|
|
27
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAI3D,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;CAChD;AASD,eAAO,MAAM,QAAQ,eAAe,CAAC;AA0BrC,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACjD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;CAClD;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAkC3D"}
|