@aglyn/tenant-data-admin 1.0.0-beta.219 → 1.0.0-beta.220

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/package.json +8 -8
  2. package/src/lib/server/api-http.js +5 -1
  3. package/src/lib/server/api-http.js.map +1 -1
  4. package/src/lib/server/api-v1-kit.d.ts +7 -0
  5. package/src/lib/server/api-v1-kit.js.map +1 -1
  6. package/src/lib/server/api-v1-resources.d.ts +1 -2
  7. package/src/lib/server/api-v1-resources.js +0 -1
  8. package/src/lib/server/api-v1-resources.js.map +1 -1
  9. package/src/lib/server/campaign-conversion-attribution.d.ts +1 -1
  10. package/src/lib/server/campaign-conversion-attribution.js +3 -3
  11. package/src/lib/server/campaign-conversion-attribution.js.map +1 -1
  12. package/src/lib/server/card-payment-velocity.d.ts +9 -0
  13. package/src/lib/server/card-payment-velocity.js +28 -2
  14. package/src/lib/server/card-payment-velocity.js.map +1 -1
  15. package/src/lib/server/consent-group-carry.js +1 -1
  16. package/src/lib/server/consent-group-carry.js.map +1 -1
  17. package/src/lib/server/consent-group-change.js +2 -2
  18. package/src/lib/server/consent-group-change.js.map +1 -1
  19. package/src/lib/server/dynamic-list-materialize.js +3 -3
  20. package/src/lib/server/dynamic-list-materialize.js.map +1 -1
  21. package/src/lib/server/email-flow-gate.js +2 -2
  22. package/src/lib/server/email-flow-gate.js.map +1 -1
  23. package/src/lib/server/email-suppression.js +1 -1
  24. package/src/lib/server/email-suppression.js.map +1 -1
  25. package/src/lib/server/host-visitor-records.d.ts +32 -4
  26. package/src/lib/server/host-visitor-records.js +73 -14
  27. package/src/lib/server/host-visitor-records.js.map +1 -1
  28. package/src/lib/server/hosted-page-review.d.ts +27 -0
  29. package/src/lib/server/hosted-page-review.js +149 -12
  30. package/src/lib/server/hosted-page-review.js.map +1 -1
  31. package/src/lib/server/media-cdn-rate-limit.d.ts +6 -3
  32. package/src/lib/server/media-cdn-rate-limit.js +6 -3
  33. package/src/lib/server/media-cdn-rate-limit.js.map +1 -1
  34. package/src/lib/server/member-email-aliases.d.ts +9 -1
  35. package/src/lib/server/member-email-aliases.js +25 -1
  36. package/src/lib/server/member-email-aliases.js.map +1 -1
  37. package/src/lib/server/notifications.d.ts +14 -0
  38. package/src/lib/server/notifications.js +29 -0
  39. package/src/lib/server/notifications.js.map +1 -1
  40. package/src/lib/server/operator-alerts.d.ts +7 -1
  41. package/src/lib/server/operator-alerts.js +5 -3
  42. package/src/lib/server/operator-alerts.js.map +1 -1
  43. package/src/lib/server/org-containers.d.ts +66 -0
  44. package/src/lib/server/org-containers.js +95 -0
  45. package/src/lib/server/org-containers.js.map +1 -0
  46. package/src/lib/server/org-member-notice.js +3 -2
  47. package/src/lib/server/org-member-notice.js.map +1 -1
  48. package/src/lib/server/payment-fraud-signal.js +5 -1
  49. package/src/lib/server/payment-fraud-signal.js.map +1 -1
  50. package/src/lib/server/payment-provider-stripe-connect-payouts.js +35 -1
  51. package/src/lib/server/payment-provider-stripe-connect-payouts.js.map +1 -1
  52. package/src/lib/server/payment-risk-record.d.ts +1 -1
  53. package/src/lib/server/payment-risk-record.js +3 -1
  54. package/src/lib/server/payment-risk-record.js.map +1 -1
  55. package/src/lib/server/platform-marketing-consent.d.ts +1 -1
  56. package/src/lib/server/platform-marketing-consent.js +1 -1
  57. package/src/lib/server/platform-marketing-consent.js.map +1 -1
  58. package/src/lib/server/risk-notice.d.ts +32 -1
  59. package/src/lib/server/risk-notice.js +90 -28
  60. package/src/lib/server/risk-notice.js.map +1 -1
  61. package/src/lib/server/sending-domains.d.ts +17 -0
  62. package/src/lib/server/sending-domains.js +28 -4
  63. package/src/lib/server/sending-domains.js.map +1 -1
  64. package/src/lib/server/sso-enforcement.js +7 -2
  65. package/src/lib/server/sso-enforcement.js.map +1 -1
  66. package/src/lib/server/staff-alert-email.js +4 -2
  67. package/src/lib/server/staff-alert-email.js.map +1 -1
  68. package/src/lib/server/{email-topic-confirmation.js → topic-subscriptions.js} +15 -11
  69. package/src/lib/server/topic-subscriptions.js.map +1 -0
  70. package/src/lib/server/upsert-contact.js +2 -2
  71. package/src/lib/server/upsert-contact.js.map +1 -1
  72. package/src/tenancy.d.ts +1 -3
  73. package/src/tenancy.js +1 -7
  74. package/src/tenancy.js.map +1 -1
  75. package/src/lib/server/contact-email-engagement.d.ts +0 -32
  76. package/src/lib/server/contact-email-engagement.js +0 -145
  77. package/src/lib/server/contact-email-engagement.js.map +0 -1
  78. package/src/lib/server/crm-booking-activity.d.ts +0 -86
  79. package/src/lib/server/crm-booking-activity.js +0 -257
  80. package/src/lib/server/crm-booking-activity.js.map +0 -1
  81. package/src/lib/server/crm-inbound-email.d.ts +0 -168
  82. package/src/lib/server/crm-inbound-email.js +0 -282
  83. package/src/lib/server/crm-inbound-email.js.map +0 -1
  84. package/src/lib/server/email-topic-confirmation.js.map +0 -1
  85. /package/src/lib/server/{email-topic-confirmation.d.ts → topic-subscriptions.d.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/consent-group-change.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\n/**\n * RUNNING A CONSENT GROUP CHANGE (AGL-3320) — the executor.\n *\n * `consent-group-change.ts` in `app-utils` decides WHAT a change requires;\n * this carries it out, across as many invocations as it takes, without ever\n * leaving a refusal unhonored in between.\n *\n * ## The order is the safety argument\n *\n * 1. START — one transaction on the org document: the stored declaration is\n * the one the editor rendered, and no other change is in flight. It sets\n * the marker `consentGroupsChange` and creates the job document.\n * 2. CARRY — every carry of the plan through the three platform stores\n * (`consent-group-carry.ts`) and the organization's retained refusals\n * (`retained-refusals.ts`), then every participant's carry phase.\n * 3. CATCH UP — everything filed since the carry began, again.\n * 4. DECLARE — one transaction: the marker still names this change in its\n * carry phase and the stored declaration is still the one the job\n * started from. Only then is the new declaration written.\n * 5. CATCH UP — once more, for whatever raced the flip.\n * 6. RE-HOME — every participant's holder records.\n * 7. SWEEP — once the sweep delay has passed, the whole carry and every\n * participant's sweep, idempotently.\n * 8. DONE — the marker is cleared.\n *\n * The flip cannot happen before every carry and both catch-up passes are\n * done, because the steps run strictly in order and `declare` is only ever\n * reached from a finished `catch-up-pre`. Until the flip the declaration in\n * force is the old one, so a change that stalls or is canceled before it\n * has changed nothing anybody can observe — except refusals honored on more\n * sites, which is the safe direction. A rename moves nothing, and runs the\n * declare and finish alone.\n *\n * ## One invocation at a time, and any invocation may finish it\n *\n * The console's progress panel calls `continue` while it is open, and the\n * cron backstop calls every fifteen minutes. Each claims a LEASE on the job\n * by transaction and renews it on every unit it saves; a lease that lapses\n * (a process that died) is simply claimed by the next caller. A unit is one\n * page of one store, or one participant call, and every unit is idempotent,\n * so a unit re-run after a crash writes nothing it already wrote.\n *\n * ## A failure is retried, and five in a row are shown\n *\n * A unit that throws releases the lease and counts a failure against the job;\n * the next invocation retries it from its cursor. Five consecutive failures\n * mark the job STALLED — the activity log says so once, and the panel shows\n * the error with a retry — and the cron keeps trying. A participant's throw\n * is one of these: the change never flips past a participant that could not\n * carry its refusals (see `plugin-consent-group-change.ts`).\n */\n\nimport {\n CONSENT_GROUP_CHANGE_STEPS,\n CONSENT_GROUP_CHANGES_COLLECTION,\n CONSENT_GROUP_RENAME_STEPS,\n CONSENT_GROUP_SWEEP_DELAY_MS,\n CONSENT_GROUPS_CHANGE_FIELD,\n type ConsentGroupChangeCounts,\n type ConsentGroupChangeLine,\n type ConsentGroupChangePlan,\n type ConsentGroupChangePreview,\n type ConsentGroupChangeStatus,\n type ConsentGroupChangeStep,\n type ConsentGroupChangeProgress,\n type ConsentGroupDeclaration,\n type ConsentGroupsApiInFlight,\n type ConsentGroupsApiRefusal,\n type ConsentGroupsApiStale,\n type ConsentGroupsChangeMarker,\n type ConsentGroupsChangePhase,\n consentGroupChangePhaseOf,\n consentGroupDisclosureChanges,\n consentGroupsFingerprint,\n describeConsentGroupChangeLine,\n discardedConsentGroupIds,\n estimateConsentGroupChange,\n orgSiteIds,\n planConsentGroupChange,\n readConsentGroupsChange,\n validateConsentGroupDeclaration,\n} from '@aglyn/aglyn/app-utils/consent-group-change'\nimport {\n CONSENT_GROUPS_FIELD,\n consentGroupsAwaitConfirmation,\n readConsentGroups,\n} from '@aglyn/aglyn/app-utils/consent-groups'\nimport { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport {\n DEFAULT_EMAIL_TOPICS,\n TOPIC_OPT_OUTS_SUBCOLLECTION,\n} from '@aglyn/aglyn/app-utils/email-topics'\nimport {\n MARKETING_CONSENT_BY_HOST_FIELD,\n MARKETING_CONSENT_FIELD,\n resolveMarketingConsentPolicy,\n} from '@aglyn/aglyn/app-utils/marketing-consent'\nimport { isOrgWideMember } from '@aglyn/aglyn/app-utils/organizations'\nimport {\n type ConsentGroupChangeParticipant,\n type ConsentGroupChangeParticipantPhase,\n listPluginConsentGroupParticipants,\n previewPluginConsentGroupChange,\n} from '@aglyn/aglyn/plugin-manager/plugin-consent-group-change'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport {\n CONSENT_GROUP_CARRY_STORES,\n CONSENT_GROUP_CARRY_SUBCOLLECTIONS,\n type ConsentGroupCarryStore,\n carryConsentGroupRefusals,\n} from './consent-group-carry'\nimport firebaseAdmin from './firebase-admin'\nimport { getLockdownVerdict } from './lockdown'\nimport { logOrgActivity } from './organizations'\nimport { carryRetainedRefusals, retainedRefusalsStore } from './retained-refusals'\n\n/** How long a claimed job stays claimed without a renewal. */\nexport const CONSENT_GROUP_CHANGE_LEASE_MS = 90_000\n\n/** Consecutive failed units before a job is shown as stalled. */\nexport const CONSENT_GROUP_CHANGE_STALL_AFTER = 5\n\n/**\n * How far before the carry began a catch-up reads. A refusal filed in the\n * seconds the carry's first page was being read, with a clock a little\n * behind, is inside it.\n */\nconst CATCH_UP_OVERLAP_MS = 5 * 60_000\n\n/** Left before a deadline for the last save. */\nconst DEADLINE_SAFETY_MS = 5_000\n\n/** The phases the cron looks for a change in. */\nconst RUNNING_PHASES: readonly ConsentGroupsChangePhase[] = ['carry', 'rehome', 'sweep']\n\n/** Who asked, as the activity log records it. */\nexport interface ConsentGroupChangeActor {\n uid: string | null\n email?: string | null\n}\n\n/** Everything the executor reaches outside itself, injectable for the specs. */\nexport interface ConsentGroupChangeContext {\n firestore?: FirebaseFirestore.Firestore\n log?: typeof logOrgActivity\n now?: () => number\n /** Whether a lockdown refuses the cron acting for this org and actor. */\n lockedOut?: (org: Record<string, unknown>, uid: string | null) => Promise<boolean>\n}\n\ninterface Context {\n firestore: FirebaseFirestore.Firestore\n log: typeof logOrgActivity\n now: () => number\n lockedOut: (org: Record<string, unknown>, uid: string | null) => Promise<boolean>\n}\n\nfunction contextOf(input: ConsentGroupChangeContext): Context {\n return {\n firestore: input.firestore ?? firebaseAdmin.app().firestore(),\n log: input.log ?? logOrgActivity,\n now: input.now ?? Date.now,\n lockedOut:\n input.lockedOut ??\n (async (org, uid) =>\n (await getLockdownVerdict({ org: org as never, uid, intent: 'write' })) !== null),\n }\n}\n\n/** Where a unit of the current step stands. */\ninterface UnitState {\n cursor: string | null\n done: boolean\n}\n\n/** The job document, `orgs/{orgId}/consentGroupChanges/{changeId}`. */\nexport interface ConsentGroupChangeJob {\n changeId: string\n orgId: string\n status: ConsentGroupChangeStatus\n step: ConsentGroupChangeStep\n /** The marker's phase while running; `done` or `canceled` after. */\n phase: ConsentGroupsChangePhase | 'done' | 'canceled'\n /** The steps this change runs, in order. */\n steps: ConsentGroupChangeStep[]\n /** The stored declaration it started from: what the flip re-checks. */\n beforeFingerprint: string\n before: ConsentGroupDeclaration\n after: ConsentGroupDeclaration\n plan: Omit<ConsentGroupChangePlan, 'before' | 'after'>\n /** The plan's lines as the activity log writes them. */\n lines: string[]\n siteNames: Record<string, string>\n /** The participants registered when it started — every one of them runs. */\n participants: string[]\n /** The current step's units, by key. */\n units: Record<string, UnitState>\n counts: ConsentGroupChangeCounts\n sitesReceiving: string[]\n plugins: Record<string, Record<string, number>>\n lease: { owner: string; untilMs: number } | null\n failures: number\n stalled: boolean\n lastError: string | null\n actor: { uid: string | null; email: string | null }\n startedAtMs: number\n carryStartedAtMs: number\n declaredAtMs: number | null\n finishedAtMs: number | null\n canceledAtMs: number | null\n}\n\n/** What the route answers for a job. */\nexport interface ConsentGroupChangeStatusBody {\n changeId: string\n phase: ConsentGroupsChangePhase | 'done' | 'canceled'\n done: boolean\n progress: ConsentGroupChangeProgress\n}\n\n/** A refusal the route turns into its status code and body. */\nexport type ConsentGroupChangeRefusal =\n | { ok: false; status: 400; body: ConsentGroupsApiRefusal }\n | { ok: false; status: 404; body: { error: string } }\n | { ok: false; status: 409; body: ConsentGroupsApiStale | ConsentGroupsApiInFlight }\n\nconst NOT_FOUND = { ok: false as const, status: 404 as const, body: { error: 'No such consent group change' } }\n\n/** A caller whose lease was taken, or whose job was canceled, under it. */\nclass LeaseLost extends Error {}\n\nconst orgDoc = (ctx: Context, orgId: string) => ctx.firestore.collection('orgs').doc(orgId)\nconst jobDoc = (ctx: Context, orgId: string, changeId: string) =>\n orgDoc(ctx, orgId).collection(CONSENT_GROUP_CHANGES_COLLECTION).doc(changeId)\n\n/** The job as the plan the participants and the carries read. */\nfunction planOf(job: ConsentGroupChangeJob): ConsentGroupChangePlan {\n return { ...job.plan, before: job.before, after: job.after }\n}\n\n/** An error as the job records it: bounded, and with no address in it. */\nfunction sanitizeError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error ?? 'Unknown error')\n return message.replace(/[^\\s@]+@[^\\s@]+/g, '[address]').slice(0, 300) || 'Unknown error'\n}\n\nexport function consentGroupChangeStatus(\n job: ConsentGroupChangeJob,\n nowMs: number = Date.now(),\n): ConsentGroupChangeStatusBody {\n const declaredAtMs = job.declaredAtMs ?? null\n const index = job.steps.indexOf(job.step)\n return {\n changeId: job.changeId,\n phase: job.phase,\n done: job.status !== 'running',\n progress: {\n status: job.status,\n step: job.step,\n stepIndex: index >= 0 ? index + 1 : job.steps.length,\n stepCount: job.steps.length,\n declaredAtMs,\n sweepAtMs:\n declaredAtMs !== null && !job.plan.renameOnly\n ? declaredAtMs + CONSENT_GROUP_SWEEP_DELAY_MS\n : null,\n stalled: job.stalled === true,\n failures: job.failures ?? 0,\n lastError: job.lastError ?? null,\n leaseUntilMs: job.lease && job.lease.untilMs > nowMs ? job.lease.untilMs : null,\n // A job started before the retained store was carried has no count for it.\n counts: { retainedRefusals: 0, ...job.counts },\n sitesReceiving: job.sitesReceiving?.length ?? 0,\n plugins: job.plugins ?? {},\n },\n }\n}\n\n/*==========================================\n * VALIDATING AND PLANNING — shared by the preview and the start\n *=========================================*/\n\ninterface Prepared {\n org: Record<string, unknown>\n before: ConsentGroupDeclaration\n after: ConsentGroupDeclaration\n plan: ConsentGroupChangePlan\n siteIds: string[]\n}\n\nfunction prepare(\n org: Record<string, unknown>,\n expected: unknown,\n groups: unknown,\n): Prepared | ConsentGroupChangeRefusal {\n const marker = readConsentGroupsChange(org)\n if (marker) {\n return {\n ok: false,\n status: 409,\n body: {\n error: 'A consent group change is still finishing',\n changeId: marker.changeId,\n phase: marker.phase,\n },\n }\n }\n const raw = org[CONSENT_GROUPS_FIELD]\n if (consentGroupsFingerprint(raw) !== consentGroupsFingerprint(expected)) {\n return {\n ok: false,\n status: 409,\n body: {\n error: 'Someone else changed consent groups while you were editing',\n current:\n raw && typeof raw === 'object' && !Array.isArray(raw)\n ? (raw as Record<string, unknown>)\n : null,\n },\n }\n }\n const before = readConsentGroups(org)\n const siteIds = orgSiteIds(org)\n const validation = validateConsentGroupDeclaration({\n groups,\n before,\n siteIds,\n reserved:\n raw && typeof raw === 'object' && !Array.isArray(raw)\n ? Object.keys(raw as Record<string, unknown>)\n : [],\n })\n if (!validation.ok) {\n return {\n ok: false,\n status: 400,\n body: {\n error: 'Those consent groups cannot be saved',\n errors: (validation as { errors: ConsentGroupsApiRefusal['errors'] }).errors,\n },\n }\n }\n const after = (validation as { after: ConsentGroupDeclaration }).after\n return {\n org,\n before,\n after,\n plan: planConsentGroupChange(before, after, { siteIds }),\n siteIds,\n }\n}\n\nconst isRefusal = (value: unknown): value is ConsentGroupChangeRefusal =>\n Boolean(value && typeof value === 'object' && (value as { ok?: unknown }).ok === false)\n\n/** Every site a plan names, for the names its lines are written with. */\nfunction plannedSites(plan: ConsentGroupChangePlan): string[] {\n return [\n ...new Set([\n ...plan.hostIds,\n ...Object.values(plan.before).flatMap((group) => group.hostIds),\n ...Object.values(plan.after).flatMap((group) => group.hostIds),\n ]),\n ]\n}\n\n/** What the console calls each site: its name, its subdomain, or its id. */\nasync function siteNamesFor(ctx: Context, hostIds: readonly string[]): Promise<Record<string, string>> {\n if (!hostIds.length) return {}\n const hosts = ctx.firestore.collection('hosts')\n const snapshots = await ctx.firestore.getAll(...hostIds.map((id) => hosts.doc(id)))\n const names: Record<string, string> = {}\n snapshots.forEach((snapshot, index) => {\n const id = hostIds[index]\n names[id] = String(snapshot.get('name') || snapshot.get('subdomain') || id)\n })\n return names\n}\n\nconst describeLines = (lines: readonly ConsentGroupChangeLine[], names: Record<string, string>) =>\n lines.map((line) => describeConsentGroupChangeLine(line, (hostId) => names[hostId] ?? hostId))\n\n/*==========================================\n * THE PREVIEW\n *=========================================*/\n\nasync function countOf(query: FirebaseFirestore.Query): Promise<number> {\n const snapshot = await query.count().get()\n return Number(snapshot.data().count) || 0\n}\n\n/**\n * What a change would do, counted and worded, writing nothing.\n *\n * Refused exactly as `apply` would be — a change in flight, a stale\n * `expected`, an invalid declaration — so the review step never shows a\n * change the next click cannot make.\n */\nexport async function previewConsentGroupChange(\n input: {\n orgId: string\n expected: unknown\n groups: unknown\n /** The org document, when the caller already read it. */\n org?: Record<string, unknown> | null\n } & ConsentGroupChangeContext,\n): Promise<{ ok: true; preview: ConsentGroupChangePreview } | ConsentGroupChangeRefusal> {\n const ctx = contextOf(input)\n const org =\n input.org ?? ((await orgDoc(ctx, input.orgId).get()).data() as Record<string, unknown> | undefined)\n if (!org) return { ok: false, status: 404, body: { error: 'No such workspace' } }\n const prepared = prepare(org, input.expected, input.groups)\n if (isRefusal(prepared)) return prepared\n const { plan, before, after } = prepared as Prepared\n\n const db = ctx.firestore\n const cache = new Map<string, Promise<number>>()\n const cached = (key: string, query: () => FirebaseFirestore.Query) => {\n let pending = cache.get(key)\n if (!pending) {\n pending = countOf(query())\n cache.set(key, pending)\n }\n return pending\n }\n const store = (hostId: string, name: ConsentGroupCarryStore) =>\n db.collection('hosts').doc(hostId).collection(CONSENT_GROUP_CARRY_SUBCOLLECTIONS[name])\n const suppressions = (hostId: string) => cached(`s:${hostId}`, () => store(hostId, 'siteSuppressions'))\n const optOuts = (hostId: string) => cached(`t:${hostId}`, () => store(hostId, 'topicOptOuts'))\n const paces = (hostId: string) =>\n cached(`p:${hostId}`, () => store(hostId, 'paces').where('cadence', '!=', null))\n const retained = (hostId: string) =>\n cached(`r:${hostId}`, () =>\n retainedRefusalsStore(orgDoc(ctx, input.orgId)).where(\n `${MARKETING_CONSENT_BY_HOST_FIELD}.${hostId}.${MARKETING_CONSENT_FIELD}`,\n '==',\n false,\n ),\n )\n\n const carries = await Promise.all(\n plan.carries.map(async (carry) => ({\n toHostId: carry.toHostId,\n fromHostId: carry.fromHostId,\n siteSuppressions: await suppressions(carry.fromHostId),\n topicOptOuts: await optOuts(carry.fromHostId),\n paces: await paces(carry.fromHostId),\n retainedRefusals: await retained(carry.fromHostId),\n })),\n )\n\n const membersIn = (declaration: ConsentGroupDeclaration, hostId: string) =>\n Object.values(declaration).find((group) => group.hostIds.includes(hostId))?.hostIds ?? [hostId]\n const joining: Array<{ hostId: string; siblings: string[] }> = []\n for (const hostId of [...new Set(Object.values(after).flatMap((group) => group.hostIds))].sort()) {\n const had = new Set(membersIn(before, hostId))\n const siblings = membersIn(after, hostId).filter((id) => id !== hostId && !had.has(id))\n if (siblings.length) joining.push({ hostId, siblings })\n }\n const inherited = await Promise.all(\n joining.map(async ({ hostId, siblings }) => {\n let refusals = 0\n for (const sibling of siblings) {\n refusals += (await suppressions(sibling)) + (await optOuts(sibling))\n }\n return { hostId, refusals }\n }),\n )\n\n /*\n * A pending confirmation holds a sibling's mail only with the switch on,\n * so only then does a separation release anything. Counted over the\n * built-in streams: the catalog of an organization's own streams belongs to\n * the Email plugin, and a stream it added is not counted here.\n */\n const pendingHolds: ConsentGroupChangePreview['pendingHolds'] = []\n if (consentGroupsAwaitConfirmation(org)) {\n const released = new Map<string, string[]>()\n for (const carry of plan.carries) {\n released.set(carry.toHostId, [...(released.get(carry.toHostId) ?? []), carry.fromHostId])\n }\n for (const [hostId, releasedHostIds] of [...released.entries()].sort()) {\n for (const topic of DEFAULT_EMAIL_TOPICS) {\n const count = await countOf(\n db\n .collection('hosts')\n .doc(hostId)\n .collection(TOPIC_OPT_OUTS_SUBCOLLECTION)\n .where(`topics.${topic.id}.confirmedAt`, '==', null),\n )\n if (count > 0) pendingHolds.push({ hostId, topicId: topic.id, count, releasedHostIds })\n }\n }\n }\n\n const changedGroups = Object.entries(after)\n .filter(([id, group]) => {\n const was = before[id]\n return !was || was.hostIds.join('\\n') !== group.hostIds.join('\\n')\n })\n .map(([, group]) => group.hostIds)\n let partialAccessMembers = 0\n if (changedGroups.length) {\n const members = await orgDoc(ctx, input.orgId).collection('members').get()\n for (const member of members.docs) {\n const data = member.data() as Record<string, unknown>\n if (isOrgWideMember(data)) continue\n const reach = new Set(Object.keys((data['hostAccess'] ?? {}) as Record<string, unknown>))\n if (\n changedGroups.some(\n (hostIds) =>\n hostIds.some((id) => reach.has(id)) && !hostIds.every((id) => reach.has(id)),\n )\n ) {\n partialAccessMembers += 1\n }\n }\n }\n\n const policy = resolveMarketingConsentPolicy(org['marketingConsentPolicy'])\n const forwardPolicyWarning =\n policy.mode === 'forward' && joining.length\n ? { hostIds: joining.map((entry) => entry.hostId), enforceFromMs: policy.enforceFromMs }\n : null\n\n const participants = await previewPluginConsentGroupChange({ orgId: input.orgId, plan })\n const names = await siteNamesFor(ctx, plannedSites(plan))\n const texts = describeLines(plan.lines, names)\n const documents =\n carries.reduce(\n (total, carry) =>\n total + carry.siteSuppressions + carry.topicOptOuts + carry.paces + carry.retainedRefusals,\n 0,\n ) +\n participants.reduce(\n (total, entry) =>\n total + (entry.lines ?? []).reduce((sum, line) => sum + (line.count ?? 0), 0),\n 0,\n )\n\n return {\n ok: true,\n preview: {\n before,\n after,\n discarded: discardedConsentGroupIds(org),\n lines: plan.lines.map((line, index) => ({ ...line, text: texts[index] })),\n disclosures: consentGroupDisclosureChanges(before, after),\n carries,\n inherited,\n pendingHolds,\n partialAccessMembers,\n forwardPolicyWarning,\n participants,\n capturesDisclosing: ['form'],\n estimate: estimateConsentGroupChange(plan, documents),\n },\n }\n}\n\n/*==========================================\n * STARTING\n *=========================================*/\n\n/**\n * Starts a change: the org marker and the job, in one transaction that\n * refuses a change in flight, a stale `expected` and an invalid declaration.\n * Writes nothing else — the caller advances it.\n */\nexport async function startConsentGroupChange(\n input: {\n orgId: string\n actor: ConsentGroupChangeActor\n expected: unknown\n groups: unknown\n } & ConsentGroupChangeContext,\n): Promise<{ ok: true; changeId: string; job: ConsentGroupChangeJob } | ConsentGroupChangeRefusal> {\n const ctx = contextOf(input)\n const orgRef = orgDoc(ctx, input.orgId)\n const snapshot = await orgRef.get()\n if (!snapshot.exists) return { ok: false, status: 404, body: { error: 'No such workspace' } }\n const outside = prepare(snapshot.data() as Record<string, unknown>, input.expected, input.groups)\n if (isRefusal(outside)) return outside\n // Names are words for the log, read once and outside the transaction.\n const names = await siteNamesFor(ctx, plannedSites((outside as Prepared).plan))\n const participants = listPluginConsentGroupParticipants().map((entry) => entry.pluginId)\n const changeId = createResourceUid()\n const jobRef = jobDoc(ctx, input.orgId, changeId)\n const nowMs = ctx.now()\n\n const started = await ctx.firestore.runTransaction(async (transaction) => {\n const current = await transaction.get(orgRef)\n if (!current.exists) {\n return { ok: false as const, status: 404 as const, body: { error: 'No such workspace' } }\n }\n const prepared = prepare(current.data() as Record<string, unknown>, input.expected, input.groups)\n if (isRefusal(prepared)) return prepared\n const { plan, before, after, org } = prepared as Prepared\n const steps = plan.renameOnly ? [...CONSENT_GROUP_RENAME_STEPS] : [...CONSENT_GROUP_CHANGE_STEPS]\n const job: ConsentGroupChangeJob = {\n changeId,\n orgId: input.orgId,\n status: 'running',\n step: steps[0],\n phase: 'carry',\n steps,\n beforeFingerprint: consentGroupsFingerprint(org[CONSENT_GROUPS_FIELD]),\n before,\n after,\n plan: {\n carries: plan.carries,\n flows: plan.flows,\n lines: plan.lines,\n hostIds: plan.hostIds,\n renameOnly: plan.renameOnly,\n },\n lines: describeLines(plan.lines, names),\n siteNames: names,\n participants: plan.renameOnly ? [] : participants,\n units: {},\n counts: { siteSuppressions: 0, topicOptOuts: 0, paces: 0, retainedRefusals: 0 },\n sitesReceiving: [],\n plugins: {},\n lease: null,\n failures: 0,\n stalled: false,\n lastError: null,\n actor: { uid: input.actor.uid ?? null, email: input.actor.email ?? null },\n startedAtMs: nowMs,\n carryStartedAtMs: nowMs,\n declaredAtMs: null,\n finishedAtMs: null,\n canceledAtMs: null,\n }\n const marker: ConsentGroupsChangeMarker = {\n changeId,\n phase: 'carry',\n hostIds: plan.hostIds,\n startedAtMs: nowMs,\n }\n transaction.update(orgRef, {\n [CONSENT_GROUPS_CHANGE_FIELD]: marker,\n updatedAt: FieldValue.serverTimestamp(),\n })\n transaction.create(jobRef, {\n ...job,\n createdAt: FieldValue.serverTimestamp(),\n updatedAt: FieldValue.serverTimestamp(),\n })\n return { ok: true as const, job }\n })\n if (isRefusal(started)) return started\n const { job } = started as { job: ConsentGroupChangeJob }\n await ctx.log(\n input.orgId,\n { uid: job.actor.uid, email: job.actor.email },\n `Started a consent group change: ${job.lines.join('; ')}`,\n { type: 'org', id: input.orgId },\n )\n return { ok: true, changeId, job }\n}\n\n/*==========================================\n * ADVANCING\n *=========================================*/\n\ntype Unit =\n | { kind: 'carry'; key: string; carryIndex: number; store: ConsentGroupCarryStore; sinceMs: number | null }\n | { kind: 'retained'; key: string; carryIndex: number; sinceMs: number | null }\n | { kind: 'participant'; key: string; pluginId: string; phase: ConsentGroupChangeParticipantPhase }\n\n/** The current step's units, in the order they run. */\nfunction unitsOf(job: ConsentGroupChangeJob): Unit[] {\n const carries = (sinceMs: number | null): Unit[] =>\n job.plan.carries.flatMap((_carry, carryIndex): Unit[] => [\n ...CONSENT_GROUP_CARRY_STORES.map((store) => ({\n kind: 'carry' as const,\n key: `c${carryIndex}:${store}`,\n carryIndex,\n store,\n sinceMs,\n })),\n { kind: 'retained' as const, key: `c${carryIndex}:retainedRefusals`, carryIndex, sinceMs },\n ])\n const participants = (phase: ConsentGroupChangeParticipantPhase): Unit[] =>\n job.participants.map((pluginId) => ({\n kind: 'participant' as const,\n key: `p:${pluginId}`,\n pluginId,\n phase,\n }))\n const since = job.carryStartedAtMs - CATCH_UP_OVERLAP_MS\n switch (job.step) {\n case 'carry':\n return [...carries(null), ...participants('carry')]\n case 'catch-up-pre':\n case 'catch-up-post':\n return [...carries(since), ...participants('carry')]\n case 'rehome':\n return participants('rehome')\n case 'sweep':\n return [...carries(null), ...participants('sweep')]\n default:\n return []\n }\n}\n\nfunction jobFrom(snapshot: FirebaseFirestore.DocumentSnapshot): ConsentGroupChangeJob | null {\n return snapshot.exists ? (snapshot.data() as ConsentGroupChangeJob) : null\n}\n\n/**\n * Saves a unit's result, renewing the lease — only while the job is still\n * running and still this caller's.\n */\nasync function save(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n owner: string,\n fields: Partial<ConsentGroupChangeJob>,\n extra?: (transaction: FirebaseFirestore.Transaction, job: ConsentGroupChangeJob) => Promise<void>,\n): Promise<ConsentGroupChangeJob> {\n return ctx.firestore.runTransaction(async (transaction) => {\n const job = jobFrom(await transaction.get(jobRef))\n if (!job || job.status !== 'running' || job.lease?.owner !== owner) throw new LeaseLost()\n if (extra) await extra(transaction, job)\n const next: Partial<ConsentGroupChangeJob> = {\n ...fields,\n lease: { owner, untilMs: ctx.now() + CONSENT_GROUP_CHANGE_LEASE_MS },\n failures: 0,\n stalled: false,\n lastError: null,\n }\n transaction.update(jobRef, { ...next, updatedAt: FieldValue.serverTimestamp() })\n return { ...job, ...next }\n })\n}\n\nfunction addCounts(\n base: Record<string, number> | undefined,\n more: Record<string, number>,\n): Record<string, number> {\n const out: Record<string, number> = { ...(base ?? {}) }\n for (const [key, value] of Object.entries(more)) {\n if (typeof value === 'number' && Number.isFinite(value)) out[key] = (out[key] ?? 0) + value\n }\n return out\n}\n\nasync function runUnit(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n job: ConsentGroupChangeJob,\n owner: string,\n unit: Unit,\n deadlineMs: number,\n): Promise<ConsentGroupChangeJob> {\n const state = job.units[unit.key] ?? { cursor: null, done: false }\n if (unit.kind === 'carry' || unit.kind === 'retained') {\n const carry = job.plan.carries[unit.carryIndex]\n const page =\n unit.kind === 'carry'\n ? await carryConsentGroupRefusals({\n firestore: ctx.firestore,\n store: unit.store,\n carry,\n changeId: job.changeId,\n cursor: state.cursor,\n sinceMs: unit.sinceMs,\n })\n : await carryRetainedRefusals({\n firestore: ctx.firestore,\n orgId: job.orgId,\n carry,\n changeId: job.changeId,\n cursor: state.cursor,\n sinceMs: unit.sinceMs,\n })\n const counted = unit.kind === 'carry' ? unit.store : 'retainedRefusals'\n const counts = { ...job.counts, [counted]: (job.counts[counted] ?? 0) + page.written }\n const sitesReceiving =\n page.written > 0 && !job.sitesReceiving.includes(carry.toHostId)\n ? [...job.sitesReceiving, carry.toHostId]\n : job.sitesReceiving\n return save(ctx, jobRef, owner, {\n units: { ...job.units, [unit.key]: { cursor: page.cursor, done: page.done } },\n counts,\n sitesReceiving,\n })\n }\n const participant = participantFor(unit.pluginId)\n const result = await participant.run({\n orgId: job.orgId,\n changeId: job.changeId,\n plan: planOf(job),\n phase: unit.phase,\n cursor: state.cursor,\n deadlineMs: deadlineMs - DEADLINE_SAFETY_MS,\n dryRun: false,\n })\n return save(ctx, jobRef, owner, {\n units: { ...job.units, [unit.key]: { cursor: result.cursor ?? null, done: result.done === true } },\n plugins: { ...job.plugins, [unit.pluginId]: addCounts(job.plugins[unit.pluginId], result.counts ?? {}) },\n })\n}\n\nfunction participantFor(pluginId: string): ConsentGroupChangeParticipant {\n const entry = listPluginConsentGroupParticipants().find((candidate) => candidate.pluginId === pluginId)\n if (!entry) {\n // The change started with this participant; running on without it would\n // skip its share. Another process — one whose declarations registered it —\n // picks the job up.\n throw new Error(`The ${pluginId} plugin's part of this change is not available here`)\n }\n return entry.participant\n}\n\n/** Moves the job to the step after its current one. */\nasync function nextStep(\n ctx: Context,\n orgId: string,\n jobRef: FirebaseFirestore.DocumentReference,\n job: ConsentGroupChangeJob,\n owner: string,\n): Promise<ConsentGroupChangeJob> {\n const next = job.steps[job.steps.indexOf(job.step) + 1] ?? 'finish'\n const phase = consentGroupChangePhaseOf(next)\n if (phase === job.phase) return save(ctx, jobRef, owner, { step: next, units: {} })\n // Entering the sweep: the marker says so, for the readers that hold off\n // while holder records may still be moving.\n const orgRef = orgDoc(ctx, orgId)\n return save(ctx, jobRef, owner, { step: next, units: {}, phase }, async (transaction) => {\n const marker = readConsentGroupsChange((await transaction.get(orgRef)).data())\n if (marker?.changeId === job.changeId) {\n transaction.update(orgRef, { [`${CONSENT_GROUPS_CHANGE_FIELD}.phase`]: phase })\n }\n })\n}\n\n/**\n * THE FLIP — the one write of `consentGroups` anywhere in the platform.\n *\n * The marker must still name this change in its carry phase and the stored\n * declaration must be the one the job started from; otherwise nothing is\n * written and the unit fails. `update`, not a merge, so a removed group's id\n * vanishes with it; an empty declaration deletes the field.\n */\nasync function declare(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n job: ConsentGroupChangeJob,\n owner: string,\n): Promise<ConsentGroupChangeJob> {\n const orgRef = orgDoc(ctx, job.orgId)\n const nowMs = ctx.now()\n const renameOnly = job.plan.renameOnly\n const declared = await save(\n ctx,\n jobRef,\n owner,\n renameOnly\n ? {\n step: 'finish',\n status: 'done',\n phase: 'done',\n declaredAtMs: nowMs,\n finishedAtMs: nowMs,\n units: {},\n }\n : { step: 'catch-up-post', phase: 'rehome', declaredAtMs: nowMs, units: {} },\n async (transaction) => {\n const org = (await transaction.get(orgRef)).data() as Record<string, unknown> | undefined\n const marker = readConsentGroupsChange(org)\n if (!org || marker?.changeId !== job.changeId || marker.phase !== 'carry') {\n throw new Error('The change no longer holds the organization, so the new groups were not saved')\n }\n if (consentGroupsFingerprint(org[CONSENT_GROUPS_FIELD]) !== job.beforeFingerprint) {\n throw new Error('The consent groups changed outside this change, so the new groups were not saved')\n }\n transaction.update(orgRef, {\n [CONSENT_GROUPS_FIELD]: Object.keys(job.after).length ? job.after : FieldValue.delete(),\n [CONSENT_GROUPS_CHANGE_FIELD]: renameOnly\n ? FieldValue.delete()\n : { ...marker, phase: 'rehome', declaredAtMs: nowMs },\n updatedAt: FieldValue.serverTimestamp(),\n })\n },\n )\n const finished = renameOnly ? { ...declared, lease: null } : declared\n if (renameOnly) await jobRef.update({ lease: null })\n for (const line of job.lines) {\n await ctx.log(job.orgId, job.actor, line, { type: 'org', id: job.orgId })\n }\n if (renameOnly) {\n await ctx.log(job.orgId, job.actor, 'Finished a consent group change', {\n type: 'org',\n id: job.orgId,\n })\n }\n return finished\n}\n\n/** The \"Finished\" line: the carries' totals, then each participant's clause. */\nfunction finishedLine(job: ConsentGroupChangeJob): string {\n const copied =\n job.counts.siteSuppressions +\n job.counts.topicOptOuts +\n job.counts.paces +\n (job.counts.retainedRefusals ?? 0)\n const sites = job.sitesReceiving.length\n const clauses = [\n `${copied} ${copied === 1 ? 'opt-out' : 'opt-outs'} copied to ${sites} ${sites === 1 ? 'site' : 'sites'}`,\n ]\n for (const pluginId of job.participants) {\n const entry = listPluginConsentGroupParticipants().find((candidate) => candidate.pluginId === pluginId)\n const clause = entry?.participant.summarize?.(job.plugins[pluginId] ?? {})\n if (clause) clauses.push(clause)\n }\n return `Finished a consent group change: ${clauses.join('; ')}`\n}\n\nasync function finish(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n job: ConsentGroupChangeJob,\n owner: string,\n): Promise<ConsentGroupChangeJob> {\n const orgRef = orgDoc(ctx, job.orgId)\n const nowMs = ctx.now()\n const done = await save(\n ctx,\n jobRef,\n owner,\n { status: 'done', phase: 'done', step: 'finish', finishedAtMs: nowMs, units: {} },\n async (transaction) => {\n const marker = readConsentGroupsChange((await transaction.get(orgRef)).data())\n if (marker?.changeId === job.changeId) {\n transaction.update(orgRef, {\n [CONSENT_GROUPS_CHANGE_FIELD]: FieldValue.delete(),\n updatedAt: FieldValue.serverTimestamp(),\n })\n }\n },\n )\n await jobRef.update({ lease: null })\n await ctx.log(job.orgId, job.actor, finishedLine(done), { type: 'org', id: job.orgId })\n return { ...done, lease: null }\n}\n\n/**\n * One unit of work, or nothing when the job is waiting for its sweep.\n */\nasync function advanceOnce(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n job: ConsentGroupChangeJob,\n owner: string,\n deadlineMs: number,\n): Promise<{ job: ConsentGroupChangeJob; idle: boolean }> {\n if (job.step === 'declare') return { job: await declare(ctx, jobRef, job, owner), idle: false }\n if (job.step === 'finish') return { job: await finish(ctx, jobRef, job, owner), idle: false }\n if (job.step === 'sweep-wait') {\n if (ctx.now() < (job.declaredAtMs ?? 0) + CONSENT_GROUP_SWEEP_DELAY_MS) return { job, idle: true }\n return { job: await nextStep(ctx, job.orgId, jobRef, job, owner), idle: false }\n }\n const unit = unitsOf(job).find((candidate) => !job.units[candidate.key]?.done)\n if (!unit) return { job: await nextStep(ctx, job.orgId, jobRef, job, owner), idle: false }\n const before = job.units[unit.key]?.cursor ?? null\n const after = await runUnit(ctx, jobRef, job, owner, unit, deadlineMs)\n const state = after.units[unit.key]\n // A participant that stopped where it started — out of time before its\n // first page — has nothing more to give this invocation.\n return { job: after, idle: unit.kind === 'participant' && !state?.done && (state?.cursor ?? null) === before }\n}\n\n/** Claims the lease, or answers why not. `null` for a job that does not exist. */\nasync function claim(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n owner: string,\n): Promise<{ job: ConsentGroupChangeJob; mine: boolean } | null> {\n return ctx.firestore.runTransaction(async (transaction) => {\n const job = jobFrom(await transaction.get(jobRef))\n if (!job) return null\n const nowMs = ctx.now()\n if (job.status !== 'running') return { job, mine: false }\n if (job.lease && job.lease.untilMs > nowMs && job.lease.owner !== owner) return { job, mine: false }\n const lease = { owner, untilMs: nowMs + CONSENT_GROUP_CHANGE_LEASE_MS }\n transaction.update(jobRef, { lease })\n return { job: { ...job, lease }, mine: true }\n })\n}\n\nasync function release(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n owner: string,\n): Promise<ConsentGroupChangeJob | null> {\n return ctx.firestore.runTransaction(async (transaction) => {\n const job = jobFrom(await transaction.get(jobRef))\n if (!job) return null\n if (job.lease?.owner !== owner) return job\n transaction.update(jobRef, { lease: null })\n return { ...job, lease: null }\n })\n}\n\n/** Counts a failed unit against the job, and says so once when it stalls. */\nasync function recordFailure(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n owner: string,\n error: unknown,\n): Promise<ConsentGroupChangeJob | null> {\n const lastError = sanitizeError(error)\n const outcome = await ctx.firestore.runTransaction(async (transaction) => {\n const job = jobFrom(await transaction.get(jobRef))\n if (!job) return null\n if (job.lease?.owner !== owner) return { job, newlyStalled: false }\n const failures = (job.failures ?? 0) + 1\n const stalled = failures >= CONSENT_GROUP_CHANGE_STALL_AFTER\n const fields = { failures, stalled, lastError, lease: null }\n transaction.update(jobRef, { ...fields, updatedAt: FieldValue.serverTimestamp() })\n return { job: { ...job, ...fields }, newlyStalled: stalled && !job.stalled }\n })\n if (!outcome) return null\n if (outcome.newlyStalled) {\n await ctx.log(\n outcome.job.orgId,\n outcome.job.actor,\n 'A consent group change stopped with an error; it will try again',\n { type: 'org', id: outcome.job.orgId },\n )\n }\n console.error(`[consent-group-change] a unit of ${outcome.job.changeId} failed: ${lastError}`)\n return outcome.job\n}\n\n/**\n * Advances a change until it finishes, waits for its sweep, or reaches\n * `deadlineMs`. Answers the job's status either way — including when another\n * caller holds it, which is not a failure.\n */\nexport async function advanceConsentGroupChange(\n input: {\n orgId: string\n changeId: string\n deadlineMs: number\n /** Who holds the lease; one per invocation. */\n owner?: string\n } & ConsentGroupChangeContext,\n): Promise<{ ok: true; status: ConsentGroupChangeStatusBody } | ConsentGroupChangeRefusal> {\n const ctx = contextOf(input)\n const jobRef = jobDoc(ctx, input.orgId, input.changeId)\n const owner = input.owner ?? `advance:${createResourceUid()}`\n const claimed = await claim(ctx, jobRef, owner)\n if (!claimed) return NOT_FOUND\n let job = claimed.job\n if (!claimed.mine) return { ok: true, status: consentGroupChangeStatus(job, ctx.now()) }\n try {\n while (job.status === 'running' && ctx.now() < input.deadlineMs - DEADLINE_SAFETY_MS) {\n const outcome = await advanceOnce(ctx, jobRef, job, owner, input.deadlineMs)\n job = outcome.job\n if (outcome.idle) break\n }\n } catch (error) {\n if (!(error instanceof LeaseLost)) {\n job = (await recordFailure(ctx, jobRef, owner, error)) ?? job\n return { ok: true, status: consentGroupChangeStatus(job, ctx.now()) }\n }\n job = jobFrom(await jobRef.get()) ?? job\n return { ok: true, status: consentGroupChangeStatus(job, ctx.now()) }\n }\n if (job.status === 'running') job = (await release(ctx, jobRef, owner)) ?? job\n return { ok: true, status: consentGroupChangeStatus(job, ctx.now()) }\n}\n\n/*==========================================\n * CANCELING AND READING\n *=========================================*/\n\n/**\n * Stops a change before it takes effect: clears the marker and leaves every\n * refusal already carried, which only ever honors an opt-out on more sites.\n * Refused once the declaration has flipped — there is nothing left to stop\n * before, and the only way back is another change.\n */\nexport async function cancelConsentGroupChange(\n input: { orgId: string; changeId: string; actor: ConsentGroupChangeActor } & ConsentGroupChangeContext,\n): Promise<{ ok: true; status: ConsentGroupChangeStatusBody } | ConsentGroupChangeRefusal> {\n const ctx = contextOf(input)\n const orgRef = orgDoc(ctx, input.orgId)\n const jobRef = jobDoc(ctx, input.orgId, input.changeId)\n const outcome = await ctx.firestore.runTransaction(async (transaction) => {\n const [orgSnapshot, jobSnapshot] = await Promise.all([transaction.get(orgRef), transaction.get(jobRef)])\n const job = jobFrom(jobSnapshot)\n if (!job) return NOT_FOUND\n if (job.status !== 'running' || job.declaredAtMs) {\n return {\n ok: false as const,\n status: 409 as const,\n body: {\n error:\n job.status === 'running' || job.status === 'done'\n ? 'This change has already taken effect'\n : 'This change is no longer running',\n changeId: job.changeId,\n phase: job.phase,\n },\n }\n }\n const marker = readConsentGroupsChange(orgSnapshot.data())\n if (marker?.changeId === job.changeId) {\n transaction.update(orgRef, {\n [CONSENT_GROUPS_CHANGE_FIELD]: FieldValue.delete(),\n updatedAt: FieldValue.serverTimestamp(),\n })\n }\n const fields = {\n status: 'canceled' as const,\n phase: 'canceled' as const,\n canceledAtMs: ctx.now(),\n lease: null,\n }\n transaction.update(jobRef, { ...fields, updatedAt: FieldValue.serverTimestamp() })\n return { ok: true as const, job: { ...job, ...fields } }\n })\n if (isRefusal(outcome)) return outcome\n const { job } = outcome as { job: ConsentGroupChangeJob }\n await ctx.log(\n input.orgId,\n { uid: input.actor.uid ?? null, email: input.actor.email ?? null },\n 'Canceled a consent group change before it took effect',\n { type: 'org', id: input.orgId },\n )\n return { ok: true, status: consentGroupChangeStatus(job, ctx.now()) }\n}\n\n/** A job's status, or `null` for a change the org never had. */\nexport async function readConsentGroupChangeStatus(\n input: { orgId: string; changeId: string } & ConsentGroupChangeContext,\n): Promise<ConsentGroupChangeStatusBody | null> {\n const ctx = contextOf(input)\n const job = jobFrom(await jobDoc(ctx, input.orgId, input.changeId).get())\n return job ? consentGroupChangeStatus(job, ctx.now()) : null\n}\n\n/*==========================================\n * THE CRON BACKSTOP\n *=========================================*/\n\nexport interface DueConsentGroupChangeReport {\n orgId: string\n changeId: string\n phase: ConsentGroupsChangePhase\n /** What happened on this run. */\n outcome: 'advanced' | 'listed' | 'locked' | 'orphaned' | 'skipped'\n status?: ConsentGroupChangeStatusBody\n error?: string\n}\n\n/**\n * Advances every change in flight, oldest org id first, until `deadlineMs`.\n *\n * A change nobody is watching still finishes: the progress panel is a\n * convenience, and this is the guarantee. A lockdown on the org — or on the\n * member who started the change — pauses it, like every other write the\n * workspace makes. A marker whose job document is gone is a change nothing\n * can finish, and is cleared rather than blocking every change after it.\n */\nexport async function advanceDueConsentGroupChanges(\n input: { deadlineMs: number; dryRun?: boolean; limit?: number } & ConsentGroupChangeContext,\n): Promise<DueConsentGroupChangeReport[]> {\n const ctx = contextOf(input)\n const snapshot = await ctx.firestore\n .collection('orgs')\n .where(`${CONSENT_GROUPS_CHANGE_FIELD}.phase`, 'in', [...RUNNING_PHASES])\n .limit(input.limit ?? 50)\n .get()\n const reports: DueConsentGroupChangeReport[] = []\n for (const org of snapshot.docs) {\n const data = org.data() as Record<string, unknown>\n const marker = readConsentGroupsChange(data)\n if (!marker) continue\n const base = { orgId: org.id, changeId: marker.changeId, phase: marker.phase }\n if (input.dryRun) {\n reports.push({ ...base, outcome: 'listed' })\n continue\n }\n if (ctx.now() >= input.deadlineMs - DEADLINE_SAFETY_MS) {\n reports.push({ ...base, outcome: 'skipped' })\n continue\n }\n try {\n const job = jobFrom(await jobDoc(ctx, org.id, marker.changeId).get())\n if (!job) {\n await orgDoc(ctx, org.id).update({ [CONSENT_GROUPS_CHANGE_FIELD]: FieldValue.delete() })\n console.error(`[consent-group-change] cleared a marker with no job in org ${org.id}`)\n reports.push({ ...base, outcome: 'orphaned' })\n continue\n }\n if (await ctx.lockedOut(data, job.actor?.uid ?? null)) {\n reports.push({ ...base, outcome: 'locked' })\n continue\n }\n const advanced = await advanceConsentGroupChange({\n orgId: org.id,\n changeId: marker.changeId,\n deadlineMs: input.deadlineMs,\n owner: `cron:${createResourceUid()}`,\n firestore: ctx.firestore,\n log: ctx.log,\n now: ctx.now,\n })\n reports.push({\n ...base,\n outcome: 'advanced',\n ...(advanced.ok ? { status: (advanced as { status: ConsentGroupChangeStatusBody }).status } : {}),\n })\n } catch (error) {\n reports.push({ ...base, outcome: 'advanced', error: sanitizeError(error) })\n }\n }\n return reports\n}\n"],"names":["CONSENT_GROUP_CHANGE_STEPS","CONSENT_GROUP_CHANGES_COLLECTION","CONSENT_GROUP_RENAME_STEPS","CONSENT_GROUP_SWEEP_DELAY_MS","CONSENT_GROUPS_CHANGE_FIELD","consentGroupChangePhaseOf","consentGroupDisclosureChanges","consentGroupsFingerprint","describeConsentGroupChangeLine","discardedConsentGroupIds","estimateConsentGroupChange","orgSiteIds","planConsentGroupChange","readConsentGroupsChange","validateConsentGroupDeclaration","CONSENT_GROUPS_FIELD","consentGroupsAwaitConfirmation","readConsentGroups","createResourceUid","DEFAULT_EMAIL_TOPICS","TOPIC_OPT_OUTS_SUBCOLLECTION","MARKETING_CONSENT_BY_HOST_FIELD","MARKETING_CONSENT_FIELD","resolveMarketingConsentPolicy","isOrgWideMember","listPluginConsentGroupParticipants","previewPluginConsentGroupChange","FieldValue","CONSENT_GROUP_CARRY_STORES","CONSENT_GROUP_CARRY_SUBCOLLECTIONS","carryConsentGroupRefusals","firebaseAdmin","getLockdownVerdict","logOrgActivity","carryRetainedRefusals","retainedRefusalsStore","CONSENT_GROUP_CHANGE_LEASE_MS","CONSENT_GROUP_CHANGE_STALL_AFTER","CATCH_UP_OVERLAP_MS","DEADLINE_SAFETY_MS","RUNNING_PHASES","contextOf","input","firestore","app","log","now","Date","lockedOut","org","uid","intent","NOT_FOUND","ok","status","body","error","LeaseLost","Error","orgDoc","ctx","orgId","collection","doc","jobDoc","changeId","planOf","job","plan","before","after","sanitizeError","message","String","replace","slice","consentGroupChangeStatus","nowMs","declaredAtMs","index","steps","indexOf","step","phase","done","progress","stepIndex","length","stepCount","sweepAtMs","renameOnly","stalled","failures","lastError","leaseUntilMs","lease","untilMs","counts","retainedRefusals","sitesReceiving","plugins","prepare","expected","groups","marker","raw","current","Array","isArray","siteIds","validation","reserved","Object","keys","errors","isRefusal","value","Boolean","plannedSites","Set","hostIds","values","flatMap","group","siteNamesFor","hosts","snapshots","getAll","map","id","names","forEach","snapshot","get","describeLines","lines","line","hostId","countOf","query","count","Number","data","previewConsentGroupChange","prepared","db","cache","Map","cached","key","pending","set","store","name","suppressions","optOuts","paces","where","retained","carries","Promise","all","carry","toHostId","fromHostId","siteSuppressions","topicOptOuts","membersIn","declaration","find","includes","joining","sort","had","siblings","filter","has","push","inherited","refusals","sibling","pendingHolds","released","releasedHostIds","entries","topic","topicId","changedGroups","was","join","partialAccessMembers","members","member","docs","reach","some","every","policy","forwardPolicyWarning","mode","entry","enforceFromMs","participants","texts","documents","reduce","total","sum","preview","discarded","text","disclosures","capturesDisclosing","estimate","startConsentGroupChange","orgRef","exists","outside","pluginId","jobRef","started","runTransaction","transaction","beforeFingerprint","flows","siteNames","units","actor","email","startedAtMs","carryStartedAtMs","finishedAtMs","canceledAtMs","update","updatedAt","serverTimestamp","create","createdAt","type","unitsOf","sinceMs","_carry","carryIndex","kind","since","jobFrom","save","owner","fields","extra","next","addCounts","base","more","out","isFinite","runUnit","unit","deadlineMs","result","state","cursor","page","counted","written","participant","participantFor","run","dryRun","candidate","nextStep","declare","declared","delete","finished","finishedLine","copied","sites","clauses","clause","summarize","finish","advanceOnce","idle","claim","mine","release","recordFailure","outcome","newlyStalled","console","advanceConsentGroupChange","claimed","cancelConsentGroupChange","orgSnapshot","jobSnapshot","readConsentGroupChangeStatus","advanceDueConsentGroupChanges","limit","reports","advanced"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/consent-group-change.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\n/**\n * RUNNING A CONSENT GROUP CHANGE (AGL-3320) — the executor.\n *\n * `consent-group-change.ts` in `app-utils` decides WHAT a change requires;\n * this carries it out, across as many invocations as it takes, without ever\n * leaving a refusal unhonored in between.\n *\n * ## The order is the safety argument\n *\n * 1. START — one transaction on the org document: the stored declaration is\n * the one the editor rendered, and no other change is in flight. It sets\n * the marker `consentGroupsChange` and creates the job document.\n * 2. CARRY — every carry of the plan through the three platform stores\n * (`consent-group-carry.ts`) and the organization's retained refusals\n * (`retained-refusals.ts`), then every participant's carry phase.\n * 3. CATCH UP — everything filed since the carry began, again.\n * 4. DECLARE — one transaction: the marker still names this change in its\n * carry phase and the stored declaration is still the one the job\n * started from. Only then is the new declaration written.\n * 5. CATCH UP — once more, for whatever raced the flip.\n * 6. RE-HOME — every participant's holder records.\n * 7. SWEEP — once the sweep delay has passed, the whole carry and every\n * participant's sweep, idempotently.\n * 8. DONE — the marker is cleared.\n *\n * The flip cannot happen before every carry and both catch-up passes are\n * done, because the steps run strictly in order and `declare` is only ever\n * reached from a finished `catch-up-pre`. Until the flip the declaration in\n * force is the old one, so a change that stalls or is canceled before it\n * has changed nothing anybody can observe — except refusals honored on more\n * sites, which is the safe direction. A rename moves nothing, and runs the\n * declare and finish alone.\n *\n * ## One invocation at a time, and any invocation may finish it\n *\n * The console's progress panel calls `continue` while it is open, and the\n * cron backstop calls every fifteen minutes. Each claims a LEASE on the job\n * by transaction and renews it on every unit it saves; a lease that lapses\n * (a process that died) is simply claimed by the next caller. A unit is one\n * page of one store, or one participant call, and every unit is idempotent,\n * so a unit re-run after a crash writes nothing it already wrote.\n *\n * ## A failure is retried, and five in a row are shown\n *\n * A unit that throws releases the lease and counts a failure against the job;\n * the next invocation retries it from its cursor. Five consecutive failures\n * mark the job STALLED — the activity log says so once, and the panel shows\n * the error with a retry — and the cron keeps trying. A participant's throw\n * is one of these: the change never flips past a participant that could not\n * carry its refusals (see `plugin-consent-group-change.ts`).\n */\n\nimport {\n CONSENT_GROUP_CHANGE_STEPS,\n CONSENT_GROUP_CHANGES_COLLECTION,\n CONSENT_GROUP_RENAME_STEPS,\n CONSENT_GROUP_SWEEP_DELAY_MS,\n CONSENT_GROUPS_CHANGE_FIELD,\n type ConsentGroupChangeCounts,\n type ConsentGroupChangeLine,\n type ConsentGroupChangePlan,\n type ConsentGroupChangePreview,\n type ConsentGroupChangeStatus,\n type ConsentGroupChangeStep,\n type ConsentGroupChangeProgress,\n type ConsentGroupDeclaration,\n type ConsentGroupsApiInFlight,\n type ConsentGroupsApiRefusal,\n type ConsentGroupsApiStale,\n type ConsentGroupsChangeMarker,\n type ConsentGroupsChangePhase,\n consentGroupChangePhaseOf,\n consentGroupDisclosureChanges,\n consentGroupsFingerprint,\n describeConsentGroupChangeLine,\n discardedConsentGroupIds,\n estimateConsentGroupChange,\n orgSiteIds,\n planConsentGroupChange,\n readConsentGroupsChange,\n validateConsentGroupDeclaration,\n} from '@aglyn/aglyn/app-utils/consent-group-change'\nimport {\n CONSENT_GROUPS_FIELD,\n consentGroupsAwaitConfirmation,\n readConsentGroups,\n} from '@aglyn/aglyn/app-utils/consent-groups'\nimport { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport {\n DECLARED_SUBSCRIPTION_TOPICS,\n TOPIC_OPT_OUTS_SUBCOLLECTION,\n} from '@aglyn/aglyn/app-utils/subscription-topics'\nimport {\n MARKETING_CONSENT_BY_HOST_FIELD,\n MARKETING_CONSENT_FIELD,\n resolveMarketingConsentPolicy,\n} from '@aglyn/aglyn/app-utils/marketing-consent'\nimport { isOrgWideMember } from '@aglyn/aglyn/app-utils/organizations'\nimport {\n type ConsentGroupChangeParticipant,\n type ConsentGroupChangeParticipantPhase,\n listPluginConsentGroupParticipants,\n previewPluginConsentGroupChange,\n} from '@aglyn/aglyn/plugin-manager/plugin-consent-group-change'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport {\n CONSENT_GROUP_CARRY_STORES,\n CONSENT_GROUP_CARRY_SUBCOLLECTIONS,\n type ConsentGroupCarryStore,\n carryConsentGroupRefusals,\n} from './consent-group-carry'\nimport firebaseAdmin from './firebase-admin'\nimport { getLockdownVerdict } from './lockdown'\nimport { logOrgActivity } from './organizations'\nimport { carryRetainedRefusals, retainedRefusalsStore } from './retained-refusals'\n\n/** How long a claimed job stays claimed without a renewal. */\nexport const CONSENT_GROUP_CHANGE_LEASE_MS = 90_000\n\n/** Consecutive failed units before a job is shown as stalled. */\nexport const CONSENT_GROUP_CHANGE_STALL_AFTER = 5\n\n/**\n * How far before the carry began a catch-up reads. A refusal filed in the\n * seconds the carry's first page was being read, with a clock a little\n * behind, is inside it.\n */\nconst CATCH_UP_OVERLAP_MS = 5 * 60_000\n\n/** Left before a deadline for the last save. */\nconst DEADLINE_SAFETY_MS = 5_000\n\n/** The phases the cron looks for a change in. */\nconst RUNNING_PHASES: readonly ConsentGroupsChangePhase[] = ['carry', 'rehome', 'sweep']\n\n/** Who asked, as the activity log records it. */\nexport interface ConsentGroupChangeActor {\n uid: string | null\n email?: string | null\n}\n\n/** Everything the executor reaches outside itself, injectable for the specs. */\nexport interface ConsentGroupChangeContext {\n firestore?: FirebaseFirestore.Firestore\n log?: typeof logOrgActivity\n now?: () => number\n /** Whether a lockdown refuses the cron acting for this org and actor. */\n lockedOut?: (org: Record<string, unknown>, uid: string | null) => Promise<boolean>\n}\n\ninterface Context {\n firestore: FirebaseFirestore.Firestore\n log: typeof logOrgActivity\n now: () => number\n lockedOut: (org: Record<string, unknown>, uid: string | null) => Promise<boolean>\n}\n\nfunction contextOf(input: ConsentGroupChangeContext): Context {\n return {\n firestore: input.firestore ?? firebaseAdmin.app().firestore(),\n log: input.log ?? logOrgActivity,\n now: input.now ?? Date.now,\n lockedOut:\n input.lockedOut ??\n (async (org, uid) =>\n (await getLockdownVerdict({ org: org as never, uid, intent: 'write' })) !== null),\n }\n}\n\n/** Where a unit of the current step stands. */\ninterface UnitState {\n cursor: string | null\n done: boolean\n}\n\n/** The job document, `orgs/{orgId}/consentGroupChanges/{changeId}`. */\nexport interface ConsentGroupChangeJob {\n changeId: string\n orgId: string\n status: ConsentGroupChangeStatus\n step: ConsentGroupChangeStep\n /** The marker's phase while running; `done` or `canceled` after. */\n phase: ConsentGroupsChangePhase | 'done' | 'canceled'\n /** The steps this change runs, in order. */\n steps: ConsentGroupChangeStep[]\n /** The stored declaration it started from: what the flip re-checks. */\n beforeFingerprint: string\n before: ConsentGroupDeclaration\n after: ConsentGroupDeclaration\n plan: Omit<ConsentGroupChangePlan, 'before' | 'after'>\n /** The plan's lines as the activity log writes them. */\n lines: string[]\n siteNames: Record<string, string>\n /** The participants registered when it started — every one of them runs. */\n participants: string[]\n /** The current step's units, by key. */\n units: Record<string, UnitState>\n counts: ConsentGroupChangeCounts\n sitesReceiving: string[]\n plugins: Record<string, Record<string, number>>\n lease: { owner: string; untilMs: number } | null\n failures: number\n stalled: boolean\n lastError: string | null\n actor: { uid: string | null; email: string | null }\n startedAtMs: number\n carryStartedAtMs: number\n declaredAtMs: number | null\n finishedAtMs: number | null\n canceledAtMs: number | null\n}\n\n/** What the route answers for a job. */\nexport interface ConsentGroupChangeStatusBody {\n changeId: string\n phase: ConsentGroupsChangePhase | 'done' | 'canceled'\n done: boolean\n progress: ConsentGroupChangeProgress\n}\n\n/** A refusal the route turns into its status code and body. */\nexport type ConsentGroupChangeRefusal =\n | { ok: false; status: 400; body: ConsentGroupsApiRefusal }\n | { ok: false; status: 404; body: { error: string } }\n | { ok: false; status: 409; body: ConsentGroupsApiStale | ConsentGroupsApiInFlight }\n\nconst NOT_FOUND = { ok: false as const, status: 404 as const, body: { error: 'No such consent group change' } }\n\n/** A caller whose lease was taken, or whose job was canceled, under it. */\nclass LeaseLost extends Error {}\n\nconst orgDoc = (ctx: Context, orgId: string) => ctx.firestore.collection('orgs').doc(orgId)\nconst jobDoc = (ctx: Context, orgId: string, changeId: string) =>\n orgDoc(ctx, orgId).collection(CONSENT_GROUP_CHANGES_COLLECTION).doc(changeId)\n\n/** The job as the plan the participants and the carries read. */\nfunction planOf(job: ConsentGroupChangeJob): ConsentGroupChangePlan {\n return { ...job.plan, before: job.before, after: job.after }\n}\n\n/** An error as the job records it: bounded, and with no address in it. */\nfunction sanitizeError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error ?? 'Unknown error')\n return message.replace(/[^\\s@]+@[^\\s@]+/g, '[address]').slice(0, 300) || 'Unknown error'\n}\n\nexport function consentGroupChangeStatus(\n job: ConsentGroupChangeJob,\n nowMs: number = Date.now(),\n): ConsentGroupChangeStatusBody {\n const declaredAtMs = job.declaredAtMs ?? null\n const index = job.steps.indexOf(job.step)\n return {\n changeId: job.changeId,\n phase: job.phase,\n done: job.status !== 'running',\n progress: {\n status: job.status,\n step: job.step,\n stepIndex: index >= 0 ? index + 1 : job.steps.length,\n stepCount: job.steps.length,\n declaredAtMs,\n sweepAtMs:\n declaredAtMs !== null && !job.plan.renameOnly\n ? declaredAtMs + CONSENT_GROUP_SWEEP_DELAY_MS\n : null,\n stalled: job.stalled === true,\n failures: job.failures ?? 0,\n lastError: job.lastError ?? null,\n leaseUntilMs: job.lease && job.lease.untilMs > nowMs ? job.lease.untilMs : null,\n // A job started before the retained store was carried has no count for it.\n counts: { retainedRefusals: 0, ...job.counts },\n sitesReceiving: job.sitesReceiving?.length ?? 0,\n plugins: job.plugins ?? {},\n },\n }\n}\n\n/*==========================================\n * VALIDATING AND PLANNING — shared by the preview and the start\n *=========================================*/\n\ninterface Prepared {\n org: Record<string, unknown>\n before: ConsentGroupDeclaration\n after: ConsentGroupDeclaration\n plan: ConsentGroupChangePlan\n siteIds: string[]\n}\n\nfunction prepare(\n org: Record<string, unknown>,\n expected: unknown,\n groups: unknown,\n): Prepared | ConsentGroupChangeRefusal {\n const marker = readConsentGroupsChange(org)\n if (marker) {\n return {\n ok: false,\n status: 409,\n body: {\n error: 'A consent group change is still finishing',\n changeId: marker.changeId,\n phase: marker.phase,\n },\n }\n }\n const raw = org[CONSENT_GROUPS_FIELD]\n if (consentGroupsFingerprint(raw) !== consentGroupsFingerprint(expected)) {\n return {\n ok: false,\n status: 409,\n body: {\n error: 'Someone else changed consent groups while you were editing',\n current:\n raw && typeof raw === 'object' && !Array.isArray(raw)\n ? (raw as Record<string, unknown>)\n : null,\n },\n }\n }\n const before = readConsentGroups(org)\n const siteIds = orgSiteIds(org)\n const validation = validateConsentGroupDeclaration({\n groups,\n before,\n siteIds,\n reserved:\n raw && typeof raw === 'object' && !Array.isArray(raw)\n ? Object.keys(raw as Record<string, unknown>)\n : [],\n })\n if (!validation.ok) {\n return {\n ok: false,\n status: 400,\n body: {\n error: 'Those consent groups cannot be saved',\n errors: (validation as { errors: ConsentGroupsApiRefusal['errors'] }).errors,\n },\n }\n }\n const after = (validation as { after: ConsentGroupDeclaration }).after\n return {\n org,\n before,\n after,\n plan: planConsentGroupChange(before, after, { siteIds }),\n siteIds,\n }\n}\n\nconst isRefusal = (value: unknown): value is ConsentGroupChangeRefusal =>\n Boolean(value && typeof value === 'object' && (value as { ok?: unknown }).ok === false)\n\n/** Every site a plan names, for the names its lines are written with. */\nfunction plannedSites(plan: ConsentGroupChangePlan): string[] {\n return [\n ...new Set([\n ...plan.hostIds,\n ...Object.values(plan.before).flatMap((group) => group.hostIds),\n ...Object.values(plan.after).flatMap((group) => group.hostIds),\n ]),\n ]\n}\n\n/** What the console calls each site: its name, its subdomain, or its id. */\nasync function siteNamesFor(ctx: Context, hostIds: readonly string[]): Promise<Record<string, string>> {\n if (!hostIds.length) return {}\n const hosts = ctx.firestore.collection('hosts')\n const snapshots = await ctx.firestore.getAll(...hostIds.map((id) => hosts.doc(id)))\n const names: Record<string, string> = {}\n snapshots.forEach((snapshot, index) => {\n const id = hostIds[index]\n names[id] = String(snapshot.get('name') || snapshot.get('subdomain') || id)\n })\n return names\n}\n\nconst describeLines = (lines: readonly ConsentGroupChangeLine[], names: Record<string, string>) =>\n lines.map((line) => describeConsentGroupChangeLine(line, (hostId) => names[hostId] ?? hostId))\n\n/*==========================================\n * THE PREVIEW\n *=========================================*/\n\nasync function countOf(query: FirebaseFirestore.Query): Promise<number> {\n const snapshot = await query.count().get()\n return Number(snapshot.data().count) || 0\n}\n\n/**\n * What a change would do, counted and worded, writing nothing.\n *\n * Refused exactly as `apply` would be — a change in flight, a stale\n * `expected`, an invalid declaration — so the review step never shows a\n * change the next click cannot make.\n */\nexport async function previewConsentGroupChange(\n input: {\n orgId: string\n expected: unknown\n groups: unknown\n /** The org document, when the caller already read it. */\n org?: Record<string, unknown> | null\n } & ConsentGroupChangeContext,\n): Promise<{ ok: true; preview: ConsentGroupChangePreview } | ConsentGroupChangeRefusal> {\n const ctx = contextOf(input)\n const org =\n input.org ?? ((await orgDoc(ctx, input.orgId).get()).data() as Record<string, unknown> | undefined)\n if (!org) return { ok: false, status: 404, body: { error: 'No such workspace' } }\n const prepared = prepare(org, input.expected, input.groups)\n if (isRefusal(prepared)) return prepared\n const { plan, before, after } = prepared as Prepared\n\n const db = ctx.firestore\n const cache = new Map<string, Promise<number>>()\n const cached = (key: string, query: () => FirebaseFirestore.Query) => {\n let pending = cache.get(key)\n if (!pending) {\n pending = countOf(query())\n cache.set(key, pending)\n }\n return pending\n }\n const store = (hostId: string, name: ConsentGroupCarryStore) =>\n db.collection('hosts').doc(hostId).collection(CONSENT_GROUP_CARRY_SUBCOLLECTIONS[name])\n const suppressions = (hostId: string) => cached(`s:${hostId}`, () => store(hostId, 'siteSuppressions'))\n const optOuts = (hostId: string) => cached(`t:${hostId}`, () => store(hostId, 'topicOptOuts'))\n const paces = (hostId: string) =>\n cached(`p:${hostId}`, () => store(hostId, 'paces').where('cadence', '!=', null))\n const retained = (hostId: string) =>\n cached(`r:${hostId}`, () =>\n retainedRefusalsStore(orgDoc(ctx, input.orgId)).where(\n `${MARKETING_CONSENT_BY_HOST_FIELD}.${hostId}.${MARKETING_CONSENT_FIELD}`,\n '==',\n false,\n ),\n )\n\n const carries = await Promise.all(\n plan.carries.map(async (carry) => ({\n toHostId: carry.toHostId,\n fromHostId: carry.fromHostId,\n siteSuppressions: await suppressions(carry.fromHostId),\n topicOptOuts: await optOuts(carry.fromHostId),\n paces: await paces(carry.fromHostId),\n retainedRefusals: await retained(carry.fromHostId),\n })),\n )\n\n const membersIn = (declaration: ConsentGroupDeclaration, hostId: string) =>\n Object.values(declaration).find((group) => group.hostIds.includes(hostId))?.hostIds ?? [hostId]\n const joining: Array<{ hostId: string; siblings: string[] }> = []\n for (const hostId of [...new Set(Object.values(after).flatMap((group) => group.hostIds))].sort()) {\n const had = new Set(membersIn(before, hostId))\n const siblings = membersIn(after, hostId).filter((id) => id !== hostId && !had.has(id))\n if (siblings.length) joining.push({ hostId, siblings })\n }\n const inherited = await Promise.all(\n joining.map(async ({ hostId, siblings }) => {\n let refusals = 0\n for (const sibling of siblings) {\n refusals += (await suppressions(sibling)) + (await optOuts(sibling))\n }\n return { hostId, refusals }\n }),\n )\n\n /*\n * A pending confirmation holds a sibling's mail only with the switch on,\n * so only then does a separation release anything. Counted over the\n * built-in streams: the catalog of an organization's own streams belongs to\n * the Email plugin, and a stream it added is not counted here.\n */\n const pendingHolds: ConsentGroupChangePreview['pendingHolds'] = []\n if (consentGroupsAwaitConfirmation(org)) {\n const released = new Map<string, string[]>()\n for (const carry of plan.carries) {\n released.set(carry.toHostId, [...(released.get(carry.toHostId) ?? []), carry.fromHostId])\n }\n for (const [hostId, releasedHostIds] of [...released.entries()].sort()) {\n for (const topic of DECLARED_SUBSCRIPTION_TOPICS) {\n const count = await countOf(\n db\n .collection('hosts')\n .doc(hostId)\n .collection(TOPIC_OPT_OUTS_SUBCOLLECTION)\n .where(`topics.${topic.id}.confirmedAt`, '==', null),\n )\n if (count > 0) pendingHolds.push({ hostId, topicId: topic.id, count, releasedHostIds })\n }\n }\n }\n\n const changedGroups = Object.entries(after)\n .filter(([id, group]) => {\n const was = before[id]\n return !was || was.hostIds.join('\\n') !== group.hostIds.join('\\n')\n })\n .map(([, group]) => group.hostIds)\n let partialAccessMembers = 0\n if (changedGroups.length) {\n const members = await orgDoc(ctx, input.orgId).collection('members').get()\n for (const member of members.docs) {\n const data = member.data() as Record<string, unknown>\n if (isOrgWideMember(data)) continue\n const reach = new Set(Object.keys((data['hostAccess'] ?? {}) as Record<string, unknown>))\n if (\n changedGroups.some(\n (hostIds) =>\n hostIds.some((id) => reach.has(id)) && !hostIds.every((id) => reach.has(id)),\n )\n ) {\n partialAccessMembers += 1\n }\n }\n }\n\n const policy = resolveMarketingConsentPolicy(org['marketingConsentPolicy'])\n const forwardPolicyWarning =\n policy.mode === 'forward' && joining.length\n ? { hostIds: joining.map((entry) => entry.hostId), enforceFromMs: policy.enforceFromMs }\n : null\n\n const participants = await previewPluginConsentGroupChange({ orgId: input.orgId, plan })\n const names = await siteNamesFor(ctx, plannedSites(plan))\n const texts = describeLines(plan.lines, names)\n const documents =\n carries.reduce(\n (total, carry) =>\n total + carry.siteSuppressions + carry.topicOptOuts + carry.paces + carry.retainedRefusals,\n 0,\n ) +\n participants.reduce(\n (total, entry) =>\n total + (entry.lines ?? []).reduce((sum, line) => sum + (line.count ?? 0), 0),\n 0,\n )\n\n return {\n ok: true,\n preview: {\n before,\n after,\n discarded: discardedConsentGroupIds(org),\n lines: plan.lines.map((line, index) => ({ ...line, text: texts[index] })),\n disclosures: consentGroupDisclosureChanges(before, after),\n carries,\n inherited,\n pendingHolds,\n partialAccessMembers,\n forwardPolicyWarning,\n participants,\n capturesDisclosing: ['form'],\n estimate: estimateConsentGroupChange(plan, documents),\n },\n }\n}\n\n/*==========================================\n * STARTING\n *=========================================*/\n\n/**\n * Starts a change: the org marker and the job, in one transaction that\n * refuses a change in flight, a stale `expected` and an invalid declaration.\n * Writes nothing else — the caller advances it.\n */\nexport async function startConsentGroupChange(\n input: {\n orgId: string\n actor: ConsentGroupChangeActor\n expected: unknown\n groups: unknown\n } & ConsentGroupChangeContext,\n): Promise<{ ok: true; changeId: string; job: ConsentGroupChangeJob } | ConsentGroupChangeRefusal> {\n const ctx = contextOf(input)\n const orgRef = orgDoc(ctx, input.orgId)\n const snapshot = await orgRef.get()\n if (!snapshot.exists) return { ok: false, status: 404, body: { error: 'No such workspace' } }\n const outside = prepare(snapshot.data() as Record<string, unknown>, input.expected, input.groups)\n if (isRefusal(outside)) return outside\n // Names are words for the log, read once and outside the transaction.\n const names = await siteNamesFor(ctx, plannedSites((outside as Prepared).plan))\n const participants = listPluginConsentGroupParticipants().map((entry) => entry.pluginId)\n const changeId = createResourceUid()\n const jobRef = jobDoc(ctx, input.orgId, changeId)\n const nowMs = ctx.now()\n\n const started = await ctx.firestore.runTransaction(async (transaction) => {\n const current = await transaction.get(orgRef)\n if (!current.exists) {\n return { ok: false as const, status: 404 as const, body: { error: 'No such workspace' } }\n }\n const prepared = prepare(current.data() as Record<string, unknown>, input.expected, input.groups)\n if (isRefusal(prepared)) return prepared\n const { plan, before, after, org } = prepared as Prepared\n const steps = plan.renameOnly ? [...CONSENT_GROUP_RENAME_STEPS] : [...CONSENT_GROUP_CHANGE_STEPS]\n const job: ConsentGroupChangeJob = {\n changeId,\n orgId: input.orgId,\n status: 'running',\n step: steps[0],\n phase: 'carry',\n steps,\n beforeFingerprint: consentGroupsFingerprint(org[CONSENT_GROUPS_FIELD]),\n before,\n after,\n plan: {\n carries: plan.carries,\n flows: plan.flows,\n lines: plan.lines,\n hostIds: plan.hostIds,\n renameOnly: plan.renameOnly,\n },\n lines: describeLines(plan.lines, names),\n siteNames: names,\n participants: plan.renameOnly ? [] : participants,\n units: {},\n counts: { siteSuppressions: 0, topicOptOuts: 0, paces: 0, retainedRefusals: 0 },\n sitesReceiving: [],\n plugins: {},\n lease: null,\n failures: 0,\n stalled: false,\n lastError: null,\n actor: { uid: input.actor.uid ?? null, email: input.actor.email ?? null },\n startedAtMs: nowMs,\n carryStartedAtMs: nowMs,\n declaredAtMs: null,\n finishedAtMs: null,\n canceledAtMs: null,\n }\n const marker: ConsentGroupsChangeMarker = {\n changeId,\n phase: 'carry',\n hostIds: plan.hostIds,\n startedAtMs: nowMs,\n }\n transaction.update(orgRef, {\n [CONSENT_GROUPS_CHANGE_FIELD]: marker,\n updatedAt: FieldValue.serverTimestamp(),\n })\n transaction.create(jobRef, {\n ...job,\n createdAt: FieldValue.serverTimestamp(),\n updatedAt: FieldValue.serverTimestamp(),\n })\n return { ok: true as const, job }\n })\n if (isRefusal(started)) return started\n const { job } = started as { job: ConsentGroupChangeJob }\n await ctx.log(\n input.orgId,\n { uid: job.actor.uid, email: job.actor.email },\n `Started a consent group change: ${job.lines.join('; ')}`,\n { type: 'org', id: input.orgId },\n )\n return { ok: true, changeId, job }\n}\n\n/*==========================================\n * ADVANCING\n *=========================================*/\n\ntype Unit =\n | { kind: 'carry'; key: string; carryIndex: number; store: ConsentGroupCarryStore; sinceMs: number | null }\n | { kind: 'retained'; key: string; carryIndex: number; sinceMs: number | null }\n | { kind: 'participant'; key: string; pluginId: string; phase: ConsentGroupChangeParticipantPhase }\n\n/** The current step's units, in the order they run. */\nfunction unitsOf(job: ConsentGroupChangeJob): Unit[] {\n const carries = (sinceMs: number | null): Unit[] =>\n job.plan.carries.flatMap((_carry, carryIndex): Unit[] => [\n ...CONSENT_GROUP_CARRY_STORES.map((store) => ({\n kind: 'carry' as const,\n key: `c${carryIndex}:${store}`,\n carryIndex,\n store,\n sinceMs,\n })),\n { kind: 'retained' as const, key: `c${carryIndex}:retainedRefusals`, carryIndex, sinceMs },\n ])\n const participants = (phase: ConsentGroupChangeParticipantPhase): Unit[] =>\n job.participants.map((pluginId) => ({\n kind: 'participant' as const,\n key: `p:${pluginId}`,\n pluginId,\n phase,\n }))\n const since = job.carryStartedAtMs - CATCH_UP_OVERLAP_MS\n switch (job.step) {\n case 'carry':\n return [...carries(null), ...participants('carry')]\n case 'catch-up-pre':\n case 'catch-up-post':\n return [...carries(since), ...participants('carry')]\n case 'rehome':\n return participants('rehome')\n case 'sweep':\n return [...carries(null), ...participants('sweep')]\n default:\n return []\n }\n}\n\nfunction jobFrom(snapshot: FirebaseFirestore.DocumentSnapshot): ConsentGroupChangeJob | null {\n return snapshot.exists ? (snapshot.data() as ConsentGroupChangeJob) : null\n}\n\n/**\n * Saves a unit's result, renewing the lease — only while the job is still\n * running and still this caller's.\n */\nasync function save(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n owner: string,\n fields: Partial<ConsentGroupChangeJob>,\n extra?: (transaction: FirebaseFirestore.Transaction, job: ConsentGroupChangeJob) => Promise<void>,\n): Promise<ConsentGroupChangeJob> {\n return ctx.firestore.runTransaction(async (transaction) => {\n const job = jobFrom(await transaction.get(jobRef))\n if (!job || job.status !== 'running' || job.lease?.owner !== owner) throw new LeaseLost()\n if (extra) await extra(transaction, job)\n const next: Partial<ConsentGroupChangeJob> = {\n ...fields,\n lease: { owner, untilMs: ctx.now() + CONSENT_GROUP_CHANGE_LEASE_MS },\n failures: 0,\n stalled: false,\n lastError: null,\n }\n transaction.update(jobRef, { ...next, updatedAt: FieldValue.serverTimestamp() })\n return { ...job, ...next }\n })\n}\n\nfunction addCounts(\n base: Record<string, number> | undefined,\n more: Record<string, number>,\n): Record<string, number> {\n const out: Record<string, number> = { ...(base ?? {}) }\n for (const [key, value] of Object.entries(more)) {\n if (typeof value === 'number' && Number.isFinite(value)) out[key] = (out[key] ?? 0) + value\n }\n return out\n}\n\nasync function runUnit(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n job: ConsentGroupChangeJob,\n owner: string,\n unit: Unit,\n deadlineMs: number,\n): Promise<ConsentGroupChangeJob> {\n const state = job.units[unit.key] ?? { cursor: null, done: false }\n if (unit.kind === 'carry' || unit.kind === 'retained') {\n const carry = job.plan.carries[unit.carryIndex]\n const page =\n unit.kind === 'carry'\n ? await carryConsentGroupRefusals({\n firestore: ctx.firestore,\n store: unit.store,\n carry,\n changeId: job.changeId,\n cursor: state.cursor,\n sinceMs: unit.sinceMs,\n })\n : await carryRetainedRefusals({\n firestore: ctx.firestore,\n orgId: job.orgId,\n carry,\n changeId: job.changeId,\n cursor: state.cursor,\n sinceMs: unit.sinceMs,\n })\n const counted = unit.kind === 'carry' ? unit.store : 'retainedRefusals'\n const counts = { ...job.counts, [counted]: (job.counts[counted] ?? 0) + page.written }\n const sitesReceiving =\n page.written > 0 && !job.sitesReceiving.includes(carry.toHostId)\n ? [...job.sitesReceiving, carry.toHostId]\n : job.sitesReceiving\n return save(ctx, jobRef, owner, {\n units: { ...job.units, [unit.key]: { cursor: page.cursor, done: page.done } },\n counts,\n sitesReceiving,\n })\n }\n const participant = participantFor(unit.pluginId)\n const result = await participant.run({\n orgId: job.orgId,\n changeId: job.changeId,\n plan: planOf(job),\n phase: unit.phase,\n cursor: state.cursor,\n deadlineMs: deadlineMs - DEADLINE_SAFETY_MS,\n dryRun: false,\n })\n return save(ctx, jobRef, owner, {\n units: { ...job.units, [unit.key]: { cursor: result.cursor ?? null, done: result.done === true } },\n plugins: { ...job.plugins, [unit.pluginId]: addCounts(job.plugins[unit.pluginId], result.counts ?? {}) },\n })\n}\n\nfunction participantFor(pluginId: string): ConsentGroupChangeParticipant {\n const entry = listPluginConsentGroupParticipants().find((candidate) => candidate.pluginId === pluginId)\n if (!entry) {\n // The change started with this participant; running on without it would\n // skip its share. Another process — one whose declarations registered it —\n // picks the job up.\n throw new Error(`The ${pluginId} plugin's part of this change is not available here`)\n }\n return entry.participant\n}\n\n/** Moves the job to the step after its current one. */\nasync function nextStep(\n ctx: Context,\n orgId: string,\n jobRef: FirebaseFirestore.DocumentReference,\n job: ConsentGroupChangeJob,\n owner: string,\n): Promise<ConsentGroupChangeJob> {\n const next = job.steps[job.steps.indexOf(job.step) + 1] ?? 'finish'\n const phase = consentGroupChangePhaseOf(next)\n if (phase === job.phase) return save(ctx, jobRef, owner, { step: next, units: {} })\n // Entering the sweep: the marker says so, for the readers that hold off\n // while holder records may still be moving.\n const orgRef = orgDoc(ctx, orgId)\n return save(ctx, jobRef, owner, { step: next, units: {}, phase }, async (transaction) => {\n const marker = readConsentGroupsChange((await transaction.get(orgRef)).data())\n if (marker?.changeId === job.changeId) {\n transaction.update(orgRef, { [`${CONSENT_GROUPS_CHANGE_FIELD}.phase`]: phase })\n }\n })\n}\n\n/**\n * THE FLIP — the one write of `consentGroups` anywhere in the platform.\n *\n * The marker must still name this change in its carry phase and the stored\n * declaration must be the one the job started from; otherwise nothing is\n * written and the unit fails. `update`, not a merge, so a removed group's id\n * vanishes with it; an empty declaration deletes the field.\n */\nasync function declare(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n job: ConsentGroupChangeJob,\n owner: string,\n): Promise<ConsentGroupChangeJob> {\n const orgRef = orgDoc(ctx, job.orgId)\n const nowMs = ctx.now()\n const renameOnly = job.plan.renameOnly\n const declared = await save(\n ctx,\n jobRef,\n owner,\n renameOnly\n ? {\n step: 'finish',\n status: 'done',\n phase: 'done',\n declaredAtMs: nowMs,\n finishedAtMs: nowMs,\n units: {},\n }\n : { step: 'catch-up-post', phase: 'rehome', declaredAtMs: nowMs, units: {} },\n async (transaction) => {\n const org = (await transaction.get(orgRef)).data() as Record<string, unknown> | undefined\n const marker = readConsentGroupsChange(org)\n if (!org || marker?.changeId !== job.changeId || marker.phase !== 'carry') {\n throw new Error('The change no longer holds the organization, so the new groups were not saved')\n }\n if (consentGroupsFingerprint(org[CONSENT_GROUPS_FIELD]) !== job.beforeFingerprint) {\n throw new Error('The consent groups changed outside this change, so the new groups were not saved')\n }\n transaction.update(orgRef, {\n [CONSENT_GROUPS_FIELD]: Object.keys(job.after).length ? job.after : FieldValue.delete(),\n [CONSENT_GROUPS_CHANGE_FIELD]: renameOnly\n ? FieldValue.delete()\n : { ...marker, phase: 'rehome', declaredAtMs: nowMs },\n updatedAt: FieldValue.serverTimestamp(),\n })\n },\n )\n const finished = renameOnly ? { ...declared, lease: null } : declared\n if (renameOnly) await jobRef.update({ lease: null })\n for (const line of job.lines) {\n await ctx.log(job.orgId, job.actor, line, { type: 'org', id: job.orgId })\n }\n if (renameOnly) {\n await ctx.log(job.orgId, job.actor, 'Finished a consent group change', {\n type: 'org',\n id: job.orgId,\n })\n }\n return finished\n}\n\n/** The \"Finished\" line: the carries' totals, then each participant's clause. */\nfunction finishedLine(job: ConsentGroupChangeJob): string {\n const copied =\n job.counts.siteSuppressions +\n job.counts.topicOptOuts +\n job.counts.paces +\n (job.counts.retainedRefusals ?? 0)\n const sites = job.sitesReceiving.length\n const clauses = [\n `${copied} ${copied === 1 ? 'opt-out' : 'opt-outs'} copied to ${sites} ${sites === 1 ? 'site' : 'sites'}`,\n ]\n for (const pluginId of job.participants) {\n const entry = listPluginConsentGroupParticipants().find((candidate) => candidate.pluginId === pluginId)\n const clause = entry?.participant.summarize?.(job.plugins[pluginId] ?? {})\n if (clause) clauses.push(clause)\n }\n return `Finished a consent group change: ${clauses.join('; ')}`\n}\n\nasync function finish(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n job: ConsentGroupChangeJob,\n owner: string,\n): Promise<ConsentGroupChangeJob> {\n const orgRef = orgDoc(ctx, job.orgId)\n const nowMs = ctx.now()\n const done = await save(\n ctx,\n jobRef,\n owner,\n { status: 'done', phase: 'done', step: 'finish', finishedAtMs: nowMs, units: {} },\n async (transaction) => {\n const marker = readConsentGroupsChange((await transaction.get(orgRef)).data())\n if (marker?.changeId === job.changeId) {\n transaction.update(orgRef, {\n [CONSENT_GROUPS_CHANGE_FIELD]: FieldValue.delete(),\n updatedAt: FieldValue.serverTimestamp(),\n })\n }\n },\n )\n await jobRef.update({ lease: null })\n await ctx.log(job.orgId, job.actor, finishedLine(done), { type: 'org', id: job.orgId })\n return { ...done, lease: null }\n}\n\n/**\n * One unit of work, or nothing when the job is waiting for its sweep.\n */\nasync function advanceOnce(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n job: ConsentGroupChangeJob,\n owner: string,\n deadlineMs: number,\n): Promise<{ job: ConsentGroupChangeJob; idle: boolean }> {\n if (job.step === 'declare') return { job: await declare(ctx, jobRef, job, owner), idle: false }\n if (job.step === 'finish') return { job: await finish(ctx, jobRef, job, owner), idle: false }\n if (job.step === 'sweep-wait') {\n if (ctx.now() < (job.declaredAtMs ?? 0) + CONSENT_GROUP_SWEEP_DELAY_MS) return { job, idle: true }\n return { job: await nextStep(ctx, job.orgId, jobRef, job, owner), idle: false }\n }\n const unit = unitsOf(job).find((candidate) => !job.units[candidate.key]?.done)\n if (!unit) return { job: await nextStep(ctx, job.orgId, jobRef, job, owner), idle: false }\n const before = job.units[unit.key]?.cursor ?? null\n const after = await runUnit(ctx, jobRef, job, owner, unit, deadlineMs)\n const state = after.units[unit.key]\n // A participant that stopped where it started — out of time before its\n // first page — has nothing more to give this invocation.\n return { job: after, idle: unit.kind === 'participant' && !state?.done && (state?.cursor ?? null) === before }\n}\n\n/** Claims the lease, or answers why not. `null` for a job that does not exist. */\nasync function claim(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n owner: string,\n): Promise<{ job: ConsentGroupChangeJob; mine: boolean } | null> {\n return ctx.firestore.runTransaction(async (transaction) => {\n const job = jobFrom(await transaction.get(jobRef))\n if (!job) return null\n const nowMs = ctx.now()\n if (job.status !== 'running') return { job, mine: false }\n if (job.lease && job.lease.untilMs > nowMs && job.lease.owner !== owner) return { job, mine: false }\n const lease = { owner, untilMs: nowMs + CONSENT_GROUP_CHANGE_LEASE_MS }\n transaction.update(jobRef, { lease })\n return { job: { ...job, lease }, mine: true }\n })\n}\n\nasync function release(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n owner: string,\n): Promise<ConsentGroupChangeJob | null> {\n return ctx.firestore.runTransaction(async (transaction) => {\n const job = jobFrom(await transaction.get(jobRef))\n if (!job) return null\n if (job.lease?.owner !== owner) return job\n transaction.update(jobRef, { lease: null })\n return { ...job, lease: null }\n })\n}\n\n/** Counts a failed unit against the job, and says so once when it stalls. */\nasync function recordFailure(\n ctx: Context,\n jobRef: FirebaseFirestore.DocumentReference,\n owner: string,\n error: unknown,\n): Promise<ConsentGroupChangeJob | null> {\n const lastError = sanitizeError(error)\n const outcome = await ctx.firestore.runTransaction(async (transaction) => {\n const job = jobFrom(await transaction.get(jobRef))\n if (!job) return null\n if (job.lease?.owner !== owner) return { job, newlyStalled: false }\n const failures = (job.failures ?? 0) + 1\n const stalled = failures >= CONSENT_GROUP_CHANGE_STALL_AFTER\n const fields = { failures, stalled, lastError, lease: null }\n transaction.update(jobRef, { ...fields, updatedAt: FieldValue.serverTimestamp() })\n return { job: { ...job, ...fields }, newlyStalled: stalled && !job.stalled }\n })\n if (!outcome) return null\n if (outcome.newlyStalled) {\n await ctx.log(\n outcome.job.orgId,\n outcome.job.actor,\n 'A consent group change stopped with an error; it will try again',\n { type: 'org', id: outcome.job.orgId },\n )\n }\n console.error(`[consent-group-change] a unit of ${outcome.job.changeId} failed: ${lastError}`)\n return outcome.job\n}\n\n/**\n * Advances a change until it finishes, waits for its sweep, or reaches\n * `deadlineMs`. Answers the job's status either way — including when another\n * caller holds it, which is not a failure.\n */\nexport async function advanceConsentGroupChange(\n input: {\n orgId: string\n changeId: string\n deadlineMs: number\n /** Who holds the lease; one per invocation. */\n owner?: string\n } & ConsentGroupChangeContext,\n): Promise<{ ok: true; status: ConsentGroupChangeStatusBody } | ConsentGroupChangeRefusal> {\n const ctx = contextOf(input)\n const jobRef = jobDoc(ctx, input.orgId, input.changeId)\n const owner = input.owner ?? `advance:${createResourceUid()}`\n const claimed = await claim(ctx, jobRef, owner)\n if (!claimed) return NOT_FOUND\n let job = claimed.job\n if (!claimed.mine) return { ok: true, status: consentGroupChangeStatus(job, ctx.now()) }\n try {\n while (job.status === 'running' && ctx.now() < input.deadlineMs - DEADLINE_SAFETY_MS) {\n const outcome = await advanceOnce(ctx, jobRef, job, owner, input.deadlineMs)\n job = outcome.job\n if (outcome.idle) break\n }\n } catch (error) {\n if (!(error instanceof LeaseLost)) {\n job = (await recordFailure(ctx, jobRef, owner, error)) ?? job\n return { ok: true, status: consentGroupChangeStatus(job, ctx.now()) }\n }\n job = jobFrom(await jobRef.get()) ?? job\n return { ok: true, status: consentGroupChangeStatus(job, ctx.now()) }\n }\n if (job.status === 'running') job = (await release(ctx, jobRef, owner)) ?? job\n return { ok: true, status: consentGroupChangeStatus(job, ctx.now()) }\n}\n\n/*==========================================\n * CANCELING AND READING\n *=========================================*/\n\n/**\n * Stops a change before it takes effect: clears the marker and leaves every\n * refusal already carried, which only ever honors an opt-out on more sites.\n * Refused once the declaration has flipped — there is nothing left to stop\n * before, and the only way back is another change.\n */\nexport async function cancelConsentGroupChange(\n input: { orgId: string; changeId: string; actor: ConsentGroupChangeActor } & ConsentGroupChangeContext,\n): Promise<{ ok: true; status: ConsentGroupChangeStatusBody } | ConsentGroupChangeRefusal> {\n const ctx = contextOf(input)\n const orgRef = orgDoc(ctx, input.orgId)\n const jobRef = jobDoc(ctx, input.orgId, input.changeId)\n const outcome = await ctx.firestore.runTransaction(async (transaction) => {\n const [orgSnapshot, jobSnapshot] = await Promise.all([transaction.get(orgRef), transaction.get(jobRef)])\n const job = jobFrom(jobSnapshot)\n if (!job) return NOT_FOUND\n if (job.status !== 'running' || job.declaredAtMs) {\n return {\n ok: false as const,\n status: 409 as const,\n body: {\n error:\n job.status === 'running' || job.status === 'done'\n ? 'This change has already taken effect'\n : 'This change is no longer running',\n changeId: job.changeId,\n phase: job.phase,\n },\n }\n }\n const marker = readConsentGroupsChange(orgSnapshot.data())\n if (marker?.changeId === job.changeId) {\n transaction.update(orgRef, {\n [CONSENT_GROUPS_CHANGE_FIELD]: FieldValue.delete(),\n updatedAt: FieldValue.serverTimestamp(),\n })\n }\n const fields = {\n status: 'canceled' as const,\n phase: 'canceled' as const,\n canceledAtMs: ctx.now(),\n lease: null,\n }\n transaction.update(jobRef, { ...fields, updatedAt: FieldValue.serverTimestamp() })\n return { ok: true as const, job: { ...job, ...fields } }\n })\n if (isRefusal(outcome)) return outcome\n const { job } = outcome as { job: ConsentGroupChangeJob }\n await ctx.log(\n input.orgId,\n { uid: input.actor.uid ?? null, email: input.actor.email ?? null },\n 'Canceled a consent group change before it took effect',\n { type: 'org', id: input.orgId },\n )\n return { ok: true, status: consentGroupChangeStatus(job, ctx.now()) }\n}\n\n/** A job's status, or `null` for a change the org never had. */\nexport async function readConsentGroupChangeStatus(\n input: { orgId: string; changeId: string } & ConsentGroupChangeContext,\n): Promise<ConsentGroupChangeStatusBody | null> {\n const ctx = contextOf(input)\n const job = jobFrom(await jobDoc(ctx, input.orgId, input.changeId).get())\n return job ? consentGroupChangeStatus(job, ctx.now()) : null\n}\n\n/*==========================================\n * THE CRON BACKSTOP\n *=========================================*/\n\nexport interface DueConsentGroupChangeReport {\n orgId: string\n changeId: string\n phase: ConsentGroupsChangePhase\n /** What happened on this run. */\n outcome: 'advanced' | 'listed' | 'locked' | 'orphaned' | 'skipped'\n status?: ConsentGroupChangeStatusBody\n error?: string\n}\n\n/**\n * Advances every change in flight, oldest org id first, until `deadlineMs`.\n *\n * A change nobody is watching still finishes: the progress panel is a\n * convenience, and this is the guarantee. A lockdown on the org — or on the\n * member who started the change — pauses it, like every other write the\n * workspace makes. A marker whose job document is gone is a change nothing\n * can finish, and is cleared rather than blocking every change after it.\n */\nexport async function advanceDueConsentGroupChanges(\n input: { deadlineMs: number; dryRun?: boolean; limit?: number } & ConsentGroupChangeContext,\n): Promise<DueConsentGroupChangeReport[]> {\n const ctx = contextOf(input)\n const snapshot = await ctx.firestore\n .collection('orgs')\n .where(`${CONSENT_GROUPS_CHANGE_FIELD}.phase`, 'in', [...RUNNING_PHASES])\n .limit(input.limit ?? 50)\n .get()\n const reports: DueConsentGroupChangeReport[] = []\n for (const org of snapshot.docs) {\n const data = org.data() as Record<string, unknown>\n const marker = readConsentGroupsChange(data)\n if (!marker) continue\n const base = { orgId: org.id, changeId: marker.changeId, phase: marker.phase }\n if (input.dryRun) {\n reports.push({ ...base, outcome: 'listed' })\n continue\n }\n if (ctx.now() >= input.deadlineMs - DEADLINE_SAFETY_MS) {\n reports.push({ ...base, outcome: 'skipped' })\n continue\n }\n try {\n const job = jobFrom(await jobDoc(ctx, org.id, marker.changeId).get())\n if (!job) {\n await orgDoc(ctx, org.id).update({ [CONSENT_GROUPS_CHANGE_FIELD]: FieldValue.delete() })\n console.error(`[consent-group-change] cleared a marker with no job in org ${org.id}`)\n reports.push({ ...base, outcome: 'orphaned' })\n continue\n }\n if (await ctx.lockedOut(data, job.actor?.uid ?? null)) {\n reports.push({ ...base, outcome: 'locked' })\n continue\n }\n const advanced = await advanceConsentGroupChange({\n orgId: org.id,\n changeId: marker.changeId,\n deadlineMs: input.deadlineMs,\n owner: `cron:${createResourceUid()}`,\n firestore: ctx.firestore,\n log: ctx.log,\n now: ctx.now,\n })\n reports.push({\n ...base,\n outcome: 'advanced',\n ...(advanced.ok ? { status: (advanced as { status: ConsentGroupChangeStatusBody }).status } : {}),\n })\n } catch (error) {\n reports.push({ ...base, outcome: 'advanced', error: sanitizeError(error) })\n }\n }\n return reports\n}\n"],"names":["CONSENT_GROUP_CHANGE_STEPS","CONSENT_GROUP_CHANGES_COLLECTION","CONSENT_GROUP_RENAME_STEPS","CONSENT_GROUP_SWEEP_DELAY_MS","CONSENT_GROUPS_CHANGE_FIELD","consentGroupChangePhaseOf","consentGroupDisclosureChanges","consentGroupsFingerprint","describeConsentGroupChangeLine","discardedConsentGroupIds","estimateConsentGroupChange","orgSiteIds","planConsentGroupChange","readConsentGroupsChange","validateConsentGroupDeclaration","CONSENT_GROUPS_FIELD","consentGroupsAwaitConfirmation","readConsentGroups","createResourceUid","DECLARED_SUBSCRIPTION_TOPICS","TOPIC_OPT_OUTS_SUBCOLLECTION","MARKETING_CONSENT_BY_HOST_FIELD","MARKETING_CONSENT_FIELD","resolveMarketingConsentPolicy","isOrgWideMember","listPluginConsentGroupParticipants","previewPluginConsentGroupChange","FieldValue","CONSENT_GROUP_CARRY_STORES","CONSENT_GROUP_CARRY_SUBCOLLECTIONS","carryConsentGroupRefusals","firebaseAdmin","getLockdownVerdict","logOrgActivity","carryRetainedRefusals","retainedRefusalsStore","CONSENT_GROUP_CHANGE_LEASE_MS","CONSENT_GROUP_CHANGE_STALL_AFTER","CATCH_UP_OVERLAP_MS","DEADLINE_SAFETY_MS","RUNNING_PHASES","contextOf","input","firestore","app","log","now","Date","lockedOut","org","uid","intent","NOT_FOUND","ok","status","body","error","LeaseLost","Error","orgDoc","ctx","orgId","collection","doc","jobDoc","changeId","planOf","job","plan","before","after","sanitizeError","message","String","replace","slice","consentGroupChangeStatus","nowMs","declaredAtMs","index","steps","indexOf","step","phase","done","progress","stepIndex","length","stepCount","sweepAtMs","renameOnly","stalled","failures","lastError","leaseUntilMs","lease","untilMs","counts","retainedRefusals","sitesReceiving","plugins","prepare","expected","groups","marker","raw","current","Array","isArray","siteIds","validation","reserved","Object","keys","errors","isRefusal","value","Boolean","plannedSites","Set","hostIds","values","flatMap","group","siteNamesFor","hosts","snapshots","getAll","map","id","names","forEach","snapshot","get","describeLines","lines","line","hostId","countOf","query","count","Number","data","previewConsentGroupChange","prepared","db","cache","Map","cached","key","pending","set","store","name","suppressions","optOuts","paces","where","retained","carries","Promise","all","carry","toHostId","fromHostId","siteSuppressions","topicOptOuts","membersIn","declaration","find","includes","joining","sort","had","siblings","filter","has","push","inherited","refusals","sibling","pendingHolds","released","releasedHostIds","entries","topic","topicId","changedGroups","was","join","partialAccessMembers","members","member","docs","reach","some","every","policy","forwardPolicyWarning","mode","entry","enforceFromMs","participants","texts","documents","reduce","total","sum","preview","discarded","text","disclosures","capturesDisclosing","estimate","startConsentGroupChange","orgRef","exists","outside","pluginId","jobRef","started","runTransaction","transaction","beforeFingerprint","flows","siteNames","units","actor","email","startedAtMs","carryStartedAtMs","finishedAtMs","canceledAtMs","update","updatedAt","serverTimestamp","create","createdAt","type","unitsOf","sinceMs","_carry","carryIndex","kind","since","jobFrom","save","owner","fields","extra","next","addCounts","base","more","out","isFinite","runUnit","unit","deadlineMs","result","state","cursor","page","counted","written","participant","participantFor","run","dryRun","candidate","nextStep","declare","declared","delete","finished","finishedLine","copied","sites","clauses","clause","summarize","finish","advanceOnce","idle","claim","mine","release","recordFailure","outcome","newlyStalled","console","advanceConsentGroupChange","claimed","cancelConsentGroupChange","orgSnapshot","jobSnapshot","readConsentGroupChangeStatus","advanceDueConsentGroupChanges","limit","reports","advanced"],"mappings":"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"}
@@ -49,7 +49,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
49
49
  * postpones enrollments; it never removes anybody, and it never finishes a
50
50
  * sweep it did not complete.
51
51
  */ import { FieldValue } from "firebase-admin/firestore";
52
- import { soloConsentGroup, candidateMatchesDynamicListRule, dynamicListRuleIsEmpty, dynamicListRuleListIds, dynamicListRuleWithoutListReference, dynamicListRuleNeedsContactFacet, dynamicListDimensionsForCrmView, dynamicListPlanningRule, CRM_COLLECTIONS, normalizeCrmViewFilters, dynamicListRuleNeedsEngagement, extractEmailFromFields, seenOnlyThroughGrant, isContactLifecycleStage, normalizeContactEmail, normalizeDynamicListRule, personKey, readCampaignIds, readContactCampaignIds, readContactFacet } from "@aglyn/aglyn/server";
52
+ import { soloConsentGroup, candidateMatchesDynamicListRule, dynamicListRuleIsEmpty, dynamicListRuleListIds, dynamicListRuleWithoutListReference, dynamicListRuleNeedsContactFacet, dynamicListDimensionsForCrmView, dynamicListPlanningRule, CRM_COLLECTIONS, normalizeCrmViewFilters, dynamicListRuleNeedsEngagement, extractEmailFromFields, seenOnlyThroughGrant, isContactLifecycleStage, normalizeContactEmail, normalizeDynamicListRule, personKey, readContainerIds, readContactContainerIds, readContactFacet } from "@aglyn/aglyn/server";
53
53
  import { consentGroupHoldersInMotion } from "@aglyn/aglyn/app-utils/consent-group-change";
54
54
  import { firebaseAdmin } from "./firebase-admin.js";
55
55
  import { readPersonEngagementByKeys } from "./email-delivery-log.js";
@@ -156,12 +156,12 @@ const millis = (value)=>{
156
156
  ltvCents: Number((_data_ltvCents = data['ltvCents']) != null ? _data_ltvCents : 0),
157
157
  lastPurchaseAtMs: millis(data['lastPurchaseAtMs'])
158
158
  }, groupId ? _extends({
159
- campaignIds: readContactCampaignIds(data, groupId)
159
+ campaignIds: readContactContainerIds(data, groupId, 'campaign')
160
160
  }, crmFieldsFromFacet(data, groupId)) : {}) : {}, source === 'formSubmissions' ? {
161
161
  formName: String((_data_formName = data['formName']) != null ? _data_formName : ''),
162
162
  // At the TOP of a submission, because a submission belongs to one
163
163
  // site — the same shape every other host resource carries it in.
164
- campaignIds: readCampaignIds(data)
164
+ campaignIds: readContainerIds(data, 'campaign')
165
165
  } : {});
166
166
  }
167
167
  /**