@usequeek/app-sdk 0.6.2 → 0.7.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 (107) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +340 -337
  3. package/dist/app-auth.d.ts +1 -2
  4. package/dist/app-auth.js +1 -2
  5. package/dist/auth-fetch.d.ts +12 -1
  6. package/dist/auth-fetch.js +13 -2
  7. package/dist/background.d.ts +9 -1
  8. package/dist/background.js +9 -1
  9. package/dist/browser.d.ts +1 -2
  10. package/dist/browser.js +1 -2
  11. package/dist/client.d.ts +130 -7
  12. package/dist/client.js +198 -41
  13. package/dist/collected.d.ts +0 -1
  14. package/dist/collected.js +0 -1
  15. package/dist/crypto.d.ts +11 -1
  16. package/dist/crypto.js +22 -7
  17. package/dist/delivery.d.ts +18 -1
  18. package/dist/delivery.js +26 -17
  19. package/dist/frame.d.ts +0 -1
  20. package/dist/frame.js +0 -1
  21. package/dist/handoff.d.ts +0 -1
  22. package/dist/handoff.js +0 -1
  23. package/dist/hono.d.ts +0 -1
  24. package/dist/hono.js +0 -1
  25. package/dist/index.d.ts +1 -2
  26. package/dist/index.js +1 -2
  27. package/dist/install-handlers.d.ts +22 -2
  28. package/dist/install-handlers.js +37 -7
  29. package/dist/logger.d.ts +7 -1
  30. package/dist/logger.js +7 -1
  31. package/dist/merchant-schema.d.ts +1184 -336
  32. package/dist/merchant-schema.js +0 -1
  33. package/dist/proxy.d.ts +2 -3
  34. package/dist/proxy.js +4 -5
  35. package/dist/react.d.ts +9 -1
  36. package/dist/react.js +10 -2
  37. package/dist/resync.d.ts +11 -1
  38. package/dist/resync.js +18 -16
  39. package/dist/scopes.d.ts +14 -1
  40. package/dist/scopes.js +18 -17
  41. package/dist/server.d.ts +0 -1
  42. package/dist/server.js +0 -1
  43. package/dist/session.d.ts +20 -1
  44. package/dist/session.js +20 -1
  45. package/dist/signatures.d.ts +26 -1
  46. package/dist/signatures.js +29 -3
  47. package/dist/store.d.ts +6 -1
  48. package/dist/store.js +0 -1
  49. package/dist/theme.d.ts +0 -1
  50. package/dist/theme.js +0 -1
  51. package/dist/tokens.d.ts +9 -1
  52. package/dist/tokens.js +23 -47
  53. package/dist/webhooks.d.ts +28 -3
  54. package/dist/webhooks.js +25 -7
  55. package/package.json +18 -2
  56. package/dist/app-auth.d.ts.map +0 -1
  57. package/dist/app-auth.js.map +0 -1
  58. package/dist/auth-fetch.d.ts.map +0 -1
  59. package/dist/auth-fetch.js.map +0 -1
  60. package/dist/background.d.ts.map +0 -1
  61. package/dist/background.js.map +0 -1
  62. package/dist/browser.d.ts.map +0 -1
  63. package/dist/browser.js.map +0 -1
  64. package/dist/client.d.ts.map +0 -1
  65. package/dist/client.js.map +0 -1
  66. package/dist/collected.d.ts.map +0 -1
  67. package/dist/collected.js.map +0 -1
  68. package/dist/crypto.d.ts.map +0 -1
  69. package/dist/crypto.js.map +0 -1
  70. package/dist/delivery.d.ts.map +0 -1
  71. package/dist/delivery.js.map +0 -1
  72. package/dist/frame.d.ts.map +0 -1
  73. package/dist/frame.js.map +0 -1
  74. package/dist/handoff.d.ts.map +0 -1
  75. package/dist/handoff.js.map +0 -1
  76. package/dist/hono.d.ts.map +0 -1
  77. package/dist/hono.js.map +0 -1
  78. package/dist/index.d.ts.map +0 -1
  79. package/dist/index.js.map +0 -1
  80. package/dist/install-handlers.d.ts.map +0 -1
  81. package/dist/install-handlers.js.map +0 -1
  82. package/dist/logger.d.ts.map +0 -1
  83. package/dist/logger.js.map +0 -1
  84. package/dist/merchant-schema.d.ts.map +0 -1
  85. package/dist/merchant-schema.js.map +0 -1
  86. package/dist/proxy.d.ts.map +0 -1
  87. package/dist/proxy.js.map +0 -1
  88. package/dist/react.d.ts.map +0 -1
  89. package/dist/react.js.map +0 -1
  90. package/dist/resync.d.ts.map +0 -1
  91. package/dist/resync.js.map +0 -1
  92. package/dist/scopes.d.ts.map +0 -1
  93. package/dist/scopes.js.map +0 -1
  94. package/dist/server.d.ts.map +0 -1
  95. package/dist/server.js.map +0 -1
  96. package/dist/session.d.ts.map +0 -1
  97. package/dist/session.js.map +0 -1
  98. package/dist/signatures.d.ts.map +0 -1
  99. package/dist/signatures.js.map +0 -1
  100. package/dist/store.d.ts.map +0 -1
  101. package/dist/store.js.map +0 -1
  102. package/dist/theme.d.ts.map +0 -1
  103. package/dist/theme.js.map +0 -1
  104. package/dist/tokens.d.ts.map +0 -1
  105. package/dist/tokens.js.map +0 -1
  106. package/dist/webhooks.d.ts.map +0 -1
  107. package/dist/webhooks.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { decodeBody, readHeader, toResponse, } from "./delivery.js";
1
+ import { decodeBody, readHeader, reportDeliveryFailure, toResponse, } from "./delivery.js";
2
2
  import { INSTALL_EVENT, RESYNC_EVENT, SCOPES_UPDATE_EVENT, SETTINGS_EVENT, UNINSTALL_EVENT, } from "./handoff.js";
3
3
  import { MAX_TIMESTAMP_SKEW_SECONDS, verifyQueekSignatureDetailed, WEBHOOK_ID_HEADER, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_TIMESTAMP_HEADER, } from "./signatures.js";
4
4
  import { normaliseNullablePid, normalisePid } from "./store.js";
@@ -106,6 +106,18 @@ export function saveResyncedInstallation(existing, data, nowIso = new Date().toI
106
106
  * `uninstall`, or `settings`, taken from the input path's last segment)
107
107
  * from plain data — no `Request`, no framework. Same options, same
108
108
  * behaviour, same errors/status codes at every layer.
109
+ *
110
+ * Answers 401 for a missing header, stale timestamp or bad signature, 400
111
+ * for a body that is not the expected envelope, 409 for a repeated
112
+ * `webhook-id`, 404 for an unknown route, and 500 when your callback or the
113
+ * store write throws (Queek then retries).
114
+ *
115
+ * @example
116
+ * const result = await handleInstallDelivery(
117
+ * { rawBody, headers, method: "POST", path: "/queek/install" },
118
+ * { appSecret: process.env.APP_SIGNING_SECRET ?? "", store },
119
+ * );
120
+ * // reply with result.status and result.body
109
121
  */
110
122
  export async function handleInstallDelivery(input, options) {
111
123
  if ((input.method ?? "POST").toUpperCase() !== "POST") {
@@ -132,6 +144,9 @@ function trailingSegment(path) {
132
144
  * Layer 2: the Web-standard wrapper, built ONLY on layer 1 — reads
133
145
  * `await request.arrayBuffer()` + headers, calls the core, builds the
134
146
  * `Response`.
147
+ *
148
+ * @example
149
+ * export const POST = (request: Request) => handleInstallRequest(request, { appSecret, store });
135
150
  */
136
151
  export async function handleInstallRequest(request, options) {
137
152
  return toResponse(await handleInstallDelivery({
@@ -169,6 +184,11 @@ function parseEnvelope(rawBody) {
169
184
  return null;
170
185
  }
171
186
  }
187
+ function hasInstallationId(data) {
188
+ return (typeof data === "object" &&
189
+ data !== null &&
190
+ typeof data.installation?.id === "string");
191
+ }
172
192
  function isInstallPayload(data) {
173
193
  return (!!data &&
174
194
  typeof data === "object" &&
@@ -243,8 +263,9 @@ async function scopesDelivery(rawBody, headers, options) {
243
263
  }
244
264
  }
245
265
  }
246
- catch {
266
+ catch (error) {
247
267
  await options.store.releaseWebhookId(checked.id);
268
+ reportDeliveryFailure(options.onError, error, { stage: "scopes_update", eventId: checked.id });
248
269
  return { status: 500, body: { ok: false, error: "scopes_update_failed" } };
249
270
  }
250
271
  return { status: 200, body: { ok: true } };
@@ -291,10 +312,11 @@ async function installDelivery(rawBody, headers, options) {
291
312
  await applyResync(options.store, envelope.data);
292
313
  }
293
314
  }
294
- catch {
315
+ catch (error) {
295
316
  // Non-2xx on purpose: Queek revokes the key and the merchant retries
296
317
  // as a fresh install. The claim is released so the retry can land.
297
318
  await options.store.releaseWebhookId(checked.id);
319
+ reportDeliveryFailure(options.onError, error, { stage: "install", eventId: checked.id });
298
320
  return { status: 500, body: { ok: false, error: "install_failed" } };
299
321
  }
300
322
  return { status: 200, body: { ok: true } };
@@ -309,6 +331,9 @@ async function uninstallDelivery(rawBody, headers, options) {
309
331
  if (envelope.type !== UNINSTALL_EVENT) {
310
332
  return { status: 400, body: { ok: false, error: "unexpected event type" } };
311
333
  }
334
+ if (!hasInstallationId(envelope.data)) {
335
+ return { status: 400, body: { ok: false, error: "invalid uninstall payload" } };
336
+ }
312
337
  if (!(await options.store.claimWebhookId(checked.id))) {
313
338
  return { status: 409, body: { ok: false, error: "duplicate delivery" } };
314
339
  }
@@ -320,8 +345,9 @@ async function uninstallDelivery(rawBody, headers, options) {
320
345
  await options.store.deleteInstallation(envelope.data.installation.id);
321
346
  }
322
347
  }
323
- catch {
348
+ catch (error) {
324
349
  await options.store.releaseWebhookId(checked.id);
350
+ reportDeliveryFailure(options.onError, error, { stage: "uninstall", eventId: checked.id });
325
351
  return { status: 500, body: { ok: false, error: "uninstall_failed" } };
326
352
  }
327
353
  return { status: 200, body: { ok: true } };
@@ -355,12 +381,16 @@ async function settingsDelivery(rawBody, headers, options) {
355
381
  try {
356
382
  await applyResync(options.store, envelope.data);
357
383
  }
358
- catch {
384
+ catch (error) {
359
385
  await options.store.releaseWebhookId(checked.id);
386
+ reportDeliveryFailure(options.onError, error, { stage: "settings", eventId: checked.id });
360
387
  return { status: 500, body: { ok: false, error: "settings_failed" } };
361
388
  }
362
389
  return { status: 200, body: { ok: true } };
363
390
  }
391
+ if (!hasInstallationId(envelope.data)) {
392
+ return { status: 400, body: { ok: false, error: "invalid settings payload" } };
393
+ }
364
394
  if (!(await options.store.claimWebhookId(checked.id))) {
365
395
  return { status: 409, body: { ok: false, error: "duplicate delivery" } };
366
396
  }
@@ -382,10 +412,10 @@ async function settingsDelivery(rawBody, headers, options) {
382
412
  });
383
413
  }
384
414
  }
385
- catch {
415
+ catch (error) {
386
416
  await options.store.releaseWebhookId(checked.id);
417
+ reportDeliveryFailure(options.onError, error, { stage: "settings", eventId: checked.id });
387
418
  return { status: 500, body: { ok: false, error: "settings_failed" } };
388
419
  }
389
420
  return { status: 200, body: { ok: true } };
390
421
  }
391
- //# sourceMappingURL=install-handlers.js.map
package/dist/logger.d.ts CHANGED
@@ -23,5 +23,11 @@ export interface Logger {
23
23
  warn(message: string, fields?: LogFields): void;
24
24
  error(message: string, fields?: LogFields): void;
25
25
  }
26
+ /**
27
+ * A JSON-lines logger with secret redaction built in.
28
+ *
29
+ * @example
30
+ * const logger = createLogger({ service: "my-app" });
31
+ * logger.info("order synced", { order: "ord_123" });
32
+ */
26
33
  export declare function createLogger(options: LoggerOptions): Logger;
27
- //# sourceMappingURL=logger.d.ts.map
package/dist/logger.js CHANGED
@@ -40,6 +40,13 @@ function redactUnknown(value, depth) {
40
40
  }
41
41
  return value;
42
42
  }
43
+ /**
44
+ * A JSON-lines logger with secret redaction built in.
45
+ *
46
+ * @example
47
+ * const logger = createLogger({ service: "my-app" });
48
+ * logger.info("order synced", { order: "ord_123" });
49
+ */
43
50
  export function createLogger(options) {
44
51
  const threshold = LEVELS[options.level ?? process.env.LOG_LEVEL ?? "info"] ?? 1;
45
52
  const sink = options.sink ??
@@ -72,4 +79,3 @@ export function createLogger(options) {
72
79
  error: (message, fields) => emit("error", message, fields),
73
80
  };
74
81
  }
75
- //# sourceMappingURL=logger.js.map