diff --git a/functions/dist/backup/cleanupRestoreRequests.js b/functions/dist/backup/cleanupRestoreRequests.js new file mode 100644 index 00000000..24a3185d --- /dev/null +++ b/functions/dist/backup/cleanupRestoreRequests.js @@ -0,0 +1,159 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.cleanupExpiredRestoreRequests = exports.NOTIFY_WINDOW_MS = exports.FALLBACK_TTL_MS = exports.GRACE_MS = void 0; +exports.shouldReapRestoreRequest = shouldReapRestoreRequest; +exports.shouldNotifyExpiry = shouldNotifyExpiry; +exports.sweepExpiredRestoreRequests = sweepExpiredRestoreRequests; +const admin = __importStar(require("firebase-admin")); +const scheduler_1 = require("firebase-functions/v2/scheduler"); +const queueAndPush_1 = require("../notifications/queueAndPush"); +const log_1 = require("../log"); +/** + * Cloud Function: cleanupExpiredRestoreRequests + * + * A `restore_requests` doc left behind (partner wrapped the couple key but the recipient never + * completed, or nobody ever answered) keeps its ECIES `keybox` — ciphertext sealed only to the + * recipient, useless to anyone else, but key material has no business lying around. Expiry is + * otherwise enforced only client-side (fulfil-time check, delete-before-re-request, delete-on- + * complete), so a request whose device disappeared lives forever. + * + * Hourly sweep: requests expire 30 minutes after creation, so this bounds a stranded keybox's life + * to ~1.5 h. Uses a collectionGroup query on purpose — it also reaps requests orphaned under + * already-deleted couple docs, which an iterate-the-couples sweep can never see (deleted parents + * don't list). Needs the COLLECTION_GROUP fieldOverride on `restore_requests.expiresAt` in + * `firestore.indexes.json`. + * + * Admin SDK bypasses the recipient-only delete rule, which is the point: nobody else CAN clean these. + */ +/** Never race a restore that is mid-completion: the client treats these as expired well before us. */ +exports.GRACE_MS = 5 * 60 * 1000; +/** Reap docs with no usable `expiresAt` once they are unambiguously ancient. */ +exports.FALLBACK_TTL_MS = 24 * 60 * 60 * 1000; +/** + * Only tell the requester about expiries that just happened (~2 sweep cycles). Without this bound, + * the first deploy would blast a notification for every ancient doc in the backlog at once. + */ +exports.NOTIFY_WINDOW_MS = 2 * 60 * 60 * 1000; +const asMillis = (v) => (typeof v === 'number' && v > 0 ? v : 0); +/** + * Pure reap decision, re-verified per doc even though the query already filtered — the query is an + * index scan, this is the contract. Deletes only on positive evidence of staleness: + * a real `expiresAt` past the grace window, or (defensively — the create rule doesn't validate field + * values) no usable `expiresAt` but a `createdAt` at least a day old. A doc with neither is left + * alone for a human to wonder about. + */ +function shouldReapRestoreRequest(data, nowMs) { + const expiresAt = asMillis(data.expiresAt); + if (expiresAt > 0) + return nowMs > expiresAt + exports.GRACE_MS; + const createdAt = asMillis(data.createdAt); + return createdAt > 0 && nowMs > createdAt + exports.FALLBACK_TTL_MS; +} +/** + * Pure notify decision: only for a request that expired *recently* while still waiting on someone + * (REQUESTED: partner never approved; READY: recipient never completed). Terminal states are debris — + * RESTORED already succeeded and DECLINED was answered; telling anyone again is noise. + */ +function shouldNotifyExpiry(data, nowMs) { + const expiresAt = asMillis(data.expiresAt); + if (expiresAt <= 0 || nowMs <= expiresAt + exports.GRACE_MS) + return false; + if (nowMs - expiresAt > exports.NOTIFY_WINDOW_MS) + return false; + return data.status === 'REQUESTED' || data.status === 'READY'; +} +/** + * The sweep, with `db` injectable for tests. Per-doc failures are isolated (one bad doc can't stop + * the pass); the doc is deleted BEFORE the requester is notified so a notify failure costs a nudge, + * never a duplicate (the doc it would re-trigger on is already gone). + */ +async function sweepExpiredRestoreRequests(db, nowMs) { + const snap = await db + .collectionGroup('restore_requests') + .where('expiresAt', '<=', nowMs - exports.GRACE_MS) + .limit(200) + .get(); + const counts = { scanned: snap.size, reaped: 0, notified: 0, skipped: 0, failed: 0 }; + const results = await Promise.allSettled(snap.docs.map(async (doc) => { + var _a; + const data = ((_a = doc.data()) !== null && _a !== void 0 ? _a : {}); + if (!shouldReapRestoreRequest(data, nowMs)) { + counts.skipped++; + log_1.logger.warn('[cleanupRestoreRequests] query hit failed re-verify; leaving doc', { path: doc.ref.path }); + return; + } + const notify = shouldNotifyExpiry(data, nowMs); + await doc.ref.delete(); + counts.reaped++; + if (!notify) + return; + // Best-effort: a missed nudge is fine, a failed delete is not — hence delete-first above. + // Skip when the couple itself is gone (an orphan reap has nobody meaningful to notify). + try { + const coupleRef = doc.ref.parent.parent; + const recipientUid = typeof data.recipientUid === 'string' && data.recipientUid ? data.recipientUid : doc.id; + if (!coupleRef) + return; + const coupleDoc = await coupleRef.get(); + if (!coupleDoc.exists) + return; + await (0, queueAndPush_1.queueAndPush)(db, recipientUid, { + type: 'restore_request_expired', + title: 'Your restore request expired', + body: 'No worries — start a new one whenever you’re ready.', + coupleId: coupleRef.id, + }); + counts.notified++; + } + catch (e) { + log_1.logger.warn('[cleanupRestoreRequests] expiry notice failed', { path: doc.ref.path, error: String(e) }); + } + })); + counts.failed = results.filter((r) => r.status === 'rejected').length; + return counts; +} +exports.cleanupExpiredRestoreRequests = (0, scheduler_1.onSchedule)({ schedule: 'every 1 hours', timeoutSeconds: 180 }, async () => { + try { + const counts = await sweepExpiredRestoreRequests(admin.firestore(), Date.now()); + log_1.logger.log(`[cleanupRestoreRequests] scanned ${counts.scanned}; reaped ${counts.reaped}; ` + + `notified ${counts.notified}; skipped ${counts.skipped}; failed ${counts.failed}`); + } + catch (e) { + // Never rethrow: a scheduled-function failure retries in a storm, and the next hourly run IS the retry. + log_1.logger.error('[cleanupRestoreRequests] sweep failed', { error: String(e) }); + } +}); +//# sourceMappingURL=cleanupRestoreRequests.js.map \ No newline at end of file diff --git a/functions/dist/backup/cleanupRestoreRequests.js.map b/functions/dist/backup/cleanupRestoreRequests.js.map new file mode 100644 index 00000000..406ad111 --- /dev/null +++ b/functions/dist/backup/cleanupRestoreRequests.js.map @@ -0,0 +1 @@ +{"version":3,"file":"cleanupRestoreRequests.js","sourceRoot":"","sources":["../../src/backup/cleanupRestoreRequests.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/functions/dist/backup/cleanupRestoreRequests.test.js b/functions/dist/backup/cleanupRestoreRequests.test.js new file mode 100644 index 00000000..c85bb2ba --- /dev/null +++ b/functions/dist/backup/cleanupRestoreRequests.test.js @@ -0,0 +1,129 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const cleanupRestoreRequests_1 = require("./cleanupRestoreRequests"); +const queueAndPush_1 = require("../notifications/queueAndPush"); +jest.mock('../notifications/queueAndPush', () => ({ queueAndPush: jest.fn().mockResolvedValue(undefined) })); +const NOW = 1800000000000; +describe('shouldReapRestoreRequest', () => { + it('keeps a fresh request', () => { + expect((0, cleanupRestoreRequests_1.shouldReapRestoreRequest)({ status: 'REQUESTED', expiresAt: NOW + 60000 }, NOW)).toBe(false); + }); + it('keeps an expired request still inside the grace window', () => { + // The client already treats it as expired; we wait out the grace so we can never race a + // mid-completion restore. + expect((0, cleanupRestoreRequests_1.shouldReapRestoreRequest)({ status: 'READY', expiresAt: NOW - cleanupRestoreRequests_1.GRACE_MS + 1000 }, NOW)).toBe(false); + }); + it('reaps expired requests regardless of status — DECLINED-after-READY still carries the keybox', () => { + const expiresAt = NOW - cleanupRestoreRequests_1.GRACE_MS - 1000; + for (const status of ['REQUESTED', 'READY', 'DECLINED', 'RESTORED', 'EXPIRED']) { + expect((0, cleanupRestoreRequests_1.shouldReapRestoreRequest)({ status, expiresAt }, NOW)).toBe(true); + } + }); + it('falls back to createdAt when expiresAt is unusable', () => { + expect((0, cleanupRestoreRequests_1.shouldReapRestoreRequest)({ createdAt: NOW - cleanupRestoreRequests_1.FALLBACK_TTL_MS - 1 }, NOW)).toBe(true); + expect((0, cleanupRestoreRequests_1.shouldReapRestoreRequest)({ expiresAt: 0, createdAt: NOW - cleanupRestoreRequests_1.FALLBACK_TTL_MS - 1 }, NOW)).toBe(true); + expect((0, cleanupRestoreRequests_1.shouldReapRestoreRequest)({ createdAt: NOW - 60000 }, NOW)).toBe(false); + }); + it('leaves a doc with neither field — delete only on positive evidence', () => { + expect((0, cleanupRestoreRequests_1.shouldReapRestoreRequest)({}, NOW)).toBe(false); + expect((0, cleanupRestoreRequests_1.shouldReapRestoreRequest)({ expiresAt: 'soon', createdAt: null }, NOW)).toBe(false); + }); +}); +describe('shouldNotifyExpiry', () => { + const justExpired = NOW - cleanupRestoreRequests_1.GRACE_MS - 60000; + it('notifies for a recently expired request still waiting on someone', () => { + expect((0, cleanupRestoreRequests_1.shouldNotifyExpiry)({ status: 'REQUESTED', expiresAt: justExpired }, NOW)).toBe(true); + expect((0, cleanupRestoreRequests_1.shouldNotifyExpiry)({ status: 'READY', expiresAt: justExpired }, NOW)).toBe(true); + }); + it('stays silent for terminal states — RESTORED succeeded, DECLINED was answered', () => { + expect((0, cleanupRestoreRequests_1.shouldNotifyExpiry)({ status: 'RESTORED', expiresAt: justExpired }, NOW)).toBe(false); + expect((0, cleanupRestoreRequests_1.shouldNotifyExpiry)({ status: 'DECLINED', expiresAt: justExpired }, NOW)).toBe(false); + }); + it('stays silent for backlog debris — first deploy must not blast ancient expiries', () => { + expect((0, cleanupRestoreRequests_1.shouldNotifyExpiry)({ status: 'REQUESTED', expiresAt: NOW - cleanupRestoreRequests_1.NOTIFY_WINDOW_MS - cleanupRestoreRequests_1.GRACE_MS - 1000 }, NOW)).toBe(false); + }); + it('stays silent for the createdAt-fallback branch (no real expiry to report)', () => { + expect((0, cleanupRestoreRequests_1.shouldNotifyExpiry)({ status: 'REQUESTED', createdAt: NOW - cleanupRestoreRequests_1.FALLBACK_TTL_MS - 1 }, NOW)).toBe(false); + }); + it('never notifies for something it would not reap', () => { + expect((0, cleanupRestoreRequests_1.shouldNotifyExpiry)({ status: 'REQUESTED', expiresAt: NOW + 60000 }, NOW)).toBe(false); + expect((0, cleanupRestoreRequests_1.shouldNotifyExpiry)({ status: 'REQUESTED', expiresAt: NOW - cleanupRestoreRequests_1.GRACE_MS + 1000 }, NOW)).toBe(false); + }); +}); +describe('sweepExpiredRestoreRequests', () => { + const mockQueueAndPush = queueAndPush_1.queueAndPush; + function fakeDoc(opts) { + var _a; + const coupleRef = { + id: `couple_${opts.id}`, + get: jest.fn().mockResolvedValue({ exists: (_a = opts.coupleExists) !== null && _a !== void 0 ? _a : true }), + }; + return { + id: opts.id, + data: () => opts.data, + ref: { + path: `couples/couple_${opts.id}/restore_requests/${opts.id}`, + parent: { parent: coupleRef }, + delete: opts.deleteError + ? jest.fn().mockRejectedValue(opts.deleteError) + : jest.fn().mockResolvedValue(undefined), + }, + }; + } + function fakeDb(docs) { + return { + collectionGroup: jest.fn().mockReturnValue({ + where: jest.fn().mockReturnValue({ + limit: jest.fn().mockReturnValue({ + get: jest.fn().mockResolvedValue({ size: docs.length, docs }), + }), + }), + }), + }; + } + beforeEach(() => mockQueueAndPush.mockClear()); + it('deletes the expired doc and nudges the requester', async () => { + const doc = fakeDoc({ id: 'uidA', data: { status: 'READY', recipientUid: 'uidA', expiresAt: NOW - cleanupRestoreRequests_1.GRACE_MS - 60000 } }); + const counts = await (0, cleanupRestoreRequests_1.sweepExpiredRestoreRequests)(fakeDb([doc]), NOW); + expect(doc.ref.delete).toHaveBeenCalledTimes(1); + expect(mockQueueAndPush).toHaveBeenCalledWith(expect.anything(), 'uidA', expect.objectContaining({ type: 'restore_request_expired' })); + expect(counts).toMatchObject({ scanned: 1, reaped: 1, notified: 1, failed: 0 }); + }); + it('leaves a query hit that fails re-verify', async () => { + // The index says expired; the doc says otherwise (clock skew, weird data). The predicate wins. + const doc = fakeDoc({ id: 'uidB', data: { status: 'REQUESTED', expiresAt: NOW + 60000 } }); + const counts = await (0, cleanupRestoreRequests_1.sweepExpiredRestoreRequests)(fakeDb([doc]), NOW); + expect(doc.ref.delete).not.toHaveBeenCalled(); + expect(counts).toMatchObject({ reaped: 0, skipped: 1 }); + }); + it('skips the nudge for an orphan reap (couple gone) but still deletes', async () => { + const doc = fakeDoc({ + id: 'uidC', + data: { status: 'READY', expiresAt: NOW - cleanupRestoreRequests_1.GRACE_MS - 60000 }, + coupleExists: false, + }); + const counts = await (0, cleanupRestoreRequests_1.sweepExpiredRestoreRequests)(fakeDb([doc]), NOW); + expect(doc.ref.delete).toHaveBeenCalledTimes(1); + expect(mockQueueAndPush).not.toHaveBeenCalled(); + expect(counts).toMatchObject({ reaped: 1, notified: 0 }); + }); + it('one failing doc never stops the sweep', async () => { + const bad = fakeDoc({ + id: 'uidD', + data: { status: 'READY', expiresAt: NOW - cleanupRestoreRequests_1.GRACE_MS - 60000 }, + deleteError: new Error('firestore unavailable'), + }); + const good = fakeDoc({ id: 'uidE', data: { status: 'REQUESTED', recipientUid: 'uidE', expiresAt: NOW - cleanupRestoreRequests_1.GRACE_MS - 60000 } }); + const counts = await (0, cleanupRestoreRequests_1.sweepExpiredRestoreRequests)(fakeDb([bad, good]), NOW); + expect(good.ref.delete).toHaveBeenCalledTimes(1); + expect(counts).toMatchObject({ scanned: 2, reaped: 1, failed: 1 }); + }); + it('a notify failure costs the nudge, never the reap or the pass', async () => { + mockQueueAndPush.mockRejectedValueOnce(new Error('fcm down')); + const doc = fakeDoc({ id: 'uidF', data: { status: 'READY', recipientUid: 'uidF', expiresAt: NOW - cleanupRestoreRequests_1.GRACE_MS - 60000 } }); + const counts = await (0, cleanupRestoreRequests_1.sweepExpiredRestoreRequests)(fakeDb([doc]), NOW); + expect(doc.ref.delete).toHaveBeenCalledTimes(1); + expect(counts).toMatchObject({ reaped: 1, notified: 0, failed: 0 }); + }); +}); +//# sourceMappingURL=cleanupRestoreRequests.test.js.map \ No newline at end of file diff --git a/functions/dist/backup/cleanupRestoreRequests.test.js.map b/functions/dist/backup/cleanupRestoreRequests.test.js.map new file mode 100644 index 00000000..b84af779 --- /dev/null +++ b/functions/dist/backup/cleanupRestoreRequests.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"cleanupRestoreRequests.test.js","sourceRoot":"","sources":["../../src/backup/cleanupRestoreRequests.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/functions/dist/backup/onRestoreRequested.js b/functions/dist/backup/onRestoreRequested.js index 1d16dcb3..5b5da694 100644 --- a/functions/dist/backup/onRestoreRequested.js +++ b/functions/dist/backup/onRestoreRequested.js @@ -38,10 +38,8 @@ exports.selfAlertAllowed = selfAlertAllowed; exports.isRestoreReadyTransition = isRestoreReadyTransition; const admin = __importStar(require("firebase-admin")); const firestore_1 = require("firebase-functions/v2/firestore"); -const quietHours_1 = require("../notifications/quietHours"); -const push_1 = require("../notifications/push"); +const queueAndPush_1 = require("../notifications/queueAndPush"); const log_1 = require("../log"); -const CHANNEL = 'partner_activity'; // Suppress duplicate pushes when a request doc is rapidly deleted+recreated (a compromised account // could loop that to spam both partners) and when an event is redelivered. The in-app queue entry is // still written every time. A genuine re-request after the window still notifies — so this is a time @@ -57,27 +55,6 @@ function selfAlertAllowed(lastAlertAt, now) { function isRestoreReadyTransition(beforeStatus, afterStatus) { return afterStatus === 'READY' && beforeStatus !== 'READY'; } -/** - * Write the durable in-app queue entry (always) and — unless quiet hours suppress it — push to every device. - * `bypassQuietHours` is for security signals (the "was this you?" self-alert) that must not be silenced. - */ -async function queueAndPush(db, uid, opts) { - const { type, title, body, coupleId, bypassQuietHours } = opts; - await db.collection('users').doc(uid).collection('notification_queue').add({ - type, title, body, read: false, createdAt: admin.firestore.FieldValue.serverTimestamp(), - }); - const userDoc = await db.collection('users').doc(uid).get(); - if (!userDoc.exists) - return; - const userData = userDoc.data(); - if (!bypassQuietHours && (0, quietHours_1.recipientInQuietHours)(userData)) - return; - await (0, push_1.sendPushToUser)(db, admin.messaging(), uid, { - notification: { title, body }, - data: { type, couple_id: coupleId }, - android: { notification: { channelId: CHANNEL } }, - }, userData); -} /** * Fires when a member starts a partner-assisted restore * (`couples/{coupleId}/restore_requests/{recipientUid}` created on a new/wiped device). Sends TWO @@ -88,14 +65,18 @@ async function queueAndPush(db, uid, opts) { * No key material is read or logged — the request carries only a public key + a nonce. */ exports.onRestoreRequested = (0, firestore_1.onDocumentCreated)('couples/{coupleId}/restore_requests/{recipientUid}', async (event) => { - var _a, _b, _c, _d; + var _a, _b, _c, _d, _e, _f; const { coupleId, recipientUid } = event.params; const db = admin.firestore(); + // The device that created the request identifies itself (create-only field, best-effort on the + // client). Used ONLY to spare that device its own "was this you?" — all other devices still get it. + const requesterToken = (_b = (_a = event.data) === null || _a === void 0 ? void 0 : _a.data()) === null || _b === void 0 ? void 0 : _b.requesterFcmToken; + const excludeTokens = typeof requesterToken === 'string' && requesterToken ? [requesterToken] : undefined; const coupleRef = db.collection('couples').doc(coupleId); const coupleDoc = await coupleRef.get(); if (!coupleDoc.exists) return; - const userIds = ((_b = (_a = coupleDoc.data()) === null || _a === void 0 ? void 0 : _a.userIds) !== null && _b !== void 0 ? _b : []); + const userIds = ((_d = (_c = coupleDoc.data()) === null || _c === void 0 ? void 0 : _c.userIds) !== null && _d !== void 0 ? _d : []); // The recipient must be a member; notify the OTHER member (the partner who can help). if (!userIds.includes(recipientUid)) return; @@ -108,9 +89,9 @@ exports.onRestoreRequested = (0, firestore_1.onDocumentCreated)('couples/{couple // request recreate-loops with the same window used for the self-alert. try { const now = Date.now(); - if (selfAlertAllowed((_c = coupleDoc.data()) === null || _c === void 0 ? void 0 : _c.lastRestorePartnerAlertAt, now)) { + if (selfAlertAllowed((_e = coupleDoc.data()) === null || _e === void 0 ? void 0 : _e.lastRestorePartnerAlertAt, now)) { await coupleRef.set({ lastRestorePartnerAlertAt: now }, { merge: true }); - await queueAndPush(db, partnerId, { + await (0, queueAndPush_1.queueAndPush)(db, partnerId, { type: 'restore_requested', title: 'Help your partner restore 💜', body: 'They’re setting up on a new device. Tap to help.', @@ -124,14 +105,15 @@ exports.onRestoreRequested = (0, firestore_1.onDocumentCreated)('couples/{couple // 2) Recipient self-alert — security signal, bypasses quiet hours, deduped against create-loops. try { const now = Date.now(); - if (selfAlertAllowed((_d = coupleDoc.data()) === null || _d === void 0 ? void 0 : _d.lastRestoreSelfAlertAt, now)) { + if (selfAlertAllowed((_f = coupleDoc.data()) === null || _f === void 0 ? void 0 : _f.lastRestoreSelfAlertAt, now)) { await coupleRef.set({ lastRestoreSelfAlertAt: now }, { merge: true }); - await queueAndPush(db, recipientUid, { + await (0, queueAndPush_1.queueAndPush)(db, recipientUid, { type: 'restore_self_alert', title: 'New device is restoring your history', body: 'If this wasn’t you, secure your account now.', coupleId, bypassQuietHours: true, + excludeTokens, }); } } @@ -150,6 +132,8 @@ exports.onRestoreFulfilled = (0, firestore_1.onDocumentUpdated)('couples/{couple const after = (_b = event.data) === null || _b === void 0 ? void 0 : _b.after.data(); if (!isRestoreReadyTransition(before === null || before === void 0 ? void 0 : before.status, after === null || after === void 0 ? void 0 : after.status)) return; + const requesterToken = after === null || after === void 0 ? void 0 : after.requesterFcmToken; + const excludeTokens = typeof requesterToken === 'string' && requesterToken ? [requesterToken] : undefined; const { coupleId, recipientUid } = event.params; const db = admin.firestore(); const coupleDoc = await db.collection('couples').doc(coupleId).get(); @@ -158,12 +142,13 @@ exports.onRestoreFulfilled = (0, firestore_1.onDocumentUpdated)('couples/{couple return; log_1.logger.log(`[onRestoreFulfilled] couple=${coupleId} recipient=${recipientUid}`); try { - await queueAndPush(db, recipientUid, { + await (0, queueAndPush_1.queueAndPush)(db, recipientUid, { type: 'restore_self_alert', title: 'Your history was just restored', body: 'A new device now has access. If this wasn’t you, secure your account now.', coupleId, bypassQuietHours: true, + excludeTokens, }); } catch (e) { diff --git a/functions/dist/backup/onRestoreRequested.js.map b/functions/dist/backup/onRestoreRequested.js.map index 89a414c6..f6c5295d 100644 --- a/functions/dist/backup/onRestoreRequested.js.map +++ b/functions/dist/backup/onRestoreRequested.js.map @@ -1 +1 @@ -{"version":3,"file":"onRestoreRequested.js","sourceRoot":"","sources":["../../src/backup/onRestoreRequested.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcA,4CAGC;AAGD,4DAEC;AAtBD,sDAAuC;AACvC,+DAAsF;AACtF,4DAAmE;AACnE,gDAAsD;AACtD,gCAA+B;AAE/B,MAAM,OAAO,GAAG,kBAAkB,CAAA;AAClC,mGAAmG;AACnG,qGAAqG;AACrG,qGAAqG;AACrG,2FAA2F;AAC9E,QAAA,oBAAoB,GAAG,EAAE,GAAG,IAAI,CAAA;AAE7C,yFAAyF;AACzF,SAAgB,gBAAgB,CAAC,WAAoB,EAAE,GAAW;IAChE,IAAI,OAAO,WAAW,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAChD,OAAO,GAAG,GAAG,WAAW,IAAI,4BAAoB,CAAA;AAClD,CAAC;AAED,kGAAkG;AAClG,SAAgB,wBAAwB,CAAC,YAAqB,EAAE,WAAoB;IAClF,OAAO,WAAW,KAAK,OAAO,IAAI,YAAY,KAAK,OAAO,CAAA;AAC5D,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,YAAY,CACzB,EAA6B,EAC7B,GAAW,EACX,IAAiG;IAEjG,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAAA;IAC9D,MAAM,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAC,GAAG,CAAC;QACzE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,eAAe,EAAE;KACxF,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAA;IAC3D,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAM;IAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;IAC/B,IAAI,CAAC,gBAAgB,IAAI,IAAA,kCAAqB,EAAC,QAAQ,CAAC;QAAE,OAAM;IAEhE,MAAM,IAAA,qBAAc,EAClB,EAAE,EACF,KAAK,CAAC,SAAS,EAAE,EACjB,GAAG,EACH;QACE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;QAC7B,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE;QACnC,OAAO,EAAE,EAAE,YAAY,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE;KAClD,EACD,QAAQ,CACT,CAAA;AACH,CAAC;AAED;;;;;;;;GAQG;AACU,QAAA,kBAAkB,GAAG,IAAA,6BAAiB,EACjD,oDAAoD,EACpD,KAAK,EAAE,KAAK,EAAE,EAAE;;IACd,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC,MAAM,CAAA;IAC/C,MAAM,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAA;IAE5B,MAAM,SAAS,GAAG,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACxD,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,CAAA;IACvC,IAAI,CAAC,SAAS,CAAC,MAAM;QAAE,OAAM;IAC7B,MAAM,OAAO,GAAG,CAAC,MAAA,MAAA,SAAS,CAAC,IAAI,EAAE,0CAAE,OAAO,mCAAI,EAAE,CAAa,CAAA;IAC7D,sFAAsF;IACtF,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;QAAE,OAAM;IAC3C,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,YAAY,CAAC,CAAA;IACzD,IAAI,CAAC,SAAS;QAAE,OAAM;IAEtB,kEAAkE;IAClE,YAAM,CAAC,GAAG,CAAC,+BAA+B,QAAQ,cAAc,YAAY,YAAY,SAAS,EAAE,CAAC,CAAA;IAEpG,6FAA6F;IAC7F,0EAA0E;IAC1E,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,gBAAgB,CAAC,MAAA,SAAS,CAAC,IAAI,EAAE,0CAAE,yBAAyB,EAAE,GAAG,CAAC,EAAE,CAAC;YACvE,MAAM,SAAS,CAAC,GAAG,CAAC,EAAE,yBAAyB,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;YACxE,MAAM,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE;gBAChC,IAAI,EAAE,mBAAmB;gBACzB,KAAK,EAAE,8BAA8B;gBACrC,IAAI,EAAE,kDAAkD;gBACxD,QAAQ;aACT,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,YAAM,CAAC,IAAI,CAAC,4CAA4C,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IACjF,CAAC;IAED,iGAAiG;IACjG,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,gBAAgB,CAAC,MAAA,SAAS,CAAC,IAAI,EAAE,0CAAE,sBAAsB,EAAE,GAAG,CAAC,EAAE,CAAC;YACpE,MAAM,SAAS,CAAC,GAAG,CAAC,EAAE,sBAAsB,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;YACrE,MAAM,YAAY,CAAC,EAAE,EAAE,YAAY,EAAE;gBACnC,IAAI,EAAE,oBAAoB;gBAC1B,KAAK,EAAE,sCAAsC;gBAC7C,IAAI,EAAE,8CAA8C;gBACpD,QAAQ;gBACR,gBAAgB,EAAE,IAAI;aACvB,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,YAAM,CAAC,IAAI,CAAC,wCAAwC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC7E,CAAC;AACH,CAAC,CACF,CAAA;AAED;;;;GAIG;AACU,QAAA,kBAAkB,GAAG,IAAA,6BAAiB,EACjD,oDAAoD,EACpD,KAAK,EAAE,KAAK,EAAE,EAAE;;IACd,MAAM,MAAM,GAAG,MAAA,KAAK,CAAC,IAAI,0CAAE,MAAM,CAAC,IAAI,EAAE,CAAA;IACxC,MAAM,KAAK,GAAG,MAAA,KAAK,CAAC,IAAI,0CAAE,KAAK,CAAC,IAAI,EAAE,CAAA;IACtC,IAAI,CAAC,wBAAwB,CAAC,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,MAAM,EAAE,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,MAAM,CAAC;QAAE,OAAM;IAEpE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC,MAAM,CAAA;IAC/C,MAAM,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAA;IAE5B,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,GAAG,EAAE,CAAA;IACpE,MAAM,OAAO,GAAG,CAAC,MAAA,MAAA,SAAS,CAAC,IAAI,EAAE,0CAAE,OAAO,mCAAI,EAAE,CAAa,CAAA;IAC7D,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;QAAE,OAAM;IAE3C,YAAM,CAAC,GAAG,CAAC,+BAA+B,QAAQ,cAAc,YAAY,EAAE,CAAC,CAAA;IAC/E,IAAI,CAAC;QACH,MAAM,YAAY,CAAC,EAAE,EAAE,YAAY,EAAE;YACnC,IAAI,EAAE,oBAAoB;YAC1B,KAAK,EAAE,gCAAgC;YACvC,IAAI,EAAE,2EAA2E;YACjF,QAAQ;YACR,gBAAgB,EAAE,IAAI;SACvB,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,YAAM,CAAC,IAAI,CAAC,wCAAwC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC7E,CAAC;AACH,CAAC,CACF,CAAA"} \ No newline at end of file +{"version":3,"file":"onRestoreRequested.js","sourceRoot":"","sources":["../../src/backup/onRestoreRequested.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/functions/dist/index.js b/functions/dist/index.js index f258d11b..3b06c8eb 100644 --- a/functions/dist/index.js +++ b/functions/dist/index.js @@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -exports.wrapReleaseKeyCallable = exports.onDesireSyncPartFinished = exports.onHowWellPartFinished = exports.onWheelPartFinished = exports.onThisOrThatPartFinished = exports.onGameSessionUpdate = exports.onUserDelete = exports.scheduledOutcomesReminder = exports.aggregateOutcomeStats = exports.submitOutcomeCallable = exports.createInviteCallable = exports.acceptInviteCallable = exports.leaveCoupleCallable = exports.onCoupleLeave = exports.onMessageWritten = exports.onAnswerRevealed = exports.onAnswerWritten = exports.assignDailyQuestionCallable = exports.assignDailyQuestion = exports.onRestoreFulfilled = exports.onRestoreRequested = exports.onDateHistoryCreated = exports.onDateReflectionRevealed = exports.onDateReflectionWritten = exports.notifyOnDateMatch = exports.checkDeviceIntegrity = exports.sendReengagementReminder = exports.sendStreakReminder = exports.sendDailyQuestionProactiveReminder = exports.unlockDueMemoryCapsules = exports.sendChallengeDayReminders = exports.sendThinkingOfYouCallable = exports.sendGentleReminderCallable = exports.onEntitlementChanged = exports.syncEntitlement = exports.revenueCatWebhook = void 0; +exports.wrapReleaseKeyCallable = exports.onDesireSyncPartFinished = exports.onHowWellPartFinished = exports.onWheelPartFinished = exports.onThisOrThatPartFinished = exports.onGameSessionUpdate = exports.onUserDelete = exports.scheduledOutcomesReminder = exports.aggregateOutcomeStats = exports.submitOutcomeCallable = exports.createInviteCallable = exports.acceptInviteCallable = exports.leaveCoupleCallable = exports.onCoupleLeave = exports.onMessageWritten = exports.onAnswerRevealed = exports.onAnswerWritten = exports.assignDailyQuestionCallable = exports.assignDailyQuestion = exports.cleanupExpiredRestoreRequests = exports.onRestoreFulfilled = exports.onRestoreRequested = exports.onDateHistoryCreated = exports.onDateReflectionRevealed = exports.onDateReflectionWritten = exports.notifyOnDateMatch = exports.checkDeviceIntegrity = exports.sendReengagementReminder = exports.sendStreakReminder = exports.sendDailyQuestionProactiveReminder = exports.unlockDueMemoryCapsules = exports.sendChallengeDayReminders = exports.sendThinkingOfYouCallable = exports.sendGentleReminderCallable = exports.onEntitlementChanged = exports.syncEntitlement = exports.revenueCatWebhook = void 0; // Global v2 options (region pin, instance cap) — MUST be imported before any function module so // setGlobalOptions runs before the functions below are defined. require("./options"); @@ -81,6 +81,8 @@ Object.defineProperty(exports, "onDateHistoryCreated", { enumerable: true, get: var onRestoreRequested_1 = require("./backup/onRestoreRequested"); Object.defineProperty(exports, "onRestoreRequested", { enumerable: true, get: function () { return onRestoreRequested_1.onRestoreRequested; } }); Object.defineProperty(exports, "onRestoreFulfilled", { enumerable: true, get: function () { return onRestoreRequested_1.onRestoreFulfilled; } }); +var cleanupRestoreRequests_1 = require("./backup/cleanupRestoreRequests"); +Object.defineProperty(exports, "cleanupExpiredRestoreRequests", { enumerable: true, get: function () { return cleanupRestoreRequests_1.cleanupExpiredRestoreRequests; } }); var assignDailyQuestion_1 = require("./questions/assignDailyQuestion"); Object.defineProperty(exports, "assignDailyQuestion", { enumerable: true, get: function () { return assignDailyQuestion_1.assignDailyQuestion; } }); Object.defineProperty(exports, "assignDailyQuestionCallable", { enumerable: true, get: function () { return assignDailyQuestion_1.assignDailyQuestionCallable; } }); diff --git a/functions/dist/index.js.map b/functions/dist/index.js.map index 5aec25c9..b4c3de6a 100644 --- a/functions/dist/index.js.map +++ b/functions/dist/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gGAAgG;AAChG,gEAAgE;AAChE,qBAAkB;AAClB,sDAAuC;AAEvC,qEAAqE;AACrE,8EAA8E;AAC9E,gFAAgF;AAChF,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;IAC5B,KAAK,CAAC,aAAa,EAAE,CAAA;AACvB,CAAC;AAED,mGAAmG;AACnG,8FAA8F;AAC9F,iGAAiG;AACjG,8CAA8C;AAC9C,6DAA6D;AAC7D,iEAA+D;AAAtD,sHAAA,iBAAiB,OAAA;AAC1B,6DAA2D;AAAlD,kHAAA,eAAe,OAAA;AACxB,uEAAqE;AAA5D,4HAAA,oBAAoB,OAAA;AAC7B,yFAAuF;AAA9E,wIAAA,0BAA0B,OAAA;AACnC,uFAAqF;AAA5E,sIAAA,yBAAyB,OAAA;AAClC,+DAGsC;AAFpC,0HAAA,yBAAyB,OAAA;AACzB,wHAAA,uBAAuB,OAAA;AAEzB,+EAA0F;AAAjF,2IAAA,kCAAkC,OAAA;AAC3C,iEAAmE;AAA1D,oHAAA,kBAAkB,OAAA;AAC3B,6DAAuE;AAA9D,wHAAA,wBAAwB,OAAA;AACjC,wEAAsE;AAA7D,4HAAA,oBAAoB,OAAA;AAC7B,2DAA2D;AAAlD,oHAAA,iBAAiB,OAAA;AAC1B,2EAAyE;AAAhE,kIAAA,uBAAuB,OAAA;AAChC,6EAA2E;AAAlE,oIAAA,wBAAwB,OAAA;AACjC,qEAAmE;AAA1D,4HAAA,oBAAoB,OAAA;AAC7B,kEAAoF;AAA3E,wHAAA,kBAAkB,OAAA;AAAE,wHAAA,kBAAkB,OAAA;AAC/C,uEAGwC;AAFtC,0HAAA,mBAAmB,OAAA;AACnB,kIAAA,2BAA2B,OAAA;AAE7B,+DAA6D;AAApD,kHAAA,eAAe,OAAA;AACxB,iEAA+D;AAAtD,oHAAA,gBAAgB,OAAA;AACzB,iEAA+D;AAAtD,oHAAA,gBAAgB,OAAA;AACzB,yDAAuD;AAA9C,8GAAA,aAAa,OAAA;AACtB,qEAAmE;AAA1D,0HAAA,mBAAmB,OAAA;AAC5B,uEAAqE;AAA5D,4HAAA,oBAAoB,OAAA;AAC7B,uEAAqE;AAA5D,4HAAA,oBAAoB,OAAA;AAC7B,yEAAuE;AAA9D,8HAAA,qBAAqB,OAAA;AAC9B,iEAAmE;AAA1D,0HAAA,qBAAqB,OAAA;AAC9B,iFAA+E;AAAtE,sIAAA,yBAAyB,OAAA;AAClC,qDAAmD;AAA1C,4GAAA,YAAY,OAAA;AACrB,mEAMoC;AALlC,0HAAA,mBAAmB,OAAA;AACnB,+HAAA,wBAAwB,OAAA;AACxB,0HAAA,mBAAmB,OAAA;AACnB,4HAAA,qBAAqB,OAAA;AACrB,+HAAA,wBAAwB,OAAA;AAG1B,8EAA4E;AAAnE,gIAAA,sBAAsB,OAAA;AAE/B,oFAAoF;AACpF,uEAAuE;AACvE,iFAAiF;AACjF,0DAA0D"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gGAAgG;AAChG,gEAAgE;AAChE,qBAAkB;AAClB,sDAAuC;AAEvC,qEAAqE;AACrE,8EAA8E;AAC9E,gFAAgF;AAChF,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;IAC5B,KAAK,CAAC,aAAa,EAAE,CAAA;AACvB,CAAC;AAED,mGAAmG;AACnG,8FAA8F;AAC9F,iGAAiG;AACjG,8CAA8C;AAC9C,6DAA6D;AAC7D,iEAA+D;AAAtD,sHAAA,iBAAiB,OAAA;AAC1B,6DAA2D;AAAlD,kHAAA,eAAe,OAAA;AACxB,uEAAqE;AAA5D,4HAAA,oBAAoB,OAAA;AAC7B,yFAAuF;AAA9E,wIAAA,0BAA0B,OAAA;AACnC,uFAAqF;AAA5E,sIAAA,yBAAyB,OAAA;AAClC,+DAGsC;AAFpC,0HAAA,yBAAyB,OAAA;AACzB,wHAAA,uBAAuB,OAAA;AAEzB,+EAA0F;AAAjF,2IAAA,kCAAkC,OAAA;AAC3C,iEAAmE;AAA1D,oHAAA,kBAAkB,OAAA;AAC3B,6DAAuE;AAA9D,wHAAA,wBAAwB,OAAA;AACjC,wEAAsE;AAA7D,4HAAA,oBAAoB,OAAA;AAC7B,2DAA2D;AAAlD,oHAAA,iBAAiB,OAAA;AAC1B,2EAAyE;AAAhE,kIAAA,uBAAuB,OAAA;AAChC,6EAA2E;AAAlE,oIAAA,wBAAwB,OAAA;AACjC,qEAAmE;AAA1D,4HAAA,oBAAoB,OAAA;AAC7B,kEAAoF;AAA3E,wHAAA,kBAAkB,OAAA;AAAE,wHAAA,kBAAkB,OAAA;AAC/C,0EAA+E;AAAtE,uIAAA,6BAA6B,OAAA;AACtC,uEAGwC;AAFtC,0HAAA,mBAAmB,OAAA;AACnB,kIAAA,2BAA2B,OAAA;AAE7B,+DAA6D;AAApD,kHAAA,eAAe,OAAA;AACxB,iEAA+D;AAAtD,oHAAA,gBAAgB,OAAA;AACzB,iEAA+D;AAAtD,oHAAA,gBAAgB,OAAA;AACzB,yDAAuD;AAA9C,8GAAA,aAAa,OAAA;AACtB,qEAAmE;AAA1D,0HAAA,mBAAmB,OAAA;AAC5B,uEAAqE;AAA5D,4HAAA,oBAAoB,OAAA;AAC7B,uEAAqE;AAA5D,4HAAA,oBAAoB,OAAA;AAC7B,yEAAuE;AAA9D,8HAAA,qBAAqB,OAAA;AAC9B,iEAAmE;AAA1D,0HAAA,qBAAqB,OAAA;AAC9B,iFAA+E;AAAtE,sIAAA,yBAAyB,OAAA;AAClC,qDAAmD;AAA1C,4GAAA,YAAY,OAAA;AACrB,mEAMoC;AALlC,0HAAA,mBAAmB,OAAA;AACnB,+HAAA,wBAAwB,OAAA;AACxB,0HAAA,mBAAmB,OAAA;AACnB,4HAAA,qBAAqB,OAAA;AACrB,+HAAA,wBAAwB,OAAA;AAG1B,8EAA4E;AAAnE,gIAAA,sBAAsB,OAAA;AAE/B,oFAAoF;AACpF,uEAAuE;AACvE,iFAAiF;AACjF,0DAA0D"} \ No newline at end of file diff --git a/functions/dist/notifications/push.js b/functions/dist/notifications/push.js index 3f9dd73e..189af975 100644 --- a/functions/dist/notifications/push.js +++ b/functions/dist/notifications/push.js @@ -47,9 +47,16 @@ function batchResponseToResults(batch) { * round-trip, then prune any tokens FCM reports as permanently dead. Best-effort: never throws; * returns a per-user summary. A whole-batch failure (auth/quota, not a per-token death) is logged * and does not prune anything. + * + * `excludeTokens` skips specific devices — e.g. the restore self-alert must not go to the very + * device that requested the restore ("was this you?" to the asker is noise; their partner's and + * their other devices' copies are the signal). Excluding everything yields a clean zero no-op. */ -async function sendPushToUser(db, messaging, uid, content, userData) { - const tokens = await getUserTokens(db, uid, userData); +async function sendPushToUser(db, messaging, uid, content, userData, excludeTokens) { + let tokens = await getUserTokens(db, uid, userData); + if (excludeTokens && excludeTokens.length > 0) { + tokens = tokens.filter((t) => !excludeTokens.includes(t)); + } if (tokens.length === 0) return { sent: 0, failed: 0, pruned: 0 }; const message = Object.assign(Object.assign({ tokens, notification: content.notification }, (content.data ? { data: content.data } : {})), (content.android ? { android: content.android } : {})); diff --git a/functions/dist/notifications/push.js.map b/functions/dist/notifications/push.js.map index 26ebc4cd..b398823c 100644 --- a/functions/dist/notifications/push.js.map +++ b/functions/dist/notifications/push.js.map @@ -1 +1 @@ -{"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/notifications/push.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/notifications/push.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/functions/dist/notifications/push.test.js b/functions/dist/notifications/push.test.js index ec46bdf6..d7437d0e 100644 --- a/functions/dist/notifications/push.test.js +++ b/functions/dist/notifications/push.test.js @@ -91,6 +91,42 @@ describe('batchResponseToResults', () => { }); }); describe('sendPushToUser', () => { + it('skips excluded tokens — the requesting device must not get its own "was this you?"', async () => { + const { db, deleted } = makeFakeDb({}, ['A', 'B', 'C']); + const sent = []; + const messaging = { + sendEachForMulticast: async (msg) => { + sent.push(msg.tokens); + return batch(msg.tokens.map((_) => ({ success: true, messageId: 'm' }))); + }, + }; + const result = await (0, push_1.sendPushToUser)(db, messaging, 'u1', { notification: { title: 't', body: 'b' } }, undefined, ['B']); + expect(sent).toEqual([['A', 'C']]); + expect(result).toEqual({ sent: 2, failed: 0, pruned: 0 }); + expect(deleted).toEqual([]); + }); + it('excluding every token is a clean zero no-op, never a throw', async () => { + const { db } = makeFakeDb({}, ['A']); + const messaging = { + sendEachForMulticast: async () => { + throw new Error('must not be called with zero tokens'); + }, + }; + const result = await (0, push_1.sendPushToUser)(db, messaging, 'u1', { notification: { title: 't', body: 'b' } }, undefined, ['A']); + expect(result).toEqual({ sent: 0, failed: 0, pruned: 0 }); + }); + it('an absent exclude list changes nothing', async () => { + const { db } = makeFakeDb({}, ['A', 'B']); + const sent = []; + const messaging = { + sendEachForMulticast: async (msg) => { + sent.push(msg.tokens); + return batch(msg.tokens.map(() => ({ success: true, messageId: 'm' }))); + }, + }; + await (0, push_1.sendPushToUser)(db, messaging, 'u1', { notification: { title: 't', body: 'b' } }); + expect(sent).toEqual([['A', 'B']]); + }); it('sends to every token and prunes exactly the permanently-dead ones', async () => { const { db, deleted, commits } = makeFakeDb({ fcmToken: 'LEG' }, ['LEG', 'DEAD', 'GOOD']); const messaging = makeFakeMessaging(batch([ diff --git a/functions/dist/notifications/push.test.js.map b/functions/dist/notifications/push.test.js.map index d65b8131..e6c1b688 100644 --- a/functions/dist/notifications/push.test.js.map +++ b/functions/dist/notifications/push.test.js.map @@ -1 +1 @@ -{"version":3,"file":"push.test.js","sourceRoot":"","sources":["../../src/notifications/push.test.ts"],"names":[],"mappings":";;AAAA,iCAA8E;AAC9E,+CAAgD;AAEhD,gFAAgF;AAChF,4DAA4D;AAE5D,MAAM,SAAS,GAAG,EAAE,IAAI,EAAE,6CAA6C,EAAE,CAAA;AACzE,MAAM,cAAc,GAAG,EAAE,IAAI,EAAE,8BAA8B,EAAE,CAAA;AAE/D,SAAS,UAAU,CAAC,QAAa,EAAE,SAAmB;IACpD,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,CAAA;IAC5B,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACvC,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE;QACpC,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;KAC3B,CAAC,CAAC,CAAA;IACH,MAAM,OAAO,GAAG;QACd,GAAG,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;QACzD,UAAU,EAAE,CAAC,IAAY,EAAE,EAAE;YAC3B,IAAI,IAAI,KAAK,WAAW;gBAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,EAAE,CAAC,CAAA;YAC7E,OAAO,EAAE,GAAG,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAA;QACjD,CAAC;KACF,CAAA;IACD,MAAM,EAAE,GAAQ;QACd,UAAU,EAAE,CAAC,IAAY,EAAE,EAAE;YAC3B,IAAI,IAAI,KAAK,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAA;YACtE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAA;QAC/B,CAAC;QACD,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YACZ,MAAM,EAAE,CAAC,GAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAC/C,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC;YACxC,MAAM,EAAE,KAAK,IAAI,EAAE;gBACjB,OAAO,CAAC,KAAK,EAAE,CAAA;YACjB,CAAC;SACF,CAAC;KACH,CAAA;IACD,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,CAAA;AACjC,CAAC;AAED,SAAS,KAAK,CAAC,SAAuE;IACpF,OAAO;QACL,SAAS;QACT,YAAY,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM;QACvD,YAAY,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM;KAClD,CAAA;AACV,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAa;IACtC,OAAO,EAAE,oBAAoB,EAAE,KAAK,IAAI,EAAE,CAAC,QAAQ,EAAS,CAAA;AAC9D,CAAC;AAED,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;QACxF,MAAM,EAAE,EAAE,EAAE,GAAG,UAAU,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;QACxD,MAAM,CAAC,MAAM,IAAA,oBAAa,EAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;IAC3D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,EAAE,EAAE,EAAE,GAAG,UAAU,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;QACzC,MAAM,CAAC,MAAM,IAAA,oBAAa,EAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;IAC3D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,EAAE,EAAE,EAAE,GAAG,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;QACjC,MAAM,CAAC,MAAM,IAAA,oBAAa,EAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACnD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,EAAE,EAAE,EAAE,GAAG,UAAU,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;QACpC,2EAA2E;QAC3E,EAAE,CAAC,UAAU,GAAG,CAAC,IAAY,EAAE,EAAE;YAC/B,IAAI,IAAI,KAAK,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAA;YACnD,OAAO;gBACL,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;oBACV,GAAG,EAAE,KAAK,IAAI,EAAE;wBACd,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;oBAC7C,CAAC;oBACD,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;iBACxF,CAAC;aACH,CAAA;QACH,CAAC,CAAA;QACD,MAAM,CAAC,MAAM,IAAA,oBAAa,EAAC,EAAE,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;IAC9E,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,sFAAsF,EAAE,GAAG,EAAE;QAC9F,MAAM,OAAO,GAAG,IAAA,6BAAsB,EACpC,KAAK,CAAC;YACJ,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE;YAClC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE;YACpC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE;SAC1C,CAAC,CACH,CAAA;QACD,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAA;QACnF,iFAAiF;QACjF,MAAM,CAAC,IAAA,8BAAgB,EAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IACnE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;QACzF,MAAM,SAAS,GAAG,iBAAiB,CACjC,KAAK,CAAC;YACJ,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,MAAM;YAC1C,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,OAAO;YAC7C,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,OAAO;SAC5C,CAAC,CACH,CAAA;QACD,MAAM,GAAG,GAAG,MAAM,IAAA,qBAAc,EAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAA;QAClG,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;QACtD,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;QACjC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAA;QAC9C,MAAM,SAAS,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,CAAA;QACvF,MAAM,GAAG,GAAG,MAAM,IAAA,qBAAc,EAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAA;QAClG,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;QACtD,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,EAAE,EAAE,EAAE,GAAG,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;QACjC,MAAM,SAAS,GAAG,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;QAC9C,MAAM,CAAC,MAAM,IAAA,qBAAc,EAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YACrG,IAAI,EAAE,CAAC;YACP,MAAM,EAAE,CAAC;YACT,MAAM,EAAE,CAAC;SACV,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACzE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;QAC7C,MAAM,SAAS,GAAG,EAAE,oBAAoB,EAAE,KAAK,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,EAAS,CAAA;QACjG,MAAM,GAAG,GAAG,MAAM,IAAA,qBAAc,EAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAA;QAClG,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;QACtD,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"} \ No newline at end of file +{"version":3,"file":"push.test.js","sourceRoot":"","sources":["../../src/notifications/push.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/functions/dist/notifications/queueAndPush.js b/functions/dist/notifications/queueAndPush.js new file mode 100644 index 00000000..1f6ad263 --- /dev/null +++ b/functions/dist/notifications/queueAndPush.js @@ -0,0 +1,65 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.queueAndPush = queueAndPush; +const admin = __importStar(require("firebase-admin")); +const quietHours_1 = require("./quietHours"); +const push_1 = require("./push"); +/** + * Write the durable in-app queue entry (always) and — unless quiet hours suppress it — push to every + * device. `bypassQuietHours` is for security signals (the "was this you?" self-alert class) that must + * not be silenced. + * + * Extracted from `backup/onRestoreRequested.ts` when the restore-request cleanup sweep needed the same + * behavior — one implementation, so queue-entry/quiet-hours/push semantics can't drift between callers. + */ +async function queueAndPush(db, uid, opts) { + const { type, title, body, coupleId, bypassQuietHours, channelId = 'partner_activity', excludeTokens } = opts; + await db.collection('users').doc(uid).collection('notification_queue').add({ + type, title, body, read: false, createdAt: admin.firestore.FieldValue.serverTimestamp(), + }); + const userDoc = await db.collection('users').doc(uid).get(); + if (!userDoc.exists) + return; + const userData = userDoc.data(); + if (!bypassQuietHours && (0, quietHours_1.recipientInQuietHours)(userData)) + return; + await (0, push_1.sendPushToUser)(db, admin.messaging(), uid, { + notification: { title, body }, + data: { type, couple_id: coupleId }, + android: { notification: { channelId } }, + }, userData, excludeTokens); +} +//# sourceMappingURL=queueAndPush.js.map \ No newline at end of file diff --git a/functions/dist/notifications/queueAndPush.js.map b/functions/dist/notifications/queueAndPush.js.map new file mode 100644 index 00000000..5a87dda8 --- /dev/null +++ b/functions/dist/notifications/queueAndPush.js.map @@ -0,0 +1 @@ +{"version":3,"file":"queueAndPush.js","sourceRoot":"","sources":["../../src/notifications/queueAndPush.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYA,oCAoCC;AAhDD,sDAAuC;AACvC,6CAAoD;AACpD,iCAAuC;AAEvC;;;;;;;GAOG;AACI,KAAK,UAAU,YAAY,CAChC,EAA6B,EAC7B,GAAW,EACX,IASC;IAED,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,SAAS,GAAG,kBAAkB,EAAE,aAAa,EAAE,GAAG,IAAI,CAAA;IAC7G,MAAM,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAC,GAAG,CAAC;QACzE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,eAAe,EAAE;KACxF,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAA;IAC3D,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAM;IAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;IAC/B,IAAI,CAAC,gBAAgB,IAAI,IAAA,kCAAqB,EAAC,QAAQ,CAAC;QAAE,OAAM;IAEhE,MAAM,IAAA,qBAAc,EAClB,EAAE,EACF,KAAK,CAAC,SAAS,EAAE,EACjB,GAAG,EACH;QACE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;QAC7B,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE;QACnC,OAAO,EAAE,EAAE,YAAY,EAAE,EAAE,SAAS,EAAE,EAAE;KACzC,EACD,QAAQ,EACR,aAAa,CACd,CAAA;AACH,CAAC"} \ No newline at end of file diff --git a/functions/dist/questions/assignDailyQuestion.test.js b/functions/dist/questions/assignDailyQuestion.test.js index dec4350d..785758d0 100644 --- a/functions/dist/questions/assignDailyQuestion.test.js +++ b/functions/dist/questions/assignDailyQuestion.test.js @@ -60,7 +60,7 @@ describe('wildcard-day cadence (mirrors client DailyModeResolver)', () => { if ((0, assignDailyQuestion_1.isWildcardDay)(dateStr)) wild++; } - expect(wild).toBe(36); // doy ∈ {3,13,…,363} → 36 days + expect(wild).toBe(37); // doy ∈ {3,13,…,363} → 37 days (~10.1%) }); }); //# sourceMappingURL=assignDailyQuestion.test.js.map \ No newline at end of file diff --git a/functions/dist/questions/assignDailyQuestion.test.js.map b/functions/dist/questions/assignDailyQuestion.test.js.map index 85d36bfb..0bc2d338 100644 --- a/functions/dist/questions/assignDailyQuestion.test.js.map +++ b/functions/dist/questions/assignDailyQuestion.test.js.map @@ -1 +1 @@ -{"version":3,"file":"assignDailyQuestion.test.js","sourceRoot":"","sources":["../../src/questions/assignDailyQuestion.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"assignDailyQuestion.test.js","sourceRoot":"","sources":["../../src/questions/assignDailyQuestion.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file