@aglyn/tenant-data-admin 1.0.0-beta.262 → 1.0.0-beta.263

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/weekly-summary.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AglynOrgMember } from '@aglyn/aglyn/foundation/definitions/organization.types'\nimport { brandSupportLine } from '@aglyn/aglyn/app-utils/brand-support-line'\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { writeCronBeat } from '@aglyn/aglyn/app-utils/health-report'\nimport {\n DIGEST_PREFS_FIELD,\n digestEnabled,\n INSIGHT_DIGESTS_FIELD,\n insightDigestSubscribed,\n} from '@aglyn/aglyn/app-utils/notifications'\nimport {\n checkEntitlement,\n resolveBrandingProfile,\n resolveEffectivePlan,\n} from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { platformConsoleOrigin } from '@aglyn/aglyn/app-utils/platform-brand'\nimport {\n isReleaseFlagOnForOrg,\n parseOrgReleaseFlagOverrides,\n type ReleaseFlagKey,\n} from '@aglyn/aglyn/app-utils/release-flags'\nimport {\n composeWeeklySummaryEmailText,\n composeWeeklySummarySubject,\n WEEKLY_SUMMARY_DAYS,\n WEEKLY_SUMMARY_DIGEST_KEY,\n weeklySummaryPeriod,\n type WeeklySummaryEntry,\n type WeeklySummarySite,\n} from '@aglyn/aglyn/app-utils/weekly-summary'\nimport {\n normalizePluginFigureTable,\n pluginFigureSummarySection,\n type ResolvedPluginFigureReader,\n} from '@aglyn/aglyn/plugin-manager/plugin-figures'\nimport { rateLimitedRetryAtMs, sendEmail } from '@aglyn/shared-util-email'\nimport { withoutMailWithheldAccounts } from './account-mail'\nimport { findUserByUidAcrossPools } from './auth-pools'\nimport { isCronAuthorized, isCronDryRun } from './cron-auth'\nimport { meterPlatformEmail } from './email-metering'\nimport { filterSuppressedEmails } from './email-suppression'\nimport { eligibleFigureReaders, type FigureReaderContext } from './figure-readers'\nimport firebaseAdmin from './firebase-admin'\nimport { listOrgMembers } from './organizations'\nimport { getServerReleaseFlagValues } from './release-flags'\nimport {\n loadSystemEmail,\n renderLoadedSystemEmail,\n systemEmailBrand,\n systemEmailContent,\n type LoadedSystemEmail,\n} from './render-system-email'\n\n/**\n * THE WEEKLY SUMMARY'S SENDS (AGL-3867): `POST /api/admin/weekly-summary`,\n * Mondays at 13:00 UTC (08:00 Central) from `scheduled-crons.yml`.\n *\n * ## Who is told\n *\n * A workspace's owners and admins: the people who reach every site and every\n * figure, so one set of figures per workspace is true for each of them. The\n * **Weekly summary** switch on their own account\n * (`users/{uid}.digestPrefs.weeklySummary`, on until turned off) governs it.\n * A locked or disabled account, a suppressed address and a suspended member\n * are not mailed.\n *\n * ## What it reads\n *\n * The workspace's busiest `WEEKLY_SUMMARY_MAX_SITES` sites by last week's\n * page views, each through every site reader that declares a\n * `weeklySummary`, and the workspace through every workspace reader that\n * does. Each reader is offered only where its plan feature is included and\n * its plugin runs (`eligibleFigureReaders`), read over the seven whole days\n * before the run, and worded by its owner. A reader that fails is left out;\n * the rest are still read.\n *\n * ## Once a week, and resumable\n *\n * `orgs/{orgId}/weeklySummaries/{week}` records each person sent, stamped\n * after each send, so a run that dies mid-workspace re-sends only to those it\n * never reached and a second run in the same week sends nothing. Workspaces\n * are swept in id order behind a cursor, the `done: false` loop the workflow\n * already runs for the CRM digest.\n *\n * The email is a `bulk` send under the platform send-rate governor: a refusal\n * stops the run and answers `done: true`, and the unsent are picked up by a\n * manual re-run in the same week. Metered as PLATFORM mail, not the\n * workspace's: it is the platform reporting to the people who run it.\n */\n\ntype Firestore = FirebaseFirestore.Firestore\ntype Snapshot = FirebaseFirestore.QueryDocumentSnapshot\n\n/** The beat `/api/health/crons` reads, once this job joins its inventory. */\nexport const WEEKLY_SUMMARY_JOB_ID = 'weekly-summary'\n\n/** `orgs/{orgId}/weeklySummaries/{week}` — server-written, matched by no rule. */\nexport const WEEKLY_SUMMARY_MARKERS = 'weeklySummaries'\n\n/** Workspaces per invocation; a chunk that times out never advances its cursor. */\nexport const WEEKLY_SUMMARY_ORG_CHUNK = 20\n\n/** Sites a workspace's sweep ranks, by document id. */\nexport const WEEKLY_SUMMARY_HOST_READ_LIMIT = 50\n\n/** Sites one summary reports on: the busiest by last week's page views. */\nexport const WEEKLY_SUMMARY_MAX_SITES = 5\n\n/** Members per workspace whose address is chased through the auth pools when their member document has none. */\nexport const WEEKLY_SUMMARY_MAX_UID_LOOKUPS = 5\n\n/** How long one call works before it stops and leaves the rest to the next. */\nexport const WEEKLY_SUMMARY_RUN_BUDGET_MS = 45_000\n\nexport interface WeeklySummarySendResult {\n sent: boolean\n /** The platform's send rate refused it for now. */\n rateLimited: boolean\n reason?: string\n}\n\nexport interface WeeklySummaryEmail {\n to: string\n subject: string\n /** The body token: the figures without the closing line the footer carries. */\n body: string\n /** The plain fallback, closing line included. */\n text: string\n org: Record<string, unknown>\n merge: Record<string, string>\n}\n\n/** Everything the sweep reaches beyond Firestore, so a spec can hold each one. */\nexport interface WeeklySummaryDeps {\n firestore: Firestore\n runAt: Date\n dryRun: boolean\n /** The console's absolute origin, or `''` when none is configured. */\n origin: string\n /** Whether a release flag is on for an org. */\n flagOn: (flag: ReleaseFlagKey, orgId: string, org: Record<string, unknown>) => boolean\n orgMembers: (orgId: string) => Promise<AglynOrgMember[]>\n /** The uids whose accounts may be mailed. */\n reachable: (uids: string[]) => Promise<string[]>\n /** The readers that declare a `weeklySummary` and pass the gates here. */\n readers: (context: Omit<FigureReaderContext, 'accept'>) => Promise<ResolvedPluginFigureReader[]>\n /** The member's deliverable address, or `null` when there is none or it is suppressed. */\n address: (member: AglynOrgMember, lookups: { left: number }) => Promise<string | null>\n send: (email: WeeklySummaryEmail) => Promise<WeeklySummarySendResult>\n budgetMs?: number\n}\n\nexport interface WeeklySummaryOrgReport {\n skipped?: string\n sites: number\n recipients: number\n sent: number\n failed?: number\n /** Why the workspace stopped short: the call's time ran out, or the send rate refused. */\n deferred?: 'time' | 'rate'\n}\n\nexport interface WeeklySummaryReport {\n week: string\n dryRun: boolean\n swept: number\n sent: number\n orgs: Record<string, WeeklySummaryOrgReport>\n deferred: false | 'time' | 'rate'\n nextCursor: string | null\n done: boolean\n}\n\nconst str = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\nfunction siteName(data: Record<string, unknown>, id: string): string {\n return str(data['displayName']) || str(data['name']) || str(data['subdomain']) || id\n}\n\n/** Last week's page views for each site, read from the daily analytics documents. */\nasync function weekViews(\n firestore: Firestore,\n hosts: readonly Snapshot[],\n days: readonly string[],\n): Promise<Map<string, number>> {\n const views = new Map<string, number>()\n if (!hosts.length) return views\n const refs = hosts.flatMap((host) => days.map((day) => host.ref.collection('analytics').doc(day)))\n const docs = await firestore.getAll(...refs)\n for (const doc of docs) {\n const hostId = doc.ref.parent.parent?.id ?? ''\n views.set(hostId, (views.get(hostId) ?? 0) + (Number(doc.get('total') ?? 0) || 0))\n }\n return views\n}\n\nfunction weekDays(from: string): string[] {\n const start = Date.parse(`${from}T00:00:00Z`)\n return Array.from({ length: WEEKLY_SUMMARY_DAYS }, (_, index) =>\n new Date(start + index * 86_400_000).toISOString().slice(0, 10),\n )\n}\n\n/**\n * Each reader's entry, in its declared order: read over the week, then worded\n * by its owner. A reader that fails, answers no table or cannot be read over\n * seven days is left out.\n */\nasync function readEntries(\n readers: readonly ResolvedPluginFigureReader[],\n base: { orgId: string; hostId: string | null; now: Date },\n): Promise<WeeklySummaryEntry[]> {\n const ordered = readers\n .filter(({ reader }) => reader.weeklySummary)\n .sort(\n (a, b) =>\n (a.reader.weeklySummary?.order ?? 0) - (b.reader.weeklySummary?.order ?? 0) ||\n a.reader.id.localeCompare(b.reader.id),\n )\n const entries: WeeklySummaryEntry[] = []\n for (const { reader } of ordered) {\n const summary = reader.weeklySummary\n if (!summary) continue\n const days = reader.windows.length ? (reader.windows.includes(WEEKLY_SUMMARY_DAYS) ? WEEKLY_SUMMARY_DAYS : null) : 0\n if (days === null) continue\n try {\n const read = await reader.read({ ...base, days, uid: null, params: {} })\n if (read.ok === false) continue\n const table = normalizePluginFigureTable(read.table)\n if (!table) continue\n entries.push({ lines: summary.lines(table), headline: summary.headline?.(table) ?? null })\n } catch (error) {\n console.error('[weekly-summary] reader failed', { reader: reader.id, orgId: base.orgId, error })\n }\n }\n return entries\n}\n\nasync function summarizeOrg(\n deps: WeeklySummaryDeps,\n orgDoc: Snapshot,\n period: ReturnType<typeof weeklySummaryPeriod>,\n overBudget: () => boolean,\n): Promise<WeeklySummaryOrgReport> {\n const { firestore } = deps\n const orgId = orgDoc.id\n const org = (orgDoc.data() ?? {}) as Record<string, unknown>\n const report: WeeklySummaryOrgReport = { sites: 0, recipients: 0, sent: 0 }\n\n const markerRef = orgDoc.ref.collection(WEEKLY_SUMMARY_MARKERS).doc(period.week)\n const sentAlready = ((await markerRef.get()).get('sent') ?? {}) as Record<string, unknown>\n\n // Owners and admins, not suspended, whose accounts may be mailed, who kept the switch on.\n const managers = (await deps.orgMembers(orgId)).filter(\n (member) => (member.role === 'owner' || member.role === 'admin') && member.orgSuspended !== true,\n )\n const reachable = new Set(await deps.reachable(managers.map((member) => member.$id)))\n const candidates = managers\n .filter((member) => reachable.has(member.$id) && !sentAlready[member.$id])\n .sort((a, b) => a.$id.localeCompare(b.$id))\n if (!candidates.length) return { ...report, skipped: managers.length ? 'nobody-left' : 'no-managers' }\n const userDocs = await firestore.getAll(\n ...candidates.map((member) => firestore.collection('users').doc(member.$id)),\n )\n const users = new Map(userDocs.map((doc) => [doc.id, doc]))\n const recipients = candidates.filter((member) =>\n digestEnabled(users.get(member.$id)?.get(DIGEST_PREFS_FIELD), WEEKLY_SUMMARY_DIGEST_KEY),\n )\n report.recipients = recipients.length\n if (!recipients.length) return { ...report, skipped: 'summary-off' }\n\n // The busiest sites by last week's page views, ties by id.\n const hostPage = await firestore\n .collection('hosts')\n .where('orgId', '==', orgId)\n .orderBy(firebaseAdmin.firestore.FieldPath.documentId())\n .limit(WEEKLY_SUMMARY_HOST_READ_LIMIT)\n .get()\n const hosts = hostPage.docs.filter((doc) => !doc.get('deletedAt'))\n const views = await weekViews(firestore, hosts, weekDays(period.from))\n const chosen = [...hosts]\n .sort((a, b) => (views.get(b.id) ?? 0) - (views.get(a.id) ?? 0) || a.id.localeCompare(b.id))\n .slice(0, WEEKLY_SUMMARY_MAX_SITES)\n\n const orgSlug = str(org['slug'])\n const sites: WeeklySummarySite[] = []\n for (const host of chosen) {\n const data = (host.data() ?? {}) as Record<string, unknown>\n const readers = (await deps.readers({ orgId, hostId: host.id, org, host: data })).filter(\n ({ reader }) => pluginFigureSummarySection(reader) === 'site',\n )\n const entries = await readEntries(readers, { orgId, hostId: host.id, now: period.now })\n const subdomain = str(data['subdomain'])\n sites.push({\n name: siteName(data, host.id),\n url:\n deps.origin && orgSlug && subdomain\n ? `${deps.origin}${buildRoute(Route.HOST_DASHBOARD, { orgSlug, host: subdomain })}`\n : null,\n entries,\n })\n }\n const orgReaders = await deps.readers({ orgId, hostId: null, org, host: null })\n const workspace = await readEntries(\n orgReaders.filter(({ reader }) => pluginFigureSummarySection(reader) === 'workspace'),\n { orgId, hostId: null, now: period.now },\n )\n\n const branding = resolveBrandingProfile(org as never)\n const workspaceName = str(org['name']) || orgSlug || 'your workspace'\n const settingsUrl = `${deps.origin}${buildRoute(Route.MANAGE_NOTIFICATION_SETTINGS)}`\n // AI weekly insights, pointed to only where the plan includes them and they are released.\n const offersInsights =\n checkEntitlement(org as never, 'aiGenerative') && deps.flagOn('release_ai_generative', orgId, org)\n const busiest = chosen[0]\n const analyticsUrl =\n offersInsights && deps.origin && orgSlug && busiest && str(busiest.get('subdomain'))\n ? `${deps.origin}${buildRoute(Route.HOST_ANALYTICS, { orgSlug, host: str(busiest.get('subdomain')) })}`\n : null\n const supportLine = brandSupportLine(branding)\n const subject = composeWeeklySummarySubject(workspaceName, sites)\n const input = {\n workspaceName,\n productName: branding.productName,\n periodLabel: period.label,\n sites,\n workspace,\n settingsUrl,\n }\n report.sites = sites.length\n const lookups = { left: WEEKLY_SUMMARY_MAX_UID_LOOKUPS }\n\n if (!composeWeeklySummaryEmailText({ ...input, aiInsightsUrl: null })) {\n return { ...report, skipped: 'nothing-to-report' }\n }\n\n for (const member of recipients) {\n if (overBudget()) return { ...report, deferred: 'time' }\n const subscribed = insightDigestSubscribed(users.get(member.$id)?.get(INSIGHT_DIGESTS_FIELD), orgId)\n const aiInsightsUrl = analyticsUrl && !subscribed ? analyticsUrl : null\n const body = composeWeeklySummaryEmailText({ ...input, aiInsightsUrl, omitClosing: true }) ?? ''\n if (deps.dryRun) {\n report.sent += 1\n continue\n }\n const to = await deps.address(member, lookups)\n if (!to) continue\n const text = [composeWeeklySummaryEmailText({ ...input, aiInsightsUrl }) ?? body, supportLine]\n .filter(Boolean)\n .join('\\n\\n')\n const result = await deps.send({\n to,\n subject,\n body,\n text,\n org,\n merge: {\n 'summary.subject': subject,\n 'org.name': workspaceName,\n 'summary.period': period.label,\n 'summary.body': body,\n settingsUrl,\n },\n })\n if (result.sent) {\n report.sent += 1\n await markerRef.set(\n {\n week: period.week,\n sent: { [member.$id]: true },\n updatedAt: firebaseAdmin.firestore.FieldValue.serverTimestamp(),\n },\n { merge: true },\n )\n } else if (result.rateLimited) {\n return { ...report, deferred: 'rate' }\n } else {\n report.failed = (report.failed ?? 0) + 1\n }\n }\n return report\n}\n\n/** One chunk of the sweep: workspaces in id order after `cursor`. */\nexport async function runWeeklySummarySweep(\n deps: WeeklySummaryDeps,\n options: { cursor?: string | null; limit?: number } = {},\n): Promise<WeeklySummaryReport> {\n const { firestore } = deps\n const startedAt = Date.now()\n const budget = deps.budgetMs ?? WEEKLY_SUMMARY_RUN_BUDGET_MS\n const overBudget = () => Date.now() - startedAt > budget\n const period = weeklySummaryPeriod(deps.runAt)\n const pageSize = Math.max(1, Math.min(Math.floor(options.limit ?? WEEKLY_SUMMARY_ORG_CHUNK), WEEKLY_SUMMARY_ORG_CHUNK))\n let query = firestore\n .collection('orgs')\n .orderBy(firebaseAdmin.firestore.FieldPath.documentId())\n .limit(pageSize + 1)\n if (options.cursor) query = query.startAfter(firestore.collection('orgs').doc(options.cursor))\n const page = await query.get()\n const hasMore = page.docs.length > pageSize\n const orgDocs = hasMore ? page.docs.slice(0, pageSize) : page.docs\n\n const report: WeeklySummaryReport = {\n week: period.week,\n dryRun: deps.dryRun,\n swept: 0,\n sent: 0,\n orgs: {},\n deferred: false,\n nextCursor: null,\n done: true,\n }\n let last: string | null = null\n for (const orgDoc of orgDocs) {\n if (overBudget()) {\n report.deferred = 'time'\n break\n }\n try {\n const orgReport = await summarizeOrg(deps, orgDoc, period, overBudget)\n report.orgs[orgDoc.id] = orgReport\n report.sent += orgReport.sent\n if (orgReport.deferred) {\n report.deferred = orgReport.deferred\n break\n }\n } catch (error) {\n console.error('[weekly-summary] workspace failed', orgDoc.id, error)\n report.orgs[orgDoc.id] = { skipped: 'failed', sites: 0, recipients: 0, sent: 0 }\n }\n report.swept += 1\n last = orgDoc.id\n }\n /*\n * Out of time: the next call starts after the last workspace that finished,\n * and the marker skips whoever the unfinished one already reached. Refused\n * by the send rate: the window is full, so the run stops (`done: true`)\n * rather than re-POSTing into it; a re-run in the same week reaches the rest.\n */\n if (report.deferred === 'time') {\n report.nextCursor = last ?? options.cursor ?? null\n report.done = false\n } else if (report.deferred === 'rate') {\n report.done = true\n } else {\n report.nextCursor = hasMore ? last : null\n report.done = report.nextCursor === null\n }\n return report\n}\n\n/** The sweep's reach into the real platform: Firestore, the flags, the roster, the mail. */\nexport async function weeklySummaryDeps(options: { runAt: Date; dryRun: boolean }): Promise<WeeklySummaryDeps> {\n const firestore = firebaseAdmin.app().firestore()\n const flags = await getServerReleaseFlagValues()\n // The designed email is read once per call and rendered per recipient.\n let loaded: Promise<LoadedSystemEmail | null> | undefined\n return {\n firestore,\n runAt: options.runAt,\n dryRun: options.dryRun,\n origin: platformConsoleOrigin(),\n flagOn: (flag, orgId, org) =>\n isReleaseFlagOnForOrg(\n flag,\n (flags as Record<string, never>)[flag],\n orgId,\n parseOrgReleaseFlagOverrides(org['releaseFlags']),\n resolveEffectivePlan(org as never),\n ),\n orgMembers: (orgId) => listOrgMembers(orgId),\n reachable: (uids) => withoutMailWithheldAccounts(uids),\n readers: (context) => eligibleFigureReaders({ ...context, accept: (reader) => Boolean(reader.weeklySummary) }),\n address: async (member, lookups) => {\n let address = str(member.email).toLowerCase()\n if (!address.includes('@') && lookups.left > 0) {\n lookups.left -= 1\n const pooled = await findUserByUidAcrossPools(member.$id).catch(() => null)\n address = str(pooled?.record?.email).toLowerCase()\n }\n if (!address.includes('@')) return null\n const [kept] = await filterSuppressedEmails([address], firestore)\n return kept ?? null\n },\n send: async (email) => {\n loaded ??= loadSystemEmail('weekly-summary').catch(() => null)\n const template = await loaded\n const brand = systemEmailBrand(email.org as never)\n const rendered = template\n ? renderLoadedSystemEmail(template, { ...brand.merge, ...email.merge }, brand.options)\n : null\n const result = await sendEmail({\n to: [email.to],\n ...systemEmailContent(rendered, { subject: email.subject, text: email.text }),\n fromName: resolveBrandingProfile(email.org as never).fromName,\n context: 'weekly-summary',\n priority: 'bulk',\n })\n if (result.sent) {\n await meterPlatformEmail().catch(() => undefined)\n return { sent: true, rateLimited: false }\n }\n return {\n sent: false,\n rateLimited: rateLimitedRetryAtMs(result) !== null,\n reason: (result as { reason?: string }).reason ?? 'failed',\n }\n },\n }\n}\n\n// lockdown-423: exempt — server-internal cron (x-cron-secret), no user caller; it reads each workspace's figures to report them to its owners and admins and writes nothing a locked org could lose.\n\n/**\n * `POST /api/admin/weekly-summary` sends; a GET, or `{\"dryRun\": true}`,\n * reports who would be sent what and sends nothing. The body may carry the\n * `cursor` a previous chunk answered and a smaller `limit`.\n */\nexport async function weeklySummaryRoute(request: Request): Promise<Response> {\n const method = request.method.toUpperCase()\n if (method !== 'POST' && method !== 'GET') {\n return Response.json({ error: 'Method not allowed' }, { status: 405, headers: { Allow: 'GET, POST' } })\n }\n if (!process.env['CRON_SECRET']) {\n return Response.json({ error: 'The weekly summary is not configured (CRON_SECRET).' }, { status: 501 })\n }\n const headers = Object.fromEntries(request.headers.entries()) as Partial<Record<string, string>>\n if (!isCronAuthorized(headers)) return Response.json({ error: 'Unauthenticated' }, { status: 401 })\n const body = method === 'POST' ? ((await request.json().catch(() => null)) as Record<string, unknown> | null) : null\n const query = Object.fromEntries(new URL(request.url).searchParams.entries())\n const dryRun = isCronDryRun({ method, body: body ?? undefined, query })\n // Stamped on the invocation, not on the work (AGL-1955), so the beat is\n // already there the day this job joins `SCHEDULED_JOBS`.\n if (method === 'POST') {\n try {\n await writeCronBeat(firebaseAdmin.app().firestore(), WEEKLY_SUMMARY_JOB_ID)\n } catch {\n // A job that stops stamping reds its own row; it never fails the run.\n }\n }\n try {\n const rawCursor = body?.['cursor']\n const cursor =\n typeof rawCursor === 'string' && rawCursor.length > 0 && !rawCursor.includes('/') ? rawCursor : null\n const limit = Number(body?.['limit'])\n const report = await runWeeklySummarySweep(await weeklySummaryDeps({ runAt: new Date(), dryRun }), {\n cursor,\n ...(Number.isFinite(limit) && limit > 0 ? { limit } : {}),\n })\n return Response.json(report, { headers: { 'Cache-Control': 'no-store' } })\n } catch (error) {\n console.error('[weekly-summary] run failed', error)\n return Response.json({ error: 'Weekly summary run failed' }, { status: 500 })\n }\n}\n"],"names":["brandSupportLine","buildRoute","Route","writeCronBeat","DIGEST_PREFS_FIELD","digestEnabled","INSIGHT_DIGESTS_FIELD","insightDigestSubscribed","checkEntitlement","resolveBrandingProfile","resolveEffectivePlan","platformConsoleOrigin","isReleaseFlagOnForOrg","parseOrgReleaseFlagOverrides","composeWeeklySummaryEmailText","composeWeeklySummarySubject","WEEKLY_SUMMARY_DAYS","WEEKLY_SUMMARY_DIGEST_KEY","weeklySummaryPeriod","normalizePluginFigureTable","pluginFigureSummarySection","rateLimitedRetryAtMs","sendEmail","withoutMailWithheldAccounts","findUserByUidAcrossPools","isCronAuthorized","isCronDryRun","meterPlatformEmail","filterSuppressedEmails","eligibleFigureReaders","firebaseAdmin","listOrgMembers","getServerReleaseFlagValues","loadSystemEmail","renderLoadedSystemEmail","systemEmailBrand","systemEmailContent","WEEKLY_SUMMARY_JOB_ID","WEEKLY_SUMMARY_MARKERS","WEEKLY_SUMMARY_ORG_CHUNK","WEEKLY_SUMMARY_HOST_READ_LIMIT","WEEKLY_SUMMARY_MAX_SITES","WEEKLY_SUMMARY_MAX_UID_LOOKUPS","WEEKLY_SUMMARY_RUN_BUDGET_MS","str","value","trim","siteName","data","id","weekViews","firestore","hosts","days","views","Map","length","refs","flatMap","host","map","day","ref","collection","doc","docs","getAll","hostId","parent","set","get","Number","weekDays","from","start","Date","parse","Array","_","index","toISOString","slice","readEntries","readers","base","ordered","filter","reader","weeklySummary","sort","a","b","order","localeCompare","entries","summary","windows","includes","read","uid","params","ok","table","push","lines","headline","error","console","orgId","summarizeOrg","deps","orgDoc","period","overBudget","org","report","sites","recipients","sent","markerRef","week","sentAlready","managers","orgMembers","member","role","orgSuspended","reachable","Set","$id","candidates","has","skipped","userDocs","users","hostPage","where","orderBy","FieldPath","documentId","limit","chosen","orgSlug","now","subdomain","name","url","origin","HOST_DASHBOARD","orgReaders","workspace","branding","workspaceName","settingsUrl","MANAGE_NOTIFICATION_SETTINGS","offersInsights","flagOn","busiest","analyticsUrl","HOST_ANALYTICS","supportLine","subject","input","productName","periodLabel","label","lookups","left","aiInsightsUrl","deferred","subscribed","body","omitClosing","dryRun","to","address","text","Boolean","join","result","send","merge","updatedAt","FieldValue","serverTimestamp","rateLimited","failed","runWeeklySummarySweep","options","startedAt","budget","budgetMs","runAt","pageSize","Math","max","min","floor","query","cursor","startAfter","page","hasMore","orgDocs","swept","orgs","nextCursor","done","last","orgReport","weeklySummaryDeps","app","flags","loaded","flag","uids","context","accept","email","toLowerCase","pooled","catch","record","kept","template","brand","rendered","fromName","priority","undefined","reason","weeklySummaryRoute","request","method","toUpperCase","Response","json","status","headers","Allow","process","env","Object","fromEntries","URL","searchParams","rawCursor","isFinite"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SAASA,gBAAgB,QAAQ,4CAA2C;AAC5E,SAASC,UAAU,EAAEC,KAAK,QAAQ,wCAAuC;AACzE,SAASC,aAAa,QAAQ,uCAAsC;AACpE,SACEC,kBAAkB,EAClBC,aAAa,EACbC,qBAAqB,EACrBC,uBAAuB,QAClB,uCAAsC;AAC7C,SACEC,gBAAgB,EAChBC,sBAAsB,EACtBC,oBAAoB,QACf,2CAA0C;AACjD,SAASC,qBAAqB,QAAQ,wCAAuC;AAC7E,SACEC,qBAAqB,EACrBC,4BAA4B,QAEvB,uCAAsC;AAC7C,SACEC,6BAA6B,EAC7BC,2BAA2B,EAC3BC,mBAAmB,EACnBC,yBAAyB,EACzBC,mBAAmB,QAGd,wCAAuC;AAC9C,SACEC,0BAA0B,EAC1BC,0BAA0B,QAErB,6CAA4C;AACnD,SAASC,oBAAoB,EAAEC,SAAS,QAAQ,2BAA0B;AAC1E,SAASC,2BAA2B,QAAQ,oBAAgB;AAC5D,SAASC,wBAAwB,QAAQ,kBAAc;AACvD,SAASC,gBAAgB,EAAEC,YAAY,QAAQ,iBAAa;AAC5D,SAASC,kBAAkB,QAAQ,sBAAkB;AACrD,SAASC,sBAAsB,QAAQ,yBAAqB;AAC5D,SAASC,qBAAqB,QAAkC,sBAAkB;AAClF,OAAOC,mBAAmB,sBAAkB;AAC5C,SAASC,cAAc,QAAQ,qBAAiB;AAChD,SAASC,0BAA0B,QAAQ,qBAAiB;AAC5D,SACEC,eAAe,EACfC,uBAAuB,EACvBC,gBAAgB,EAChBC,kBAAkB,QAEb,2BAAuB;AA0C9B,2EAA2E,GAC3E,OAAO,MAAMC,wBAAwB,iBAAgB;AAErD,gFAAgF,GAChF,OAAO,MAAMC,yBAAyB,kBAAiB;AAEvD,iFAAiF,GACjF,OAAO,MAAMC,2BAA2B,GAAE;AAE1C,qDAAqD,GACrD,OAAO,MAAMC,iCAAiC,GAAE;AAEhD,yEAAyE,GACzE,OAAO,MAAMC,2BAA2B,EAAC;AAEzC,8GAA8G,GAC9G,OAAO,MAAMC,iCAAiC,EAAC;AAE/C,6EAA6E,GAC7E,OAAO,MAAMC,+BAA+B,MAAM;AA6DlD,MAAMC,MAAM,CAACC,QAA4B,OAAOA,UAAU,WAAWA,MAAMC,IAAI,KAAK;AAEpF,SAASC,SAASC,IAA6B,EAAEC,EAAU;IACzD,OAAOL,IAAII,IAAI,CAAC,cAAc,KAAKJ,IAAII,IAAI,CAAC,OAAO,KAAKJ,IAAII,IAAI,CAAC,YAAY,KAAKC;AACpF;AAEA,mFAAmF,GACnF,eAAeC,UACbC,SAAoB,EACpBC,KAA0B,EAC1BC,IAAuB;IAEvB,MAAMC,QAAQ,IAAIC;IAClB,IAAI,CAACH,MAAMI,MAAM,EAAE,OAAOF;IAC1B,MAAMG,OAAOL,MAAMM,OAAO,CAAC,CAACC,OAASN,KAAKO,GAAG,CAAC,CAACC,MAAQF,KAAKG,GAAG,CAACC,UAAU,CAAC,aAAaC,GAAG,CAACH;IAC5F,MAAMI,OAAO,MAAMd,UAAUe,MAAM,IAAIT;IACvC,KAAK,MAAMO,OAAOC,KAAM;kBAEHX,YAAkCU;YADtCA;QAAf,MAAMG,kBAASH,yBAAAA,IAAIF,GAAG,CAACM,MAAM,CAACA,MAAM,qBAArBJ,uBAAuBf,EAAE,mBAAI;QAC5CK,MAAMe,GAAG,CAACF,QAAQ,EAACb,aAAAA,MAAMgB,GAAG,CAACH,mBAAVb,aAAqB,KAAMiB,CAAAA,QAAOP,WAAAA,IAAIM,GAAG,CAAC,oBAARN,WAAoB,MAAM,CAAA;IACjF;IACA,OAAOV;AACT;AAEA,SAASkB,SAASC,IAAY;IAC5B,MAAMC,QAAQC,KAAKC,KAAK,CAAC,GAAGH,KAAK,UAAU,CAAC;IAC5C,OAAOI,MAAMJ,IAAI,CAAC;QAAEjB,QAAQxC;IAAoB,GAAG,CAAC8D,GAAGC,QACrD,IAAIJ,KAAKD,QAAQK,QAAQ,UAAYC,WAAW,GAAGC,KAAK,CAAC,GAAG;AAEhE;AAEA;;;;CAIC,GACD,eAAeC,YACbC,OAA8C,EAC9CC,IAAyD;IAEzD,MAAMC,UAAUF,QACbG,MAAM,CAAC,CAAC,EAAEC,MAAM,EAAE,GAAKA,OAAOC,aAAa,EAC3CC,IAAI,CACH,CAACC,GAAGC;;YACDD,yBAAuCC;eAAxC,UAACD,0BAAAA,EAAEH,MAAM,CAACC,aAAa,qBAAtBE,wBAAwBE,KAAK,mBAAI,gBAAMD,0BAAAA,EAAEJ,MAAM,CAACC,aAAa,qBAAtBG,wBAAwBC,KAAK,oBAAI,MACzEF,EAAEH,MAAM,CAACtC,EAAE,CAAC4C,aAAa,CAACF,EAAEJ,MAAM,CAACtC,EAAE;;IAE3C,MAAM6C,UAAgC,EAAE;IACxC,KAAK,MAAM,EAAEP,MAAM,EAAE,IAAIF,QAAS;QAChC,MAAMU,UAAUR,OAAOC,aAAa;QACpC,IAAI,CAACO,SAAS;QACd,MAAM1C,OAAOkC,OAAOS,OAAO,CAACxC,MAAM,GAAI+B,OAAOS,OAAO,CAACC,QAAQ,CAACjF,uBAAuBA,sBAAsB,OAAQ;QACnH,IAAIqC,SAAS,MAAM;QACnB,IAAI;;YACF,MAAM6C,OAAO,MAAMX,OAAOW,IAAI,CAAC,aAAKd;gBAAM/B;gBAAM8C,KAAK;gBAAMC,QAAQ,CAAC;;YACpE,IAAIF,KAAKG,EAAE,KAAK,OAAO;YACvB,MAAMC,QAAQnF,2BAA2B+E,KAAKI,KAAK;YACnD,IAAI,CAACA,OAAO;YACZR,QAAQS,IAAI,CAAC;gBAAEC,OAAOT,QAAQS,KAAK,CAACF;gBAAQG,QAAQ,UAAEV,QAAQU,QAAQ,oBAAhBV,QAAQU,QAAQ,MAAhBV,SAAmBO,yBAAU;YAAK;QAC1F,EAAE,OAAOI,OAAO;YACdC,QAAQD,KAAK,CAAC,kCAAkC;gBAAEnB,QAAQA,OAAOtC,EAAE;gBAAE2D,OAAOxB,KAAKwB,KAAK;gBAAEF;YAAM;QAChG;IACF;IACA,OAAOZ;AACT;AAEA,eAAee,aACbC,IAAuB,EACvBC,MAAgB,EAChBC,MAA8C,EAC9CC,UAAyB;QAIZF,cAIQ;IANrB,MAAM,EAAE5D,SAAS,EAAE,GAAG2D;IACtB,MAAMF,QAAQG,OAAO9D,EAAE;IACvB,MAAMiE,OAAOH,eAAAA,OAAO/D,IAAI,cAAX+D,eAAiB,CAAC;IAC/B,MAAMI,SAAiC;QAAEC,OAAO;QAAGC,YAAY;QAAGC,MAAM;IAAE;IAE1E,MAAMC,YAAYR,OAAOjD,GAAG,CAACC,UAAU,CAACzB,wBAAwB0B,GAAG,CAACgD,OAAOQ,IAAI;IAC/E,MAAMC,eAAe,OAAA,AAAC,CAAA,MAAMF,UAAUjD,GAAG,EAAC,EAAGA,GAAG,CAAC,mBAA5B,OAAuC,CAAC;IAE7D,0FAA0F;IAC1F,MAAMoD,WAAW,AAAC,CAAA,MAAMZ,KAAKa,UAAU,CAACf,MAAK,EAAGtB,MAAM,CACpD,CAACsC,SAAW,AAACA,CAAAA,OAAOC,IAAI,KAAK,WAAWD,OAAOC,IAAI,KAAK,OAAM,KAAMD,OAAOE,YAAY,KAAK;IAE9F,MAAMC,YAAY,IAAIC,IAAI,MAAMlB,KAAKiB,SAAS,CAACL,SAAS9D,GAAG,CAAC,CAACgE,SAAWA,OAAOK,GAAG;IAClF,MAAMC,aAAaR,SAChBpC,MAAM,CAAC,CAACsC,SAAWG,UAAUI,GAAG,CAACP,OAAOK,GAAG,KAAK,CAACR,WAAW,CAACG,OAAOK,GAAG,CAAC,EACxExC,IAAI,CAAC,CAACC,GAAGC,IAAMD,EAAEuC,GAAG,CAACpC,aAAa,CAACF,EAAEsC,GAAG;IAC3C,IAAI,CAACC,WAAW1E,MAAM,EAAE,OAAO,aAAK2D;QAAQiB,SAASV,SAASlE,MAAM,GAAG,gBAAgB;;IACvF,MAAM6E,WAAW,MAAMlF,UAAUe,MAAM,IAClCgE,WAAWtE,GAAG,CAAC,CAACgE,SAAWzE,UAAUY,UAAU,CAAC,SAASC,GAAG,CAAC4D,OAAOK,GAAG;IAE5E,MAAMK,QAAQ,IAAI/E,IAAI8E,SAASzE,GAAG,CAAC,CAACI,MAAQ;YAACA,IAAIf,EAAE;YAAEe;SAAI;IACzD,MAAMqD,aAAaa,WAAW5C,MAAM,CAAC,CAACsC;YACtBU;eAAdjI,eAAciI,aAAAA,MAAMhE,GAAG,CAACsD,OAAOK,GAAG,sBAApBK,WAAuBhE,GAAG,CAAClE,qBAAqBa;;IAEhEkG,OAAOE,UAAU,GAAGA,WAAW7D,MAAM;IACrC,IAAI,CAAC6D,WAAW7D,MAAM,EAAE,OAAO,aAAK2D;QAAQiB,SAAS;;IAErD,2DAA2D;IAC3D,MAAMG,WAAW,MAAMpF,UACpBY,UAAU,CAAC,SACXyE,KAAK,CAAC,SAAS,MAAM5B,OACrB6B,OAAO,CAAC3G,cAAcqB,SAAS,CAACuF,SAAS,CAACC,UAAU,IACpDC,KAAK,CAACpG,gCACN8B,GAAG;IACN,MAAMlB,QAAQmF,SAAStE,IAAI,CAACqB,MAAM,CAAC,CAACtB,MAAQ,CAACA,IAAIM,GAAG,CAAC;IACrD,MAAMhB,QAAQ,MAAMJ,UAAUC,WAAWC,OAAOoB,SAASwC,OAAOvC,IAAI;IACpE,MAAMoE,SAAS;WAAIzF;KAAM,CACtBqC,IAAI,CAAC,CAACC,GAAGC;YAAOrC,YAAyBA;eAA1B,EAACA,aAAAA,MAAMgB,GAAG,CAACqB,EAAE1C,EAAE,aAAdK,aAAmB,OAAMA,cAAAA,MAAMgB,GAAG,CAACoB,EAAEzC,EAAE,aAAdK,cAAmB,MAAMoC,EAAEzC,EAAE,CAAC4C,aAAa,CAACF,EAAE1C,EAAE;OACzFgC,KAAK,CAAC,GAAGxC;IAEZ,MAAMqG,UAAUlG,IAAIsE,GAAG,CAAC,OAAO;IAC/B,MAAME,QAA6B,EAAE;IACrC,KAAK,MAAMzD,QAAQkF,OAAQ;YACXlF;QAAd,MAAMX,QAAQW,aAAAA,KAAKX,IAAI,cAATW,aAAe,CAAC;QAC9B,MAAMwB,UAAU,AAAC,CAAA,MAAM2B,KAAK3B,OAAO,CAAC;YAAEyB;YAAOzC,QAAQR,KAAKV,EAAE;YAAEiE;YAAKvD,MAAMX;QAAK,EAAC,EAAGsC,MAAM,CACtF,CAAC,EAAEC,MAAM,EAAE,GAAKnE,2BAA2BmE,YAAY;QAEzD,MAAMO,UAAU,MAAMZ,YAAYC,SAAS;YAAEyB;YAAOzC,QAAQR,KAAKV,EAAE;YAAE8F,KAAK/B,OAAO+B,GAAG;QAAC;QACrF,MAAMC,YAAYpG,IAAII,IAAI,CAAC,YAAY;QACvCoE,MAAMb,IAAI,CAAC;YACT0C,MAAMlG,SAASC,MAAMW,KAAKV,EAAE;YAC5BiG,KACEpC,KAAKqC,MAAM,IAAIL,WAAWE,YACtB,GAAGlC,KAAKqC,MAAM,GAAGlJ,WAAWC,MAAMkJ,cAAc,EAAE;gBAAEN;gBAASnF,MAAMqF;YAAU,IAAI,GACjF;YACNlD;QACF;IACF;IACA,MAAMuD,aAAa,MAAMvC,KAAK3B,OAAO,CAAC;QAAEyB;QAAOzC,QAAQ;QAAM+C;QAAKvD,MAAM;IAAK;IAC7E,MAAM2F,YAAY,MAAMpE,YACtBmE,WAAW/D,MAAM,CAAC,CAAC,EAAEC,MAAM,EAAE,GAAKnE,2BAA2BmE,YAAY,cACzE;QAAEqB;QAAOzC,QAAQ;QAAM4E,KAAK/B,OAAO+B,GAAG;IAAC;IAGzC,MAAMQ,WAAW9I,uBAAuByG;IACxC,MAAMsC,gBAAgB5G,IAAIsE,GAAG,CAAC,OAAO,KAAK4B,WAAW;IACrD,MAAMW,cAAc,GAAG3C,KAAKqC,MAAM,GAAGlJ,WAAWC,MAAMwJ,4BAA4B,GAAG;IACrF,0FAA0F;IAC1F,MAAMC,iBACJnJ,iBAAiB0G,KAAc,mBAAmBJ,KAAK8C,MAAM,CAAC,yBAAyBhD,OAAOM;IAChG,MAAM2C,UAAUhB,MAAM,CAAC,EAAE;IACzB,MAAMiB,eACJH,kBAAkB7C,KAAKqC,MAAM,IAAIL,WAAWe,WAAWjH,IAAIiH,QAAQvF,GAAG,CAAC,gBACnE,GAAGwC,KAAKqC,MAAM,GAAGlJ,WAAWC,MAAM6J,cAAc,EAAE;QAAEjB;QAASnF,MAAMf,IAAIiH,QAAQvF,GAAG,CAAC;IAAc,IAAI,GACrG;IACN,MAAM0F,cAAchK,iBAAiBuJ;IACrC,MAAMU,UAAUlJ,4BAA4ByI,eAAepC;IAC3D,MAAM8C,QAAQ;QACZV;QACAW,aAAaZ,SAASY,WAAW;QACjCC,aAAapD,OAAOqD,KAAK;QACzBjD;QACAkC;QACAG;IACF;IACAtC,OAAOC,KAAK,GAAGA,MAAM5D,MAAM;IAC3B,MAAM8G,UAAU;QAAEC,MAAM7H;IAA+B;IAEvD,IAAI,CAAC5B,8BAA8B,aAAKoJ;QAAOM,eAAe;SAAS;QACrE,OAAO,aAAKrD;YAAQiB,SAAS;;IAC/B;IAEA,KAAK,MAAMR,UAAUP,WAAY;YAIlBvG,gCAOCA;YAT6BwH;QAD3C,IAAIrB,cAAc,OAAO,aAAKE;YAAQsD,UAAU;;QAChD,MAAMC,aAAanK,yBAAwB+H,aAAAA,MAAMhE,GAAG,CAACsD,OAAOK,GAAG,sBAApBK,WAAuBhE,GAAG,CAAChE,wBAAwBsG;QAC9F,MAAM4D,gBAAgBV,gBAAgB,CAACY,aAAaZ,eAAe;QACnE,MAAMa,QAAO7J,iCAAAA,8BAA8B,aAAKoJ;YAAOM;YAAeI,aAAa;uBAAtE9J,iCAAiF;QAC9F,IAAIgG,KAAK+D,MAAM,EAAE;YACf1D,OAAOG,IAAI,IAAI;YACf;QACF;QACA,MAAMwD,KAAK,MAAMhE,KAAKiE,OAAO,CAACnD,QAAQ0C;QACtC,IAAI,CAACQ,IAAI;QACT,MAAME,OAAO;aAAClK,kCAAAA,8BAA8B,aAAKoJ;gBAAOM;2BAA1C1J,kCAA8D6J;YAAMX;SAAY,CAC3F1E,MAAM,CAAC2F,SACPC,IAAI,CAAC;QACR,MAAMC,SAAS,MAAMrE,KAAKsE,IAAI,CAAC;YAC7BN;YACAb;YACAU;YACAK;YACA9D;YACAmE,OAAO;gBACL,mBAAmBpB;gBACnB,YAAYT;gBACZ,kBAAkBxC,OAAOqD,KAAK;gBAC9B,gBAAgBM;gBAChBlB;YACF;QACF;QACA,IAAI0B,OAAO7D,IAAI,EAAE;YACfH,OAAOG,IAAI,IAAI;YACf,MAAMC,UAAUlD,GAAG,CACjB;gBACEmD,MAAMR,OAAOQ,IAAI;gBACjBF,MAAM;oBAAE,CAACM,OAAOK,GAAG,CAAC,EAAE;gBAAK;gBAC3BqD,WAAWxJ,cAAcqB,SAAS,CAACoI,UAAU,CAACC,eAAe;YAC/D,GACA;gBAAEH,OAAO;YAAK;QAElB,OAAO,IAAIF,OAAOM,WAAW,EAAE;YAC7B,OAAO,aAAKtE;gBAAQsD,UAAU;;QAChC,OAAO;gBACYtD;YAAjBA,OAAOuE,MAAM,GAAG,EAACvE,iBAAAA,OAAOuE,MAAM,YAAbvE,iBAAiB,KAAK;QACzC;IACF;IACA,OAAOA;AACT;AAEA,mEAAmE,GACnE,OAAO,eAAewE,sBACpB7E,IAAuB,EACvB8E,UAAsD,CAAC,CAAC;QAIzC9E,gBAGkC8E;IALjD,MAAM,EAAEzI,SAAS,EAAE,GAAG2D;IACtB,MAAM+E,YAAYlH,KAAKoE,GAAG;IAC1B,MAAM+C,UAAShF,iBAAAA,KAAKiF,QAAQ,YAAbjF,iBAAiBnE;IAChC,MAAMsE,aAAa,IAAMtC,KAAKoE,GAAG,KAAK8C,YAAYC;IAClD,MAAM9E,SAAS9F,oBAAoB4F,KAAKkF,KAAK;IAC7C,MAAMC,WAAWC,KAAKC,GAAG,CAAC,GAAGD,KAAKE,GAAG,CAACF,KAAKG,KAAK,EAACT,iBAAAA,QAAQhD,KAAK,YAAbgD,iBAAiBrJ,2BAA2BA;IAC7F,IAAI+J,QAAQnJ,UACTY,UAAU,CAAC,QACX0E,OAAO,CAAC3G,cAAcqB,SAAS,CAACuF,SAAS,CAACC,UAAU,IACpDC,KAAK,CAACqD,WAAW;IACpB,IAAIL,QAAQW,MAAM,EAAED,QAAQA,MAAME,UAAU,CAACrJ,UAAUY,UAAU,CAAC,QAAQC,GAAG,CAAC4H,QAAQW,MAAM;IAC5F,MAAME,OAAO,MAAMH,MAAMhI,GAAG;IAC5B,MAAMoI,UAAUD,KAAKxI,IAAI,CAACT,MAAM,GAAGyI;IACnC,MAAMU,UAAUD,UAAUD,KAAKxI,IAAI,CAACgB,KAAK,CAAC,GAAGgH,YAAYQ,KAAKxI,IAAI;IAElE,MAAMkD,SAA8B;QAClCK,MAAMR,OAAOQ,IAAI;QACjBqD,QAAQ/D,KAAK+D,MAAM;QACnB+B,OAAO;QACPtF,MAAM;QACNuF,MAAM,CAAC;QACPpC,UAAU;QACVqC,YAAY;QACZC,MAAM;IACR;IACA,IAAIC,OAAsB;IAC1B,KAAK,MAAMjG,UAAU4F,QAAS;QAC5B,IAAI1F,cAAc;YAChBE,OAAOsD,QAAQ,GAAG;YAClB;QACF;QACA,IAAI;YACF,MAAMwC,YAAY,MAAMpG,aAAaC,MAAMC,QAAQC,QAAQC;YAC3DE,OAAO0F,IAAI,CAAC9F,OAAO9D,EAAE,CAAC,GAAGgK;YACzB9F,OAAOG,IAAI,IAAI2F,UAAU3F,IAAI;YAC7B,IAAI2F,UAAUxC,QAAQ,EAAE;gBACtBtD,OAAOsD,QAAQ,GAAGwC,UAAUxC,QAAQ;gBACpC;YACF;QACF,EAAE,OAAO/D,OAAO;YACdC,QAAQD,KAAK,CAAC,qCAAqCK,OAAO9D,EAAE,EAAEyD;YAC9DS,OAAO0F,IAAI,CAAC9F,OAAO9D,EAAE,CAAC,GAAG;gBAAEmF,SAAS;gBAAUhB,OAAO;gBAAGC,YAAY;gBAAGC,MAAM;YAAE;QACjF;QACAH,OAAOyF,KAAK,IAAI;QAChBI,OAAOjG,OAAO9D,EAAE;IAClB;IACA;;;;;GAKC,GACD,IAAIkE,OAAOsD,QAAQ,KAAK,QAAQ;YACVuC;QAApB7F,OAAO2F,UAAU,IAAGE,OAAAA,eAAAA,OAAQpB,QAAQW,MAAM,YAAtBS,OAA0B;QAC9C7F,OAAO4F,IAAI,GAAG;IAChB,OAAO,IAAI5F,OAAOsD,QAAQ,KAAK,QAAQ;QACrCtD,OAAO4F,IAAI,GAAG;IAChB,OAAO;QACL5F,OAAO2F,UAAU,GAAGJ,UAAUM,OAAO;QACrC7F,OAAO4F,IAAI,GAAG5F,OAAO2F,UAAU,KAAK;IACtC;IACA,OAAO3F;AACT;AAEA,0FAA0F,GAC1F,OAAO,eAAe+F,kBAAkBtB,OAAyC;IAC/E,MAAMzI,YAAYrB,cAAcqL,GAAG,GAAGhK,SAAS;IAC/C,MAAMiK,QAAQ,MAAMpL;IACpB,uEAAuE;IACvE,IAAIqL;IACJ,OAAO;QACLlK;QACA6I,OAAOJ,QAAQI,KAAK;QACpBnB,QAAQe,QAAQf,MAAM;QACtB1B,QAAQxI;QACRiJ,QAAQ,CAAC0D,MAAM1G,OAAOM,MACpBtG,sBACE0M,MACA,AAACF,KAA+B,CAACE,KAAK,EACtC1G,OACA/F,6BAA6BqG,GAAG,CAAC,eAAe,GAChDxG,qBAAqBwG;QAEzBS,YAAY,CAACf,QAAU7E,eAAe6E;QACtCmB,WAAW,CAACwF,OAAShM,4BAA4BgM;QACjDpI,SAAS,CAACqI,UAAY3L,sBAAsB,aAAK2L;gBAASC,QAAQ,CAAClI,SAAW0F,QAAQ1F,OAAOC,aAAa;;QAC1GuF,SAAS,OAAOnD,QAAQ0C;YACtB,IAAIS,UAAUnI,IAAIgF,OAAO8F,KAAK,EAAEC,WAAW;YAC3C,IAAI,CAAC5C,QAAQ9E,QAAQ,CAAC,QAAQqE,QAAQC,IAAI,GAAG,GAAG;oBAGhCqD;gBAFdtD,QAAQC,IAAI,IAAI;gBAChB,MAAMqD,SAAS,MAAMpM,yBAAyBoG,OAAOK,GAAG,EAAE4F,KAAK,CAAC,IAAM;gBACtE9C,UAAUnI,IAAIgL,2BAAAA,iBAAAA,OAAQE,MAAM,qBAAdF,eAAgBF,KAAK,EAAEC,WAAW;YAClD;YACA,IAAI,CAAC5C,QAAQ9E,QAAQ,CAAC,MAAM,OAAO;YACnC,MAAM,CAAC8H,KAAK,GAAG,MAAMnM,uBAAuB;gBAACmJ;aAAQ,EAAE5H;YACvD,OAAO4K,eAAAA,OAAQ;QACjB;QACA3C,MAAM,OAAOsC;gBAqBD;YApBVL,iBAAAA,SAAAA,SAAWpL,gBAAgB,kBAAkB4L,KAAK,CAAC,IAAM;YACzD,MAAMG,WAAW,MAAMX;YACvB,MAAMY,QAAQ9L,iBAAiBuL,MAAMxG,GAAG;YACxC,MAAMgH,WAAWF,WACb9L,wBAAwB8L,UAAU,aAAKC,MAAM5C,KAAK,EAAKqC,MAAMrC,KAAK,GAAI4C,MAAMrC,OAAO,IACnF;YACJ,MAAMT,SAAS,MAAM7J,UAAU;gBAC7BwJ,IAAI;oBAAC4C,MAAM5C,EAAE;iBAAC;eACX1I,mBAAmB8L,UAAU;gBAAEjE,SAASyD,MAAMzD,OAAO;gBAAEe,MAAM0C,MAAM1C,IAAI;YAAC;gBAC3EmD,UAAU1N,uBAAuBiN,MAAMxG,GAAG,EAAWiH,QAAQ;gBAC7DX,SAAS;gBACTY,UAAU;;YAEZ,IAAIjD,OAAO7D,IAAI,EAAE;gBACf,MAAM3F,qBAAqBkM,KAAK,CAAC,IAAMQ;gBACvC,OAAO;oBAAE/G,MAAM;oBAAMmE,aAAa;gBAAM;YAC1C;YACA,OAAO;gBACLnE,MAAM;gBACNmE,aAAapK,qBAAqB8J,YAAY;gBAC9CmD,MAAM,GAAE,iBAAA,AAACnD,OAA+BmD,MAAM,YAAtC,iBAA0C;YACpD;QACF;IACF;AACF;AAEA,qMAAqM;AAErM;;;;CAIC,GACD,OAAO,eAAeC,mBAAmBC,OAAgB;IACvD,MAAMC,SAASD,QAAQC,MAAM,CAACC,WAAW;IACzC,IAAID,WAAW,UAAUA,WAAW,OAAO;QACzC,OAAOE,SAASC,IAAI,CAAC;YAAElI,OAAO;QAAqB,GAAG;YAAEmI,QAAQ;YAAKC,SAAS;gBAAEC,OAAO;YAAY;QAAE;IACvG;IACA,IAAI,CAACC,QAAQC,GAAG,CAAC,cAAc,EAAE;QAC/B,OAAON,SAASC,IAAI,CAAC;YAAElI,OAAO;QAAsD,GAAG;YAAEmI,QAAQ;QAAI;IACvG;IACA,MAAMC,UAAUI,OAAOC,WAAW,CAACX,QAAQM,OAAO,CAAChJ,OAAO;IAC1D,IAAI,CAACrE,iBAAiBqN,UAAU,OAAOH,SAASC,IAAI,CAAC;QAAElI,OAAO;IAAkB,GAAG;QAAEmI,QAAQ;IAAI;IACjG,MAAMlE,OAAO8D,WAAW,SAAW,MAAMD,QAAQI,IAAI,GAAGf,KAAK,CAAC,IAAM,QAA4C;IAChH,MAAMvB,QAAQ4C,OAAOC,WAAW,CAAC,IAAIC,IAAIZ,QAAQtF,GAAG,EAAEmG,YAAY,CAACvJ,OAAO;IAC1E,MAAM+E,SAASnJ,aAAa;QAAE+M;QAAQ9D,IAAI,EAAEA,eAAAA,OAAQ0D;QAAW/B;IAAM;IACrE,wEAAwE;IACxE,yDAAyD;IACzD,IAAImC,WAAW,QAAQ;QACrB,IAAI;YACF,MAAMtO,cAAc2B,cAAcqL,GAAG,GAAGhK,SAAS,IAAId;QACvD,EAAE,eAAM;QACN,sEAAsE;QACxE;IACF;IACA,IAAI;QACF,MAAMiN,YAAY3E,wBAAAA,IAAM,CAAC,SAAS;QAClC,MAAM4B,SACJ,OAAO+C,cAAc,YAAYA,UAAU9L,MAAM,GAAG,KAAK,CAAC8L,UAAUrJ,QAAQ,CAAC,OAAOqJ,YAAY;QAClG,MAAM1G,QAAQrE,OAAOoG,wBAAAA,IAAM,CAAC,QAAQ;QACpC,MAAMxD,SAAS,MAAMwE,sBAAsB,MAAMuB,kBAAkB;YAAElB,OAAO,IAAIrH;YAAQkG;QAAO,IAAI;YACjG0B;WACIhI,OAAOgL,QAAQ,CAAC3G,UAAUA,QAAQ,IAAI;YAAEA;QAAM,IAAI,CAAC;QAEzD,OAAO+F,SAASC,IAAI,CAACzH,QAAQ;YAAE2H,SAAS;gBAAE,iBAAiB;YAAW;QAAE;IAC1E,EAAE,OAAOpI,OAAO;QACdC,QAAQD,KAAK,CAAC,+BAA+BA;QAC7C,OAAOiI,SAASC,IAAI,CAAC;YAAElI,OAAO;QAA4B,GAAG;YAAEmI,QAAQ;QAAI;IAC7E;AACF"}
1
+ {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/weekly-summary.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AglynOrgMember } from '@aglyn/aglyn/foundation/definitions/organization.types'\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { writeCronBeat } from '@aglyn/aglyn/app-utils/health-report'\nimport {\n DIGEST_PREFS_FIELD,\n digestEnabled,\n INSIGHT_DIGESTS_FIELD,\n insightDigestSubscribed,\n} from '@aglyn/aglyn/app-utils/notifications'\nimport { liveCustomDomain, TENANT_APEX } from '@aglyn/aglyn/app-utils/host-naming'\nimport { hostRoleFor } from '@aglyn/aglyn/app-utils/organizations'\nimport {\n checkEntitlement,\n resolveBrandingProfile,\n resolveEffectivePlan,\n} from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { platformConsoleOrigin } from '@aglyn/aglyn/app-utils/platform-brand'\nimport {\n isReleaseFlagOnForOrg,\n parseOrgReleaseFlagOverrides,\n type ReleaseFlagKey,\n} from '@aglyn/aglyn/app-utils/release-flags'\nimport {\n composeWeeklySummaryOverviewEmail,\n composeWeeklySummarySiteEmail,\n WEEKLY_SUMMARY_DAYS,\n WEEKLY_SUMMARY_DIGEST_KEY,\n weeklySummaryHasNews,\n weeklySummaryPeriod,\n type WeeklySummaryColors,\n type WeeklySummaryEmail,\n type WeeklySummarySite,\n} from '@aglyn/aglyn/app-utils/weekly-summary'\nimport {\n normalizePluginFigureTable,\n pluginFigureSummarySection,\n type PluginFigureSummaryPart,\n type ResolvedPluginFigureReader,\n} from '@aglyn/aglyn/plugin-manager/plugin-figures'\nimport {\n emailAccent,\n PLATFORM_EMAIL_PALETTE,\n rateLimitedRetryAtMs,\n resolveEmailColor,\n sendEmail,\n} from '@aglyn/shared-util-email'\nimport { withoutMailWithheldAccounts } from './account-mail'\nimport { findUserByUidAcrossPools } from './auth-pools'\nimport { isCronAuthorized, isCronDryRun } from './cron-auth'\nimport { meterPlatformEmail } from './email-metering'\nimport { filterSuppressedEmails } from './email-suppression'\nimport { eligibleFigureReaders, type FigureReaderContext } from './figure-readers'\nimport firebaseAdmin from './firebase-admin'\nimport { listOrgMembers } from './organizations'\nimport { getServerReleaseFlagValues } from './release-flags'\nimport {\n loadSystemEmail,\n renderLoadedSystemEmail,\n systemEmailBrand,\n systemEmailContent,\n systemEmailTheme,\n type LoadedSystemEmail,\n} from './render-system-email'\n\n/**\n * THE WEEKLY SUMMARY'S SENDS (AGL-3867): `POST /api/admin/weekly-summary`,\n * Mondays at 13:00 UTC (08:00 Central) from `scheduled-crons.yml`.\n *\n * ## What is sent, and to whom\n *\n * One email for each of a workspace's sites that had something to report,\n * and a workspace overview that adds them up: totals across the sites, a line\n * for each, and the workspace's own figures, such as its deals.\n *\n * - The workspace's owners and admins get the overview and every site's\n * email. In a workspace where only one site had anything to report, the\n * overview would repeat it, so that site's email carries the workspace's\n * own figures instead and no overview is sent.\n * - A member who is an admin of a site, without being one of the\n * workspace's, gets that site's email: they run it.\n *\n * The **Weekly summary** switch on each person's account\n * (`users/{uid}.digestPrefs.weeklySummary`, on until turned off) governs all\n * of it. A locked or disabled account, a suppressed address and a suspended\n * member are not mailed.\n *\n * ## What it reads\n *\n * The workspace's busiest `WEEKLY_SUMMARY_MAX_SITES` sites by last week's\n * page views, each through every site reader that declares a\n * `weeklySummary`, and the workspace through every workspace reader that\n * does. Each reader is offered only where its plan feature is included and\n * its plugin runs (`eligibleFigureReaders`), read over the seven whole days\n * before the run, and summarized by its owner. A reader that fails is left\n * out; the rest are still read. Every figure is read once per workspace, and\n * every recipient is sent the same figures.\n *\n * ## Once a week, and resumable\n *\n * `orgs/{orgId}/weeklySummaries/{week}.sent.{uid}.{email}` records each\n * email sent — `overview`, or a site's id — stamped after each send, so a run\n * that dies mid-workspace sends only what never went, and a second run in the\n * same week sends nothing. Workspaces are swept in id order behind a cursor,\n * the `done: false` loop the workflow already runs for the CRM digest.\n *\n * The email is a `bulk` send under the platform send-rate governor: a refusal\n * stops the run and answers `done: true`, and a manual re-run in the same week\n * picks up the rest. Metered as PLATFORM mail, not the workspace's: it is the\n * platform reporting to the people who run it.\n */\n\ntype Firestore = FirebaseFirestore.Firestore\ntype Snapshot = FirebaseFirestore.QueryDocumentSnapshot\n\n/** The beat `/api/health/crons` reads, once this job joins its inventory. */\nexport const WEEKLY_SUMMARY_JOB_ID = 'weekly-summary'\n\n/** `orgs/{orgId}/weeklySummaries/{week}` — server-written, matched by no rule. */\nexport const WEEKLY_SUMMARY_MARKERS = 'weeklySummaries'\n\n/** The marker key of the overview; a site's email is keyed by the site's id. */\nexport const WEEKLY_SUMMARY_OVERVIEW = 'overview'\n\n/** The system emails: the overview, and one site's. */\nexport const WEEKLY_SUMMARY_TEMPLATE = 'weekly-summary'\nexport const WEEKLY_SUMMARY_SITE_TEMPLATE = 'weekly-summary-site'\n\n/** Workspaces per invocation; a chunk that times out never advances its cursor. */\nexport const WEEKLY_SUMMARY_ORG_CHUNK = 20\n\n/** Sites a workspace's sweep ranks, by document id. */\nexport const WEEKLY_SUMMARY_HOST_READ_LIMIT = 50\n\n/** Sites a week's summary reports on: the busiest by last week's page views. */\nexport const WEEKLY_SUMMARY_MAX_SITES = 5\n\n/** Members per workspace whose address is chased through the auth pools when their member document has none. */\nexport const WEEKLY_SUMMARY_MAX_UID_LOOKUPS = 5\n\n/** How long one call works before it stops and leaves the rest to the next. */\nexport const WEEKLY_SUMMARY_RUN_BUDGET_MS = 45_000\n\n/** The theme tokens each color of the layout is drawn from. */\nconst COLOR_TOKENS: Record<keyof WeeklySummaryColors, string> = {\n text: 'text.primary',\n muted: 'text.secondary',\n border: 'divider',\n surface: 'grey.50',\n accent: 'primary.main',\n link: 'primary.main',\n up: 'success.dark',\n down: 'error.dark',\n}\n\nexport interface WeeklySummarySendResult {\n sent: boolean\n /** The platform's send rate refused it for now. */\n rateLimited: boolean\n reason?: string\n}\n\nexport interface WeeklySummaryMessage {\n to: string\n template: typeof WEEKLY_SUMMARY_TEMPLATE | typeof WEEKLY_SUMMARY_SITE_TEMPLATE\n email: WeeklySummaryEmail\n org: Record<string, unknown>\n merge: Record<string, string>\n}\n\n/** Everything the sweep reaches beyond Firestore, so a spec can hold each one. */\nexport interface WeeklySummaryDeps {\n firestore: Firestore\n runAt: Date\n dryRun: boolean\n /** The console's absolute origin, or `''` when none is configured. */\n origin: string\n /** Whether a release flag is on for an org. */\n flagOn: (flag: ReleaseFlagKey, orgId: string, org: Record<string, unknown>) => boolean\n orgMembers: (orgId: string) => Promise<AglynOrgMember[]>\n /** The uids whose accounts may be mailed. */\n reachable: (uids: string[]) => Promise<string[]>\n /** The readers that declare a `weeklySummary` and pass the gates here. */\n readers: (context: Omit<FigureReaderContext, 'accept'>) => Promise<ResolvedPluginFigureReader[]>\n /** The org's email colors, from its theme. */\n colors: (org: Record<string, unknown>) => WeeklySummaryColors\n /** The member's deliverable address, or `null` when there is none or it is suppressed. */\n address: (member: AglynOrgMember, lookups: { left: number }) => Promise<string | null>\n send: (message: WeeklySummaryMessage) => Promise<WeeklySummarySendResult>\n budgetMs?: number\n}\n\nexport interface WeeklySummaryOrgReport {\n skipped?: string\n sites: number\n recipients: number\n sent: number\n failed?: number\n /** Why the workspace stopped short: the call's time ran out, or the send rate refused. */\n deferred?: 'time' | 'rate'\n}\n\nexport interface WeeklySummaryReport {\n week: string\n dryRun: boolean\n swept: number\n sent: number\n orgs: Record<string, WeeklySummaryOrgReport>\n deferred: false | 'time' | 'rate'\n nextCursor: string | null\n done: boolean\n}\n\nconst str = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\nfunction siteName(data: Record<string, unknown>, id: string): string {\n return str(data['displayName']) || str(data['name']) || str(data['subdomain']) || id\n}\n\n/** Last week's page views for each site, read from the daily analytics documents. */\nasync function weekViews(\n firestore: Firestore,\n hosts: readonly Snapshot[],\n days: readonly string[],\n): Promise<Map<string, number>> {\n const views = new Map<string, number>()\n if (!hosts.length) return views\n const refs = hosts.flatMap((host) => days.map((day) => host.ref.collection('analytics').doc(day)))\n const docs = await firestore.getAll(...refs)\n for (const doc of docs) {\n const hostId = doc.ref.parent.parent?.id ?? ''\n views.set(hostId, (views.get(hostId) ?? 0) + (Number(doc.get('total') ?? 0) || 0))\n }\n return views\n}\n\nfunction weekDays(from: string): string[] {\n const start = Date.parse(`${from}T00:00:00Z`)\n return Array.from({ length: WEEKLY_SUMMARY_DAYS }, (_, index) =>\n new Date(start + index * 86_400_000).toISOString().slice(0, 10),\n )\n}\n\n/**\n * Each reader's part, in its declared order: read over the week, then\n * summarized by its owner. A reader that fails, answers no table, has nothing\n * to say or cannot be read over seven days is left out.\n */\nasync function readParts(\n readers: readonly ResolvedPluginFigureReader[],\n base: { orgId: string; hostId: string | null; now: Date },\n): Promise<PluginFigureSummaryPart[]> {\n const ordered = readers\n .filter(({ reader }) => reader.weeklySummary)\n .sort(\n (a, b) =>\n (a.reader.weeklySummary?.order ?? 0) - (b.reader.weeklySummary?.order ?? 0) ||\n a.reader.id.localeCompare(b.reader.id),\n )\n const parts: PluginFigureSummaryPart[] = []\n for (const { reader } of ordered) {\n const summary = reader.weeklySummary\n if (!summary) continue\n const days = reader.windows.length ? (reader.windows.includes(WEEKLY_SUMMARY_DAYS) ? WEEKLY_SUMMARY_DAYS : null) : 0\n if (days === null) continue\n try {\n const read = await reader.read({ ...base, days, uid: null, params: {} })\n if (read.ok === false) continue\n const table = normalizePluginFigureTable(read.table)\n if (!table) continue\n const part = summary.summarize(table)\n if (part && (part.stats?.length || part.rows?.length)) parts.push(part)\n } catch (error) {\n console.error('[weekly-summary] reader failed', { reader: reader.id, orgId: base.orgId, error })\n }\n }\n return parts\n}\n\n/** The emails one person is owed this week, by marker key, in the order they are sent. */\nfunction emailsFor(\n member: AglynOrgMember,\n manager: boolean,\n sites: ReadonlyArray<{ hostId: string; site: WeeklySummarySite }>,\n workspace: readonly PluginFigureSummaryPart[],\n): Array<{ key: string; hostId: string | null; withWorkspace: boolean }> {\n const active = sites.filter(({ site }) => weeklySummaryHasNews(site))\n if (!manager) {\n return active\n .filter(({ hostId }) => hostRoleFor(member, hostId) === 'admin')\n .map(({ hostId }) => ({ key: hostId, hostId, withWorkspace: false }))\n }\n // One active site: its email carries the workspace's figures, and an\n // overview would only say it again.\n if (active.length === 1) {\n return [{ key: active[0].hostId, hostId: active[0].hostId, withWorkspace: true }]\n }\n return [\n ...(active.length || workspace.length ? [{ key: WEEKLY_SUMMARY_OVERVIEW, hostId: null, withWorkspace: false }] : []),\n ...active.map(({ hostId }) => ({ key: hostId, hostId, withWorkspace: false })),\n ]\n}\n\nasync function summarizeOrg(\n deps: WeeklySummaryDeps,\n orgDoc: Snapshot,\n period: ReturnType<typeof weeklySummaryPeriod>,\n overBudget: () => boolean,\n): Promise<WeeklySummaryOrgReport> {\n const { firestore } = deps\n const orgId = orgDoc.id\n const org = (orgDoc.data() ?? {}) as Record<string, unknown>\n const report: WeeklySummaryOrgReport = { sites: 0, recipients: 0, sent: 0 }\n\n const markerRef = orgDoc.ref.collection(WEEKLY_SUMMARY_MARKERS).doc(period.week)\n const sentAlready = ((await markerRef.get()).get('sent') ?? {}) as Record<string, Record<string, unknown>>\n\n // Who could be told: owners and admins, and each site's own admins. Not\n // suspended, an account that may be mailed, the switch kept on.\n const members = (await deps.orgMembers(orgId)).filter((member) => member.orgSuspended !== true)\n const isManager = (member: AglynOrgMember) => member.role === 'owner' || member.role === 'admin'\n const candidates = members.filter(\n (member) => isManager(member) || Object.values(member.hostAccess ?? {}).includes('admin'),\n )\n const reachable = new Set(await deps.reachable(candidates.map((member) => member.$id)))\n const reached = candidates.filter((member) => reachable.has(member.$id)).sort((a, b) => a.$id.localeCompare(b.$id))\n if (!reached.length) return { ...report, skipped: candidates.length ? 'nobody-reachable' : 'no-managers' }\n const userDocs = await firestore.getAll(...reached.map((member) => firestore.collection('users').doc(member.$id)))\n const users = new Map(userDocs.map((doc) => [doc.id, doc]))\n const recipients = reached.filter((member) =>\n digestEnabled(users.get(member.$id)?.get(DIGEST_PREFS_FIELD), WEEKLY_SUMMARY_DIGEST_KEY),\n )\n report.recipients = recipients.length\n if (!recipients.length) return { ...report, skipped: 'summary-off' }\n\n // The busiest sites by last week's page views, ties by id.\n const hostPage = await firestore\n .collection('hosts')\n .where('orgId', '==', orgId)\n .orderBy(firebaseAdmin.firestore.FieldPath.documentId())\n .limit(WEEKLY_SUMMARY_HOST_READ_LIMIT)\n .get()\n const hosts = hostPage.docs.filter((doc) => !doc.get('deletedAt'))\n const views = await weekViews(firestore, hosts, weekDays(period.from))\n const chosen = [...hosts]\n .sort((a, b) => (views.get(b.id) ?? 0) - (views.get(a.id) ?? 0) || a.id.localeCompare(b.id))\n .slice(0, WEEKLY_SUMMARY_MAX_SITES)\n\n const orgSlug = str(org['slug'])\n const sites: Array<{ hostId: string; site: WeeklySummarySite; subdomain: string }> = []\n for (const host of chosen) {\n const data = (host.data() ?? {}) as Record<string, unknown>\n const readers = (await deps.readers({ orgId, hostId: host.id, org, host: data })).filter(\n ({ reader }) => pluginFigureSummarySection(reader) === 'site',\n )\n const parts = await readParts(readers, { orgId, hostId: host.id, now: period.now })\n const subdomain = str(data['subdomain'])\n const domain = liveCustomDomain(data as never) ?? (subdomain ? `${subdomain}.${TENANT_APEX}` : '')\n sites.push({\n hostId: host.id,\n subdomain,\n site: {\n name: siteName(data, host.id),\n address: domain ? { label: domain, url: `https://${domain}` } : null,\n url:\n deps.origin && orgSlug && subdomain\n ? `${deps.origin}${buildRoute(Route.HOST_DASHBOARD, { orgSlug, host: subdomain })}`\n : null,\n parts,\n },\n })\n }\n const orgReaders = await deps.readers({ orgId, hostId: null, org, host: null })\n const workspace = await readParts(\n orgReaders.filter(({ reader }) => pluginFigureSummarySection(reader) === 'workspace'),\n { orgId, hostId: null, now: period.now },\n )\n report.sites = sites.filter(({ site }) => weeklySummaryHasNews(site)).length\n if (!report.sites && !workspace.length) return { ...report, skipped: 'nothing-to-report' }\n\n const branding = resolveBrandingProfile(org as never)\n const workspaceName = str(org['name']) || orgSlug || 'your workspace'\n const settingsUrl = `${deps.origin}${buildRoute(Route.MANAGE_NOTIFICATION_SETTINGS)}`\n const colors = deps.colors(org)\n // AI weekly insights, pointed to only where the plan includes them and they are released.\n const offersInsights =\n checkEntitlement(org as never, 'aiGenerative') && deps.flagOn('release_ai_generative', orgId, org)\n const analyticsUrl = (subdomain: string) =>\n offersInsights && deps.origin && orgSlug && subdomain\n ? `${deps.origin}${buildRoute(Route.HOST_ANALYTICS, { orgSlug, host: subdomain })}`\n : null\n const common = {\n workspaceName,\n productName: branding.productName,\n periodLabel: period.label,\n colors,\n settingsUrl,\n }\n const lookups = { left: WEEKLY_SUMMARY_MAX_UID_LOOKUPS }\n const busiest = sites.find(({ site }) => weeklySummaryHasNews(site)) ?? sites[0]\n\n for (const member of recipients) {\n const owed = emailsFor(member, isManager(member), sites, workspace).filter(\n ({ key }) => !sentAlready[member.$id]?.[key],\n )\n if (!owed.length) continue\n const subscribed = insightDigestSubscribed(users.get(member.$id)?.get(INSIGHT_DIGESTS_FIELD), orgId)\n const to = deps.dryRun ? '' : await deps.address(member, lookups)\n if (!deps.dryRun && !to) continue\n for (const { key, hostId, withWorkspace } of owed) {\n if (overBudget()) return { ...report, deferred: 'time' }\n const entry = hostId ? sites.find((site) => site.hostId === hostId) : null\n const aiInsightsUrl = subscribed ? null : analyticsUrl(entry?.subdomain ?? busiest?.subdomain ?? '')\n const email = entry\n ? composeWeeklySummarySiteEmail({\n ...common,\n aiInsightsUrl,\n site: entry.site,\n ...(withWorkspace ? { workspace } : {}),\n })\n : composeWeeklySummaryOverviewEmail({\n ...common,\n aiInsightsUrl,\n sites: sites.map(({ site }) => site),\n workspace,\n url: deps.origin && orgSlug ? `${deps.origin}${buildRoute(Route.ORG_HOME, { orgSlug })}` : '',\n })\n if (!email) continue\n if (deps.dryRun) {\n report.sent += 1\n continue\n }\n const result = await deps.send({\n to: to as string,\n template: entry ? WEEKLY_SUMMARY_SITE_TEMPLATE : WEEKLY_SUMMARY_TEMPLATE,\n email,\n org,\n merge: {\n 'summary.subject': email.subject,\n 'summary.title': email.title,\n 'summary.period': email.period,\n 'summary.html': email.html,\n 'summary.buttonLabel': email.buttonLabel,\n 'summary.url': email.url,\n 'org.name': workspaceName,\n settingsUrl,\n },\n })\n if (result.sent) {\n report.sent += 1\n await markerRef.set(\n {\n week: period.week,\n sent: { [member.$id]: { [key]: true } },\n updatedAt: firebaseAdmin.firestore.FieldValue.serverTimestamp(),\n },\n { merge: true },\n )\n } else if (result.rateLimited) {\n return { ...report, deferred: 'rate' }\n } else {\n report.failed = (report.failed ?? 0) + 1\n }\n }\n }\n return report\n}\n\n/** One chunk of the sweep: workspaces in id order after `cursor`. */\nexport async function runWeeklySummarySweep(\n deps: WeeklySummaryDeps,\n options: { cursor?: string | null; limit?: number } = {},\n): Promise<WeeklySummaryReport> {\n const { firestore } = deps\n const startedAt = Date.now()\n const budget = deps.budgetMs ?? WEEKLY_SUMMARY_RUN_BUDGET_MS\n const overBudget = () => Date.now() - startedAt > budget\n const period = weeklySummaryPeriod(deps.runAt)\n const pageSize = Math.max(1, Math.min(Math.floor(options.limit ?? WEEKLY_SUMMARY_ORG_CHUNK), WEEKLY_SUMMARY_ORG_CHUNK))\n let query = firestore\n .collection('orgs')\n .orderBy(firebaseAdmin.firestore.FieldPath.documentId())\n .limit(pageSize + 1)\n if (options.cursor) query = query.startAfter(firestore.collection('orgs').doc(options.cursor))\n const page = await query.get()\n const hasMore = page.docs.length > pageSize\n const orgDocs = hasMore ? page.docs.slice(0, pageSize) : page.docs\n\n const report: WeeklySummaryReport = {\n week: period.week,\n dryRun: deps.dryRun,\n swept: 0,\n sent: 0,\n orgs: {},\n deferred: false,\n nextCursor: null,\n done: true,\n }\n let last: string | null = null\n for (const orgDoc of orgDocs) {\n if (overBudget()) {\n report.deferred = 'time'\n break\n }\n try {\n const orgReport = await summarizeOrg(deps, orgDoc, period, overBudget)\n report.orgs[orgDoc.id] = orgReport\n report.sent += orgReport.sent\n if (orgReport.deferred) {\n report.deferred = orgReport.deferred\n break\n }\n } catch (error) {\n console.error('[weekly-summary] workspace failed', orgDoc.id, error)\n report.orgs[orgDoc.id] = { skipped: 'failed', sites: 0, recipients: 0, sent: 0 }\n }\n report.swept += 1\n last = orgDoc.id\n }\n /*\n * Out of time: the next call starts after the last workspace that finished,\n * and the marker skips whoever the unfinished one already reached. Refused\n * by the send rate: the window is full, so the run stops (`done: true`)\n * rather than re-POSTing into it; a re-run in the same week reaches the rest.\n */\n if (report.deferred === 'time') {\n report.nextCursor = last ?? options.cursor ?? null\n report.done = false\n } else if (report.deferred === 'rate') {\n report.done = true\n } else {\n report.nextCursor = hasMore ? last : null\n report.done = report.nextCursor === null\n }\n return report\n}\n\n/** The sweep's reach into the real platform: Firestore, the flags, the roster, the mail. */\n/** The sweep's reach into the real platform: Firestore, the flags, the roster, the mail. */\nexport async function weeklySummaryDeps(options: { runAt: Date; dryRun: boolean }): Promise<WeeklySummaryDeps> {\n const firestore = firebaseAdmin.app().firestore()\n const flags = await getServerReleaseFlagValues()\n // Each designed email is read once per call and rendered per recipient.\n const loaded = new Map<string, Promise<LoadedSystemEmail | null>>()\n return {\n firestore,\n runAt: options.runAt,\n dryRun: options.dryRun,\n origin: platformConsoleOrigin(),\n flagOn: (flag, orgId, org) =>\n isReleaseFlagOnForOrg(\n flag,\n (flags as Record<string, never>)[flag],\n orgId,\n parseOrgReleaseFlagOverrides(org['releaseFlags']),\n resolveEffectivePlan(org as never),\n ),\n orgMembers: (orgId) => listOrgMembers(orgId),\n reachable: (uids) => withoutMailWithheldAccounts(uids),\n readers: (context) => eligibleFigureReaders({ ...context, accept: (reader) => Boolean(reader.weeklySummary) }),\n colors: (org) => {\n const { palette } = systemEmailTheme(systemEmailBrand(org as never).options)\n const colors = {} as WeeklySummaryColors\n for (const key of Object.keys(COLOR_TOKENS) as Array<keyof WeeklySummaryColors>) {\n // A brand palette that cannot resolve a token falls back to the\n // platform's, which resolves every one of them.\n colors[key] =\n resolveEmailColor(COLOR_TOKENS[key], palette) ??\n resolveEmailColor(COLOR_TOKENS[key], PLATFORM_EMAIL_PALETTE) ??\n 'inherit'\n }\n // Links take the color the renderer gives every other link in the\n // email, not the raw primary, which is too light to read as text.\n colors.link = emailAccent(palette).link\n return colors\n },\n address: async (member, lookups) => {\n let address = str(member.email).toLowerCase()\n if (!address.includes('@') && lookups.left > 0) {\n lookups.left -= 1\n const pooled = await findUserByUidAcrossPools(member.$id).catch(() => null)\n address = str(pooled?.record?.email).toLowerCase()\n }\n if (!address.includes('@')) return null\n const [kept] = await filterSuppressedEmails([address], firestore)\n return kept ?? null\n },\n send: async ({ to, template, email, org, merge }) => {\n if (!loaded.has(template)) loaded.set(template, loadSystemEmail(template).catch(() => null))\n const design = await loaded.get(template)\n const brand = systemEmailBrand(org as never)\n const rendered = design ? renderLoadedSystemEmail(design, { ...brand.merge, ...merge }, brand.options) : null\n const content = systemEmailContent(rendered, { subject: email.subject, text: email.text })\n const result = await sendEmail({\n to: [to],\n ...content,\n // The figures' plain text, rather than the drawn block with its tags stripped.\n text: email.text,\n fromName: resolveBrandingProfile(org as never).fromName,\n context: template,\n priority: 'bulk',\n })\n if (result.sent) {\n await meterPlatformEmail().catch(() => undefined)\n return { sent: true, rateLimited: false }\n }\n return {\n sent: false,\n rateLimited: rateLimitedRetryAtMs(result) !== null,\n reason: (result as { reason?: string }).reason ?? 'failed',\n }\n },\n }\n}\n\n// lockdown-423: exempt — server-internal cron (x-cron-secret), no user caller; it reads each workspace's figures to report them to its owners and admins and writes nothing a locked org could lose.\n\n/**\n * `POST /api/admin/weekly-summary` sends; a GET, or `{\"dryRun\": true}`,\n * reports who would be sent what and sends nothing. The body may carry the\n * `cursor` a previous chunk answered and a smaller `limit`.\n */\nexport async function weeklySummaryRoute(request: Request): Promise<Response> {\n const method = request.method.toUpperCase()\n if (method !== 'POST' && method !== 'GET') {\n return Response.json({ error: 'Method not allowed' }, { status: 405, headers: { Allow: 'GET, POST' } })\n }\n if (!process.env['CRON_SECRET']) {\n return Response.json({ error: 'The weekly summary is not configured (CRON_SECRET).' }, { status: 501 })\n }\n const headers = Object.fromEntries(request.headers.entries()) as Partial<Record<string, string>>\n if (!isCronAuthorized(headers)) return Response.json({ error: 'Unauthenticated' }, { status: 401 })\n const body = method === 'POST' ? ((await request.json().catch(() => null)) as Record<string, unknown> | null) : null\n const query = Object.fromEntries(new URL(request.url).searchParams.entries())\n const dryRun = isCronDryRun({ method, body: body ?? undefined, query })\n // Stamped on the invocation, not on the work (AGL-1955), so the beat is\n // already there the day this job joins `SCHEDULED_JOBS`.\n if (method === 'POST') {\n try {\n await writeCronBeat(firebaseAdmin.app().firestore(), WEEKLY_SUMMARY_JOB_ID)\n } catch {\n // A job that stops stamping reds its own row; it never fails the run.\n }\n }\n try {\n const rawCursor = body?.['cursor']\n const cursor =\n typeof rawCursor === 'string' && rawCursor.length > 0 && !rawCursor.includes('/') ? rawCursor : null\n const limit = Number(body?.['limit'])\n const report = await runWeeklySummarySweep(await weeklySummaryDeps({ runAt: new Date(), dryRun }), {\n cursor,\n ...(Number.isFinite(limit) && limit > 0 ? { limit } : {}),\n })\n return Response.json(report, { headers: { 'Cache-Control': 'no-store' } })\n } catch (error) {\n console.error('[weekly-summary] run failed', error)\n return Response.json({ error: 'Weekly summary run failed' }, { status: 500 })\n }\n}\n"],"names":["buildRoute","Route","writeCronBeat","DIGEST_PREFS_FIELD","digestEnabled","INSIGHT_DIGESTS_FIELD","insightDigestSubscribed","liveCustomDomain","TENANT_APEX","hostRoleFor","checkEntitlement","resolveBrandingProfile","resolveEffectivePlan","platformConsoleOrigin","isReleaseFlagOnForOrg","parseOrgReleaseFlagOverrides","composeWeeklySummaryOverviewEmail","composeWeeklySummarySiteEmail","WEEKLY_SUMMARY_DAYS","WEEKLY_SUMMARY_DIGEST_KEY","weeklySummaryHasNews","weeklySummaryPeriod","normalizePluginFigureTable","pluginFigureSummarySection","emailAccent","PLATFORM_EMAIL_PALETTE","rateLimitedRetryAtMs","resolveEmailColor","sendEmail","withoutMailWithheldAccounts","findUserByUidAcrossPools","isCronAuthorized","isCronDryRun","meterPlatformEmail","filterSuppressedEmails","eligibleFigureReaders","firebaseAdmin","listOrgMembers","getServerReleaseFlagValues","loadSystemEmail","renderLoadedSystemEmail","systemEmailBrand","systemEmailContent","systemEmailTheme","WEEKLY_SUMMARY_JOB_ID","WEEKLY_SUMMARY_MARKERS","WEEKLY_SUMMARY_OVERVIEW","WEEKLY_SUMMARY_TEMPLATE","WEEKLY_SUMMARY_SITE_TEMPLATE","WEEKLY_SUMMARY_ORG_CHUNK","WEEKLY_SUMMARY_HOST_READ_LIMIT","WEEKLY_SUMMARY_MAX_SITES","WEEKLY_SUMMARY_MAX_UID_LOOKUPS","WEEKLY_SUMMARY_RUN_BUDGET_MS","COLOR_TOKENS","text","muted","border","surface","accent","link","up","down","str","value","trim","siteName","data","id","weekViews","firestore","hosts","days","views","Map","length","refs","flatMap","host","map","day","ref","collection","doc","docs","getAll","hostId","parent","set","get","Number","weekDays","from","start","Date","parse","Array","_","index","toISOString","slice","readParts","readers","base","ordered","filter","reader","weeklySummary","sort","a","b","order","localeCompare","parts","summary","windows","includes","part","read","uid","params","ok","table","summarize","stats","rows","push","error","console","orgId","emailsFor","member","manager","sites","workspace","active","site","key","withWorkspace","summarizeOrg","deps","orgDoc","period","overBudget","org","report","recipients","sent","markerRef","week","sentAlready","members","orgMembers","orgSuspended","isManager","role","candidates","Object","values","hostAccess","reachable","Set","$id","reached","has","skipped","userDocs","users","hostPage","where","orderBy","FieldPath","documentId","limit","chosen","orgSlug","now","subdomain","domain","name","address","label","url","origin","HOST_DASHBOARD","orgReaders","branding","workspaceName","settingsUrl","MANAGE_NOTIFICATION_SETTINGS","colors","offersInsights","flagOn","analyticsUrl","HOST_ANALYTICS","common","productName","periodLabel","lookups","left","busiest","find","owed","subscribed","to","dryRun","entry","deferred","aiInsightsUrl","email","ORG_HOME","result","send","template","merge","subject","title","html","buttonLabel","updatedAt","FieldValue","serverTimestamp","rateLimited","failed","runWeeklySummarySweep","options","startedAt","budget","budgetMs","runAt","pageSize","Math","max","min","floor","query","cursor","startAfter","page","hasMore","orgDocs","swept","orgs","nextCursor","done","last","orgReport","weeklySummaryDeps","app","flags","loaded","flag","uids","context","accept","Boolean","palette","keys","toLowerCase","pooled","catch","record","kept","design","brand","rendered","content","fromName","priority","undefined","reason","weeklySummaryRoute","request","method","toUpperCase","Response","json","status","headers","Allow","process","env","fromEntries","entries","body","URL","searchParams","rawCursor","isFinite"],"mappings":"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"}