@aglyn/tenant-data-admin 1.0.0-beta.221 → 1.0.0-beta.222
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -7
- package/src/lib/server/api-keys.js +3 -3
- package/src/lib/server/api-keys.js.map +1 -1
- package/src/lib/server/api-v1-resources.d.ts +44 -3
- package/src/lib/server/api-v1-resources.js +82 -5
- package/src/lib/server/api-v1-resources.js.map +1 -1
- package/src/lib/server/collection-live-pages.d.ts +2 -2
- package/src/lib/server/collection-live-pages.js +1 -1
- package/src/lib/server/collection-live-pages.js.map +1 -1
- package/src/lib/server/email-delivery-log.d.ts +17 -73
- package/src/lib/server/email-delivery-log.js +44 -222
- package/src/lib/server/email-delivery-log.js.map +1 -1
- package/src/lib/server/email-flow-gate.js +27 -38
- package/src/lib/server/email-flow-gate.js.map +1 -1
- package/src/lib/server/erase-person.d.ts +34 -54
- package/src/lib/server/erase-person.js +44 -239
- package/src/lib/server/erase-person.js.map +1 -1
- package/src/lib/server/host-visitor-records.d.ts +12 -4
- package/src/lib/server/host-visitor-records.js +20 -10
- package/src/lib/server/host-visitor-records.js.map +1 -1
- package/src/lib/server/hosted-page-review.js +46 -33
- package/src/lib/server/hosted-page-review.js.map +1 -1
- package/src/lib/server/link-reputation-review.d.ts +6 -2
- package/src/lib/server/link-reputation-review.js +61 -20
- package/src/lib/server/link-reputation-review.js.map +1 -1
- package/src/lib/server/live-page-drops.d.ts +67 -0
- package/src/lib/server/live-page-drops.js +57 -0
- package/src/lib/server/live-page-drops.js.map +1 -0
- package/src/lib/server/media-bandwidth-cap.d.ts +78 -0
- package/src/lib/server/media-bandwidth-cap.js +180 -0
- package/src/lib/server/media-bandwidth-cap.js.map +1 -0
- package/src/lib/server/media-cdn-icon.d.ts +77 -0
- package/src/lib/server/media-cdn-icon.js +140 -0
- package/src/lib/server/media-cdn-icon.js.map +1 -0
- package/src/lib/server/media-delivery-regeneration.d.ts +132 -0
- package/src/lib/server/media-delivery-regeneration.js +216 -0
- package/src/lib/server/media-delivery-regeneration.js.map +1 -0
- package/src/lib/server/media-delivery.d.ts +2 -0
- package/src/lib/server/media-delivery.js +2 -1
- package/src/lib/server/media-delivery.js.map +1 -1
- package/src/lib/server/media-serve-count.d.ts +50 -0
- package/src/lib/server/media-serve-count.js +62 -0
- package/src/lib/server/media-serve-count.js.map +1 -0
- package/src/lib/server/media-signing.d.ts +13 -2
- package/src/lib/server/media-signing.js +12 -8
- package/src/lib/server/media-signing.js.map +1 -1
- package/src/lib/server/media-tombstone.js +7 -5
- package/src/lib/server/media-tombstone.js.map +1 -1
- package/src/lib/server/media-variants.d.ts +151 -20
- package/src/lib/server/media-variants.js +204 -27
- package/src/lib/server/media-variants.js.map +1 -1
- package/src/lib/server/organizations.d.ts +102 -2
- package/src/lib/server/organizations.js +388 -58
- package/src/lib/server/organizations.js.map +1 -1
- package/src/lib/server/outbound-send-review.js +5 -4
- package/src/lib/server/outbound-send-review.js.map +1 -1
- package/src/lib/server/paged-sweeps.d.ts +51 -0
- package/src/lib/server/paged-sweeps.js +79 -0
- package/src/lib/server/paged-sweeps.js.map +1 -0
- package/src/lib/server/platform-marketing-consent.d.ts +0 -49
- package/src/lib/server/platform-marketing-consent.js +27 -11
- package/src/lib/server/platform-marketing-consent.js.map +1 -1
- package/src/lib/server/serve-media-cdn.d.ts +69 -1
- package/src/lib/server/serve-media-cdn.js +424 -70
- package/src/lib/server/serve-media-cdn.js.map +1 -1
- package/src/lib/server/site-icon-render.d.ts +68 -0
- package/src/lib/server/site-icon-render.js +157 -0
- package/src/lib/server/site-icon-render.js.map +1 -0
- package/src/lib/server/staff-seat.d.ts +50 -0
- package/src/lib/server/staff-seat.js +118 -0
- package/src/lib/server/staff-seat.js.map +1 -0
- package/src/lib/server/transfer-export.d.ts +46 -0
- package/src/lib/server/transfer-export.js +221 -0
- package/src/lib/server/transfer-export.js.map +1 -0
- package/src/lib/server/transfer-jobs.d.ts +385 -0
- package/src/lib/server/transfer-jobs.js +2368 -0
- package/src/lib/server/transfer-jobs.js.map +1 -0
- package/src/lib/server/transfer-packages.d.ts +127 -0
- package/src/lib/server/transfer-packages.js +1036 -0
- package/src/lib/server/transfer-packages.js.map +1 -0
- package/src/lib/server/upgrade-proposal.d.ts +97 -0
- package/src/lib/server/upgrade-proposal.js +312 -0
- package/src/lib/server/upgrade-proposal.js.map +1 -0
- package/src/lib/server/upsert-contact.d.ts +17 -8
- package/src/lib/server/upsert-contact.js +82 -16
- package/src/lib/server/upsert-contact.js.map +1 -1
- package/src/lib/server/web-risk.d.ts +48 -9
- package/src/lib/server/web-risk.js +281 -143
- package/src/lib/server/web-risk.js.map +1 -1
- package/src/tenancy.d.ts +3 -1
- package/src/tenancy.js +5 -1
- package/src/tenancy.js.map +1 -1
- package/src/lib/server/campaign-attribution-store.d.ts +0 -55
- package/src/lib/server/campaign-attribution-store.js +0 -95
- package/src/lib/server/campaign-attribution-store.js.map +0 -1
- package/src/lib/server/campaign-conversion-attribution.d.ts +0 -283
- package/src/lib/server/campaign-conversion-attribution.js +0 -450
- package/src/lib/server/campaign-conversion-attribution.js.map +0 -1
- package/src/lib/server/dataset-live-pages.d.ts +0 -179
- package/src/lib/server/dataset-live-pages.js +0 -421
- package/src/lib/server/dataset-live-pages.js.map +0 -1
- package/src/lib/server/email-revenue-attribution.d.ts +0 -185
- package/src/lib/server/email-revenue-attribution.js +0 -321
- package/src/lib/server/email-revenue-attribution.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/transfer-packages.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 * WORKSPACE PACKAGES (AGL-3535) — a workspace's sequences, campaigns,\n * automations and email templates as one file, imported as a job.\n *\n * A row import's file is rows; a package's is items, each owned by the\n * plugin whose `package` transfer resource its kind names. So a package\n * does not go through the row engine's upload, analyze and chunked plan —\n * there is no column to map — but it is the same kind of job, under the\n * same guarantees, in the same place:\n *\n * - the job is `orgs/{orgId}/transferJobs/{jobId}` with `kind: 'package'`\n * and `resource: 'package'`, so the hub's history lists it beside every\n * row import, the sweep resumes and cleans it, and only one package\n * import runs per workspace at a time (the lease's per-resource lock);\n * - the file is stored where a row import's is\n * (`orgs/{orgId}/transfers/{jobId}/source`), and the dry run in\n * `chunks/0` — the plan the person reviewed is the plan applied;\n * - every item's write is recorded in the ledger the moment it lands, so\n * a retried apply never writes an item twice;\n * - the undo snapshot (`undo/0`) is taken BEFORE the first write: each\n * replaced item's content as the workspace held it. Once the writes\n * land, each written item's content hash is added to it, so undo can\n * tell the import's own content from a later edit and asks before it\n * overwrites or deletes one — for seven days;\n * - plan, apply and undo are audited by the route.\n *\n * Why not the row engine's routes: a row import plans 200 rows a chunk\n * against a column mapping; a package is planned whole, against item\n * identities and references (`planTransferPackage`), and its writes are\n * grouped by owning plugin in dependency order. One route\n * (`/api/transfer/package`) speaks its actions; this module is the engine\n * behind it, built on the row engine's job, ledger and lease.\n *\n * The plugin reads, writes and reverts through its own paths; this module\n * decides what to write, in which order, under which id, and what undo\n * does.\n *=========================================*/\n\nimport {\n TRANSFER_PACKAGE_MAX_BYTES,\n TRANSFER_PACKAGE_MAX_ITEMS,\n TRANSFER_PACKAGE_RESOURCE,\n TRANSFER_RESULT_COLUMNS,\n TRANSFER_SITE_KIND,\n buildPackageManifest,\n canApplyTransferPackage,\n contentHash,\n missingPackageAcknowledgements,\n packageIdMap,\n packageItemKey,\n planTransferPackage,\n readPackageManifest,\n summarizeTransferResults,\n transferLedgerKey,\n transferUndoAvailable,\n transferUndoExpiresAt,\n TRANSFER_WRITE_CONCURRENCY,\n type ExistingPackageItem,\n type PackageItemDecision,\n type PackageManifestItem,\n type TransferJobRecord,\n type TransferPackage,\n type TransferPackageDependencyChoice,\n type TransferPackageJobState,\n type TransferPackageListItem,\n type TransferPackagePlan,\n type TransferPackagePlanItem,\n type TransferPackageReference,\n type TransferPackageResourceList,\n type TransferPackageRuleView,\n type TransferPackageUndoConflict,\n type TransferPackageUndoEntry,\n type TransferPackageWarningClass,\n type TransferReferenceKind,\n type TransferRowResult,\n type TransferUndoCounts,\n type TransferUndoDecision,\n type TransferUndoState,\n} from '@aglyn/aglyn/data-transfer'\nimport { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport { escapeCsvCell } from '@aglyn/aglyn/app-utils/csv'\nimport { inspectUploadBytes } from '@aglyn/aglyn/app-utils/upload-inspection'\nimport {\n resolveTransferResource,\n transferPackageHooks,\n TransferResourceUnavailableError,\n type ResolvedTransferResource,\n type TransferApplyWriter,\n type TransferPackageHooks,\n type TransferPackageItemWrite,\n type TransferPackageRevertStep,\n type TransferResourceContext,\n} from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources'\nimport {\n clock,\n eachLimited,\n emptyResults,\n emptyUndoCounts,\n isAlreadyExists,\n moveJob,\n readJob,\n readJsonDoc,\n saveJob,\n stored,\n takeLease,\n transferJobsCollection,\n transferSourcePath,\n TransferEngineError,\n TRANSFER_LEASE_GRACE_MS,\n TRANSFER_MIN_CHUNK_BUDGET_MS,\n writeJsonDoc,\n type TransferEngineDeps,\n} from './transfer-jobs'\n\n/*==========================================\n * RESOURCES\n *=========================================*/\n\ninterface PackageResource {\n resource: ResolvedTransferResource\n hooks: TransferPackageHooks\n}\n\n/** The package resources among `keys` that the workspace may move, by key. */\nasync function packageResources(\n deps: TransferEngineDeps,\n keys: readonly string[],\n): Promise<Map<string, PackageResource>> {\n const found = new Map<string, PackageResource>()\n for (const key of keys) {\n try {\n const resource = await (deps.resolveResource ?? resolveTransferResource)(key)\n if (resource.scope !== 'org' || !resource.kinds.includes('package')) continue\n found.set(resource.key, { resource, hooks: transferPackageHooks(resource) })\n } catch (error) {\n if (error instanceof TransferResourceUnavailableError && error.reason === 'undeclared') continue\n if (error instanceof TransferResourceUnavailableError) {\n throw new TransferEngineError('unavailable', 500, 'Importing packages is unavailable right now. Try again in a minute.')\n }\n throw error\n }\n }\n return found\n}\n\nfunction contextOf(orgId: string, resource: string, actorUid: string | null, jobId?: string): TransferResourceContext {\n return { resource, orgId, hostId: null, actorUid, ...(jobId ? { jobId } : {}) }\n}\n\nfunction rulesOf(entry: PackageResource): TransferPackageRuleView[] {\n return (entry.hooks.rules ?? []).map((rule) => ({ id: rule.id, label: rule.label, reason: rule.reason }))\n}\n\n/** The workspace's sites, as references to the `site` kind resolve. */\nasync function siteTargets(deps: TransferEngineDeps, orgId: string): Promise<TransferReferenceKind> {\n const snapshot = await deps.firestore.collection('hosts').where('orgId', '==', orgId).get()\n return {\n kind: TRANSFER_SITE_KIND,\n label: 'Site',\n targets: snapshot.docs\n .filter((doc) => !(doc.data() as { deletedAt?: unknown }).deletedAt)\n .map((doc) => {\n const data = doc.data() as { name?: unknown; title?: unknown }\n const name = String(data.name ?? data.title ?? '').trim()\n return { id: doc.id, ...(name ? { name } : {}) }\n }),\n }\n}\n\n/*==========================================\n * LIST AND EXPORT\n *=========================================*/\n\nexport interface TransferPackageListInput {\n orgId: string\n actorUid: string\n /** The package resources the workspace may move (its enabled plugins'). */\n allowed: readonly string[]\n /** Only these; every allowed one when absent. */\n resources?: readonly string[]\n}\n\n/** Every item of each package resource, with its name and what it names, for the export picker. */\nexport async function listTransferPackageItems(\n deps: TransferEngineDeps,\n input: TransferPackageListInput,\n): Promise<TransferPackageResourceList[]> {\n const keys = input.resources?.length ? input.allowed.filter((key) => input.resources?.includes(key)) : input.allowed\n const resources = await packageResources(deps, keys)\n const lists: TransferPackageResourceList[] = []\n for (const [key, entry] of resources) {\n const ctx = contextOf(input.orgId, key, input.actorUid)\n const held = await entry.hooks.items(ctx)\n const contents = held.length ? await entry.hooks.readItems(ctx, held.map((item) => item.id)) : []\n const items: TransferPackageListItem[] = contents.map((one) => ({\n key: packageItemKey({ kind: key, id: one.id }),\n kind: key,\n id: one.id,\n ...(one.name ? { name: one.name } : {}),\n deps: entry.hooks.dependencies(one.content),\n }))\n lists.push({\n key,\n label: entry.resource.label,\n pluginId: entry.resource.pluginId,\n ...(entry.resource.description ? { description: entry.resource.description } : {}),\n items,\n rules: rulesOf(entry),\n })\n }\n return lists\n}\n\nexport interface TransferPackageExportInput extends TransferPackageListInput {\n /** Item keys; every item of the resources when absent. */\n items?: readonly string[]\n /** Add what the chosen items name that the workspace holds as package items, transitively. */\n dependencies?: boolean\n /** Where the file says it came from (\"Workspace: Acme\"). */\n source?: string\n}\n\n/** The package file: the chosen items (and, when asked, what they need), hashed and listed. */\nexport async function exportTransferPackage(\n deps: TransferEngineDeps,\n input: TransferPackageExportInput,\n): Promise<TransferPackage> {\n const resources = await packageResources(deps, input.allowed)\n const wanted = input.resources?.length ? new Set(input.resources) : null\n const contents = new Map<string, { kind: string; id: string; name?: string; slug?: string; content: unknown }>()\n const read = async (key: string, ids: readonly string[]) => {\n const entry = resources.get(key)\n if (!entry || !ids.length) return\n for (const one of await entry.hooks.readItems(contextOf(input.orgId, key, input.actorUid), ids)) {\n contents.set(packageItemKey({ kind: key, id: one.id }), {\n kind: key,\n id: one.id,\n ...(one.name ? { name: one.name } : {}),\n ...(one.slug ? { slug: one.slug } : {}),\n content: one.content,\n })\n }\n }\n if (input.items?.length) {\n const byKind = new Map<string, string[]>()\n for (const itemKey of input.items) {\n const at = itemKey.indexOf('/')\n const kind = itemKey.slice(0, at)\n if (at < 1 || (wanted && !wanted.has(kind))) continue\n byKind.set(kind, [...(byKind.get(kind) ?? []), itemKey.slice(at + 1)])\n }\n for (const [kind, ids] of byKind) await read(kind, ids)\n } else {\n for (const [key, entry] of resources) {\n if (wanted && !wanted.has(key)) continue\n const held = await entry.hooks.items(contextOf(input.orgId, key, input.actorUid))\n await read(key, held.map((item) => item.id))\n }\n }\n if (input.dependencies) {\n // What the chosen items name that is itself a package item here, until nothing new is named.\n for (let pass = 0; pass < TRANSFER_PACKAGE_MAX_ITEMS; pass += 1) {\n const missing = new Map<string, string[]>()\n for (const item of contents.values()) {\n for (const dep of resources.get(item.kind)?.hooks.dependencies(item.content) ?? []) {\n if (!resources.has(dep.kind) || contents.has(packageItemKey(dep))) continue\n missing.set(dep.kind, [...new Set([...(missing.get(dep.kind) ?? []), dep.id])])\n }\n }\n if (!missing.size) break\n const before = contents.size\n for (const [kind, ids] of missing) await read(kind, ids)\n if (contents.size === before) break\n }\n }\n if (contents.size > TRANSFER_PACKAGE_MAX_ITEMS) {\n throw new TransferEngineError('tooLarge', 413, `A package holds at most ${TRANSFER_PACKAGE_MAX_ITEMS} items. Choose fewer.`)\n }\n return buildPackageManifest(\n [...contents.values()],\n (item) => {\n const held = contents.get(packageItemKey(item))\n return {\n ...(held?.name ? { name: held.name } : {}),\n ...(held?.slug ? { slug: held.slug } : {}),\n deps: resources.get(item.kind)?.hooks.dependencies(item.content) ?? [],\n }\n },\n { createdAt: clock(deps), ...(input.source ? { source: input.source } : {}) },\n )\n}\n\n/*==========================================\n * PLAN\n *=========================================*/\n\n/** The dry run as `chunks/0` keeps it. */\ninterface StoredPackagePlan {\n items: TransferPackagePlanItem[]\n references: TransferPackageReference[]\n order: string[]\n idMap: Record<string, string>\n}\n\n/** A package read from untrusted JSON, every item's content present and matching its hash — or why not. */\nasync function readPackageFile(raw: unknown): Promise<TransferPackage> {\n const source = (raw && typeof raw === 'object' ? raw : {}) as { manifest?: unknown; items?: unknown }\n const read = readPackageManifest(source.manifest)\n if (read.ok === false) {\n throw new TransferEngineError('rejectedFile', 422, 'This file is not a package this workspace can import.', { problems: read.problems })\n }\n const items = (source.items && typeof source.items === 'object' ? source.items : {}) as Record<string, unknown>\n if (read.manifest.items.length > TRANSFER_PACKAGE_MAX_ITEMS) {\n throw new TransferEngineError('tooLarge', 413, `A package holds at most ${TRANSFER_PACKAGE_MAX_ITEMS} items.`)\n }\n const problems: string[] = []\n for (const item of read.manifest.items) {\n const key = packageItemKey(item)\n if (!(key in items)) {\n problems.push(`${item.name ?? key} is listed and not carried.`)\n continue\n }\n if ((await contentHash(items[key])) !== item.contentHash) {\n problems.push(`${item.name ?? key} does not match its content hash: the file was changed after it was made.`)\n }\n }\n if (problems.length) {\n throw new TransferEngineError('rejectedFile', 422, 'This package is damaged.', { problems })\n }\n return { manifest: read.manifest, items }\n}\n\nasync function loadPackage(deps: TransferEngineDeps, job: TransferJobRecord): Promise<TransferPackage> {\n if (!job.sourcePath) throw new TransferEngineError('state', 409, 'The file behind this import is gone.')\n const [bytes] = await deps.bucket.file(job.sourcePath).download()\n return JSON.parse(bytes.toString('utf8')) as TransferPackage\n}\n\nasync function readStoredPlan(deps: TransferEngineDeps, job: TransferJobRecord): Promise<StoredPackagePlan> {\n const plan = await readJsonDoc<StoredPackagePlan>(\n transferJobsCollection(deps.firestore, job.orgId).doc(job.id).collection('chunks').doc('0'),\n )\n if (!plan) throw new TransferEngineError('state', 409, 'This import has no dry run.')\n return plan\n}\n\nexport interface PlanTransferPackageInput {\n orgId: string\n actorUid: string\n allowed: readonly string[]\n jobId?: string | null\n /** The file's parsed JSON, on the first call. */\n package?: unknown\n fileName?: string | null\n decisions?: Readonly<Record<string, PackageItemDecision>>\n dependencyChoices?: Readonly<Record<string, TransferPackageDependencyChoice>>\n}\n\nexport interface PlanTransferPackageOutcome {\n job: TransferJobRecord\n plan: TransferPackagePlan\n resources: Record<string, { label: string; rules: TransferPackageRuleView[] }>\n /** The first call made the job. */\n created: boolean\n}\n\n/**\n * The dry run (see the block header): reads what the workspace holds of\n * every kind the package carries or names, plans with the person's\n * choices, asks each written item's plugin what its own write would\n * refuse, and stores the plan. Writes no item.\n */\nexport async function planTransferPackageImport(\n deps: TransferEngineDeps,\n input: PlanTransferPackageInput,\n): Promise<PlanTransferPackageOutcome> {\n const now = clock(deps)\n let job: TransferJobRecord\n let pkg: TransferPackage\n let created = false\n if (input.jobId) {\n job = await readJob(deps, input.orgId, input.jobId)\n if (job.kind !== 'package') throw new TransferEngineError('invalid', 400, 'This import is not a package.')\n if (job.applyStartedAt !== undefined) {\n throw new TransferEngineError('state', 409, 'This import has started writing and cannot be planned again.')\n }\n if (job.status !== 'planned' && job.status !== 'failed') {\n throw new TransferEngineError('state', 409, `This import is ${job.status} and cannot be planned again.`)\n }\n pkg = await loadPackage(deps, job)\n } else {\n if (input.package === undefined) throw new TransferEngineError('invalid', 400, 'Choose a package file.')\n const text = JSON.stringify(input.package)\n if (Buffer.byteLength(text, 'utf8') > TRANSFER_PACKAGE_MAX_BYTES) {\n throw new TransferEngineError('tooLarge', 413, 'A package file holds at most 3 MB.')\n }\n pkg = await readPackageFile(input.package)\n const id = (deps.newJobId ?? createResourceUid)()\n const sourcePath = transferSourcePath(input.orgId, id)\n // A member's file, stored beside the workspace's other objects: inspected like every upload first.\n const bytes = Buffer.from(JSON.stringify(pkg), 'utf8')\n const refusal = inspectUploadBytes({ bytes, contentType: 'application/json', fileName: String(input.fileName ?? 'package.json') })\n if (refusal) throw new TransferEngineError('rejectedFile', 422, refusal.message, { code: refusal.code })\n await deps.bucket.file(sourcePath).save(bytes, {\n contentType: 'application/json',\n resumable: false,\n })\n created = true\n job = {\n id,\n resource: TRANSFER_PACKAGE_RESOURCE,\n kind: 'package',\n direction: 'import',\n format: 'json',\n status: 'planned',\n orgId: input.orgId,\n createdBy: input.actorUid,\n createdAt: now,\n updatedAt: now,\n fileName: String(input.fileName ?? '').trim().slice(0, 200) || 'package.json',\n sourcePath,\n rowCount: pkg.manifest.items.length,\n chunkCount: 1,\n }\n // Kept before planning, so a plan that fails still leaves a job the sweep expires with its file.\n await saveJob(deps, job)\n }\n\n const resources = await packageResources(deps, input.allowed)\n const owned = new Set(resources.keys())\n const manifest = pkg.manifest\n const kindsNeeded = new Set<string>()\n const foreignByResource = new Map<string, Set<string>>()\n let needsSites = false\n for (const item of manifest.items) {\n if (!owned.has(item.kind)) continue\n kindsNeeded.add(item.kind)\n for (const dep of item.deps) {\n if (owned.has(dep.kind)) kindsNeeded.add(dep.kind)\n else if (dep.kind === TRANSFER_SITE_KIND) needsSites = true\n else foreignByResource.set(item.kind, new Set([...(foreignByResource.get(item.kind) ?? []), dep.kind]))\n }\n }\n const existing: ExistingPackageItem[] = []\n for (const kind of kindsNeeded) {\n const entry = resources.get(kind) as PackageResource\n for (const one of await entry.hooks.items(contextOf(input.orgId, kind, input.actorUid, job.id))) {\n existing.push({ ...one, kind })\n }\n }\n const references: TransferReferenceKind[] = needsSites ? [await siteTargets(deps, input.orgId)] : []\n for (const [key, kinds] of foreignByResource) {\n const answered = await resources.get(key)?.hooks.referenceTargets?.(contextOf(input.orgId, key, input.actorUid, job.id), [...kinds])\n for (const kind of answered ?? []) {\n if (!references.some((known) => known.kind === kind.kind)) references.push(kind)\n }\n }\n const kindLabels = Object.fromEntries(\n [...resources].map(([key, entry]) => [key, entry.resource.singularLabel ?? entry.resource.label]),\n )\n // Kept-both ids are minted once and reused by the second pass, so the problems asked about are the ids applied.\n const minted: string[] = []\n let mintedAt = 0\n const newId = () => {\n if (mintedAt >= minted.length) minted.push(createResourceUid())\n return minted[mintedAt++] as string\n }\n const base = {\n manifest,\n ownedKinds: owned,\n existing,\n references,\n kindLabels,\n decisions: input.decisions ?? job.package?.decisions ?? {},\n dependencyChoices: input.dependencyChoices ?? job.package?.dependencyChoices ?? {},\n }\n const first = planTransferPackage({ ...base, newId })\n const problems: Record<string, string[]> = {}\n const idMap = packageIdMap(first.idMap)\n for (const item of first.items) {\n if (item.verdict === 'skip' || item.targetId === undefined) continue\n const entry = resources.get(item.kind) as PackageResource\n const hook = entry.hooks.problems\n if (!hook) continue\n const write = writeFor(entry, item, manifest.items[item.row] as PackageManifestItem, pkg.items[item.key], idMap)\n const found = await hook(contextOf(input.orgId, item.kind, input.actorUid, job.id), write)\n if (found.length) problems[item.key] = [...found]\n }\n mintedAt = 0\n const plan = planTransferPackage({ ...base, problems, newId })\n\n const jobRef = transferJobsCollection(deps.firestore, input.orgId).doc(job.id)\n const kept: StoredPackagePlan = { items: plan.items, references: plan.references, order: plan.order, idMap: plan.idMap }\n await writeJsonDoc(jobRef.collection('chunks').doc('0'), { jobId: job.id, index: 0, package: true }, kept)\n const state: TransferPackageJobState = {\n ...(manifest.source ? { source: manifest.source } : {}),\n resources: [...new Set(plan.items.filter((item) => owned.has(item.kind)).map((item) => item.kind))],\n summary: plan.summary,\n blocking: plan.blocking,\n acknowledgementsRequired: plan.acknowledgementsRequired,\n unknownKinds: plan.unknownKinds,\n decisions: { ...base.decisions },\n dependencyChoices: { ...base.dependencyChoices },\n }\n job =\n job.status === 'failed'\n ? moveJob(job, 'planned', clock(deps), { package: state, plannedAt: clock(deps) })\n : { ...job, status: 'planned', package: state, plannedAt: clock(deps), updatedAt: clock(deps) }\n await saveJob(deps, job)\n return {\n job,\n plan,\n created,\n resources: Object.fromEntries(\n [...resources].map(([key, entry]) => [key, { label: entry.resource.label, rules: rulesOf(entry) }]),\n ),\n }\n}\n\n/** One planned item as its plugin writes it: references rewritten, a kept-both copy's new name and slug. */\nfunction writeFor(\n entry: PackageResource,\n item: TransferPackagePlanItem,\n manifestItem: PackageManifestItem,\n content: unknown,\n idMap: ReadonlyMap<string, string>,\n): TransferPackageItemWrite {\n return {\n row: item.row,\n item: manifestItem,\n decision: item.decision,\n targetId: item.targetId as string,\n ...(item.rename ? { rename: item.rename } : {}),\n content: entry.hooks.remapIds(content, idMap),\n }\n}\n\n/*==========================================\n * APPLY\n *=========================================*/\n\nexport interface ApplyTransferPackageInput {\n orgId: string\n jobId: string\n actorUid: string\n acknowledged?: readonly TransferPackageWarningClass[]\n deadlineMs: number\n driver: string\n}\n\nexport interface ApplyTransferPackageOutcome {\n job: TransferJobRecord\n done: boolean\n /** This call moved the job into `applying`. */\n started: boolean\n resumed: boolean\n results: TransferRowResult[]\n}\n\nconst WRITE_VERDICTS = new Set(['create', 'replace', 'keepBoth'])\n\n/** What an item the plan does not write comes to. */\nfunction unwrittenResult(item: TransferPackagePlanItem): TransferRowResult {\n if (item.verdict === 'fail') {\n return { row: item.row, outcome: 'failed', reason: 'problems', ...(item.problems[0] ? { message: item.problems.join(' ') } : {}) }\n }\n return { row: item.row, outcome: 'skipped', ...(item.reason ? { reason: item.reason } : {}) }\n}\n\n/**\n * Writes the planned items (see the block header): the undo snapshot\n * first, then each owning plugin's items in dependency order through the\n * ledger, then the written hashes and the results. Called again until\n * `done`; a plugin that throws fails the job, and calling again resumes it.\n */\nexport async function applyTransferPackage(\n deps: TransferEngineDeps,\n input: ApplyTransferPackageInput,\n): Promise<ApplyTransferPackageOutcome> {\n let job = await readJob(deps, input.orgId, input.jobId)\n if (job.kind !== 'package' || !job.package) throw new TransferEngineError('invalid', 400, 'This import is not a package.')\n if (job.status === 'applied') return { job, done: true, started: false, resumed: false, results: [] }\n if (job.status !== 'planned' && job.status !== 'applying' && job.status !== 'failed') {\n throw new TransferEngineError('state', 409, `This import is ${job.status}; plan it before applying.`)\n }\n const acknowledged = [...(input.acknowledged ?? job.package.acknowledged ?? [])]\n if (job.applyStartedAt === undefined) {\n if (job.package.blocking.length) {\n throw new TransferEngineError('choicesNeeded', 422, 'Answer every choice the dry run asks before importing.', {\n blocking: job.package.blocking,\n })\n }\n if (!canApplyTransferPackage(job.package, acknowledged)) {\n const missing = missingPackageAcknowledgements(job.package, acknowledged)\n if (!missing.length) throw new TransferEngineError('state', 409, 'The dry run writes nothing.')\n throw new TransferEngineError('acknowledgementsMissing', 422, 'Acknowledge every warning before importing.', { missing })\n }\n }\n let started = false\n let resumed = false\n job = await takeLease(deps, job, input.driver, input.deadlineMs + TRANSFER_LEASE_GRACE_MS, (current, now) => {\n if (current.status === 'applying') return current\n started = true\n resumed = current.status === 'failed'\n return moveJob(current, 'applying', now, {\n ...(current.applyStartedAt === undefined && current.package\n ? { package: { ...current.package, acknowledged: acknowledged as TransferPackageWarningClass[] } }\n : {}),\n cursor: current.cursor ?? { chunk: 0, rowsDone: 0 },\n applyStartedAt: current.applyStartedAt ?? now,\n results: current.results ?? emptyResults(),\n })\n })\n\n const jobRef = transferJobsCollection(deps.firestore, job.orgId).doc(job.id)\n const timeLeft = () => input.deadlineMs - clock(deps)\n try {\n const plan = await readStoredPlan(deps, job)\n const pkg = await loadPackage(deps, job)\n const resources = await packageResources(deps, job.package?.resources ?? [])\n const byKey = new Map(plan.items.map((item) => [item.key, item]))\n const writes = plan.order.map((key) => byKey.get(key)).filter((item): item is TransferPackagePlanItem =>\n Boolean(item && WRITE_VERDICTS.has(item.verdict)),\n )\n for (const item of writes) {\n if (!resources.has(item.kind)) throw new Error(`nothing here writes ${item.kind} any more`)\n }\n\n // The snapshot first: nothing is written that undo could not put back.\n const undoRef = jobRef.collection('undo').doc('0')\n let entries = await readJsonDoc<TransferPackageUndoEntry[]>(undoRef)\n if (!entries) {\n entries = []\n const replaced = new Map<string, string[]>()\n for (const item of writes) {\n if (item.verdict === 'replace') replaced.set(item.kind, [...(replaced.get(item.kind) ?? []), item.targetId as string])\n }\n const previous = new Map<string, unknown>()\n for (const [kind, ids] of replaced) {\n const entry = resources.get(kind) as PackageResource\n for (const one of await entry.hooks.readItems(contextOf(job.orgId, kind, input.actorUid, job.id), ids)) {\n previous.set(packageItemKey({ kind, id: one.id }), one.content)\n }\n }\n for (const item of writes) {\n const targetKey = packageItemKey({ kind: item.kind, id: item.targetId as string })\n const before = previous.get(targetKey)\n entries.push({\n row: item.row,\n key: item.key,\n resource: item.kind,\n id: item.targetId as string,\n ...((item.rename?.name ?? item.existing?.name ?? item.name) ? { name: item.rename?.name ?? item.existing?.name ?? item.name } : {}),\n action: item.verdict === 'replace' ? 'updated' : 'created',\n ...(before !== undefined ? { previous: before, previousHash: await contentHash(before) } : {}),\n })\n }\n await writeJsonDoc(undoRef, { jobId: job.id, chunk: 0, package: true }, entries)\n }\n\n const ledgerCollection = jobRef.collection('ledger')\n const ledger = new Map<number, TransferRowResult>()\n for (const doc of (await ledgerCollection.where('chunk', '==', 0).get()).docs) {\n const data = doc.data() as { row: number; result: TransferRowResult }\n ledger.set(data.row, data.result)\n }\n const writer: TransferApplyWriter = {\n async alreadyApplied(row) {\n return ledger.get(row) ?? null\n },\n async markApplied(result) {\n if (ledger.has(result.row)) return\n try {\n await ledgerCollection\n .doc(transferLedgerKey(job.id, result.row))\n .create(stored({ row: result.row, chunk: 0, result, undoJson: null, at: clock(deps) }))\n } catch (error) {\n if (!isAlreadyExists(error)) throw error\n }\n ledger.set(result.row, result)\n },\n timeLeftMs: timeLeft,\n }\n\n const idMap = packageIdMap(plan.idMap)\n // Each owner's run of items in dependency order, handed over together.\n const runs: TransferPackagePlanItem[][] = []\n for (const item of writes) {\n const last = runs[runs.length - 1]\n if (last && last[0]?.kind === item.kind) last.push(item)\n else runs.push([item])\n }\n for (const run of runs) {\n const pending = run.filter((item) => !ledger.has(item.row))\n if (!pending.length) continue\n if (timeLeft() <= TRANSFER_MIN_CHUNK_BUDGET_MS) break\n const kind = run[0]?.kind as string\n const entry = resources.get(kind) as PackageResource\n const items = pending.map((item) =>\n writeFor(entry, item, pkg.manifest.items[item.row] as PackageManifestItem, pkg.items[item.key], idMap),\n )\n const applied = await entry.hooks.writeItems(contextOf(job.orgId, kind, input.actorUid, job.id), items, writer)\n const planned = new Set(pending.map((item) => item.row))\n for (const result of applied.results) {\n if (planned.has(result.row) && !ledger.has(result.row)) await writer.markApplied(result)\n }\n }\n\n if (writes.some((item) => !ledger.has(item.row))) {\n job = { ...job, lease: null, updatedAt: clock(deps) }\n await saveJob(deps, job)\n return { job, done: false, started, resumed, results: [] }\n }\n\n // Every write landed: what each item holds now is what undo compares a later edit against.\n const hashes = new Map<string, string>()\n for (const kind of new Set(writes.map((item) => item.kind))) {\n const entry = resources.get(kind) as PackageResource\n for (const one of await entry.hooks.items(contextOf(job.orgId, kind, input.actorUid, job.id))) {\n hashes.set(packageItemKey({ kind, id: one.id }), one.contentHash)\n }\n }\n const settled = entries.map((entry) => {\n const written = ledger.get(entry.row)\n const hash = hashes.get(packageItemKey({ kind: entry.resource, id: entry.id }))\n return written && written.outcome !== 'failed' && hash ? { ...entry, writtenHash: hash } : entry\n }).filter((entry) => ledger.get(entry.row)?.outcome !== 'failed')\n await writeJsonDoc(undoRef, { jobId: job.id, chunk: 0, package: true }, settled)\n\n const results = plan.items.map((item) => (WRITE_VERDICTS.has(item.verdict) ? (ledger.get(item.row) as TransferRowResult) : unwrittenResult(item)))\n const now = clock(deps)\n // The results before the job: a crash between the two resumes into the same results from the ledger.\n await jobRef.collection('results').doc('0').set(stored({ jobId: job.id, index: 0, results, completedAt: now }))\n job = moveJob(job, 'applied', now, {\n lease: null,\n cursor: { chunk: 1, rowsDone: plan.items.length },\n results: summarizeTransferResults(results),\n })\n await saveJob(deps, job)\n const ledgerDocs = (await ledgerCollection.where('chunk', '==', 0).get()).docs\n await eachLimited(ledgerDocs, TRANSFER_WRITE_CONCURRENCY, async (doc) => {\n await doc.ref.delete()\n })\n return { job, done: true, started, resumed, results }\n } catch (error) {\n if (error instanceof TransferEngineError) throw error\n const message = error instanceof Error ? error.message : String(error)\n job = moveJob(job, 'failed', clock(deps), {\n error: { code: 'applyFailed', message: `The package could not be written: ${message}`, chunk: 0 },\n lease: null,\n })\n await saveJob(deps, job)\n return { job, done: false, started, resumed, results: [] }\n }\n}\n\n/*==========================================\n * RESULTS\n *=========================================*/\n\n/** The result file of a package import: each item, what was decided, and what happened. */\nexport async function transferPackageResultFile(\n deps: TransferEngineDeps,\n job: TransferJobRecord,\n): Promise<{ csv: string; rows: number; fileName: string }> {\n if (job.trimmedAt !== undefined) {\n throw new TransferEngineError('state', 409, 'This import was cleared once its undo window closed; its results are still counted.')\n }\n const plan = await readStoredPlan(deps, job)\n const jobRef = transferJobsCollection(deps.firestore, job.orgId).doc(job.id)\n const results = new Map<number, TransferRowResult>()\n for (const doc of (await jobRef.collection('results').get()).docs) {\n for (const result of (doc.data() as { results?: TransferRowResult[] }).results ?? []) results.set(result.row, result)\n }\n const cell = (value: string) => escapeCsvCell(value)\n const lines = [['Kind', 'Name', 'ID', 'Decision', ...TRANSFER_RESULT_COLUMNS].map(cell).join(',')]\n for (const item of plan.items) {\n const result = results.get(item.row)\n lines.push(\n [\n item.kind,\n item.name ?? '',\n item.id,\n item.verdict,\n result?.outcome ?? 'pending',\n result ? (result.message ?? (result.reason ? String(result.reason) : '')) : '',\n result?.recordId ?? '',\n ]\n .map(cell)\n .join(','),\n )\n }\n const base = (job.fileName ?? 'package').replace(/\\.[a-z0-9]+$/i, '')\n return { csv: `${lines.join('\\r\\n')}\\r\\n`, rows: plan.items.length, fileName: `${base}-results.csv` }\n}\n\n/*==========================================\n * UNDO\n *=========================================*/\n\ntype PackageUndoStep =\n | { action: 'delete' | 'restore'; entry: TransferPackageUndoEntry }\n | { action: 'conflict'; entry: TransferPackageUndoEntry }\n | { action: 'nothing'; entry: TransferPackageUndoEntry }\n\n/**\n * What undo does to one item, against what it holds now: an item the\n * import created and nobody touched is deleted; one it replaced and nobody\n * touched is restored; one edited since is a conflict; one already back\n * where it was, or gone, needs nothing.\n */\nfunction packageUndoStep(entry: TransferPackageUndoEntry, currentHash: string | undefined): PackageUndoStep {\n if (currentHash === undefined) return { action: 'nothing', entry }\n if (entry.action === 'updated' && entry.previousHash && currentHash === entry.previousHash) return { action: 'nothing', entry }\n if (entry.writtenHash && currentHash === entry.writtenHash) {\n return { action: entry.action === 'created' ? 'delete' : 'restore', entry }\n }\n return { action: 'conflict', entry }\n}\n\nasync function undoSteps(\n deps: TransferEngineDeps,\n job: TransferJobRecord,\n actorUid: string,\n): Promise<{ steps: PackageUndoStep[]; resources: Map<string, PackageResource> }> {\n const entries =\n (await readJsonDoc<TransferPackageUndoEntry[]>(\n transferJobsCollection(deps.firestore, job.orgId).doc(job.id).collection('undo').doc('0'),\n )) ?? []\n const resources = await packageResources(deps, [...new Set(entries.map((entry) => entry.resource))])\n const hashes = new Map<string, string>()\n for (const [kind, entry] of resources) {\n for (const one of await entry.hooks.items(contextOf(job.orgId, kind, actorUid, job.id))) {\n hashes.set(packageItemKey({ kind, id: one.id }), one.contentHash)\n }\n }\n const steps = entries.map((entry) => packageUndoStep(entry, hashes.get(packageItemKey({ kind: entry.resource, id: entry.id }))))\n return { steps, resources }\n}\n\nfunction assertPackageUndoOpen(job: TransferJobRecord, now: number): void {\n if (job.kind !== 'package') throw new TransferEngineError('invalid', 400, 'This import is not a package.')\n if (job.status !== 'applied') throw new TransferEngineError('state', 409, `This import is ${job.status}; only an applied import can be undone.`)\n if (job.undo?.status === 'done') throw new TransferEngineError('state', 409, 'This import was already undone.')\n if (!job.undo && !transferUndoAvailable(job, now)) {\n throw new TransferEngineError('undoExpired', 410, 'An import can be undone for seven days; this one is older.')\n }\n}\n\n/** What undo would do, writing nothing: counts, and every item edited since the import. */\nexport async function planTransferPackageUndo(\n deps: TransferEngineDeps,\n input: { orgId: string; jobId: string; actorUid: string },\n): Promise<{ job: TransferJobRecord; counts: TransferUndoCounts; conflicts: TransferPackageUndoConflict[]; expiresAt: number | null }> {\n const job = await readJob(deps, input.orgId, input.jobId)\n assertPackageUndoOpen(job, clock(deps))\n const { steps } = await undoSteps(deps, job, input.actorUid)\n const counts = emptyUndoCounts()\n const conflicts: TransferPackageUndoConflict[] = []\n for (const step of steps) {\n counts[step.action] += 1\n if (step.action === 'conflict') {\n const { entry } = step\n conflicts.push({\n row: entry.row,\n key: entry.key,\n resource: entry.resource,\n id: entry.id,\n ...(entry.name ? { name: entry.name } : {}),\n action: entry.action,\n })\n }\n }\n return { job, counts, conflicts, expiresAt: transferUndoExpiresAt(job) }\n}\n\nexport interface ApplyTransferPackageUndoInput {\n orgId: string\n jobId: string\n actorUid: string\n /** By item id. */\n decisions?: Readonly<Record<string, TransferUndoDecision>>\n otherwise: TransferUndoDecision\n deadlineMs: number\n driver: string\n}\n\n/**\n * Carries undo out through each plugin's `revertItems`: every untouched\n * item, and every edited one the person (or `otherwise`) said to revert.\n * A step a plugin's own rules refuse is counted as a conflict and left.\n */\nexport async function applyTransferPackageUndo(\n deps: TransferEngineDeps,\n input: ApplyTransferPackageUndoInput,\n): Promise<{ job: TransferJobRecord; undo: TransferUndoState; done: boolean; started: boolean }> {\n if (input.otherwise !== 'keep' && input.otherwise !== 'revert') {\n throw new TransferEngineError('invalid', 400, 'Say what happens to an item edited since the import.')\n }\n let job = await readJob(deps, input.orgId, input.jobId)\n assertPackageUndoOpen(job, clock(deps))\n const decisions: Record<string, TransferUndoDecision> = { ...(input.decisions ?? {}) }\n let started = false\n job = await takeLease(deps, job, input.driver, input.deadlineMs + TRANSFER_LEASE_GRACE_MS, (current, now) => {\n if (current.undo) {\n return {\n ...current,\n undo: { ...current.undo, otherwise: input.otherwise, decisions: { ...current.undo.decisions, ...decisions } },\n updatedAt: now,\n }\n }\n started = true\n return {\n ...current,\n updatedAt: now,\n undo: {\n status: 'running',\n chunk: 0,\n startedAt: now,\n startedBy: input.actorUid,\n otherwise: input.otherwise,\n decisions,\n counts: emptyUndoCounts(),\n },\n }\n })\n let undo = job.undo as TransferUndoState\n Object.assign(decisions, undo.decisions ?? {}, input.decisions ?? {})\n try {\n const { steps, resources } = await undoSteps(deps, job, input.actorUid)\n const counts = emptyUndoCounts()\n const byResource = new Map<string, TransferPackageRevertStep[]>()\n for (const step of steps) {\n const { entry } = step\n const decided = decisions[entry.id] ?? input.otherwise\n if (step.action === 'nothing') {\n counts.nothing += 1\n continue\n }\n if (step.action === 'conflict' && decided !== 'revert') {\n counts.conflict += 1\n continue\n }\n const revert: TransferPackageRevertStep =\n entry.action === 'created'\n ? { action: 'delete', id: entry.id }\n : { action: 'restore', id: entry.id, content: entry.previous }\n byResource.set(entry.resource, [...(byResource.get(entry.resource) ?? []), revert])\n }\n for (const [kind, list] of byResource) {\n const entry = resources.get(kind)\n if (!entry) {\n counts.conflict += list.length\n continue\n }\n const reverted = await entry.hooks.revertItems(contextOf(job.orgId, kind, input.actorUid, job.id), list)\n for (const step of list) {\n if (reverted.done.includes(step.id)) counts[step.action] += 1\n }\n counts.conflict += reverted.refused.length\n }\n undo = { ...undo, chunk: 1, counts, status: 'done' }\n job = moveJob({ ...job, undo }, 'undone', clock(deps), { lease: null })\n await saveJob(deps, job)\n return { job, undo, done: true, started }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n job = { ...job, lease: null, updatedAt: clock(deps), error: { code: 'undoFailed', message, chunk: 0 } }\n await saveJob(deps, job)\n throw new TransferEngineError('failed', 500, `Undo stopped: ${message}. Try again to continue.`)\n }\n}\n"],"names":["TRANSFER_PACKAGE_MAX_BYTES","TRANSFER_PACKAGE_MAX_ITEMS","TRANSFER_PACKAGE_RESOURCE","TRANSFER_RESULT_COLUMNS","TRANSFER_SITE_KIND","buildPackageManifest","canApplyTransferPackage","contentHash","missingPackageAcknowledgements","packageIdMap","packageItemKey","planTransferPackage","readPackageManifest","summarizeTransferResults","transferLedgerKey","transferUndoAvailable","transferUndoExpiresAt","TRANSFER_WRITE_CONCURRENCY","createResourceUid","escapeCsvCell","inspectUploadBytes","resolveTransferResource","transferPackageHooks","TransferResourceUnavailableError","clock","eachLimited","emptyResults","emptyUndoCounts","isAlreadyExists","moveJob","readJob","readJsonDoc","saveJob","stored","takeLease","transferJobsCollection","transferSourcePath","TransferEngineError","TRANSFER_LEASE_GRACE_MS","TRANSFER_MIN_CHUNK_BUDGET_MS","writeJsonDoc","packageResources","deps","keys","found","Map","key","resource","resolveResource","scope","kinds","includes","set","hooks","error","reason","contextOf","orgId","actorUid","jobId","hostId","rulesOf","entry","rules","map","rule","id","label","siteTargets","snapshot","firestore","collection","where","get","kind","targets","docs","filter","doc","data","deletedAt","name","String","title","trim","listTransferPackageItems","input","resources","length","allowed","lists","ctx","held","items","contents","readItems","item","one","dependencies","content","push","pluginId","description","exportTransferPackage","wanted","Set","read","ids","slug","byKind","itemKey","at","indexOf","slice","has","pass","missing","values","dep","size","before","createdAt","source","readPackageFile","raw","manifest","ok","problems","loadPackage","job","sourcePath","bytes","bucket","file","download","JSON","parse","toString","readStoredPlan","plan","planTransferPackageImport","now","pkg","created","applyStartedAt","undefined","status","package","text","stringify","Buffer","byteLength","newJobId","from","refusal","contentType","fileName","message","code","save","resumable","direction","format","createdBy","updatedAt","rowCount","chunkCount","owned","kindsNeeded","foreignByResource","needsSites","add","existing","references","answered","referenceTargets","some","known","kindLabels","Object","fromEntries","singularLabel","minted","mintedAt","newId","base","ownedKinds","decisions","dependencyChoices","first","idMap","verdict","targetId","hook","write","writeFor","row","jobRef","kept","order","index","state","summary","blocking","acknowledgementsRequired","unknownKinds","plannedAt","manifestItem","decision","rename","remapIds","WRITE_VERDICTS","unwrittenResult","outcome","join","applyTransferPackage","done","started","resumed","results","acknowledged","driver","deadlineMs","current","cursor","chunk","rowsDone","timeLeft","byKey","writes","Boolean","Error","undoRef","entries","replaced","previous","targetKey","action","previousHash","ledgerCollection","ledger","result","writer","alreadyApplied","markApplied","create","undoJson","timeLeftMs","runs","last","run","pending","applied","writeItems","planned","lease","hashes","settled","written","hash","writtenHash","completedAt","ledgerDocs","ref","delete","transferPackageResultFile","trimmedAt","cell","value","lines","recordId","replace","csv","rows","packageUndoStep","currentHash","undoSteps","steps","assertPackageUndoOpen","undo","planTransferPackageUndo","counts","conflicts","step","expiresAt","applyTransferPackageUndo","otherwise","startedAt","startedBy","assign","byResource","decided","nothing","conflict","revert","list","reverted","revertItems","refused"],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The platform team's ask that a workspace buy a plan, server side
|
|
19
|
+
* (AGL-3466): staff propose and withdraw it, and a live subscription settles
|
|
20
|
+
* it.
|
|
21
|
+
*
|
|
22
|
+
* The sequence it serves: staff build a workspace for a client, comp it to
|
|
23
|
+
* the tier being sold with the `trial` reason, and hand it over. The client
|
|
24
|
+
* evaluates on that tier with no upgrade asked of them. When the evaluation
|
|
25
|
+
* is done, staff record a proposal here; the owner is emailed and the
|
|
26
|
+
* workspace's billing managers see a card with the plan preselected. When
|
|
27
|
+
* the subscription goes live, the proposal and the trial comp both clear,
|
|
28
|
+
* so a comp granted to evaluate cannot revive free access if the customer
|
|
29
|
+
* later cancels.
|
|
30
|
+
*/
|
|
31
|
+
import type { OrgUpgradeProposal } from '@aglyn/aglyn/server';
|
|
32
|
+
/** The override reason a sales-evaluation comp is granted under. */
|
|
33
|
+
export declare const TRIAL_PLAN_COMP_REASON = "trial";
|
|
34
|
+
/** A proposal refused before anything was written. */
|
|
35
|
+
export declare class UpgradeProposalError extends Error {
|
|
36
|
+
readonly status: number;
|
|
37
|
+
constructor(message: string, status?: number);
|
|
38
|
+
}
|
|
39
|
+
export interface ProposeOrgUpgradeOptions {
|
|
40
|
+
orgId: string;
|
|
41
|
+
plan: unknown;
|
|
42
|
+
note?: unknown;
|
|
43
|
+
/** The staff member proposing. */
|
|
44
|
+
actor: {
|
|
45
|
+
uid: string;
|
|
46
|
+
email?: string | null;
|
|
47
|
+
};
|
|
48
|
+
/** The console origin the email's link is built on. */
|
|
49
|
+
origin: string;
|
|
50
|
+
}
|
|
51
|
+
export interface ProposeOrgUpgradeResult {
|
|
52
|
+
proposal: OrgUpgradeProposal;
|
|
53
|
+
/** Whether the owner's email actually went out. */
|
|
54
|
+
emailed: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Record a proposal on the org, audit it, and email the owner.
|
|
58
|
+
*
|
|
59
|
+
* The org write and its audit row commit together, the property every staff
|
|
60
|
+
* write to an org document keeps (AGL-1784). The email is sent after the
|
|
61
|
+
* commit and is best-effort: the card on the org home is the proposal, and
|
|
62
|
+
* an email that failed must not undo it.
|
|
63
|
+
*
|
|
64
|
+
* Refused when the workspace already has a live subscription — the question
|
|
65
|
+
* a proposal asks is already answered — and for any plan a customer cannot
|
|
66
|
+
* buy themselves.
|
|
67
|
+
*/
|
|
68
|
+
export declare function proposeOrgUpgrade(options: ProposeOrgUpgradeOptions): Promise<ProposeOrgUpgradeResult>;
|
|
69
|
+
/** Remove a standing proposal, audited. Answers whether one stood. */
|
|
70
|
+
export declare function withdrawOrgUpgrade(options: {
|
|
71
|
+
orgId: string;
|
|
72
|
+
actor: {
|
|
73
|
+
uid: string;
|
|
74
|
+
email?: string | null;
|
|
75
|
+
};
|
|
76
|
+
}): Promise<boolean>;
|
|
77
|
+
/**
|
|
78
|
+
* What a live subscription settles (AGL-3466), cleared in one transaction
|
|
79
|
+
* with an audit row that says why:
|
|
80
|
+
*
|
|
81
|
+
* - the standing `upgradeProposal`, whose question the purchase answered;
|
|
82
|
+
* - a stored plan comp granted for a sales TRIAL. A comp waits dormant under
|
|
83
|
+
* a live subscription and applies again if it ends (`resolvePlanComp`), so
|
|
84
|
+
* a trial comp left in place would hand a customer who cancels the tier for
|
|
85
|
+
* free. Comps granted for any other reason are left alone: those are
|
|
86
|
+
* decisions staff made for reasons a purchase does not answer.
|
|
87
|
+
*
|
|
88
|
+
* Called by the Stripe webhook once its mirror has recorded a live status.
|
|
89
|
+
* Idempotent: a redelivery finds nothing to clear and writes nothing.
|
|
90
|
+
*/
|
|
91
|
+
export declare function settleSubscriptionStart(orgId: string, context: {
|
|
92
|
+
status: string;
|
|
93
|
+
subscriptionId?: string | null;
|
|
94
|
+
}): Promise<{
|
|
95
|
+
proposalCleared: boolean;
|
|
96
|
+
trialCompRemoved: boolean;
|
|
97
|
+
}>;
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /**
|
|
17
|
+
* The platform team's ask that a workspace buy a plan, server side
|
|
18
|
+
* (AGL-3466): staff propose and withdraw it, and a live subscription settles
|
|
19
|
+
* it.
|
|
20
|
+
*
|
|
21
|
+
* The sequence it serves: staff build a workspace for a client, comp it to
|
|
22
|
+
* the tier being sold with the `trial` reason, and hand it over. The client
|
|
23
|
+
* evaluates on that tier with no upgrade asked of them. When the evaluation
|
|
24
|
+
* is done, staff record a proposal here; the owner is emailed and the
|
|
25
|
+
* workspace's billing managers see a card with the plan preselected. When
|
|
26
|
+
* the subscription goes live, the proposal and the trial comp both clear,
|
|
27
|
+
* so a comp granted to evaluate cannot revive free access if the customer
|
|
28
|
+
* later cancels.
|
|
29
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
30
|
+
import { PLAN_LABELS } from "@aglyn/aglyn/app-utils/plan-entitlements";
|
|
31
|
+
import { PLATFORM_BRAND_NAME } from "@aglyn/aglyn/app-utils/platform-brand";
|
|
32
|
+
import { isUpgradeProposalPlan, normalizeUpgradeProposalNote, upgradeProposalDestination } from "@aglyn/aglyn/app-utils/upgrade-proposal";
|
|
33
|
+
// Leaf modules, as `organizations.ts` imports its constants (AGL-1289).
|
|
34
|
+
import { isLiveSubscriptionStatus, ORG_BILLING_DOC_ID, ORG_BILLING_SUBCOLLECTION } from "@aglyn/aglyn/app-utils/org-billing-doc";
|
|
35
|
+
import { isEmailConfigured, sendEmail } from "@aglyn/shared-util-email";
|
|
36
|
+
import { FieldValue } from "firebase-admin/firestore";
|
|
37
|
+
import { setAdminAudit } from "./admin-audit-write.js";
|
|
38
|
+
import { meterOrgEmail } from "./email-metering.js";
|
|
39
|
+
import firebaseAdmin from "./firebase-admin.js";
|
|
40
|
+
import { logOrgActivity } from "./organizations.js";
|
|
41
|
+
import { renderSystemEmailContent, systemEmailBrand } from "./render-system-email.js";
|
|
42
|
+
const firestore = ()=>firebaseAdmin.app().firestore();
|
|
43
|
+
/** The override reason a sales-evaluation comp is granted under. */ export const TRIAL_PLAN_COMP_REASON = 'trial';
|
|
44
|
+
/** A proposal refused before anything was written. */ export class UpgradeProposalError extends Error {
|
|
45
|
+
constructor(message, status = 400){
|
|
46
|
+
super(message);
|
|
47
|
+
this.name = 'UpgradeProposalError';
|
|
48
|
+
this.status = status;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Record a proposal on the org, audit it, and email the owner.
|
|
53
|
+
*
|
|
54
|
+
* The org write and its audit row commit together, the property every staff
|
|
55
|
+
* write to an org document keeps (AGL-1784). The email is sent after the
|
|
56
|
+
* commit and is best-effort: the card on the org home is the proposal, and
|
|
57
|
+
* an email that failed must not undo it.
|
|
58
|
+
*
|
|
59
|
+
* Refused when the workspace already has a live subscription — the question
|
|
60
|
+
* a proposal asks is already answered — and for any plan a customer cannot
|
|
61
|
+
* buy themselves.
|
|
62
|
+
*/ export async function proposeOrgUpgrade(options) {
|
|
63
|
+
var _actor_email;
|
|
64
|
+
const { orgId, actor, origin } = options;
|
|
65
|
+
if (!isUpgradeProposalPlan(options.plan)) {
|
|
66
|
+
throw new UpgradeProposalError('Choose a paid plan the workspace can buy itself');
|
|
67
|
+
}
|
|
68
|
+
const plan = options.plan;
|
|
69
|
+
const note = normalizeUpgradeProposalNote(options.note);
|
|
70
|
+
const db = firestore();
|
|
71
|
+
const orgRef = db.collection('orgs').doc(orgId);
|
|
72
|
+
const proposal = _extends({
|
|
73
|
+
plan,
|
|
74
|
+
proposedBy: actor.uid,
|
|
75
|
+
proposedAt: Date.now()
|
|
76
|
+
}, note ? {
|
|
77
|
+
note
|
|
78
|
+
} : {});
|
|
79
|
+
const org = await db.runTransaction(async (tx)=>{
|
|
80
|
+
var _ref, _data_upgradeProposal;
|
|
81
|
+
var _billing_get;
|
|
82
|
+
const snapshot = await tx.get(orgRef);
|
|
83
|
+
if (!snapshot.exists) throw new UpgradeProposalError('Unknown organization', 404);
|
|
84
|
+
const data = snapshot.data();
|
|
85
|
+
const billing = await tx.get(orgRef.collection(ORG_BILLING_SUBCOLLECTION).doc(ORG_BILLING_DOC_ID));
|
|
86
|
+
const status = (_ref = (_billing_get = billing.get('subscription')) == null ? void 0 : _billing_get.status) != null ? _ref : data.billingStatus;
|
|
87
|
+
if (isLiveSubscriptionStatus(status)) {
|
|
88
|
+
throw new UpgradeProposalError('This workspace already has a live subscription, so there is nothing to propose', 409);
|
|
89
|
+
}
|
|
90
|
+
tx.set(orgRef, {
|
|
91
|
+
upgradeProposal: proposal,
|
|
92
|
+
updatedAt: FieldValue.serverTimestamp()
|
|
93
|
+
}, {
|
|
94
|
+
merge: true
|
|
95
|
+
});
|
|
96
|
+
setAdminAudit(tx, db, {
|
|
97
|
+
actorUid: actor.uid,
|
|
98
|
+
action: 'org.upgradeProposal.propose',
|
|
99
|
+
target: `orgs/${orgId}`,
|
|
100
|
+
before: {
|
|
101
|
+
upgradeProposal: (_data_upgradeProposal = data.upgradeProposal) != null ? _data_upgradeProposal : null
|
|
102
|
+
},
|
|
103
|
+
after: {
|
|
104
|
+
upgradeProposal: proposal
|
|
105
|
+
},
|
|
106
|
+
at: FieldValue.serverTimestamp()
|
|
107
|
+
});
|
|
108
|
+
return data;
|
|
109
|
+
});
|
|
110
|
+
await logOrgActivity(orgId, {
|
|
111
|
+
uid: actor.uid,
|
|
112
|
+
email: (_actor_email = actor.email) != null ? _actor_email : null
|
|
113
|
+
}, `Proposed the ${PLAN_LABELS[plan]} plan`, {
|
|
114
|
+
type: 'subscription',
|
|
115
|
+
id: orgId
|
|
116
|
+
}, {
|
|
117
|
+
staffActorId: actor.uid
|
|
118
|
+
});
|
|
119
|
+
const emailed = await emailUpgradeProposal({
|
|
120
|
+
orgId,
|
|
121
|
+
org,
|
|
122
|
+
proposal,
|
|
123
|
+
origin
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
proposal,
|
|
127
|
+
emailed
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/** Remove a standing proposal, audited. Answers whether one stood. */ export async function withdrawOrgUpgrade(options) {
|
|
131
|
+
var _actor_email;
|
|
132
|
+
const { orgId, actor } = options;
|
|
133
|
+
const db = firestore();
|
|
134
|
+
const orgRef = db.collection('orgs').doc(orgId);
|
|
135
|
+
const withdrawn = await db.runTransaction(async (tx)=>{
|
|
136
|
+
const snapshot = await tx.get(orgRef);
|
|
137
|
+
if (!snapshot.exists) throw new UpgradeProposalError('Unknown organization', 404);
|
|
138
|
+
const stored = snapshot.get('upgradeProposal');
|
|
139
|
+
if (!stored) return null;
|
|
140
|
+
tx.set(orgRef, {
|
|
141
|
+
upgradeProposal: FieldValue.delete(),
|
|
142
|
+
updatedAt: FieldValue.serverTimestamp()
|
|
143
|
+
}, {
|
|
144
|
+
merge: true
|
|
145
|
+
});
|
|
146
|
+
setAdminAudit(tx, db, {
|
|
147
|
+
actorUid: actor.uid,
|
|
148
|
+
action: 'org.upgradeProposal.withdraw',
|
|
149
|
+
target: `orgs/${orgId}`,
|
|
150
|
+
before: {
|
|
151
|
+
upgradeProposal: stored
|
|
152
|
+
},
|
|
153
|
+
after: {
|
|
154
|
+
upgradeProposal: null
|
|
155
|
+
},
|
|
156
|
+
at: FieldValue.serverTimestamp()
|
|
157
|
+
});
|
|
158
|
+
return stored;
|
|
159
|
+
});
|
|
160
|
+
if (!withdrawn) return false;
|
|
161
|
+
await logOrgActivity(orgId, {
|
|
162
|
+
uid: actor.uid,
|
|
163
|
+
email: (_actor_email = actor.email) != null ? _actor_email : null
|
|
164
|
+
}, `Withdrew the ${planName(withdrawn.plan)} plan proposal`, {
|
|
165
|
+
type: 'subscription',
|
|
166
|
+
id: orgId
|
|
167
|
+
}, {
|
|
168
|
+
staffActorId: actor.uid
|
|
169
|
+
});
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* What a live subscription settles (AGL-3466), cleared in one transaction
|
|
174
|
+
* with an audit row that says why:
|
|
175
|
+
*
|
|
176
|
+
* - the standing `upgradeProposal`, whose question the purchase answered;
|
|
177
|
+
* - a stored plan comp granted for a sales TRIAL. A comp waits dormant under
|
|
178
|
+
* a live subscription and applies again if it ends (`resolvePlanComp`), so
|
|
179
|
+
* a trial comp left in place would hand a customer who cancels the tier for
|
|
180
|
+
* free. Comps granted for any other reason are left alone: those are
|
|
181
|
+
* decisions staff made for reasons a purchase does not answer.
|
|
182
|
+
*
|
|
183
|
+
* Called by the Stripe webhook once its mirror has recorded a live status.
|
|
184
|
+
* Idempotent: a redelivery finds nothing to clear and writes nothing.
|
|
185
|
+
*/ export async function settleSubscriptionStart(orgId, context) {
|
|
186
|
+
const db = firestore();
|
|
187
|
+
const orgRef = db.collection('orgs').doc(orgId);
|
|
188
|
+
const outcome = await db.runTransaction(async (tx)=>{
|
|
189
|
+
var _data_upgradeProposal, _data_entitlements;
|
|
190
|
+
const snapshot = await tx.get(orgRef);
|
|
191
|
+
if (!snapshot.exists) return null;
|
|
192
|
+
const data = snapshot.data();
|
|
193
|
+
const proposal = (_data_upgradeProposal = data.upgradeProposal) != null ? _data_upgradeProposal : null;
|
|
194
|
+
const entitlements = (_data_entitlements = data.entitlements) != null ? _data_entitlements : null;
|
|
195
|
+
const comp = entitlements == null ? void 0 : entitlements['planComp'];
|
|
196
|
+
const trialComp = (comp == null ? void 0 : comp.reason) === TRIAL_PLAN_COMP_REASON ? comp : null;
|
|
197
|
+
if (!proposal && !trialComp) return null;
|
|
198
|
+
// The map goes with its last key, so the staff list's override count
|
|
199
|
+
// never reads an empty `entitlements` as one.
|
|
200
|
+
const onlyComp = !!entitlements && Object.keys(entitlements).every((key)=>key === 'planComp');
|
|
201
|
+
tx.set(orgRef, _extends({}, proposal ? {
|
|
202
|
+
upgradeProposal: FieldValue.delete()
|
|
203
|
+
} : {}, trialComp ? onlyComp ? {
|
|
204
|
+
entitlements: FieldValue.delete()
|
|
205
|
+
} : {
|
|
206
|
+
entitlements: {
|
|
207
|
+
planComp: FieldValue.delete()
|
|
208
|
+
}
|
|
209
|
+
} : {}, {
|
|
210
|
+
updatedAt: FieldValue.serverTimestamp()
|
|
211
|
+
}), {
|
|
212
|
+
merge: true
|
|
213
|
+
});
|
|
214
|
+
setAdminAudit(tx, db, {
|
|
215
|
+
actorUid: 'system',
|
|
216
|
+
action: 'org.subscriptionStarted.settle',
|
|
217
|
+
target: `orgs/${orgId}`,
|
|
218
|
+
reason: {
|
|
219
|
+
code: 'subscription_live',
|
|
220
|
+
note: `The subscription went live (${context.status})` + (context.subscriptionId ? `, ${context.subscriptionId}` : '') + ', which settles the upgrade proposal and ends the sales-trial comp.'
|
|
221
|
+
},
|
|
222
|
+
before: {
|
|
223
|
+
upgradeProposal: proposal,
|
|
224
|
+
planComp: comp != null ? comp : null
|
|
225
|
+
},
|
|
226
|
+
after: {
|
|
227
|
+
upgradeProposal: null,
|
|
228
|
+
// A comp granted for any other reason stays exactly as it was.
|
|
229
|
+
planComp: trialComp ? null : comp != null ? comp : null
|
|
230
|
+
},
|
|
231
|
+
at: FieldValue.serverTimestamp()
|
|
232
|
+
});
|
|
233
|
+
return {
|
|
234
|
+
proposal,
|
|
235
|
+
trialComp
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
if (!outcome) return {
|
|
239
|
+
proposalCleared: false,
|
|
240
|
+
trialCompRemoved: false
|
|
241
|
+
};
|
|
242
|
+
// Nobody is present at a Stripe flip, and the row says so rather than
|
|
243
|
+
// naming whoever last touched billing.
|
|
244
|
+
if (outcome.proposal) {
|
|
245
|
+
await logOrgActivity(orgId, {
|
|
246
|
+
uid: null
|
|
247
|
+
}, `The ${planName(outcome.proposal.plan)} plan proposal closed — the subscription is live`, {
|
|
248
|
+
type: 'subscription',
|
|
249
|
+
id: orgId
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
if (outcome.trialComp) {
|
|
253
|
+
await logOrgActivity(orgId, {
|
|
254
|
+
uid: null
|
|
255
|
+
}, `The ${planName(outcome.trialComp.plan)} sales-trial comp ended — the subscription is live`, {
|
|
256
|
+
type: 'subscription',
|
|
257
|
+
id: orgId
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
proposalCleared: !!outcome.proposal,
|
|
262
|
+
trialCompRemoved: !!outcome.trialComp
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/** Mail the owner the proposal, in the workspace's brand. Never throws. */ async function emailUpgradeProposal(options) {
|
|
266
|
+
const { orgId, org, proposal, origin } = options;
|
|
267
|
+
try {
|
|
268
|
+
var _proposal_note;
|
|
269
|
+
if (!isEmailConfigured()) return false;
|
|
270
|
+
const ownerUid = org.ownerUid;
|
|
271
|
+
if (typeof ownerUid !== 'string' || !ownerUid) return false;
|
|
272
|
+
const owner = await firestore().collection('orgs').doc(orgId).collection('members').doc(ownerUid).get();
|
|
273
|
+
const to = owner.get('email');
|
|
274
|
+
if (typeof to !== 'string' || !to) return false;
|
|
275
|
+
const brand = systemEmailBrand(org);
|
|
276
|
+
const orgName = brand.orgName || 'your workspace';
|
|
277
|
+
const slug = typeof org.slug === 'string' ? org.slug : '';
|
|
278
|
+
const billingUrl = slug ? `${origin}${upgradeProposalDestination(slug, proposal)}` : origin;
|
|
279
|
+
const label = planName(proposal.plan);
|
|
280
|
+
const productName = brand.merge['brand.productName'] || PLATFORM_BRAND_NAME;
|
|
281
|
+
const content = await renderSystemEmailContent('org-upgrade-proposal', {
|
|
282
|
+
'org.name': orgName,
|
|
283
|
+
'plan.name': label,
|
|
284
|
+
'proposal.note': (_proposal_note = proposal.note) != null ? _proposal_note : '',
|
|
285
|
+
billingUrl
|
|
286
|
+
}, brand, {
|
|
287
|
+
subject: `Your ${productName} team proposed the ${label} plan for ${orgName}`,
|
|
288
|
+
text: `Your ${productName} team proposed moving ${orgName} to the ${label} ` + 'plan. When you are ready, start the subscription from Billing, ' + `where the plan is already selected:\n\n${billingUrl}` + (proposal.note ? `\n\n${proposal.note}` : '')
|
|
289
|
+
});
|
|
290
|
+
const result = await sendEmail(_extends({
|
|
291
|
+
to,
|
|
292
|
+
subject: content.subject,
|
|
293
|
+
text: content.text
|
|
294
|
+
}, content.html ? {
|
|
295
|
+
html: content.html
|
|
296
|
+
} : {}, brand.fromName ? {
|
|
297
|
+
fromName: brand.fromName
|
|
298
|
+
} : {}, {
|
|
299
|
+
context: 'org-upgrade-proposal'
|
|
300
|
+
}));
|
|
301
|
+
if (result.sent) await meterOrgEmail(orgId);
|
|
302
|
+
return result.sent;
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.error('[upgrade-proposal] owner email failed', error);
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function planName(plan) {
|
|
309
|
+
return typeof plan === 'string' && plan in PLAN_LABELS ? PLAN_LABELS[plan] : String(plan != null ? plan : 'proposed');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
//# sourceMappingURL=upgrade-proposal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/upgrade-proposal.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 * The platform team's ask that a workspace buy a plan, server side\n * (AGL-3466): staff propose and withdraw it, and a live subscription settles\n * it.\n *\n * The sequence it serves: staff build a workspace for a client, comp it to\n * the tier being sold with the `trial` reason, and hand it over. The client\n * evaluates on that tier with no upgrade asked of them. When the evaluation\n * is done, staff record a proposal here; the owner is emailed and the\n * workspace's billing managers see a card with the plan preselected. When\n * the subscription goes live, the proposal and the trial comp both clear,\n * so a comp granted to evaluate cannot revive free access if the customer\n * later cancels.\n */\n\nimport type {\n AglynOrgBilling,\n OrgPlan,\n OrgUpgradeProposal,\n} from '@aglyn/aglyn/server'\nimport { PLAN_LABELS } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn/app-utils/platform-brand'\nimport {\n isUpgradeProposalPlan,\n normalizeUpgradeProposalNote,\n upgradeProposalDestination,\n} from '@aglyn/aglyn/app-utils/upgrade-proposal'\n// Leaf modules, as `organizations.ts` imports its constants (AGL-1289).\nimport {\n isLiveSubscriptionStatus,\n ORG_BILLING_DOC_ID,\n ORG_BILLING_SUBCOLLECTION,\n} from '@aglyn/aglyn/app-utils/org-billing-doc'\nimport { isEmailConfigured, sendEmail } from '@aglyn/shared-util-email'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { setAdminAudit } from './admin-audit-write'\nimport { meterOrgEmail } from './email-metering'\nimport firebaseAdmin from './firebase-admin'\nimport { logOrgActivity } from './organizations'\nimport {\n renderSystemEmailContent,\n systemEmailBrand,\n} from './render-system-email'\n\nconst firestore = () => firebaseAdmin.app().firestore()\n\n/** The override reason a sales-evaluation comp is granted under. */\nexport const TRIAL_PLAN_COMP_REASON = 'trial'\n\n/** A proposal refused before anything was written. */\nexport class UpgradeProposalError extends Error {\n readonly status: number\n constructor(message: string, status = 400) {\n super(message)\n this.name = 'UpgradeProposalError'\n this.status = status\n }\n}\n\nexport interface ProposeOrgUpgradeOptions {\n orgId: string\n plan: unknown\n note?: unknown\n /** The staff member proposing. */\n actor: { uid: string; email?: string | null }\n /** The console origin the email's link is built on. */\n origin: string\n}\n\nexport interface ProposeOrgUpgradeResult {\n proposal: OrgUpgradeProposal\n /** Whether the owner's email actually went out. */\n emailed: boolean\n}\n\n/**\n * Record a proposal on the org, audit it, and email the owner.\n *\n * The org write and its audit row commit together, the property every staff\n * write to an org document keeps (AGL-1784). The email is sent after the\n * commit and is best-effort: the card on the org home is the proposal, and\n * an email that failed must not undo it.\n *\n * Refused when the workspace already has a live subscription — the question\n * a proposal asks is already answered — and for any plan a customer cannot\n * buy themselves.\n */\nexport async function proposeOrgUpgrade(\n options: ProposeOrgUpgradeOptions,\n): Promise<ProposeOrgUpgradeResult> {\n const { orgId, actor, origin } = options\n if (!isUpgradeProposalPlan(options.plan)) {\n throw new UpgradeProposalError(\n 'Choose a paid plan the workspace can buy itself',\n )\n }\n const plan: OrgPlan = options.plan\n const note = normalizeUpgradeProposalNote(options.note)\n const db = firestore()\n const orgRef = db.collection('orgs').doc(orgId)\n const proposal: OrgUpgradeProposal = {\n plan,\n proposedBy: actor.uid,\n proposedAt: Date.now(),\n ...(note ? { note } : {}),\n }\n const org = await db.runTransaction(async (tx) => {\n const snapshot = await tx.get(orgRef)\n if (!snapshot.exists) throw new UpgradeProposalError('Unknown organization', 404)\n const data = snapshot.data() as Partial<AglynOrgBilling>\n const billing = await tx.get(\n orgRef.collection(ORG_BILLING_SUBCOLLECTION).doc(ORG_BILLING_DOC_ID),\n )\n const status =\n (billing.get('subscription') as { status?: unknown } | undefined)?.status ??\n data.billingStatus\n if (isLiveSubscriptionStatus(status)) {\n throw new UpgradeProposalError(\n 'This workspace already has a live subscription, so there is nothing to propose',\n 409,\n )\n }\n tx.set(\n orgRef,\n { upgradeProposal: proposal, updatedAt: FieldValue.serverTimestamp() },\n { merge: true },\n )\n setAdminAudit(tx, db, {\n actorUid: actor.uid,\n action: 'org.upgradeProposal.propose',\n target: `orgs/${orgId}`,\n before: { upgradeProposal: data.upgradeProposal ?? null },\n after: { upgradeProposal: proposal },\n at: FieldValue.serverTimestamp(),\n })\n return data\n })\n await logOrgActivity(\n orgId,\n { uid: actor.uid, email: actor.email ?? null },\n `Proposed the ${PLAN_LABELS[plan]} plan`,\n { type: 'subscription', id: orgId },\n { staffActorId: actor.uid },\n )\n const emailed = await emailUpgradeProposal({ orgId, org, proposal, origin })\n return { proposal, emailed }\n}\n\n/** Remove a standing proposal, audited. Answers whether one stood. */\nexport async function withdrawOrgUpgrade(options: {\n orgId: string\n actor: { uid: string; email?: string | null }\n}): Promise<boolean> {\n const { orgId, actor } = options\n const db = firestore()\n const orgRef = db.collection('orgs').doc(orgId)\n const withdrawn = await db.runTransaction(async (tx) => {\n const snapshot = await tx.get(orgRef)\n if (!snapshot.exists) throw new UpgradeProposalError('Unknown organization', 404)\n const stored = snapshot.get('upgradeProposal') as OrgUpgradeProposal | undefined\n if (!stored) return null\n tx.set(\n orgRef,\n { upgradeProposal: FieldValue.delete(), updatedAt: FieldValue.serverTimestamp() },\n { merge: true },\n )\n setAdminAudit(tx, db, {\n actorUid: actor.uid,\n action: 'org.upgradeProposal.withdraw',\n target: `orgs/${orgId}`,\n before: { upgradeProposal: stored },\n after: { upgradeProposal: null },\n at: FieldValue.serverTimestamp(),\n })\n return stored\n })\n if (!withdrawn) return false\n await logOrgActivity(\n orgId,\n { uid: actor.uid, email: actor.email ?? null },\n `Withdrew the ${planName(withdrawn.plan)} plan proposal`,\n { type: 'subscription', id: orgId },\n { staffActorId: actor.uid },\n )\n return true\n}\n\n/**\n * What a live subscription settles (AGL-3466), cleared in one transaction\n * with an audit row that says why:\n *\n * - the standing `upgradeProposal`, whose question the purchase answered;\n * - a stored plan comp granted for a sales TRIAL. A comp waits dormant under\n * a live subscription and applies again if it ends (`resolvePlanComp`), so\n * a trial comp left in place would hand a customer who cancels the tier for\n * free. Comps granted for any other reason are left alone: those are\n * decisions staff made for reasons a purchase does not answer.\n *\n * Called by the Stripe webhook once its mirror has recorded a live status.\n * Idempotent: a redelivery finds nothing to clear and writes nothing.\n */\nexport async function settleSubscriptionStart(\n orgId: string,\n context: { status: string; subscriptionId?: string | null },\n): Promise<{ proposalCleared: boolean; trialCompRemoved: boolean }> {\n const db = firestore()\n const orgRef = db.collection('orgs').doc(orgId)\n const outcome = await db.runTransaction(async (tx) => {\n const snapshot = await tx.get(orgRef)\n if (!snapshot.exists) return null\n const data = snapshot.data() as Partial<AglynOrgBilling>\n const proposal = data.upgradeProposal ?? null\n const entitlements = (data.entitlements ?? null) as Record<string, unknown> | null\n const comp = entitlements?.['planComp'] as\n | { plan?: string; reason?: string }\n | undefined\n const trialComp = comp?.reason === TRIAL_PLAN_COMP_REASON ? comp : null\n if (!proposal && !trialComp) return null\n // The map goes with its last key, so the staff list's override count\n // never reads an empty `entitlements` as one.\n const onlyComp =\n !!entitlements && Object.keys(entitlements).every((key) => key === 'planComp')\n tx.set(\n orgRef,\n {\n ...(proposal ? { upgradeProposal: FieldValue.delete() } : {}),\n ...(trialComp\n ? onlyComp\n ? { entitlements: FieldValue.delete() }\n : { entitlements: { planComp: FieldValue.delete() } }\n : {}),\n updatedAt: FieldValue.serverTimestamp(),\n },\n { merge: true },\n )\n setAdminAudit(tx, db, {\n actorUid: 'system',\n action: 'org.subscriptionStarted.settle',\n target: `orgs/${orgId}`,\n reason: {\n code: 'subscription_live',\n note:\n `The subscription went live (${context.status})` +\n (context.subscriptionId ? `, ${context.subscriptionId}` : '') +\n ', which settles the upgrade proposal and ends the sales-trial comp.',\n },\n before: { upgradeProposal: proposal, planComp: comp ?? null },\n after: {\n upgradeProposal: null,\n // A comp granted for any other reason stays exactly as it was.\n planComp: trialComp ? null : (comp ?? null),\n },\n at: FieldValue.serverTimestamp(),\n })\n return { proposal, trialComp }\n })\n if (!outcome) return { proposalCleared: false, trialCompRemoved: false }\n // Nobody is present at a Stripe flip, and the row says so rather than\n // naming whoever last touched billing.\n if (outcome.proposal) {\n await logOrgActivity(\n orgId,\n { uid: null },\n `The ${planName(outcome.proposal.plan)} plan proposal closed — the subscription is live`,\n { type: 'subscription', id: orgId },\n )\n }\n if (outcome.trialComp) {\n await logOrgActivity(\n orgId,\n { uid: null },\n `The ${planName(outcome.trialComp.plan)} sales-trial comp ended — the subscription is live`,\n { type: 'subscription', id: orgId },\n )\n }\n return {\n proposalCleared: !!outcome.proposal,\n trialCompRemoved: !!outcome.trialComp,\n }\n}\n\n/** Mail the owner the proposal, in the workspace's brand. Never throws. */\nasync function emailUpgradeProposal(options: {\n orgId: string\n org: Partial<AglynOrgBilling>\n proposal: OrgUpgradeProposal\n origin: string\n}): Promise<boolean> {\n const { orgId, org, proposal, origin } = options\n try {\n if (!isEmailConfigured()) return false\n const ownerUid = (org as { ownerUid?: unknown }).ownerUid\n if (typeof ownerUid !== 'string' || !ownerUid) return false\n const owner = await firestore()\n .collection('orgs')\n .doc(orgId)\n .collection('members')\n .doc(ownerUid)\n .get()\n const to = owner.get('email')\n if (typeof to !== 'string' || !to) return false\n const brand = systemEmailBrand(org as Record<string, unknown>)\n const orgName = brand.orgName || 'your workspace'\n const slug = typeof org.slug === 'string' ? org.slug : ''\n const billingUrl = slug\n ? `${origin}${upgradeProposalDestination(slug, proposal)}`\n : origin\n const label = planName(proposal.plan)\n const productName = brand.merge['brand.productName'] || PLATFORM_BRAND_NAME\n const content = await renderSystemEmailContent(\n 'org-upgrade-proposal',\n {\n 'org.name': orgName,\n 'plan.name': label,\n 'proposal.note': proposal.note ?? '',\n billingUrl,\n },\n brand,\n {\n subject: `Your ${productName} team proposed the ${label} plan for ${orgName}`,\n text:\n `Your ${productName} team proposed moving ${orgName} to the ${label} ` +\n 'plan. When you are ready, start the subscription from Billing, ' +\n `where the plan is already selected:\\n\\n${billingUrl}` +\n (proposal.note ? `\\n\\n${proposal.note}` : ''),\n },\n )\n const result = await sendEmail({\n to,\n subject: content.subject,\n text: content.text,\n ...(content.html ? { html: content.html } : {}),\n ...(brand.fromName ? { fromName: brand.fromName } : {}),\n context: 'org-upgrade-proposal',\n })\n if (result.sent) await meterOrgEmail(orgId)\n return result.sent\n } catch (error) {\n console.error('[upgrade-proposal] owner email failed', error)\n return false\n }\n}\n\nfunction planName(plan: unknown): string {\n return typeof plan === 'string' && plan in PLAN_LABELS\n ? PLAN_LABELS[plan as OrgPlan]\n : String(plan ?? 'proposed')\n}\n"],"names":["PLAN_LABELS","PLATFORM_BRAND_NAME","isUpgradeProposalPlan","normalizeUpgradeProposalNote","upgradeProposalDestination","isLiveSubscriptionStatus","ORG_BILLING_DOC_ID","ORG_BILLING_SUBCOLLECTION","isEmailConfigured","sendEmail","FieldValue","setAdminAudit","meterOrgEmail","firebaseAdmin","logOrgActivity","renderSystemEmailContent","systemEmailBrand","firestore","app","TRIAL_PLAN_COMP_REASON","UpgradeProposalError","Error","message","status","name","proposeOrgUpgrade","options","actor","orgId","origin","plan","note","db","orgRef","collection","doc","proposal","proposedBy","uid","proposedAt","Date","now","org","runTransaction","tx","data","billing","snapshot","get","exists","billingStatus","set","upgradeProposal","updatedAt","serverTimestamp","merge","actorUid","action","target","before","after","at","email","type","id","staffActorId","emailed","emailUpgradeProposal","withdrawOrgUpgrade","withdrawn","stored","delete","planName","settleSubscriptionStart","context","outcome","entitlements","comp","trialComp","reason","onlyComp","Object","keys","every","key","planComp","code","subscriptionId","proposalCleared","trialCompRemoved","ownerUid","owner","to","brand","orgName","slug","billingUrl","label","productName","content","subject","text","result","html","fromName","sent","error","console","String"],"mappings":"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"}
|