@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/store.js ADDED
@@ -0,0 +1,610 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ import { Pool } from "pg";
3
+ import { decryptSecret, encryptSecret, parseStoreKey } from "./crypto.js";
4
+ const SEEN_TTL_SECONDS = 24 * 60 * 60;
5
+ const INSTALLATIONS_TABLE_SQLITE = `
6
+ CREATE TABLE IF NOT EXISTS installations (
7
+ installation_id TEXT PRIMARY KEY,
8
+ installation_pid TEXT NOT NULL,
9
+ vendor_id TEXT NOT NULL,
10
+ store_pid TEXT,
11
+ store_name TEXT NOT NULL,
12
+ api_base TEXT NOT NULL,
13
+ token_enc TEXT,
14
+ token_expires_at TEXT,
15
+ token_kid TEXT,
16
+ pending INTEGER NOT NULL DEFAULT 0,
17
+ scopes_json TEXT NOT NULL,
18
+ settings_json TEXT NOT NULL,
19
+ webhook_secret_enc TEXT,
20
+ proxy_secret_enc TEXT,
21
+ embed_secret_enc TEXT,
22
+ app_id TEXT,
23
+ webhook_url TEXT,
24
+ webhook_topics_json TEXT NOT NULL,
25
+ installed_at TEXT NOT NULL,
26
+ updated_at TEXT NOT NULL
27
+ );
28
+ CREATE TABLE IF NOT EXISTS seen_webhook_ids (
29
+ webhook_id TEXT PRIMARY KEY,
30
+ seen_at INTEGER NOT NULL
31
+ );
32
+ `;
33
+ /**
34
+ * Default local/dev/test `InstallationStore`: SQLite through the Node 22
35
+ * built-in `node:sqlite` (`DatabaseSync` — synchronous, file-backed, WAL
36
+ * mode).
37
+ *
38
+ * Why not better-sqlite3: its prebuilt binary segfaults on the project's
39
+ * Node 22.12 runtime (observed, not theorised), and building from source
40
+ * would force a C++ toolchain into every environment — dev Macs, CI, and
41
+ * the slim alpine app images. `node:sqlite` is dependency-free with an
42
+ * identical synchronous shape.
43
+ *
44
+ * `node:sqlite` needs no flag since Node 22.13.0 (pinned runtime 22.23.3;
45
+ * an ExperimentalWarning on stderr remains — Stability 1.1). Devs on an
46
+ * older 22 minor set `NODE_OPTIONS=--experimental-sqlite` in their shell.
47
+ * Revisit when the apps run on a Node line where it is fully stable — the
48
+ * swap is contained here, behind `InstallationStore`.
49
+ *
50
+ * 0.1.x → 0.2.0 upgrade: databases created by SDK 0.1.x carry a legacy
51
+ * `api_key_enc` column (the handoff no longer delivers that key, so its
52
+ * value is dead — minted tokens replace it; resync restores connectivity).
53
+ * Opening such a database adds the token-cache columns and drops
54
+ * `api_key_enc`; the legacy ciphertext is discarded, never decrypted.
55
+ */
56
+ export class SqliteInstallationStore {
57
+ db;
58
+ key;
59
+ constructor(options) {
60
+ this.key = parseStoreKey(options.storeKey);
61
+ this.db = new DatabaseSync(options.path);
62
+ this.db.exec("PRAGMA journal_mode = WAL;");
63
+ this.db.exec(INSTALLATIONS_TABLE_SQLITE);
64
+ this.migrateFromApiKeySchema();
65
+ }
66
+ migrateFromApiKeySchema() {
67
+ const columns = this.db.prepare(`PRAGMA table_info(installations)`).all();
68
+ const names = new Set(columns.map((column) => column.name));
69
+ if (names.has("api_key_enc")) {
70
+ for (const column of ["token_enc", "token_expires_at", "token_kid"]) {
71
+ if (!names.has(column))
72
+ this.db.exec(`ALTER TABLE installations ADD COLUMN ${column} TEXT;`);
73
+ }
74
+ // The 0.1.x handoff key is superseded by minted installation tokens:
75
+ // its ciphertext is dropped, never decrypted or re-encrypted.
76
+ this.db.exec(`ALTER TABLE installations DROP COLUMN api_key_enc;`);
77
+ }
78
+ // Schema v2 (B2 review r2): the persisted 409-pending mark. Existing
79
+ // rows backfill to 0 (not pending); fresh tables already carry the
80
+ // column via the CREATE above, so this is a no-op for them.
81
+ const live = new Set(this.db.prepare(`PRAGMA table_info(installations)`).all().map((column) => column.name));
82
+ if (!live.has("pending")) {
83
+ this.db.exec(`ALTER TABLE installations ADD COLUMN pending INTEGER NOT NULL DEFAULT 0;`);
84
+ }
85
+ // Schema v3 (FA1): the installation proxy secret for slot-claim
86
+ // signing. Existing rows backfill to NULL (no proxy secret known
87
+ // until the next install/resync handoff); fresh tables already carry
88
+ // the column via the CREATE above, so this is a no-op for them.
89
+ if (!live.has("proxy_secret_enc")) {
90
+ this.db.exec(`ALTER TABLE installations ADD COLUMN proxy_secret_enc TEXT;`);
91
+ }
92
+ // Schema v4 (S4): the embed secret + app id that verify dashboard
93
+ // session tokens. Existing rows backfill to NULL until the next
94
+ // install/resync handoff delivers them.
95
+ if (!live.has("embed_secret_enc")) {
96
+ this.db.exec(`ALTER TABLE installations ADD COLUMN embed_secret_enc TEXT;`);
97
+ }
98
+ if (!live.has("app_id")) {
99
+ this.db.exec(`ALTER TABLE installations ADD COLUMN app_id TEXT;`);
100
+ }
101
+ }
102
+ saveInstallation(record) {
103
+ const now = new Date().toISOString();
104
+ this.db
105
+ .prepare(`INSERT INTO installations (
106
+ installation_id, installation_pid, vendor_id, store_pid, store_name,
107
+ api_base, token_enc, token_expires_at, token_kid, pending,
108
+ scopes_json, settings_json,
109
+ webhook_secret_enc, proxy_secret_enc, embed_secret_enc, app_id, webhook_url, webhook_topics_json,
110
+ installed_at, updated_at
111
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
112
+ ON CONFLICT(installation_id) DO UPDATE SET
113
+ installation_pid = excluded.installation_pid,
114
+ vendor_id = excluded.vendor_id,
115
+ store_pid = excluded.store_pid,
116
+ store_name = excluded.store_name,
117
+ api_base = excluded.api_base,
118
+ token_enc = excluded.token_enc,
119
+ token_expires_at = excluded.token_expires_at,
120
+ token_kid = excluded.token_kid,
121
+ pending = excluded.pending,
122
+ scopes_json = excluded.scopes_json,
123
+ settings_json = excluded.settings_json,
124
+ webhook_secret_enc = excluded.webhook_secret_enc,
125
+ proxy_secret_enc = excluded.proxy_secret_enc,
126
+ embed_secret_enc = excluded.embed_secret_enc,
127
+ app_id = excluded.app_id,
128
+ webhook_url = excluded.webhook_url,
129
+ webhook_topics_json = excluded.webhook_topics_json,
130
+ updated_at = excluded.updated_at`)
131
+ .run(record.installationId, record.installationPid, record.vendorId, record.storePid, record.storeName, record.apiBase, record.token === null ? null : encryptSecret(record.token, this.key), record.tokenExpiresAt, record.tokenKid, record.pending ? 1 : 0, JSON.stringify(record.scopes), encryptSecret(JSON.stringify(record.settings), this.key), record.webhookSecret === null ? null : encryptSecret(record.webhookSecret, this.key), record.proxySecret === null || record.proxySecret === undefined
132
+ ? null
133
+ : encryptSecret(record.proxySecret, this.key), record.embedSecret === null || record.embedSecret === undefined
134
+ ? null
135
+ : encryptSecret(record.embedSecret, this.key), record.appId ?? null, record.webhookUrl, JSON.stringify(record.webhookTopics), record.installedAt, now);
136
+ }
137
+ getInstallation(installationId) {
138
+ const row = this.db
139
+ .prepare(`SELECT * FROM installations WHERE installation_id = ?`)
140
+ .get(installationId);
141
+ if (!row)
142
+ return null;
143
+ return rowToRecord(row, this.key);
144
+ }
145
+ listInstallations() {
146
+ const rows = this.db.prepare(`SELECT * FROM installations ORDER BY installation_id`).all();
147
+ return rows.map((row) => rowToRecord(row, this.key));
148
+ }
149
+ clearCachedToken(installationId) {
150
+ this.db
151
+ .prepare(`UPDATE installations SET token_enc = NULL, token_expires_at = NULL, token_kid = NULL
152
+ WHERE installation_id = ?`)
153
+ .run(installationId);
154
+ }
155
+ clearAllCachedTokens() {
156
+ this.db
157
+ .prepare(`UPDATE installations SET token_enc = NULL, token_expires_at = NULL, token_kid = NULL`)
158
+ .run();
159
+ }
160
+ deleteInstallation(installationId) {
161
+ this.db.prepare(`DELETE FROM installations WHERE installation_id = ?`).run(installationId);
162
+ }
163
+ markInstallationPending(installationId) {
164
+ this.db.prepare(`UPDATE installations SET pending = 1 WHERE installation_id = ?`).run(installationId);
165
+ }
166
+ clearInstallationPending(installationId) {
167
+ this.db.prepare(`UPDATE installations SET pending = 0 WHERE installation_id = ?`).run(installationId);
168
+ }
169
+ isKnownPending(installationId) {
170
+ const row = this.db
171
+ .prepare(`SELECT pending FROM installations WHERE installation_id = ?`)
172
+ .get(installationId);
173
+ if (!row)
174
+ return false;
175
+ return toPendingFlag(row.pending);
176
+ }
177
+ listWebhookSecrets() {
178
+ const rows = this.db
179
+ .prepare(`SELECT installation_id, webhook_secret_enc FROM installations WHERE webhook_secret_enc IS NOT NULL`)
180
+ .all();
181
+ return rows.map((row) => ({
182
+ installationId: row.installation_id,
183
+ secret: decryptSecret(row.webhook_secret_enc, this.key),
184
+ }));
185
+ }
186
+ hasSeenWebhookId(webhookId) {
187
+ this.pruneSeenIds();
188
+ return (this.db.prepare(`SELECT 1 AS one FROM seen_webhook_ids WHERE webhook_id = ?`).get(webhookId) !==
189
+ undefined);
190
+ }
191
+ markWebhookSeen(webhookId) {
192
+ this.db
193
+ .prepare(`INSERT OR IGNORE INTO seen_webhook_ids (webhook_id, seen_at) VALUES (?, ?)`)
194
+ .run(webhookId, Date.now());
195
+ }
196
+ claimWebhookId(webhookId) {
197
+ this.pruneSeenIds();
198
+ const result = this.db
199
+ .prepare(`INSERT OR IGNORE INTO seen_webhook_ids (webhook_id, seen_at) VALUES (?, ?)`)
200
+ .run(webhookId, Date.now());
201
+ return Number(result.changes) === 1;
202
+ }
203
+ releaseWebhookId(webhookId) {
204
+ this.db.prepare(`DELETE FROM seen_webhook_ids WHERE webhook_id = ?`).run(webhookId);
205
+ }
206
+ pruneSeenIds() {
207
+ this.db
208
+ .prepare(`DELETE FROM seen_webhook_ids WHERE seen_at < ?`)
209
+ .run(Date.now() - SEEN_TTL_SECONDS * 1000);
210
+ }
211
+ close() {
212
+ this.db.close();
213
+ }
214
+ }
215
+ function rowToRecord(row, key) {
216
+ return {
217
+ installationId: column(row.installation_id),
218
+ installationPid: column(row.installation_pid),
219
+ vendorId: column(row.vendor_id),
220
+ storePid: row.store_pid ?? null,
221
+ storeName: column(row.store_name),
222
+ apiBase: column(row.api_base),
223
+ token: row.token_enc === null ? null : decryptSecret(column(row.token_enc), key),
224
+ tokenExpiresAt: row.token_expires_at ?? null,
225
+ tokenKid: row.token_kid ?? null,
226
+ pending: toPendingFlag(row.pending),
227
+ scopes: JSON.parse(column(row.scopes_json)),
228
+ settings: JSON.parse(decryptSecret(column(row.settings_json), key)),
229
+ webhookSecret: row.webhook_secret_enc === null ? null : decryptSecret(column(row.webhook_secret_enc), key),
230
+ proxySecret: row.proxy_secret_enc === null || row.proxy_secret_enc === undefined
231
+ ? null
232
+ : decryptSecret(column(row.proxy_secret_enc), key),
233
+ embedSecret: row.embed_secret_enc === null || row.embed_secret_enc === undefined
234
+ ? null
235
+ : decryptSecret(column(row.embed_secret_enc), key),
236
+ appId: row.app_id ?? null,
237
+ webhookUrl: row.webhook_url ?? null,
238
+ webhookTopics: JSON.parse(column(row.webhook_topics_json)),
239
+ installedAt: column(row.installed_at),
240
+ updatedAt: column(row.updated_at),
241
+ };
242
+ }
243
+ function column(value) {
244
+ if (typeof value !== "string")
245
+ throw new Error("Unexpected non-string column in installations table.");
246
+ return value;
247
+ }
248
+ /**
249
+ * Read the persisted pending flag across drivers: SQLite stores
250
+ * `INTEGER 0/1`, Postgres `BOOLEAN`. Anything unrecognised (including
251
+ * NULL on a pre-migration row, which cannot happen — the column is NOT
252
+ * NULL with a default — but belt-and-braces) reads as not pending.
253
+ */
254
+ function toPendingFlag(value) {
255
+ return value === true || value === 1 || value === "1" || value === "t" || value === "true";
256
+ }
257
+ /**
258
+ * Current schema revision, stored as exactly one row in `schema_version`
259
+ * (Postgres) and enforced by column-presence migration (SQLite).
260
+ * v2 adds the persisted 409-pending mark (`pending`, NOT NULL DEFAULT
261
+ * false on both drivers); v3 the proxy secret; v4 the embed secret and
262
+ * app id that verify dashboard session tokens (all nullable).
263
+ */
264
+ export const INSTALLATION_SCHEMA_VERSION = 4;
265
+ /**
266
+ * Build one shared `pg` Pool for a process (the SDK store and the app's
267
+ * own store both run on it — total connections per app stay small: one
268
+ * pool of N per process instead of one pool per store). Attaches the same
269
+ * idle-client error guard the store constructors use, so a dropped idle
270
+ * connection never surfaces as an uncaught exception.
271
+ */
272
+ export function createPostgresPool(connectionString, max = 2) {
273
+ const pool = new Pool({ connectionString, max });
274
+ pool.on("error", () => {
275
+ // Idle-client errors have no request to fail: without this listener
276
+ // they throw as uncaught exceptions. Query errors still reject their
277
+ // own promises. (Deliberately no logging here: the store owns no
278
+ // logger, and logging pool internals would risk leaking the DSN.)
279
+ });
280
+ return pool;
281
+ }
282
+ const SCHEMA_ADVISORY_LOCK = "queek-app-sdk:installations-schema-v1";
283
+ /**
284
+ * Production `InstallationStore`: Postgres through `pg` (`Pool`, max 2).
285
+ *
286
+ * Schema safety when two containers boot together: the first statement of
287
+ * `ensureSchema()` takes a Postgres advisory lock, then creates the tables
288
+ * plus exactly one `schema_version` row, then releases the lock — so
289
+ * concurrent boots serialize instead of racing DDL. Every store method
290
+ * awaits the same cached init promise, so no query runs before the schema
291
+ * exists. The AES-256-GCM envelope is byte-identical to the SQLite store
292
+ * (same `encryptSecret`/`decryptSecret`, same `APP_ENCRYPTION_KEY`).
293
+ */
294
+ export class PostgresInstallationStore {
295
+ pool;
296
+ ownsPool;
297
+ key;
298
+ ready;
299
+ constructor(options) {
300
+ this.key = parseStoreKey(options.storeKey);
301
+ if (options.pool) {
302
+ this.pool = options.pool;
303
+ this.ownsPool = false;
304
+ }
305
+ else if (options.connectionString) {
306
+ this.pool = createPostgresPool(options.connectionString, options.poolMax ?? 2);
307
+ this.ownsPool = true;
308
+ }
309
+ else {
310
+ throw new Error("PostgresInstallationStore needs a connectionString or a shared pool.");
311
+ }
312
+ this.ready = this.ensureSchema();
313
+ // Methods awaiting `ready` surface a connection failure themselves;
314
+ // this preempts an unhandled rejection when Postgres is unreachable at
315
+ // construction (the first call still throws the real error).
316
+ this.ready.catch(() => undefined);
317
+ }
318
+ async ensureSchema() {
319
+ const client = await this.pool.connect();
320
+ try {
321
+ await client.query(`SELECT pg_advisory_lock(hashtext($1))`, [SCHEMA_ADVISORY_LOCK]);
322
+ try {
323
+ await client.query(`
324
+ CREATE TABLE IF NOT EXISTS schema_version (
325
+ version INTEGER PRIMARY KEY,
326
+ applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
327
+ );
328
+ CREATE TABLE IF NOT EXISTS installations (
329
+ installation_id TEXT PRIMARY KEY,
330
+ installation_pid TEXT NOT NULL,
331
+ vendor_id TEXT NOT NULL,
332
+ store_pid TEXT,
333
+ store_name TEXT NOT NULL,
334
+ api_base TEXT NOT NULL,
335
+ token_enc TEXT,
336
+ token_expires_at TIMESTAMPTZ,
337
+ token_kid TEXT,
338
+ pending BOOLEAN NOT NULL DEFAULT FALSE,
339
+ scopes_json TEXT NOT NULL,
340
+ settings_json TEXT NOT NULL,
341
+ webhook_secret_enc TEXT,
342
+ proxy_secret_enc TEXT,
343
+ embed_secret_enc TEXT,
344
+ app_id TEXT,
345
+ webhook_url TEXT,
346
+ webhook_topics_json TEXT NOT NULL,
347
+ installed_at TIMESTAMPTZ NOT NULL,
348
+ updated_at TIMESTAMPTZ NOT NULL
349
+ );
350
+ CREATE TABLE IF NOT EXISTS seen_webhook_ids (
351
+ webhook_id TEXT PRIMARY KEY,
352
+ seen_at BIGINT NOT NULL
353
+ );
354
+ -- Schema v2 (B2 review r2): the persisted 409-pending mark.
355
+ -- Fresh tables already carry the column; this migrates v1
356
+ -- databases in place (existing rows backfill to FALSE).
357
+ ALTER TABLE installations
358
+ ADD COLUMN IF NOT EXISTS pending BOOLEAN NOT NULL DEFAULT FALSE;
359
+ -- Schema v3 (FA1): the installation proxy secret for slot-claim
360
+ -- signing. Existing rows backfill to NULL (unknown until the
361
+ -- next install/resync handoff).
362
+ ALTER TABLE installations
363
+ ADD COLUMN IF NOT EXISTS proxy_secret_enc TEXT;
364
+ -- Schema v4 (S4): the embed secret + app id that verify
365
+ -- dashboard session tokens (NULL until the next handoff).
366
+ ALTER TABLE installations
367
+ ADD COLUMN IF NOT EXISTS embed_secret_enc TEXT;
368
+ ALTER TABLE installations
369
+ ADD COLUMN IF NOT EXISTS app_id TEXT;
370
+ -- The guard row always converges to exactly one row at the
371
+ -- current version: v1 databases gain the v2 row and lose the v1
372
+ -- row; fresh databases insert it directly.
373
+ INSERT INTO schema_version (version) VALUES (${INSTALLATION_SCHEMA_VERSION})
374
+ ON CONFLICT (version) DO NOTHING;
375
+ DELETE FROM schema_version WHERE version < ${INSTALLATION_SCHEMA_VERSION};
376
+ `);
377
+ }
378
+ finally {
379
+ await client.query(`SELECT pg_advisory_unlock(hashtext($1))`, [SCHEMA_ADVISORY_LOCK]);
380
+ }
381
+ }
382
+ finally {
383
+ client.release();
384
+ }
385
+ }
386
+ async saveInstallation(record) {
387
+ await this.ready;
388
+ const now = new Date().toISOString();
389
+ await this.pool.query(`INSERT INTO installations (
390
+ installation_id, installation_pid, vendor_id, store_pid, store_name,
391
+ api_base, token_enc, token_expires_at, token_kid, pending,
392
+ scopes_json, settings_json,
393
+ webhook_secret_enc, proxy_secret_enc, embed_secret_enc, app_id, webhook_url, webhook_topics_json,
394
+ installed_at, updated_at
395
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
396
+ ON CONFLICT (installation_id) DO UPDATE SET
397
+ installation_pid = excluded.installation_pid,
398
+ vendor_id = excluded.vendor_id,
399
+ store_pid = excluded.store_pid,
400
+ store_name = excluded.store_name,
401
+ api_base = excluded.api_base,
402
+ token_enc = excluded.token_enc,
403
+ token_expires_at = excluded.token_expires_at,
404
+ token_kid = excluded.token_kid,
405
+ pending = excluded.pending,
406
+ scopes_json = excluded.scopes_json,
407
+ settings_json = excluded.settings_json,
408
+ webhook_secret_enc = excluded.webhook_secret_enc,
409
+ proxy_secret_enc = excluded.proxy_secret_enc,
410
+ embed_secret_enc = excluded.embed_secret_enc,
411
+ app_id = excluded.app_id,
412
+ webhook_url = excluded.webhook_url,
413
+ webhook_topics_json = excluded.webhook_topics_json,
414
+ updated_at = excluded.updated_at`, [
415
+ record.installationId,
416
+ record.installationPid,
417
+ record.vendorId,
418
+ record.storePid,
419
+ record.storeName,
420
+ record.apiBase,
421
+ record.token === null ? null : encryptSecret(record.token, this.key),
422
+ record.tokenExpiresAt,
423
+ record.tokenKid,
424
+ record.pending,
425
+ JSON.stringify(record.scopes),
426
+ encryptSecret(JSON.stringify(record.settings), this.key),
427
+ record.webhookSecret === null ? null : encryptSecret(record.webhookSecret, this.key),
428
+ record.proxySecret === null || record.proxySecret === undefined
429
+ ? null
430
+ : encryptSecret(record.proxySecret, this.key),
431
+ record.embedSecret === null || record.embedSecret === undefined
432
+ ? null
433
+ : encryptSecret(record.embedSecret, this.key),
434
+ record.appId ?? null,
435
+ record.webhookUrl,
436
+ JSON.stringify(record.webhookTopics),
437
+ record.installedAt,
438
+ now,
439
+ ]);
440
+ }
441
+ async getInstallation(installationId) {
442
+ await this.ready;
443
+ const result = await this.pool.query(`SELECT * FROM installations WHERE installation_id = $1`, [
444
+ installationId,
445
+ ]);
446
+ const row = result.rows[0];
447
+ if (!row)
448
+ return null;
449
+ return pgRowToRecord(row, this.key);
450
+ }
451
+ async listInstallations() {
452
+ await this.ready;
453
+ const result = await this.pool.query(`SELECT * FROM installations ORDER BY installation_id`);
454
+ return result.rows.map((row) => pgRowToRecord(row, this.key));
455
+ }
456
+ async clearCachedToken(installationId) {
457
+ await this.ready;
458
+ await this.pool.query(`UPDATE installations SET token_enc = NULL, token_expires_at = NULL, token_kid = NULL
459
+ WHERE installation_id = $1`, [installationId]);
460
+ }
461
+ async clearAllCachedTokens() {
462
+ await this.ready;
463
+ await this.pool.query(`UPDATE installations SET token_enc = NULL, token_expires_at = NULL, token_kid = NULL`);
464
+ }
465
+ async deleteInstallation(installationId) {
466
+ await this.ready;
467
+ await this.pool.query(`DELETE FROM installations WHERE installation_id = $1`, [installationId]);
468
+ }
469
+ async markInstallationPending(installationId) {
470
+ await this.ready;
471
+ await this.pool.query(`UPDATE installations SET pending = TRUE WHERE installation_id = $1`, [
472
+ installationId,
473
+ ]);
474
+ }
475
+ async clearInstallationPending(installationId) {
476
+ await this.ready;
477
+ await this.pool.query(`UPDATE installations SET pending = FALSE WHERE installation_id = $1`, [
478
+ installationId,
479
+ ]);
480
+ }
481
+ async isKnownPending(installationId) {
482
+ await this.ready;
483
+ const result = await this.pool.query(`SELECT pending FROM installations WHERE installation_id = $1`, [
484
+ installationId,
485
+ ]);
486
+ const row = result.rows[0];
487
+ if (!row)
488
+ return false;
489
+ return toPendingFlag(row.pending);
490
+ }
491
+ async listWebhookSecrets() {
492
+ await this.ready;
493
+ const result = await this.pool.query(`SELECT installation_id, webhook_secret_enc FROM installations WHERE webhook_secret_enc IS NOT NULL`);
494
+ return result.rows.map((row) => ({
495
+ installationId: row.installation_id,
496
+ secret: decryptSecret(row.webhook_secret_enc, this.key),
497
+ }));
498
+ }
499
+ async hasSeenWebhookId(webhookId) {
500
+ await this.ready;
501
+ await this.pruneSeenIds();
502
+ const result = await this.pool.query(`SELECT 1 AS one FROM seen_webhook_ids WHERE webhook_id = $1`, [
503
+ webhookId,
504
+ ]);
505
+ return result.rowCount !== 0;
506
+ }
507
+ async markWebhookSeen(webhookId) {
508
+ await this.ready;
509
+ await this.pool.query(`INSERT INTO seen_webhook_ids (webhook_id, seen_at) VALUES ($1, $2) ON CONFLICT DO NOTHING`, [webhookId, Date.now()]);
510
+ }
511
+ async claimWebhookId(webhookId) {
512
+ await this.ready;
513
+ await this.pruneSeenIds();
514
+ const result = await this.pool.query(`INSERT INTO seen_webhook_ids (webhook_id, seen_at) VALUES ($1, $2)
515
+ ON CONFLICT (webhook_id) DO NOTHING RETURNING webhook_id`, [webhookId, Date.now()]);
516
+ return (result.rowCount ?? 0) === 1;
517
+ }
518
+ async releaseWebhookId(webhookId) {
519
+ await this.ready;
520
+ await this.pool.query(`DELETE FROM seen_webhook_ids WHERE webhook_id = $1`, [webhookId]);
521
+ }
522
+ async pruneSeenIds() {
523
+ await this.pool.query(`DELETE FROM seen_webhook_ids WHERE seen_at < $1`, [
524
+ Date.now() - SEEN_TTL_SECONDS * 1000,
525
+ ]);
526
+ }
527
+ /**
528
+ * Drain the pool (tests and graceful shutdown). A no-op when the store
529
+ * runs on a shared pool it does not own — the pool owner ends it.
530
+ */
531
+ async close() {
532
+ if (this.ownsPool)
533
+ await this.pool.end();
534
+ }
535
+ }
536
+ function pgText(value, name) {
537
+ if (typeof value !== "string")
538
+ throw new Error(`Unexpected non-string column ${name}.`);
539
+ return value;
540
+ }
541
+ function pgRowToRecord(row, key) {
542
+ const tokenEnc = row.token_enc;
543
+ const webhookSecretEnc = row.webhook_secret_enc;
544
+ const expires = row.token_expires_at;
545
+ return {
546
+ installationId: pgText(row.installation_id, "installation_id"),
547
+ installationPid: pgText(row.installation_pid, "installation_pid"),
548
+ vendorId: pgText(row.vendor_id, "vendor_id"),
549
+ storePid: row.store_pid ?? null,
550
+ storeName: pgText(row.store_name, "store_name"),
551
+ apiBase: pgText(row.api_base, "api_base"),
552
+ token: tokenEnc === null ? null : decryptSecret(tokenEnc, key),
553
+ tokenExpiresAt: expires === null
554
+ ? null
555
+ : expires instanceof Date
556
+ ? expires.toISOString()
557
+ : new Date(expires).toISOString(),
558
+ tokenKid: row.token_kid ?? null,
559
+ pending: toPendingFlag(row.pending),
560
+ scopes: JSON.parse(pgText(row.scopes_json, "scopes_json")),
561
+ settings: JSON.parse(decryptSecret(pgText(row.settings_json, "settings_json"), key)),
562
+ webhookSecret: webhookSecretEnc === null ? null : decryptSecret(webhookSecretEnc, key),
563
+ proxySecret: (() => {
564
+ const enc = row.proxy_secret_enc;
565
+ return enc === null || enc === undefined ? null : decryptSecret(enc, key);
566
+ })(),
567
+ embedSecret: (() => {
568
+ const enc = row.embed_secret_enc;
569
+ return enc === null || enc === undefined ? null : decryptSecret(enc, key);
570
+ })(),
571
+ appId: row.app_id ?? null,
572
+ webhookUrl: row.webhook_url ?? null,
573
+ webhookTopics: JSON.parse(pgText(row.webhook_topics_json, "webhook_topics_json")),
574
+ installedAt: toIso(row.installed_at),
575
+ updatedAt: toIso(row.updated_at),
576
+ };
577
+ }
578
+ function toIso(value) {
579
+ if (value instanceof Date)
580
+ return value.toISOString();
581
+ return new Date(pgText(value, "timestamp")).toISOString();
582
+ }
583
+ /**
584
+ * Pick the installation store: `DATABASE_URL` set (or a shared `pool`
585
+ * given) → Postgres; otherwise SQLite — which production REFUSES (fail
586
+ * fast with a clear message: a live app must never run on a
587
+ * container-local database).
588
+ */
589
+ export function createInstallationStore(options) {
590
+ if (options.pool) {
591
+ return new PostgresInstallationStore({ storeKey: options.storeKey, pool: options.pool });
592
+ }
593
+ const databaseUrl = (options.databaseUrl ?? process.env.DATABASE_URL ?? "").trim();
594
+ if (databaseUrl !== "") {
595
+ return new PostgresInstallationStore({
596
+ connectionString: databaseUrl,
597
+ storeKey: options.storeKey,
598
+ poolMax: options.poolMax,
599
+ });
600
+ }
601
+ if ((process.env.NODE_ENV ?? "") === "production") {
602
+ throw new Error("Refusing to start on SQLite in production: set DATABASE_URL to the app's Postgres database " +
603
+ "(one database per app; SQLite is local/dev/test only).");
604
+ }
605
+ return new SqliteInstallationStore({
606
+ path: options.sqlitePath ?? process.env.QUEEK_DB_PATH ?? "./data/installations.db",
607
+ storeKey: options.storeKey,
608
+ });
609
+ }
610
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}