@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.
Files changed (77) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/LICENSE +21 -0
  3. package/README.md +210 -0
  4. package/dist/app-auth.d.ts +139 -0
  5. package/dist/app-auth.d.ts.map +1 -0
  6. package/dist/app-auth.js +205 -0
  7. package/dist/app-auth.js.map +1 -0
  8. package/dist/background.d.ts +68 -0
  9. package/dist/background.d.ts.map +1 -0
  10. package/dist/background.js +78 -0
  11. package/dist/background.js.map +1 -0
  12. package/dist/client.d.ts +148 -0
  13. package/dist/client.d.ts.map +1 -0
  14. package/dist/client.js +269 -0
  15. package/dist/client.js.map +1 -0
  16. package/dist/collected.d.ts +103 -0
  17. package/dist/collected.d.ts.map +1 -0
  18. package/dist/collected.js +59 -0
  19. package/dist/collected.js.map +1 -0
  20. package/dist/crypto.d.ts +6 -0
  21. package/dist/crypto.d.ts.map +1 -0
  22. package/dist/crypto.js +76 -0
  23. package/dist/crypto.js.map +1 -0
  24. package/dist/frame.d.ts +58 -0
  25. package/dist/frame.d.ts.map +1 -0
  26. package/dist/frame.js +56 -0
  27. package/dist/frame.js.map +1 -0
  28. package/dist/handoff.d.ts +113 -0
  29. package/dist/handoff.d.ts.map +1 -0
  30. package/dist/handoff.js +29 -0
  31. package/dist/handoff.js.map +1 -0
  32. package/dist/index.d.ts +25 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +25 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/install-handlers.d.ts +62 -0
  37. package/dist/install-handlers.d.ts.map +1 -0
  38. package/dist/install-handlers.js +238 -0
  39. package/dist/install-handlers.js.map +1 -0
  40. package/dist/logger.d.ts +27 -0
  41. package/dist/logger.d.ts.map +1 -0
  42. package/dist/logger.js +75 -0
  43. package/dist/logger.js.map +1 -0
  44. package/dist/merchant-schema.d.ts +13963 -0
  45. package/dist/merchant-schema.d.ts.map +1 -0
  46. package/dist/merchant-schema.js +11 -0
  47. package/dist/merchant-schema.js.map +1 -0
  48. package/dist/resync.d.ts +80 -0
  49. package/dist/resync.d.ts.map +1 -0
  50. package/dist/resync.js +203 -0
  51. package/dist/resync.js.map +1 -0
  52. package/dist/server.d.ts +9 -0
  53. package/dist/server.d.ts.map +1 -0
  54. package/dist/server.js +8 -0
  55. package/dist/server.js.map +1 -0
  56. package/dist/session.d.ts +66 -0
  57. package/dist/session.d.ts.map +1 -0
  58. package/dist/session.js +155 -0
  59. package/dist/session.js.map +1 -0
  60. package/dist/signatures.d.ts +55 -0
  61. package/dist/signatures.d.ts.map +1 -0
  62. package/dist/signatures.js +74 -0
  63. package/dist/signatures.js.map +1 -0
  64. package/dist/store.d.ts +278 -0
  65. package/dist/store.d.ts.map +1 -0
  66. package/dist/store.js +610 -0
  67. package/dist/store.js.map +1 -0
  68. package/dist/tokens.d.ts +162 -0
  69. package/dist/tokens.d.ts.map +1 -0
  70. package/dist/tokens.js +411 -0
  71. package/dist/tokens.js.map +1 -0
  72. package/dist/webhooks.d.ts +60 -0
  73. package/dist/webhooks.d.ts.map +1 -0
  74. package/dist/webhooks.js +125 -0
  75. package/dist/webhooks.js.map +1 -0
  76. package/openapi/merchant.json +21452 -0
  77. 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"}
@@ -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"}
@@ -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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"}
@@ -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"}