@aglyn/tenant-data-admin 1.0.0-beta.200 → 1.0.0-beta.202
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 +8 -7
- package/src/lib/server/card-payment-velocity.d.ts +85 -0
- package/src/lib/server/card-payment-velocity.js +202 -0
- package/src/lib/server/card-payment-velocity.js.map +1 -0
- package/src/lib/server/configured-url-fetch.d.ts +42 -0
- package/src/lib/server/configured-url-fetch.js +118 -0
- package/src/lib/server/configured-url-fetch.js.map +1 -0
- package/src/lib/server/dns-probe.d.ts +7 -0
- package/src/lib/server/dns-probe.js +52 -0
- package/src/lib/server/dns-probe.js.map +1 -1
- package/src/lib/server/email-deliverability.d.ts +150 -0
- package/src/lib/server/email-deliverability.js +538 -0
- package/src/lib/server/email-deliverability.js.map +1 -0
- package/src/lib/server/email-sender-reputation.d.ts +2 -11
- package/src/lib/server/email-sender-reputation.js +8 -25
- package/src/lib/server/email-sender-reputation.js.map +1 -1
- package/src/lib/server/email-suppression.d.ts +10 -1
- package/src/lib/server/email-suppression.js +4 -3
- package/src/lib/server/email-suppression.js.map +1 -1
- package/src/lib/server/erase-person.js +31 -3
- package/src/lib/server/erase-person.js.map +1 -1
- package/src/lib/server/host-visitor-records.d.ts +16 -2
- package/src/lib/server/host-visitor-records.js +31 -6
- package/src/lib/server/host-visitor-records.js.map +1 -1
- package/src/lib/server/hosted-page-review.d.ts +112 -0
- package/src/lib/server/hosted-page-review.js +378 -0
- package/src/lib/server/hosted-page-review.js.map +1 -0
- package/src/lib/server/member-email-aliases.js +18 -7
- package/src/lib/server/member-email-aliases.js.map +1 -1
- package/src/lib/server/notifications.d.ts +8 -0
- package/src/lib/server/notifications.js +92 -17
- package/src/lib/server/notifications.js.map +1 -1
- package/src/lib/server/org-age.d.ts +27 -0
- package/src/lib/server/org-age.js +44 -0
- package/src/lib/server/org-age.js.map +1 -0
- package/src/lib/server/org-member-notice.js +20 -5
- package/src/lib/server/org-member-notice.js.map +1 -1
- package/src/lib/server/organizations.d.ts +10 -1
- package/src/lib/server/organizations.js +12 -4
- package/src/lib/server/organizations.js.map +1 -1
- package/src/lib/server/outbound-send-review.d.ts +224 -0
- package/src/lib/server/outbound-send-review.js +479 -0
- package/src/lib/server/outbound-send-review.js.map +1 -0
- package/src/lib/server/payment-fraud-signal.d.ts +156 -0
- package/src/lib/server/payment-fraud-signal.js +368 -0
- package/src/lib/server/payment-fraud-signal.js.map +1 -0
- package/src/lib/server/payment-risk-record.d.ts +59 -0
- package/src/lib/server/payment-risk-record.js +76 -0
- package/src/lib/server/payment-risk-record.js.map +1 -0
- package/src/lib/server/render-system-email.d.ts +176 -0
- package/src/lib/server/render-system-email.js +315 -0
- package/src/lib/server/render-system-email.js.map +1 -0
- package/src/lib/server/sending-domains.d.ts +6 -0
- package/src/lib/server/sending-domains.js +98 -6
- package/src/lib/server/sending-domains.js.map +1 -1
- package/src/lib/server/staff-alert-email.d.ts +2 -1
- package/src/lib/server/staff-alert-email.js +16 -5
- package/src/lib/server/staff-alert-email.js.map +1 -1
- package/src/lib/server/stripe-card-authentication.d.ts +35 -0
- package/src/lib/server/stripe-card-authentication.js +63 -0
- package/src/lib/server/stripe-card-authentication.js.map +1 -0
- package/src/lib/server/tenant-write-lockdown.d.ts +32 -2
- package/src/lib/server/tenant-write-lockdown.js +33 -3
- package/src/lib/server/tenant-write-lockdown.js.map +1 -1
- package/src/tenancy.d.ts +5 -0
- package/src/tenancy.js +10 -0
- package/src/tenancy.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/org-member-notice.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isEmailConfigured, sendEmail } from '@aglyn/shared-util-email'\nimport { findUserByUidAcrossPools } from './auth-pools'\nimport { filterSuppressedEmails } from './email-suppression'\nimport { listOrgMembers } from './organizations'\n\n/**\n * A NOTICE TO NAMED MEMBERS OF AN ORGANIZATION, BY EMAIL (AGL-3244).\n *\n * The console notification (`notifications.ts`) is the record, and its email\n * channel is a preference that defaults off. Some things cannot wait on a\n * preference: a sequence mailbox that paused itself holds every enrollment\n * on it until its owner acts, and the owner is not watching the page. This\n * sends the email regardless, to the members named and — when asked — to\n * the organization's owners and admins beside them, one send per address\n * so nobody is shown anyone else's.\n *\n * Addresses come from the roster (`orgs/{orgId}/members/{uid}.email`), and\n * for a member whose row carries none, from the directory. Suppressed\n * addresses are skipped, the send is metered to the organization, and\n * nothing here throws: the notice is a courtesy beside a state the caller\n * already wrote.\n */\n\nexport interface OrgMemberNoticeInput {\n orgId: string\n /** The members to write to, by uid. */\n uids: readonly string[]\n /** Also the organization's owners and admins. */\n includeAdmins?: boolean\n subject: string\n text: string\n /** Resend tag / log label. */\n context: string\n}\n\nexport interface OrgMemberNoticeResult {\n /** Addresses the notice went to. */\n sent: number\n /** Why nothing could be sent at all, when nothing could. */\n reason?: 'unconfigured' | 'no-recipients' | 'failed'\n}\n\n/** At most this many directory lookups, and this many sends, per notice. */\nconst MAX_LOOKUPS = 10\nconst MAX_SENDS = 25\n\n/** Sends one notice to each member — see the module note. */\nexport async function sendOrgMemberNotice(input: OrgMemberNoticeInput): Promise<OrgMemberNoticeResult> {\n if (!isEmailConfigured()) return { sent: 0, reason: 'unconfigured' }\n try {\n const members = await listOrgMembers(input.orgId)\n const rosterEmail = new Map<string, string>()\n for (const member of members) {\n const address = String(member.email ?? '')\n .trim()\n .toLowerCase()\n if (address.includes('@')) rosterEmail.set(member.$id, address)\n }\n const uids = new Set(input.uids.filter(Boolean))\n if (input.includeAdmins) {\n for (const member of members) {\n if (member.role === 'owner' || member.role === 'admin') uids.add(member.$id)\n }\n }\n const addresses = new Set<string>()\n let lookups = 0\n for (const uid of uids) {\n let address = rosterEmail.get(uid) ?? ''\n if (!address && lookups < MAX_LOOKUPS) {\n lookups += 1\n const pooled = await findUserByUidAcrossPools(uid).catch(() => null)\n address = String(pooled?.record?.email ?? '')\n .trim()\n .toLowerCase()\n }\n if (address.includes('@')) addresses.add(address)\n }\n const recipients = await filterSuppressedEmails([...addresses])\n if (!recipients.length) return { sent: 0, reason: 'no-recipients' }\n let sent = 0\n for (const to of recipients.slice(0, MAX_SENDS)) {\n const result = await sendEmail({
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/org-member-notice.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isEmailConfigured, sendEmail } from '@aglyn/shared-util-email'\nimport { findUserByUidAcrossPools } from './auth-pools'\nimport { filterSuppressedEmails } from './email-suppression'\nimport { listOrgMembers } from './organizations'\nimport { orgSystemEmailBrand, renderSystemEmailContent } from './render-system-email'\n\n/**\n * A NOTICE TO NAMED MEMBERS OF AN ORGANIZATION, BY EMAIL (AGL-3244).\n *\n * The console notification (`notifications.ts`) is the record, and its email\n * channel is a preference that defaults off. Some things cannot wait on a\n * preference: a sequence mailbox that paused itself holds every enrollment\n * on it until its owner acts, and the owner is not watching the page. This\n * sends the email regardless, to the members named and — when asked — to\n * the organization's owners and admins beside them, one send per address\n * so nobody is shown anyone else's.\n *\n * Addresses come from the roster (`orgs/{orgId}/members/{uid}.email`), and\n * for a member whose row carries none, from the directory. Suppressed\n * addresses are skipped, the send is metered to the organization, and\n * nothing here throws: the notice is a courtesy beside a state the caller\n * already wrote.\n */\n\nexport interface OrgMemberNoticeInput {\n orgId: string\n /** The members to write to, by uid. */\n uids: readonly string[]\n /** Also the organization's owners and admins. */\n includeAdmins?: boolean\n subject: string\n text: string\n /** Resend tag / log label. */\n context: string\n}\n\nexport interface OrgMemberNoticeResult {\n /** Addresses the notice went to. */\n sent: number\n /** Why nothing could be sent at all, when nothing could. */\n reason?: 'unconfigured' | 'no-recipients' | 'failed'\n}\n\n/** At most this many directory lookups, and this many sends, per notice. */\nconst MAX_LOOKUPS = 10\nconst MAX_SENDS = 25\n\n/** Sends one notice to each member — see the module note. */\nexport async function sendOrgMemberNotice(input: OrgMemberNoticeInput): Promise<OrgMemberNoticeResult> {\n if (!isEmailConfigured()) return { sent: 0, reason: 'unconfigured' }\n try {\n const members = await listOrgMembers(input.orgId)\n const rosterEmail = new Map<string, string>()\n for (const member of members) {\n const address = String(member.email ?? '')\n .trim()\n .toLowerCase()\n if (address.includes('@')) rosterEmail.set(member.$id, address)\n }\n const uids = new Set(input.uids.filter(Boolean))\n if (input.includeAdmins) {\n for (const member of members) {\n if (member.role === 'owner' || member.role === 'admin') uids.add(member.$id)\n }\n }\n const addresses = new Set<string>()\n let lookups = 0\n for (const uid of uids) {\n let address = rosterEmail.get(uid) ?? ''\n if (!address && lookups < MAX_LOOKUPS) {\n lookups += 1\n const pooled = await findUserByUidAcrossPools(uid).catch(() => null)\n address = String(pooled?.record?.email ?? '')\n .trim()\n .toLowerCase()\n }\n if (address.includes('@')) addresses.add(address)\n }\n const recipients = await filterSuppressedEmails([...addresses])\n if (!recipients.length) return { sent: 0, reason: 'no-recipients' }\n // One message for everyone, in the organization's brand, as the\n // `workspace-notice` system email (AGL-3367).\n const brand = await orgSystemEmailBrand(input.orgId)\n const content = await renderSystemEmailContent(\n 'workspace-notice',\n {\n 'notice.subject': input.subject,\n 'notice.body': input.text,\n 'org.name': brand.orgName ?? '',\n },\n brand,\n { subject: input.subject, text: input.text },\n )\n let sent = 0\n for (const to of recipients.slice(0, MAX_SENDS)) {\n const result = await sendEmail({\n to,\n ...content,\n ...(brand.fromName ? { fromName: brand.fromName } : {}),\n context: input.context,\n })\n if (!result.sent) continue\n sent += 1\n const { meterOrgEmail } = await import('./email-metering')\n await meterOrgEmail(input.orgId).catch(() => undefined)\n }\n return sent ? { sent } : { sent: 0, reason: 'failed' }\n } catch (error) {\n console.error('[org-member-notice] send failed', error)\n return { sent: 0, reason: 'failed' }\n }\n}\n"],"names":["isEmailConfigured","sendEmail","findUserByUidAcrossPools","filterSuppressedEmails","listOrgMembers","orgSystemEmailBrand","renderSystemEmailContent","MAX_LOOKUPS","MAX_SENDS","sendOrgMemberNotice","input","sent","reason","brand","members","orgId","rosterEmail","Map","member","address","String","email","trim","toLowerCase","includes","set","$id","uids","Set","filter","Boolean","includeAdmins","role","add","addresses","lookups","uid","get","pooled","catch","record","recipients","length","content","subject","text","orgName","to","slice","result","fromName","context","meterOrgEmail","undefined","error","console"],"mappings":"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"}
|
|
@@ -423,7 +423,16 @@ export declare function logOrgActivity(orgId: string,
|
|
|
423
423
|
actor: {
|
|
424
424
|
uid: string | null;
|
|
425
425
|
email?: string | null;
|
|
426
|
-
}, action: string, target: OrgActivityTarget
|
|
426
|
+
}, action: string, target: OrgActivityTarget,
|
|
427
|
+
/**
|
|
428
|
+
* `staffActorId`: the staff member behind an entry the workspace did not
|
|
429
|
+
* perform (a staff cancellation, AGL-3369). Kept apart from `actorId` so
|
|
430
|
+
* the filterable actor stays honest; `/api/orgs/activity` shows it to
|
|
431
|
+
* staff only.
|
|
432
|
+
*/
|
|
433
|
+
options?: {
|
|
434
|
+
staffActorId?: string | null;
|
|
435
|
+
}): Promise<void>;
|
|
427
436
|
/** What a host activity entry points at. Mirrors `HostActivityTarget`. */
|
|
428
437
|
export interface HostActivityTarget {
|
|
429
438
|
type: 'host' | 'screen' | 'layout' | 'theme' | 'media' | 'content' | 'variable' | 'function' | 'workflow' | 'member' | 'component' | 'template' | 'contact' | 'company' | 'deal' | 'lead';
|
|
@@ -898,11 +898,19 @@ const MEMBER_PROJECTION_WRITE = {
|
|
|
898
898
|
* performed — and `actorId` is a filterable field, so the invented
|
|
899
899
|
* attribution would then show up under that person when somebody asks what
|
|
900
900
|
* they have done.
|
|
901
|
-
*/ actor, action, target
|
|
901
|
+
*/ actor, action, target, /**
|
|
902
|
+
* `staffActorId`: the staff member behind an entry the workspace did not
|
|
903
|
+
* perform (a staff cancellation, AGL-3369). Kept apart from `actorId` so
|
|
904
|
+
* the filterable actor stays honest; `/api/orgs/activity` shows it to
|
|
905
|
+
* staff only.
|
|
906
|
+
*/ options) {
|
|
902
907
|
var _actor_uid, _actor_email;
|
|
903
|
-
await firestore().collection('orgs').doc(orgId).collection('activity').add({
|
|
908
|
+
await firestore().collection('orgs').doc(orgId).collection('activity').add(_extends({
|
|
904
909
|
actorId: (_actor_uid = actor.uid) != null ? _actor_uid : null,
|
|
905
|
-
actorEmail: (_actor_email = actor.email) != null ? _actor_email : null
|
|
910
|
+
actorEmail: (_actor_email = actor.email) != null ? _actor_email : null
|
|
911
|
+
}, (options == null ? void 0 : options.staffActorId) ? {
|
|
912
|
+
staffActorId: options.staffActorId
|
|
913
|
+
} : {}, {
|
|
906
914
|
action,
|
|
907
915
|
target: _extends({
|
|
908
916
|
type: target.type
|
|
@@ -919,7 +927,7 @@ const MEMBER_PROJECTION_WRITE = {
|
|
|
919
927
|
target
|
|
920
928
|
}),
|
|
921
929
|
createdAt: FieldValue.serverTimestamp()
|
|
922
|
-
}).catch(()=>undefined);
|
|
930
|
+
})).catch(()=>undefined);
|
|
923
931
|
}
|
|
924
932
|
/**
|
|
925
933
|
* Append to `hosts/{hostId}/activity` with the ADMIN SDK (AGL-118).
|