hazo_notify 3.0.0 → 4.0.2
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/README.md +122 -17
- package/config/hazo_notify_config.ini +37 -0
- package/dist/components/notification_banner/index.d.ts +27 -0
- package/dist/components/notification_banner/index.d.ts.map +1 -0
- package/dist/components/notification_banner/index.js +42 -0
- package/dist/components/notification_banner/index.js.map +1 -0
- package/dist/components/notification_bell/index.d.ts +56 -0
- package/dist/components/notification_bell/index.d.ts.map +1 -0
- package/dist/components/notification_bell/index.js +73 -0
- package/dist/components/notification_bell/index.js.map +1 -0
- package/dist/components/template_manager/template_editor.js +3 -3
- package/dist/components/template_manager/template_editor.js.map +1 -1
- package/dist/components/template_manager/template_manager_admin.d.ts.map +1 -1
- package/dist/components/template_manager/template_manager_admin.js +3 -4
- package/dist/components/template_manager/template_manager_admin.js.map +1 -1
- package/dist/lib/adapters/email/adapter.d.ts +49 -0
- package/dist/lib/adapters/email/adapter.d.ts.map +1 -0
- package/dist/lib/adapters/email/adapter.js +87 -0
- package/dist/lib/adapters/email/adapter.js.map +1 -0
- package/dist/lib/adapters/email/config.d.ts +15 -0
- package/dist/lib/adapters/email/config.d.ts.map +1 -0
- package/dist/lib/{emailer/emailer.js → adapters/email/config.js} +7 -113
- package/dist/lib/adapters/email/config.js.map +1 -0
- package/dist/lib/adapters/email/envelope.d.ts +29 -0
- package/dist/lib/adapters/email/envelope.d.ts.map +1 -0
- package/dist/lib/adapters/email/envelope.js +46 -0
- package/dist/lib/adapters/email/envelope.js.map +1 -0
- package/dist/lib/adapters/email/index.d.ts +12 -0
- package/dist/lib/adapters/email/index.d.ts.map +1 -0
- package/dist/lib/adapters/email/index.js +11 -0
- package/dist/lib/adapters/email/index.js.map +1 -0
- package/dist/lib/adapters/email/providers/index.d.ts.map +1 -0
- package/dist/lib/adapters/email/providers/index.js.map +1 -0
- package/dist/lib/adapters/email/providers/pop3_provider.d.ts.map +1 -0
- package/dist/lib/adapters/email/providers/pop3_provider.js.map +1 -0
- package/dist/lib/adapters/email/providers/smtp_provider.d.ts.map +1 -0
- package/dist/lib/adapters/email/providers/smtp_provider.js.map +1 -0
- package/dist/lib/adapters/email/providers/zeptomail_provider.d.ts.map +1 -0
- package/dist/lib/adapters/email/providers/zeptomail_provider.js.map +1 -0
- package/dist/lib/{emailer/emailer.d.ts → adapters/email/send_email.d.ts} +9 -10
- package/dist/lib/adapters/email/send_email.d.ts.map +1 -0
- package/dist/lib/adapters/email/send_email.js +122 -0
- package/dist/lib/adapters/email/send_email.js.map +1 -0
- package/dist/lib/adapters/email/types.d.ts.map +1 -0
- package/dist/lib/adapters/email/types.js.map +1 -0
- package/dist/lib/adapters/email/utils/constants.d.ts.map +1 -0
- package/dist/lib/adapters/email/utils/constants.js.map +1 -0
- package/dist/lib/adapters/email/utils/index.d.ts.map +1 -0
- package/dist/lib/adapters/email/utils/index.js.map +1 -0
- package/dist/lib/adapters/email/utils/logger.d.ts.map +1 -0
- package/dist/lib/adapters/email/utils/logger.js.map +1 -0
- package/dist/lib/adapters/email/utils/validation.d.ts.map +1 -0
- package/dist/lib/adapters/email/utils/validation.js.map +1 -0
- package/dist/lib/adapters/telegram/adapter.d.ts +25 -0
- package/dist/lib/adapters/telegram/adapter.d.ts.map +1 -0
- package/dist/lib/adapters/telegram/adapter.js +75 -0
- package/dist/lib/adapters/telegram/adapter.js.map +1 -0
- package/dist/lib/adapters/telegram/config.d.ts +22 -0
- package/dist/lib/adapters/telegram/config.d.ts.map +1 -0
- package/dist/lib/adapters/telegram/config.js +60 -0
- package/dist/lib/adapters/telegram/config.js.map +1 -0
- package/dist/lib/adapters/telegram/index.d.ts +8 -0
- package/dist/lib/adapters/telegram/index.d.ts.map +1 -0
- package/dist/lib/adapters/telegram/index.js +5 -0
- package/dist/lib/adapters/telegram/index.js.map +1 -0
- package/dist/lib/adapters/telegram/splitter.d.ts +7 -0
- package/dist/lib/adapters/telegram/splitter.d.ts.map +1 -0
- package/dist/lib/adapters/telegram/splitter.js +47 -0
- package/dist/lib/adapters/telegram/splitter.js.map +1 -0
- package/dist/lib/adapters/telegram/transport.d.ts +31 -0
- package/dist/lib/adapters/telegram/transport.d.ts.map +1 -0
- package/dist/lib/adapters/telegram/transport.js +112 -0
- package/dist/lib/adapters/telegram/transport.js.map +1 -0
- package/dist/lib/api/banner.d.ts +41 -0
- package/dist/lib/api/banner.d.ts.map +1 -0
- package/dist/lib/api/banner.js +53 -0
- package/dist/lib/api/banner.js.map +1 -0
- package/dist/lib/api/inbox.d.ts +33 -0
- package/dist/lib/api/inbox.d.ts.map +1 -0
- package/dist/lib/api/inbox.js +52 -0
- package/dist/lib/api/inbox.js.map +1 -0
- package/dist/lib/channels/index.d.ts +3 -0
- package/dist/lib/channels/index.d.ts.map +1 -0
- package/dist/lib/channels/index.js +3 -0
- package/dist/lib/channels/index.js.map +1 -0
- package/dist/lib/channels/registry.d.ts +7 -0
- package/dist/lib/channels/registry.d.ts.map +1 -0
- package/dist/lib/channels/registry.js +24 -0
- package/dist/lib/channels/registry.js.map +1 -0
- package/dist/lib/channels/types.d.ts +82 -0
- package/dist/lib/channels/types.d.ts.map +1 -0
- package/dist/lib/channels/types.js +8 -0
- package/dist/lib/channels/types.js.map +1 -0
- package/dist/lib/dispatcher/batch-key.d.ts +15 -0
- package/dist/lib/dispatcher/batch-key.d.ts.map +1 -0
- package/dist/lib/dispatcher/batch-key.js +19 -0
- package/dist/lib/dispatcher/batch-key.js.map +1 -0
- package/dist/lib/dispatcher/index.d.ts +4 -0
- package/dist/lib/dispatcher/index.d.ts.map +1 -0
- package/dist/lib/dispatcher/index.js +73 -0
- package/dist/lib/dispatcher/index.js.map +1 -0
- package/dist/lib/inbox/connection.d.ts +19 -0
- package/dist/lib/inbox/connection.d.ts.map +1 -0
- package/dist/lib/inbox/connection.js +34 -0
- package/dist/lib/inbox/connection.js.map +1 -0
- package/dist/lib/inbox/dialects/index.d.ts +10 -0
- package/dist/lib/inbox/dialects/index.d.ts.map +1 -0
- package/dist/lib/inbox/dialects/index.js +13 -0
- package/dist/lib/inbox/dialects/index.js.map +1 -0
- package/dist/lib/inbox/dialects/postgres.d.ts +3 -0
- package/dist/lib/inbox/dialects/postgres.d.ts.map +1 -0
- package/dist/lib/inbox/dialects/postgres.js +110 -0
- package/dist/lib/inbox/dialects/postgres.js.map +1 -0
- package/dist/lib/inbox/dialects/sqlite.d.ts +3 -0
- package/dist/lib/inbox/dialects/sqlite.d.ts.map +1 -0
- package/dist/lib/inbox/dialects/sqlite.js +120 -0
- package/dist/lib/inbox/dialects/sqlite.js.map +1 -0
- package/dist/lib/inbox/dialects/types.d.ts +53 -0
- package/dist/lib/inbox/dialects/types.d.ts.map +1 -0
- package/dist/lib/inbox/dialects/types.js +2 -0
- package/dist/lib/inbox/dialects/types.js.map +1 -0
- package/dist/lib/inbox/index.d.ts +9 -0
- package/dist/lib/inbox/index.d.ts.map +1 -0
- package/dist/lib/inbox/index.js +6 -0
- package/dist/lib/inbox/index.js.map +1 -0
- package/dist/lib/inbox/storage.d.ts +21 -0
- package/dist/lib/inbox/storage.d.ts.map +1 -0
- package/dist/lib/inbox/storage.js +94 -0
- package/dist/lib/inbox/storage.js.map +1 -0
- package/dist/lib/inbox/types.d.ts +57 -0
- package/dist/lib/inbox/types.d.ts.map +1 -0
- package/dist/lib/inbox/types.js +2 -0
- package/dist/lib/inbox/types.js.map +1 -0
- package/dist/lib/inbox/worker/cleanup.d.ts +14 -0
- package/dist/lib/inbox/worker/cleanup.d.ts.map +1 -0
- package/dist/lib/inbox/worker/cleanup.js +37 -0
- package/dist/lib/inbox/worker/cleanup.js.map +1 -0
- package/dist/lib/inbox/worker/flush.d.ts +31 -0
- package/dist/lib/inbox/worker/flush.d.ts.map +1 -0
- package/dist/lib/inbox/worker/flush.js +97 -0
- package/dist/lib/inbox/worker/flush.js.map +1 -0
- package/dist/lib/inbox/worker/render.d.ts +23 -0
- package/dist/lib/inbox/worker/render.d.ts.map +1 -0
- package/dist/lib/inbox/worker/render.js +42 -0
- package/dist/lib/inbox/worker/render.js.map +1 -0
- package/dist/lib/inbox/worker/start.d.ts +29 -0
- package/dist/lib/inbox/worker/start.d.ts.map +1 -0
- package/dist/lib/inbox/worker/start.js +100 -0
- package/dist/lib/inbox/worker/start.js.map +1 -0
- package/dist/lib/index.d.ts +1 -6
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +4 -6
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/jobs/handler.d.ts +25 -0
- package/dist/lib/jobs/handler.d.ts.map +1 -0
- package/dist/lib/jobs/handler.js +36 -0
- package/dist/lib/jobs/handler.js.map +1 -0
- package/dist/lib/jobs/index.d.ts +16 -0
- package/dist/lib/jobs/index.d.ts.map +1 -0
- package/dist/lib/jobs/index.js +13 -0
- package/dist/lib/jobs/index.js.map +1 -0
- package/dist/lib/jobs/submit.d.ts +20 -0
- package/dist/lib/jobs/submit.d.ts.map +1 -0
- package/dist/lib/jobs/submit.js +25 -0
- package/dist/lib/jobs/submit.js.map +1 -0
- package/dist/lib/jobs/types.d.ts +42 -0
- package/dist/lib/jobs/types.d.ts.map +1 -0
- package/dist/lib/jobs/types.js +12 -0
- package/dist/lib/jobs/types.js.map +1 -0
- package/dist/lib/template_manager/db/template_repository.d.ts.map +1 -1
- package/dist/lib/template_manager/db/template_repository.js +16 -31
- package/dist/lib/template_manager/db/template_repository.js.map +1 -1
- package/dist/lib/template_manager/engine/variable_resolver.js +4 -4
- package/dist/lib/template_manager/engine/variable_resolver.js.map +1 -1
- package/dist/lib/template_manager/handlers/adapters/hazo_auth.d.ts +20 -0
- package/dist/lib/template_manager/handlers/adapters/hazo_auth.d.ts.map +1 -0
- package/dist/lib/template_manager/handlers/adapters/hazo_auth.js +46 -0
- package/dist/lib/template_manager/handlers/adapters/hazo_auth.js.map +1 -0
- package/dist/lib/template_manager/handlers/auth.d.ts +47 -0
- package/dist/lib/template_manager/handlers/auth.d.ts.map +1 -0
- package/dist/lib/template_manager/handlers/auth.js +67 -0
- package/dist/lib/template_manager/handlers/auth.js.map +1 -0
- package/dist/lib/template_manager/handlers/index.d.ts +67 -0
- package/dist/lib/template_manager/handlers/index.d.ts.map +1 -0
- package/dist/lib/template_manager/handlers/index.js +282 -0
- package/dist/lib/template_manager/handlers/index.js.map +1 -0
- package/dist/lib/template_manager/seed/sync.d.ts.map +1 -1
- package/dist/lib/template_manager/seed/sync.js +20 -4
- package/dist/lib/template_manager/seed/sync.js.map +1 -1
- package/dist/lib/template_manager/template_manager.d.ts.map +1 -1
- package/dist/lib/template_manager/template_manager.js +67 -26
- package/dist/lib/template_manager/template_manager.js.map +1 -1
- package/dist/lib/template_manager/types.d.ts +35 -9
- package/dist/lib/template_manager/types.d.ts.map +1 -1
- package/dist/lib/template_manager/utils/index.d.ts +1 -1
- package/dist/lib/template_manager/utils/index.d.ts.map +1 -1
- package/dist/lib/template_manager/utils/index.js +1 -1
- package/dist/lib/template_manager/utils/index.js.map +1 -1
- package/dist/lib/template_manager/utils/validation.d.ts +12 -11
- package/dist/lib/template_manager/utils/validation.d.ts.map +1 -1
- package/dist/lib/template_manager/utils/validation.js +15 -22
- package/dist/lib/template_manager/utils/validation.js.map +1 -1
- package/migrations/003_create_notifications_table.sql +48 -0
- package/migrations/004_inbox_upsert_rpc.sql +63 -0
- package/migrations/005_inbox_v4_refactor.pg.sql +53 -0
- package/migrations/005_inbox_v4_refactor.sqlite.sql +30 -0
- package/migrations/006_channel_deliveries.pg.sql +61 -0
- package/migrations/006_channel_deliveries.sqlite.sql +26 -0
- package/migrations/007_templates_bodies_jsonb.pg.sql +17 -0
- package/migrations/007_templates_bodies_jsonb.sqlite.sql +18 -0
- package/package.json +63 -14
- package/dist/lib/emailer/emailer.d.ts.map +0 -1
- package/dist/lib/emailer/emailer.js.map +0 -1
- package/dist/lib/emailer/index.d.ts +0 -12
- package/dist/lib/emailer/index.d.ts.map +0 -1
- package/dist/lib/emailer/index.js +0 -13
- package/dist/lib/emailer/index.js.map +0 -1
- package/dist/lib/emailer/providers/index.d.ts.map +0 -1
- package/dist/lib/emailer/providers/index.js.map +0 -1
- package/dist/lib/emailer/providers/pop3_provider.d.ts.map +0 -1
- package/dist/lib/emailer/providers/pop3_provider.js.map +0 -1
- package/dist/lib/emailer/providers/smtp_provider.d.ts.map +0 -1
- package/dist/lib/emailer/providers/smtp_provider.js.map +0 -1
- package/dist/lib/emailer/providers/zeptomail_provider.d.ts.map +0 -1
- package/dist/lib/emailer/providers/zeptomail_provider.js.map +0 -1
- package/dist/lib/emailer/types.d.ts.map +0 -1
- package/dist/lib/emailer/types.js.map +0 -1
- package/dist/lib/emailer/utils/constants.d.ts.map +0 -1
- package/dist/lib/emailer/utils/constants.js.map +0 -1
- package/dist/lib/emailer/utils/index.d.ts.map +0 -1
- package/dist/lib/emailer/utils/index.js.map +0 -1
- package/dist/lib/emailer/utils/logger.d.ts.map +0 -1
- package/dist/lib/emailer/utils/logger.js.map +0 -1
- package/dist/lib/emailer/utils/validation.d.ts.map +0 -1
- package/dist/lib/emailer/utils/validation.js.map +0 -1
- /package/dist/lib/{emailer → adapters/email}/providers/index.d.ts +0 -0
- /package/dist/lib/{emailer → adapters/email}/providers/index.js +0 -0
- /package/dist/lib/{emailer → adapters/email}/providers/pop3_provider.d.ts +0 -0
- /package/dist/lib/{emailer → adapters/email}/providers/pop3_provider.js +0 -0
- /package/dist/lib/{emailer → adapters/email}/providers/smtp_provider.d.ts +0 -0
- /package/dist/lib/{emailer → adapters/email}/providers/smtp_provider.js +0 -0
- /package/dist/lib/{emailer → adapters/email}/providers/zeptomail_provider.d.ts +0 -0
- /package/dist/lib/{emailer → adapters/email}/providers/zeptomail_provider.js +0 -0
- /package/dist/lib/{emailer → adapters/email}/types.d.ts +0 -0
- /package/dist/lib/{emailer → adapters/email}/types.js +0 -0
- /package/dist/lib/{emailer → adapters/email}/utils/constants.d.ts +0 -0
- /package/dist/lib/{emailer → adapters/email}/utils/constants.js +0 -0
- /package/dist/lib/{emailer → adapters/email}/utils/index.d.ts +0 -0
- /package/dist/lib/{emailer → adapters/email}/utils/index.js +0 -0
- /package/dist/lib/{emailer → adapters/email}/utils/logger.d.ts +0 -0
- /package/dist/lib/{emailer → adapters/email}/utils/logger.js +0 -0
- /package/dist/lib/{emailer → adapters/email}/utils/validation.d.ts +0 -0
- /package/dist/lib/{emailer → adapters/email}/utils/validation.js +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// src/lib/inbox/dialects/sqlite.ts
|
|
2
|
+
// SQLite uses ? params (not $1), no jsonb cast, app-supplied UUIDs,
|
|
3
|
+
// and SELECT ... RETURNING (3.35+).
|
|
4
|
+
//
|
|
5
|
+
// Concurrent claim: SQLite is single-writer; a BEGIN IMMEDIATE transaction
|
|
6
|
+
// serialises the claim+update step. SKIP LOCKED is not needed.
|
|
7
|
+
//
|
|
8
|
+
// Insert-vs-update detection:
|
|
9
|
+
// `rowid = last_insert_rowid()` is unreliable for ON CONFLICT DO UPDATE —
|
|
10
|
+
// SQLite sets last_insert_rowid() to the existing row's rowid on conflict,
|
|
11
|
+
// so the expression is always 1 (true). Instead:
|
|
12
|
+
// - inbox rows: check `aggregate_count = 1` (1 means freshly inserted)
|
|
13
|
+
// - deliveries: check `id = ?` with the candidate new_id (true on fresh insert)
|
|
14
|
+
//
|
|
15
|
+
// Datetime format:
|
|
16
|
+
// The dispatcher stores flush_after as a JS ISO 8601 string ("2026-05-16T13:00:00.000Z").
|
|
17
|
+
// SQLite's datetime('now') returns "2026-05-16 13:00:00" (space-separated, no ms/Z).
|
|
18
|
+
// Comparing them lexicographically always treats ISO as "greater", so `<= datetime('now')`
|
|
19
|
+
// is never true. Use strftime('%Y-%m-%dT%H:%M:%SZ','now') to match ISO format.
|
|
20
|
+
import { randomUUID } from "crypto";
|
|
21
|
+
export const SqliteDialect = {
|
|
22
|
+
async upsertInboxRow(conn, i) {
|
|
23
|
+
// Try INSERT; on conflict UPDATE; detect which path ran via aggregate_count.
|
|
24
|
+
// aggregate_count = 1 means freshly inserted (default value); > 1 means aggregated.
|
|
25
|
+
// (rowid = last_insert_rowid() is unreliable for ON CONFLICT DO UPDATE — see file header.)
|
|
26
|
+
const new_id = randomUUID();
|
|
27
|
+
const rows = await conn.raw(`INSERT INTO hazo_notify_inbox (
|
|
28
|
+
id, scope_id, user_id, event_type, subject_id, batch_key,
|
|
29
|
+
in_app_text, deep_link, payload, surfaces, batch_closed_at
|
|
30
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
31
|
+
ON CONFLICT (batch_key) WHERE batch_closed_at IS NULL DO UPDATE SET
|
|
32
|
+
aggregate_count = aggregate_count + 1,
|
|
33
|
+
in_app_text = excluded.in_app_text,
|
|
34
|
+
payload = excluded.payload,
|
|
35
|
+
updated_at = datetime('now')
|
|
36
|
+
RETURNING id, aggregate_count`, [
|
|
37
|
+
new_id, i.scope_id, i.user_id, i.event_type, i.subject_id, i.batch_key,
|
|
38
|
+
i.in_app_text, i.deep_link,
|
|
39
|
+
JSON.stringify(i.payload), JSON.stringify(i.surfaces),
|
|
40
|
+
i.batch_closed_at,
|
|
41
|
+
]);
|
|
42
|
+
return { id: rows[0].id, inserted: rows[0].aggregate_count === 1 };
|
|
43
|
+
},
|
|
44
|
+
async upsertDelivery(conn, i) {
|
|
45
|
+
// Detect insert-vs-update by comparing the returned id against the candidate new_id.
|
|
46
|
+
// On a fresh INSERT the returned id == new_id; on conflict the existing id is returned.
|
|
47
|
+
// (rowid = last_insert_rowid() is unreliable for ON CONFLICT DO UPDATE — see file header.)
|
|
48
|
+
const new_id = randomUUID();
|
|
49
|
+
const rows = await conn.raw(`INSERT INTO hazo_notify_channel_deliveries (
|
|
50
|
+
id, inbox_id, channel_id, payload, flush_after
|
|
51
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
52
|
+
ON CONFLICT (inbox_id, channel_id) DO UPDATE SET
|
|
53
|
+
payload = excluded.payload,
|
|
54
|
+
flush_after = CASE WHEN attempt_count = 0 THEN excluded.flush_after ELSE flush_after END,
|
|
55
|
+
updated_at = datetime('now')
|
|
56
|
+
RETURNING id, attempt_count`, [new_id, i.inbox_id, i.channel_id, JSON.stringify(i.payload), i.flush_after]);
|
|
57
|
+
return {
|
|
58
|
+
id: rows[0].id,
|
|
59
|
+
inserted: rows[0].id === new_id,
|
|
60
|
+
attempt_in_flight: rows[0].attempt_count > 0,
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
async claimDueDeliveries(conn, channel_id, batch_size) {
|
|
64
|
+
// Two-step under BEGIN IMMEDIATE: select candidate ids, then UPDATE+RETURNING.
|
|
65
|
+
//
|
|
66
|
+
// flush_after is stored as ISO 8601 ("2026-05-16T13:00:00.000Z").
|
|
67
|
+
// SQLite's datetime('now') returns "2026-05-16 13:00:00" (space-separated, no Z).
|
|
68
|
+
// These are lexicographically incompatible, so we use strftime('%Y-%m-%dT%H:%M:%SZ','now')
|
|
69
|
+
// to match the ISO format stored in flush_after. See file header for full explanation.
|
|
70
|
+
return await conn.raw(`WITH claimed AS (
|
|
71
|
+
SELECT id FROM hazo_notify_channel_deliveries
|
|
72
|
+
WHERE channel_id = ? AND status = 'pending'
|
|
73
|
+
AND flush_after <= strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
|
|
74
|
+
ORDER BY flush_after ASC LIMIT ?
|
|
75
|
+
)
|
|
76
|
+
UPDATE hazo_notify_channel_deliveries
|
|
77
|
+
SET attempt_count = attempt_count + 1, updated_at = datetime('now')
|
|
78
|
+
WHERE id IN (SELECT id FROM claimed)
|
|
79
|
+
RETURNING
|
|
80
|
+
id AS delivery_id, inbox_id, channel_id, payload, attempt_count,
|
|
81
|
+
(SELECT user_id FROM hazo_notify_inbox WHERE id = inbox_id) AS user_id,
|
|
82
|
+
(SELECT scope_id FROM hazo_notify_inbox WHERE id = inbox_id) AS scope_id`, [channel_id, batch_size]);
|
|
83
|
+
},
|
|
84
|
+
async markDeliverySent(conn, id, message_id) {
|
|
85
|
+
await conn.raw(`UPDATE hazo_notify_channel_deliveries
|
|
86
|
+
SET status='sent', message_id=?, finalized_at=datetime('now'), updated_at=datetime('now')
|
|
87
|
+
WHERE id=?`, [message_id, id]);
|
|
88
|
+
},
|
|
89
|
+
async markDeliveryFailed(conn, id, error) {
|
|
90
|
+
await conn.raw(`UPDATE hazo_notify_channel_deliveries
|
|
91
|
+
SET status='failed', last_error=?, finalized_at=datetime('now'), updated_at=datetime('now')
|
|
92
|
+
WHERE id=?`, [error.slice(0, 1000), id]);
|
|
93
|
+
},
|
|
94
|
+
async rescheduleDelivery(conn, id, flush_after, error) {
|
|
95
|
+
await conn.raw(`UPDATE hazo_notify_channel_deliveries
|
|
96
|
+
SET flush_after=?, last_error=?, updated_at=datetime('now')
|
|
97
|
+
WHERE id=?`, [flush_after, error.slice(0, 1000), id]);
|
|
98
|
+
},
|
|
99
|
+
async closeStaleBatches(conn, window_ms) {
|
|
100
|
+
const rows = await conn.raw(`UPDATE hazo_notify_inbox
|
|
101
|
+
SET batch_closed_at = datetime('now')
|
|
102
|
+
WHERE batch_closed_at IS NULL
|
|
103
|
+
AND datetime(created_at, '+' || ? || ' seconds') < datetime('now')
|
|
104
|
+
RETURNING id`, [Math.floor(window_ms / 1000)]);
|
|
105
|
+
return rows.length;
|
|
106
|
+
},
|
|
107
|
+
async purgeReadInbox(conn, older_than) {
|
|
108
|
+
const rows = await conn.raw(`DELETE FROM hazo_notify_inbox
|
|
109
|
+
WHERE read_at IS NOT NULL AND read_at < ?
|
|
110
|
+
RETURNING id`, [older_than]);
|
|
111
|
+
return rows.length;
|
|
112
|
+
},
|
|
113
|
+
async purgeFinalizedDeliveries(conn, older_than) {
|
|
114
|
+
const rows = await conn.raw(`DELETE FROM hazo_notify_channel_deliveries
|
|
115
|
+
WHERE finalized_at IS NOT NULL AND finalized_at < ?
|
|
116
|
+
RETURNING id`, [older_than]);
|
|
117
|
+
return rows.length;
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
//# sourceMappingURL=sqlite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../../../src/lib/inbox/dialects/sqlite.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { HazoConnectInstance } from "../../template_manager/types.js";
|
|
2
|
+
export interface UpsertInboxRowInput {
|
|
3
|
+
scope_id: string;
|
|
4
|
+
user_id: string;
|
|
5
|
+
event_type: string;
|
|
6
|
+
subject_id: string | null;
|
|
7
|
+
batch_key: string;
|
|
8
|
+
in_app_text: string;
|
|
9
|
+
deep_link: string;
|
|
10
|
+
payload: Record<string, unknown>;
|
|
11
|
+
surfaces: {
|
|
12
|
+
in_app: boolean;
|
|
13
|
+
banner: boolean;
|
|
14
|
+
};
|
|
15
|
+
batch_closed_at: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface UpsertInboxRowResult {
|
|
18
|
+
id: string;
|
|
19
|
+
inserted: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface UpsertDeliveryInput {
|
|
22
|
+
inbox_id: string;
|
|
23
|
+
channel_id: string;
|
|
24
|
+
payload: Record<string, unknown>;
|
|
25
|
+
flush_after: string;
|
|
26
|
+
}
|
|
27
|
+
export interface UpsertDeliveryResult {
|
|
28
|
+
id: string;
|
|
29
|
+
inserted: boolean;
|
|
30
|
+
/** True if the worker has already started attempts on this row. */
|
|
31
|
+
attempt_in_flight: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface ClaimedDelivery {
|
|
34
|
+
delivery_id: string;
|
|
35
|
+
inbox_id: string;
|
|
36
|
+
channel_id: string;
|
|
37
|
+
payload: Record<string, unknown>;
|
|
38
|
+
attempt_count: number;
|
|
39
|
+
user_id: string;
|
|
40
|
+
scope_id: string;
|
|
41
|
+
}
|
|
42
|
+
export interface InboxDialect {
|
|
43
|
+
upsertInboxRow(conn: HazoConnectInstance, input: UpsertInboxRowInput): Promise<UpsertInboxRowResult>;
|
|
44
|
+
upsertDelivery(conn: HazoConnectInstance, input: UpsertDeliveryInput): Promise<UpsertDeliveryResult>;
|
|
45
|
+
claimDueDeliveries(conn: HazoConnectInstance, channel_id: string, batch_size: number): Promise<ClaimedDelivery[]>;
|
|
46
|
+
markDeliverySent(conn: HazoConnectInstance, delivery_id: string, message_id: string): Promise<void>;
|
|
47
|
+
markDeliveryFailed(conn: HazoConnectInstance, delivery_id: string, error: string): Promise<void>;
|
|
48
|
+
rescheduleDelivery(conn: HazoConnectInstance, delivery_id: string, flush_after: string, error: string): Promise<void>;
|
|
49
|
+
closeStaleBatches(conn: HazoConnectInstance, window_ms: number): Promise<number>;
|
|
50
|
+
purgeReadInbox(conn: HazoConnectInstance, older_than: string): Promise<number>;
|
|
51
|
+
purgeFinalizedDeliveries(conn: HazoConnectInstance, older_than: string): Promise<number>;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/lib/inbox/dialects/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAE3E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,QAAQ,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;IAClB,mEAAmE;IACnE,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,cAAc,CAAC,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACrG,cAAc,CAAC,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACrG,kBAAkB,CAAC,IAAI,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAClH,gBAAgB,CAAC,IAAI,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpG,kBAAkB,CAAC,IAAI,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjG,kBAAkB,CAAC,IAAI,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtH,iBAAiB,CAAC,IAAI,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjF,cAAc,CAAC,IAAI,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/E,wBAAwB,CAAC,IAAI,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1F"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/lib/inbox/dialects/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { initInbox } from "./connection.js";
|
|
2
|
+
export type { InitInboxOptions } from "./connection.js";
|
|
3
|
+
export type { InboxRow, DeliveryRow, NotificationSurfaces, DispatchInput, DispatchResult, } from "./types.js";
|
|
4
|
+
export { listUnreadForUser, unreadCount, markRead, markAllRead, } from "./storage.js";
|
|
5
|
+
export { startInboxWorker } from "./worker/start.js";
|
|
6
|
+
export type { StartInboxWorkerOptions, WorkerHandle } from "./worker/start.js";
|
|
7
|
+
export { flushChannelOnce } from "./worker/flush.js";
|
|
8
|
+
export type { FlushOptions, FlushResult } from "./worker/flush.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/inbox/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,YAAY,EACV,QAAQ,EAAE,WAAW,EAAE,oBAAoB,EAC3C,aAAa,EAAE,cAAc,GAC9B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,GACtD,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,YAAY,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// src/lib/inbox/index.ts — replacement
|
|
2
|
+
export { initInbox } from "./connection.js";
|
|
3
|
+
export { listUnreadForUser, unreadCount, markRead, markAllRead, } from "./storage.js";
|
|
4
|
+
export { startInboxWorker } from "./worker/start.js";
|
|
5
|
+
export { flushChannelOnce } from "./worker/flush.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/inbox/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAM5C,OAAO,EACL,iBAAiB,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,GACtD,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { UpsertInboxRowInput, UpsertInboxRowResult, UpsertDeliveryInput, UpsertDeliveryResult, ClaimedDelivery } from "./dialects/types.js";
|
|
2
|
+
import type { InboxRow } from "./types.js";
|
|
3
|
+
export declare function upsertInboxRow(i: UpsertInboxRowInput): Promise<UpsertInboxRowResult>;
|
|
4
|
+
export declare function upsertDelivery(i: UpsertDeliveryInput): Promise<UpsertDeliveryResult>;
|
|
5
|
+
export declare function claimDueDeliveries(channel_id: string, batch_size: number): Promise<ClaimedDelivery[]>;
|
|
6
|
+
export declare function markDeliverySent(id: string, message_id: string): Promise<void>;
|
|
7
|
+
export declare function markDeliveryFailed(id: string, error: string): Promise<void>;
|
|
8
|
+
export declare function rescheduleDelivery(id: string, flush_after: string, error: string): Promise<void>;
|
|
9
|
+
export declare function closeStaleBatches(window_ms: number): Promise<number>;
|
|
10
|
+
export declare function purgeReadInbox(older_than: string): Promise<number>;
|
|
11
|
+
export declare function purgeFinalizedDeliveries(older_than: string): Promise<number>;
|
|
12
|
+
export interface ListOptions {
|
|
13
|
+
limit?: number;
|
|
14
|
+
cursor?: string;
|
|
15
|
+
surface?: "in_app" | "banner";
|
|
16
|
+
}
|
|
17
|
+
export declare function listUnreadForUser(user_id: string, scope_id: string, opts?: ListOptions): Promise<InboxRow[]>;
|
|
18
|
+
export declare function unreadCount(user_id: string, scope_id: string): Promise<number>;
|
|
19
|
+
export declare function markRead(id: string, user_id: string): Promise<boolean>;
|
|
20
|
+
export declare function markAllRead(user_id: string, scope_id: string): Promise<number>;
|
|
21
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/lib/inbox/storage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAO3C,wBAAsB,cAAc,CAAC,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAG1F;AAED,wBAAsB,cAAc,CAAC,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAG1F;AAED,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAG3G;AAED,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBAGpE;AAED,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,iBAGjE;AAED,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,iBAGtF;AAED,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG1E;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGxE;AAED,wBAAsB,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGlF;AAID,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;CAC/B;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAoBrB;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASpF;AAED,wBAAsB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAU5E;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUpF"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { getInboxConnection } from "./connection.js";
|
|
2
|
+
import { pickDialect } from "./dialects/index.js";
|
|
3
|
+
function d() {
|
|
4
|
+
const conn = getInboxConnection();
|
|
5
|
+
return { conn, dialect: pickDialect(conn) };
|
|
6
|
+
}
|
|
7
|
+
export async function upsertInboxRow(i) {
|
|
8
|
+
const { conn, dialect } = d();
|
|
9
|
+
return dialect.upsertInboxRow(conn, i);
|
|
10
|
+
}
|
|
11
|
+
export async function upsertDelivery(i) {
|
|
12
|
+
const { conn, dialect } = d();
|
|
13
|
+
return dialect.upsertDelivery(conn, i);
|
|
14
|
+
}
|
|
15
|
+
export async function claimDueDeliveries(channel_id, batch_size) {
|
|
16
|
+
const { conn, dialect } = d();
|
|
17
|
+
return dialect.claimDueDeliveries(conn, channel_id, batch_size);
|
|
18
|
+
}
|
|
19
|
+
export async function markDeliverySent(id, message_id) {
|
|
20
|
+
const { conn, dialect } = d();
|
|
21
|
+
return dialect.markDeliverySent(conn, id, message_id);
|
|
22
|
+
}
|
|
23
|
+
export async function markDeliveryFailed(id, error) {
|
|
24
|
+
const { conn, dialect } = d();
|
|
25
|
+
return dialect.markDeliveryFailed(conn, id, error);
|
|
26
|
+
}
|
|
27
|
+
export async function rescheduleDelivery(id, flush_after, error) {
|
|
28
|
+
const { conn, dialect } = d();
|
|
29
|
+
return dialect.rescheduleDelivery(conn, id, flush_after, error);
|
|
30
|
+
}
|
|
31
|
+
export async function closeStaleBatches(window_ms) {
|
|
32
|
+
const { conn, dialect } = d();
|
|
33
|
+
return dialect.closeStaleBatches(conn, window_ms);
|
|
34
|
+
}
|
|
35
|
+
export async function purgeReadInbox(older_than) {
|
|
36
|
+
const { conn, dialect } = d();
|
|
37
|
+
return dialect.purgeReadInbox(conn, older_than);
|
|
38
|
+
}
|
|
39
|
+
export async function purgeFinalizedDeliveries(older_than) {
|
|
40
|
+
const { conn, dialect } = d();
|
|
41
|
+
return dialect.purgeFinalizedDeliveries(conn, older_than);
|
|
42
|
+
}
|
|
43
|
+
export async function listUnreadForUser(user_id, scope_id, opts = {}) {
|
|
44
|
+
const conn = getInboxConnection();
|
|
45
|
+
const limit = Math.min(opts.limit ?? 20, 100);
|
|
46
|
+
const isSqlite = conn.type === "sqlite";
|
|
47
|
+
const ph = (n) => (isSqlite ? "?" : `$${n}`);
|
|
48
|
+
const where = [`user_id = ${ph(1)}`, `scope_id = ${ph(2)}`, "read_at IS NULL"];
|
|
49
|
+
const params = [user_id, scope_id];
|
|
50
|
+
let i = 3;
|
|
51
|
+
if (opts.cursor) {
|
|
52
|
+
where.push(`created_at < ${ph(i)}`);
|
|
53
|
+
params.push(opts.cursor);
|
|
54
|
+
i++;
|
|
55
|
+
}
|
|
56
|
+
if (opts.surface) {
|
|
57
|
+
where.push(isSqlite
|
|
58
|
+
? `json_extract(surfaces, '$.${opts.surface}') = 1`
|
|
59
|
+
: `(surfaces->>'${opts.surface}')::boolean = true`);
|
|
60
|
+
}
|
|
61
|
+
params.push(limit);
|
|
62
|
+
return await conn.raw(`SELECT * FROM hazo_notify_inbox WHERE ${where.join(" AND ")}
|
|
63
|
+
ORDER BY created_at DESC LIMIT ${ph(i)}`, params);
|
|
64
|
+
}
|
|
65
|
+
export async function unreadCount(user_id, scope_id) {
|
|
66
|
+
const conn = getInboxConnection();
|
|
67
|
+
const isSqlite = conn.type === "sqlite";
|
|
68
|
+
const rows = await conn.raw(`SELECT count(*) AS n FROM hazo_notify_inbox
|
|
69
|
+
WHERE user_id = ${isSqlite ? "?" : "$1"} AND scope_id = ${isSqlite ? "?" : "$2"} AND read_at IS NULL`, [user_id, scope_id]);
|
|
70
|
+
return Number(rows[0]?.n ?? 0);
|
|
71
|
+
}
|
|
72
|
+
export async function markRead(id, user_id) {
|
|
73
|
+
const conn = getInboxConnection();
|
|
74
|
+
const isSqlite = conn.type === "sqlite";
|
|
75
|
+
const sql = isSqlite
|
|
76
|
+
? `UPDATE hazo_notify_inbox SET read_at=datetime('now')
|
|
77
|
+
WHERE id=? AND user_id=? AND read_at IS NULL RETURNING id`
|
|
78
|
+
: `UPDATE hazo_notify_inbox SET read_at=now()
|
|
79
|
+
WHERE id=$1 AND user_id=$2 AND read_at IS NULL RETURNING id`;
|
|
80
|
+
const rows = await conn.raw(sql, [id, user_id]);
|
|
81
|
+
return rows.length > 0;
|
|
82
|
+
}
|
|
83
|
+
export async function markAllRead(user_id, scope_id) {
|
|
84
|
+
const conn = getInboxConnection();
|
|
85
|
+
const isSqlite = conn.type === "sqlite";
|
|
86
|
+
const sql = isSqlite
|
|
87
|
+
? `UPDATE hazo_notify_inbox SET read_at=datetime('now')
|
|
88
|
+
WHERE user_id=? AND scope_id=? AND read_at IS NULL RETURNING id`
|
|
89
|
+
: `UPDATE hazo_notify_inbox SET read_at=now()
|
|
90
|
+
WHERE user_id=$1 AND scope_id=$2 AND read_at IS NULL RETURNING id`;
|
|
91
|
+
const rows = await conn.raw(sql, [user_id, scope_id]);
|
|
92
|
+
return rows.length;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/lib/inbox/storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export interface NotificationSurfaces {
|
|
2
|
+
in_app: boolean;
|
|
3
|
+
banner: boolean;
|
|
4
|
+
}
|
|
5
|
+
export interface InboxRow {
|
|
6
|
+
id: string;
|
|
7
|
+
scope_id: string;
|
|
8
|
+
user_id: string;
|
|
9
|
+
event_type: string;
|
|
10
|
+
subject_id: string | null;
|
|
11
|
+
batch_key: string;
|
|
12
|
+
in_app_text: string;
|
|
13
|
+
deep_link: string;
|
|
14
|
+
payload: Record<string, unknown>;
|
|
15
|
+
surfaces: NotificationSurfaces;
|
|
16
|
+
aggregate_count: number;
|
|
17
|
+
batch_closed_at: string | null;
|
|
18
|
+
read_at: string | null;
|
|
19
|
+
created_at: string;
|
|
20
|
+
updated_at: string;
|
|
21
|
+
}
|
|
22
|
+
export interface DeliveryRow {
|
|
23
|
+
id: string;
|
|
24
|
+
inbox_id: string;
|
|
25
|
+
channel_id: string;
|
|
26
|
+
payload: Record<string, unknown>;
|
|
27
|
+
status: "pending" | "sent" | "failed";
|
|
28
|
+
flush_after: string;
|
|
29
|
+
attempt_count: number;
|
|
30
|
+
message_id: string | null;
|
|
31
|
+
last_error: string | null;
|
|
32
|
+
finalized_at: string | null;
|
|
33
|
+
created_at: string;
|
|
34
|
+
updated_at: string;
|
|
35
|
+
}
|
|
36
|
+
export interface DispatchInput {
|
|
37
|
+
event_type: string;
|
|
38
|
+
subject_id: string | null;
|
|
39
|
+
scope_id: string;
|
|
40
|
+
recipient_user_ids: string[];
|
|
41
|
+
in_app_text: string;
|
|
42
|
+
deep_link: string;
|
|
43
|
+
surfaces: NotificationSurfaces;
|
|
44
|
+
channels: Partial<Record<string, boolean>>;
|
|
45
|
+
channel_payloads?: Record<string, Record<string, unknown>>;
|
|
46
|
+
payload?: Record<string, unknown>;
|
|
47
|
+
batch_window_ms?: number;
|
|
48
|
+
}
|
|
49
|
+
export interface DispatchResult {
|
|
50
|
+
inbox_rows_upserted: number;
|
|
51
|
+
inbox_rows_inserted: number;
|
|
52
|
+
inbox_rows_aggregated: number;
|
|
53
|
+
deliveries_created: number;
|
|
54
|
+
deliveries_refreshed: number;
|
|
55
|
+
channels_dispatched: string[];
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/inbox/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3C,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/lib/inbox/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sweep functions for the inbox worker:
|
|
3
|
+
* - runBatchCloseSweep — closes batches whose window has elapsed
|
|
4
|
+
* - runCleanupSweep — purges read inbox rows + finalized deliveries past retention
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
import type { ChannelLogger } from "../../channels/types.js";
|
|
9
|
+
export declare function runBatchCloseSweep(window_ms: number, logger: ChannelLogger): Promise<number>;
|
|
10
|
+
export declare function runCleanupSweep(read_inbox_retention_ms: number, failed_delivery_retention_ms: number, logger: ChannelLogger): Promise<{
|
|
11
|
+
inbox_purged: number;
|
|
12
|
+
deliveries_purged: number;
|
|
13
|
+
}>;
|
|
14
|
+
//# sourceMappingURL=cleanup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../../../src/lib/inbox/worker/cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CASlG;AAED,wBAAsB,eAAe,CACnC,uBAAuB,EAAE,MAAM,EAC/B,4BAA4B,EAAE,MAAM,EACpC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAC,CAc9D"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sweep functions for the inbox worker:
|
|
3
|
+
* - runBatchCloseSweep — closes batches whose window has elapsed
|
|
4
|
+
* - runCleanupSweep — purges read inbox rows + finalized deliveries past retention
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
import { closeStaleBatches, purgeReadInbox, purgeFinalizedDeliveries, } from "../storage.js";
|
|
9
|
+
export async function runBatchCloseSweep(window_ms, logger) {
|
|
10
|
+
try {
|
|
11
|
+
const n = await closeStaleBatches(window_ms);
|
|
12
|
+
if (n > 0)
|
|
13
|
+
logger.info("inbox.batch_close_sweep", { closed: n });
|
|
14
|
+
return n;
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
logger.error("inbox.batch_close_sweep_failed", { error: e instanceof Error ? e.message : String(e) });
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export async function runCleanupSweep(read_inbox_retention_ms, failed_delivery_retention_ms, logger) {
|
|
22
|
+
try {
|
|
23
|
+
const inbox_cutoff = new Date(Date.now() - read_inbox_retention_ms).toISOString();
|
|
24
|
+
const del_cutoff = new Date(Date.now() - failed_delivery_retention_ms).toISOString();
|
|
25
|
+
const inbox_purged = await purgeReadInbox(inbox_cutoff);
|
|
26
|
+
const deliveries_purged = await purgeFinalizedDeliveries(del_cutoff);
|
|
27
|
+
if (inbox_purged + deliveries_purged > 0) {
|
|
28
|
+
logger.info("inbox.cleanup_sweep", { inbox_purged, deliveries_purged });
|
|
29
|
+
}
|
|
30
|
+
return { inbox_purged, deliveries_purged };
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
logger.error("inbox.cleanup_sweep_failed", { error: e instanceof Error ? e.message : String(e) });
|
|
34
|
+
return { inbox_purged: 0, deliveries_purged: 0 };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=cleanup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../../../src/lib/inbox/worker/cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,iBAAiB,EAAE,cAAc,EAAE,wBAAwB,GAC5D,MAAM,eAAe,CAAC;AAGvB,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,SAAiB,EAAE,MAAqB;IAC/E,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,CAAC,GAAG,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QACjE,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE,EAAE,KAAK,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACtG,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,uBAA+B,EAC/B,4BAAoC,EACpC,MAAqB;IAErB,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,uBAAuB,CAAC,CAAC,WAAW,EAAE,CAAC;QAClF,MAAM,UAAU,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,4BAA4B,CAAC,CAAC,WAAW,EAAE,CAAC;QACrF,MAAM,YAAY,GAAG,MAAM,cAAc,CAAC,YAAY,CAAC,CAAC;QACxD,MAAM,iBAAiB,GAAG,MAAM,wBAAwB,CAAC,UAAU,CAAC,CAAC;QACrE,IAAI,YAAY,GAAG,iBAAiB,GAAG,CAAC,EAAE,CAAC;YACzC,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,YAAY,EAAE,iBAAiB,EAAE,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAC7C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,CAAC,KAAK,CAAC,4BAA4B,EAAE,EAAE,KAAK,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAClG,OAAO,EAAE,YAAY,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;IACnD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic per-channel flush worker tick.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the v3 email-only `flushOnce` with `flushChannelOnce(channel_id, opts)`,
|
|
5
|
+
* which works for any registered channel adapter. Retry policy is read from
|
|
6
|
+
* `adapter.capabilities.retry`; `attempt_count` is already incremented at claim
|
|
7
|
+
* (spec §7.2), so the retry condition is `attempt_count < max_attempts`.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
import type { ChannelLogger, ScopeBranding } from "../../channels/types.js";
|
|
12
|
+
export interface FlushOptions {
|
|
13
|
+
batchSize: number;
|
|
14
|
+
concurrency: number;
|
|
15
|
+
resolveRecipient: (user_id: string, scope_id: string, channel_id: string) => Promise<string | null>;
|
|
16
|
+
resolveScopeBranding?: (scope_id: string) => Promise<ScopeBranding | null>;
|
|
17
|
+
logger?: ChannelLogger;
|
|
18
|
+
}
|
|
19
|
+
export interface FlushResult {
|
|
20
|
+
processed: number;
|
|
21
|
+
sent: number;
|
|
22
|
+
failed: number;
|
|
23
|
+
retried: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Process one batch of deliveries due for the given channel.
|
|
27
|
+
* Per-row errors are captured on the row rather than aborting the batch.
|
|
28
|
+
* Concurrency is bounded by `opts.concurrency`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function flushChannelOnce(channel_id: string, opts: FlushOptions): Promise<FlushResult>;
|
|
31
|
+
//# sourceMappingURL=flush.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flush.d.ts","sourceRoot":"","sources":["../../../../src/lib/inbox/worker/flush.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAUH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE5E,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACpG,oBAAoB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC3E,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAOD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,WAAW,CAAC,CAyFtB"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic per-channel flush worker tick.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the v3 email-only `flushOnce` with `flushChannelOnce(channel_id, opts)`,
|
|
5
|
+
* which works for any registered channel adapter. Retry policy is read from
|
|
6
|
+
* `adapter.capabilities.retry`; `attempt_count` is already incremented at claim
|
|
7
|
+
* (spec §7.2), so the retry condition is `attempt_count < max_attempts`.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
import { getChannel } from "../../channels/registry.js";
|
|
12
|
+
import { claimDueDeliveries, markDeliverySent, markDeliveryFailed, rescheduleDelivery, } from "../storage.js";
|
|
13
|
+
import { renderForChannel } from "./render.js";
|
|
14
|
+
const noopLogger = {
|
|
15
|
+
info: () => { },
|
|
16
|
+
error: () => { },
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Process one batch of deliveries due for the given channel.
|
|
20
|
+
* Per-row errors are captured on the row rather than aborting the batch.
|
|
21
|
+
* Concurrency is bounded by `opts.concurrency`.
|
|
22
|
+
*/
|
|
23
|
+
export async function flushChannelOnce(channel_id, opts) {
|
|
24
|
+
const adapter = getChannel(channel_id);
|
|
25
|
+
if (!adapter)
|
|
26
|
+
throw new Error(`flushChannelOnce: ${channel_id} not registered`);
|
|
27
|
+
const logger = opts.logger ?? noopLogger;
|
|
28
|
+
const rows = await claimDueDeliveries(channel_id, opts.batchSize);
|
|
29
|
+
if (rows.length === 0)
|
|
30
|
+
return { processed: 0, sent: 0, failed: 0, retried: 0 };
|
|
31
|
+
let sent = 0, failed = 0, retried = 0;
|
|
32
|
+
const runOne = async (row) => {
|
|
33
|
+
try {
|
|
34
|
+
const recipient = await opts.resolveRecipient(row.user_id, row.scope_id, channel_id);
|
|
35
|
+
if (!recipient) {
|
|
36
|
+
await markDeliveryFailed(row.delivery_id, "no_recipient");
|
|
37
|
+
failed++;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const rendered_bodies = await renderForChannel(adapter, {
|
|
41
|
+
scope_id: row.scope_id, payload: row.payload,
|
|
42
|
+
});
|
|
43
|
+
const branding = opts.resolveScopeBranding
|
|
44
|
+
? await opts.resolveScopeBranding(row.scope_id)
|
|
45
|
+
: null;
|
|
46
|
+
const result = await adapter.send(row.payload, {
|
|
47
|
+
inbox_id: row.inbox_id,
|
|
48
|
+
scope_id: row.scope_id,
|
|
49
|
+
user_id: row.user_id,
|
|
50
|
+
attempt_count: row.attempt_count,
|
|
51
|
+
recipient,
|
|
52
|
+
rendered_bodies,
|
|
53
|
+
branding,
|
|
54
|
+
logger,
|
|
55
|
+
});
|
|
56
|
+
if (result.ok) {
|
|
57
|
+
await markDeliverySent(row.delivery_id, result.message_id ?? "");
|
|
58
|
+
sent++;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// attempt_count was already incremented at claim (spec §7.2).
|
|
62
|
+
// First attempt has attempt_count = 1, so `1 < max_attempts` means retry is available.
|
|
63
|
+
if (result.retryable && row.attempt_count < adapter.capabilities.retry.max_attempts) {
|
|
64
|
+
const delay = adapter.capabilities.retry.backoff_ms(row.attempt_count, result.retry_after_ms);
|
|
65
|
+
await rescheduleDelivery(row.delivery_id, new Date(Date.now() + delay).toISOString(), result.error ?? "transient");
|
|
66
|
+
retried++;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
await markDeliveryFailed(row.delivery_id, result.error ?? "send_failed");
|
|
70
|
+
failed++;
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
// Uncaught adapter exceptions are conservatively treated as retryable up to the cap (spec §7.2).
|
|
74
|
+
const err = e instanceof Error ? e.message : String(e);
|
|
75
|
+
if (row.attempt_count < adapter.capabilities.retry.max_attempts) {
|
|
76
|
+
const delay = adapter.capabilities.retry.backoff_ms(row.attempt_count);
|
|
77
|
+
await rescheduleDelivery(row.delivery_id, new Date(Date.now() + delay).toISOString(), err);
|
|
78
|
+
retried++;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
await markDeliveryFailed(row.delivery_id, err);
|
|
82
|
+
failed++;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
// Bounded concurrency: spin up `concurrency` workers that drain the queue.
|
|
87
|
+
const queue = [...rows];
|
|
88
|
+
await Promise.all(Array.from({ length: Math.max(1, opts.concurrency) }, async () => {
|
|
89
|
+
while (queue.length > 0) {
|
|
90
|
+
const next = queue.shift();
|
|
91
|
+
if (next)
|
|
92
|
+
await runOne(next);
|
|
93
|
+
}
|
|
94
|
+
}));
|
|
95
|
+
return { processed: rows.length, sent, failed, retried };
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=flush.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flush.js","sourceRoot":"","sources":["../../../../src/lib/inbox/worker/flush.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAkB/C,MAAM,UAAU,GAAkB;IAChC,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;CAChB,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,UAAkB,EAClB,IAAkB;IAElB,MAAM,OAAO,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACvC,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,UAAU,iBAAiB,CAAC,CAAC;IAEhF,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,UAAU,CAAC;IACzC,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAClE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAE/E,IAAI,IAAI,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC;IAEtC,MAAM,MAAM,GAAG,KAAK,EAAE,GAAmB,EAAE,EAAE;QAC3C,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;YACrF,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,MAAM,kBAAkB,CAAC,GAAG,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;gBAC1D,MAAM,EAAE,CAAC;gBACT,OAAO;YACT,CAAC;YAED,MAAM,eAAe,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE;gBACtD,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO;aAC7C,CAAC,CAAC;YAEH,MAAM,QAAQ,GAAG,IAAI,CAAC,oBAAoB;gBACxC,CAAC,CAAC,MAAM,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAC/C,CAAC,CAAC,IAAI,CAAC;YAET,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,OAAgB,EAAE;gBACtD,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtB,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtB,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,aAAa,EAAE,GAAG,CAAC,aAAa;gBAChC,SAAS;gBACT,eAAe;gBACf,QAAQ;gBACR,MAAM;aACP,CAAC,CAAC;YAEH,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;gBACd,MAAM,gBAAgB,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;gBACjE,IAAI,EAAE,CAAC;gBACP,OAAO;YACT,CAAC;YAED,8DAA8D;YAC9D,uFAAuF;YACvF,IAAI,MAAM,CAAC,SAAS,IAAI,GAAG,CAAC,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;gBACpF,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;gBAC9F,MAAM,kBAAkB,CACtB,GAAG,CAAC,WAAW,EACf,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,WAAW,EAAE,EAC1C,MAAM,CAAC,KAAK,IAAI,WAAW,CAC5B,CAAC;gBACF,OAAO,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YAED,MAAM,kBAAkB,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,KAAK,IAAI,aAAa,CAAC,CAAC;YACzE,MAAM,EAAE,CAAC;QACX,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,iGAAiG;YACjG,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACvD,IAAI,GAAG,CAAC,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;gBAChE,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;gBACvE,MAAM,kBAAkB,CACtB,GAAG,CAAC,WAAW,EACf,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,WAAW,EAAE,EAC1C,GAAG,CACJ,CAAC;gBACF,OAAO,EAAE,CAAC;YACZ,CAAC;iBAAM,CAAC;gBACN,MAAM,kBAAkB,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;gBAC/C,MAAM,EAAE,CAAC;YACX,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,2EAA2E;IAC3E,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IACxB,MAAM,OAAO,CAAC,GAAG,CACf,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,IAAI,EAAE;QAC/D,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;YAC3B,IAAI,IAAI;gBAAE,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAC3D,CAAC"}
|