@aglyn/aglyn 1.0.0-beta.219 → 1.0.0-beta.221
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 +12 -12
- package/src/lib/app-utils/actions.d.ts +1 -59
- package/src/lib/app-utils/actions.js +7 -72
- package/src/lib/app-utils/actions.js.map +1 -1
- package/src/lib/app-utils/consent-groups.d.ts +1 -1
- package/src/lib/app-utils/consent-groups.js.map +1 -1
- package/src/lib/app-utils/console-routes.d.ts +0 -36
- package/src/lib/app-utils/console-routes.js +2 -37
- package/src/lib/app-utils/console-routes.js.map +1 -1
- package/src/lib/app-utils/contacts.d.ts +1 -1
- package/src/lib/app-utils/contacts.js.map +1 -1
- package/src/lib/app-utils/container-membership.d.ts +93 -0
- package/src/lib/app-utils/container-membership.js +165 -0
- package/src/lib/app-utils/container-membership.js.map +1 -0
- package/src/lib/app-utils/crm-task-reminders.d.ts +23 -6
- package/src/lib/app-utils/crm-task-reminders.js +32 -11
- package/src/lib/app-utils/crm-task-reminders.js.map +1 -1
- package/src/lib/app-utils/crm.d.ts +24 -4
- package/src/lib/app-utils/crm.js +23 -0
- package/src/lib/app-utils/crm.js.map +1 -1
- package/src/lib/app-utils/csv-upload.d.ts +1 -1
- package/src/lib/app-utils/csv-upload.js +1 -1
- package/src/lib/app-utils/csv-upload.js.map +1 -1
- package/src/lib/app-utils/{dataset-csv.d.ts → csv.d.ts} +10 -43
- package/src/lib/app-utils/{dataset-csv.js → csv.js} +10 -81
- package/src/lib/app-utils/csv.js.map +1 -0
- package/src/lib/app-utils/datasets.d.ts +0 -19
- package/src/lib/app-utils/datasets.js +0 -13
- package/src/lib/app-utils/datasets.js.map +1 -1
- package/src/lib/app-utils/docs-help.generated.d.ts +4 -4
- package/src/lib/app-utils/docs-help.generated.js +5 -0
- package/src/lib/app-utils/docs-help.generated.js.map +1 -1
- package/src/lib/app-utils/docs-index.generated.js +71 -36
- package/src/lib/app-utils/docs-index.generated.js.map +1 -1
- package/src/lib/app-utils/dynamic-list-rule.js +3 -3
- package/src/lib/app-utils/dynamic-list-rule.js.map +1 -1
- package/src/lib/app-utils/email-text.d.ts +80 -0
- package/src/lib/app-utils/email-text.js +189 -0
- package/src/lib/app-utils/email-text.js.map +1 -0
- package/src/lib/app-utils/{list-assignment-policy.d.ts → enrollment-basis.d.ts} +4 -0
- package/src/lib/app-utils/{list-assignment-policy.js → enrollment-basis.js} +5 -1
- package/src/lib/app-utils/enrollment-basis.js.map +1 -0
- package/src/lib/app-utils/expand-repeatables.js +4 -4
- package/src/lib/app-utils/expand-repeatables.js.map +1 -1
- package/src/lib/app-utils/forms.js +2 -2
- package/src/lib/app-utils/forms.js.map +1 -1
- package/src/lib/app-utils/health-report.d.ts +2 -3
- package/src/lib/app-utils/health-report.js +30 -6
- package/src/lib/app-utils/health-report.js.map +1 -1
- package/src/lib/app-utils/inbound-mail-domain.d.ts +27 -0
- package/src/lib/app-utils/inbound-mail-domain.js +31 -0
- package/src/lib/app-utils/inbound-mail-domain.js.map +1 -0
- package/src/lib/app-utils/leaving-notice.d.ts +96 -0
- package/src/lib/app-utils/leaving-notice.js +157 -0
- package/src/lib/app-utils/leaving-notice.js.map +1 -0
- package/src/lib/app-utils/lockdown.d.ts +11 -0
- package/src/lib/app-utils/lockdown.js +33 -3
- package/src/lib/app-utils/lockdown.js.map +1 -1
- package/src/lib/app-utils/member-email-aliases.d.ts +25 -0
- package/src/lib/app-utils/member-email-aliases.js +29 -0
- package/src/lib/app-utils/member-email-aliases.js.map +1 -1
- package/src/lib/app-utils/notifications.d.ts +69 -0
- package/src/lib/app-utils/notifications.js +119 -0
- package/src/lib/app-utils/notifications.js.map +1 -1
- package/src/lib/app-utils/operator-alerts.d.ts +15 -1
- package/src/lib/app-utils/operator-alerts.js +64 -10
- package/src/lib/app-utils/operator-alerts.js.map +1 -1
- package/src/lib/app-utils/plan-entitlements.d.ts +164 -178
- package/src/lib/app-utils/plan-entitlements.js +200 -224
- package/src/lib/app-utils/plan-entitlements.js.map +1 -1
- package/src/lib/app-utils/platform-marketing-consent.d.ts +56 -2
- package/src/lib/app-utils/platform-marketing-consent.js +6 -5
- package/src/lib/app-utils/platform-marketing-consent.js.map +1 -1
- package/src/lib/app-utils/plugin-starter-templates.generated.d.ts +12 -0
- package/src/lib/app-utils/plugin-starter-templates.generated.js +711 -0
- package/src/lib/app-utils/plugin-starter-templates.generated.js.map +1 -0
- package/src/lib/app-utils/repeat-query.d.ts +56 -0
- package/src/lib/app-utils/{dataset-query.js → repeat-query.js} +16 -16
- package/src/lib/app-utils/repeat-query.js.map +1 -0
- package/src/lib/app-utils/screen-link-context-value.d.ts +7 -0
- package/src/lib/app-utils/screen-link-context-value.js.map +1 -1
- package/src/lib/app-utils/screen-link-context.js +14 -2
- package/src/lib/app-utils/screen-link-context.js.map +1 -1
- package/src/lib/app-utils/screen-route.d.ts +3 -0
- package/src/lib/app-utils/screen-route.js +6 -1
- package/src/lib/app-utils/screen-route.js.map +1 -1
- package/src/lib/app-utils/server.d.ts +7 -9
- package/src/lib/app-utils/server.js +18 -26
- package/src/lib/app-utils/server.js.map +1 -1
- package/src/lib/app-utils/starter-template-nodes.d.ts +137 -0
- package/src/lib/app-utils/starter-template-nodes.js +129 -0
- package/src/lib/app-utils/starter-template-nodes.js.map +1 -0
- package/src/lib/app-utils/starter-templates.d.ts +11 -34
- package/src/lib/app-utils/starter-templates.js +19 -230
- package/src/lib/app-utils/starter-templates.js.map +1 -1
- package/src/lib/app-utils/{email-topics.d.ts → subscription-topics.d.ts} +64 -48
- package/src/lib/app-utils/{email-topics.js → subscription-topics.js} +39 -109
- package/src/lib/app-utils/subscription-topics.js.map +1 -0
- package/src/lib/app-utils/usage-budget.d.ts +73 -188
- package/src/lib/app-utils/usage-budget.js +58 -194
- package/src/lib/app-utils/usage-budget.js.map +1 -1
- package/src/lib/app-utils/utm-attribution.d.ts +1 -1
- package/src/lib/app-utils/utm-attribution.js +1 -1
- package/src/lib/app-utils/utm-attribution.js.map +1 -1
- package/src/lib/app-utils/{campaign-touch.d.ts → utm-touch.d.ts} +21 -13
- package/src/lib/app-utils/{campaign-touch.js → utm-touch.js} +30 -22
- package/src/lib/app-utils/utm-touch.js.map +1 -0
- package/src/lib/app-utils/variables.d.ts +9 -9
- package/src/lib/app-utils/variables.js.map +1 -1
- package/src/lib/app-utils/visitor-record-ceiling.d.ts +32 -0
- package/src/lib/app-utils/visitor-record-ceiling.js +39 -0
- package/src/lib/app-utils/visitor-record-ceiling.js.map +1 -1
- package/src/lib/foundation/definitions/org-billing.types.d.ts +2 -2
- package/src/lib/foundation/definitions/org-billing.types.js.map +1 -1
- package/src/lib/plugin-manager/feature-plugins.d.ts +80 -79
- package/src/lib/plugin-manager/feature-plugins.js +22 -35
- package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
- package/src/lib/plugin-manager/first-party-plugins.generated.d.ts +18 -1
- package/src/lib/plugin-manager/first-party-plugins.generated.js +62 -2
- package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
- package/src/lib/plugin-manager/index.d.ts +0 -1
- package/src/lib/plugin-manager/index.js +0 -1
- package/src/lib/plugin-manager/index.js.map +1 -1
- package/src/lib/plugin-manager/plugin-contact-capture.d.ts +9 -4
- package/src/lib/plugin-manager/plugin-contact-capture.js.map +1 -1
- package/src/lib/plugin-manager/plugin-containers.d.ts +68 -0
- package/src/lib/plugin-manager/plugin-containers.generated.d.ts +10 -0
- package/src/lib/plugin-manager/plugin-containers.generated.js +20 -0
- package/src/lib/plugin-manager/plugin-containers.generated.js.map +1 -0
- package/src/lib/plugin-manager/plugin-containers.js +27 -0
- package/src/lib/plugin-manager/plugin-containers.js.map +1 -0
- package/src/lib/plugin-manager/plugin-entitlements.d.ts +8 -0
- package/src/lib/plugin-manager/plugin-entitlements.js.map +1 -1
- package/src/lib/plugin-manager/plugin-membership-detach.js +1 -1
- package/src/lib/plugin-manager/plugin-membership-detach.js.map +1 -1
- package/src/lib/plugin-manager/plugin-record-email-state.d.ts +69 -0
- package/src/lib/plugin-manager/plugin-record-email-state.js +32 -0
- package/src/lib/plugin-manager/plugin-record-email-state.js.map +1 -1
- package/src/lib/plugin-manager/plugin-record-lists.d.ts +115 -0
- package/src/lib/plugin-manager/plugin-record-lists.js +75 -0
- package/src/lib/plugin-manager/plugin-record-lists.js.map +1 -0
- package/src/lib/plugin-manager/plugin-record-timeline.d.ts +42 -8
- package/src/lib/plugin-manager/plugin-record-timeline.js.map +1 -1
- package/src/lib/plugin-manager/plugin-server-steps.d.ts +170 -0
- package/src/lib/plugin-manager/plugin-server-steps.js +116 -0
- package/src/lib/plugin-manager/plugin-server-steps.js.map +1 -0
- package/src/lib/plugin-manager/plugin-site-beacons.d.ts +71 -0
- package/src/lib/plugin-manager/plugin-site-beacons.js +54 -0
- package/src/lib/plugin-manager/plugin-site-beacons.js.map +1 -0
- package/src/lib/plugin-manager/plugin-subscription-topics.generated.d.ts +11 -0
- package/src/lib/plugin-manager/plugin-subscription-topics.generated.js +41 -0
- package/src/lib/plugin-manager/plugin-subscription-topics.generated.js.map +1 -0
- package/src/lib/plugin-manager/plugin-usage-axes.d.ts +212 -0
- package/src/lib/plugin-manager/plugin-usage-axes.js +82 -3
- package/src/lib/plugin-manager/plugin-usage-axes.js.map +1 -1
- package/src/lib/plugin-manager/plugin-usage-meters.d.ts +108 -0
- package/src/lib/plugin-manager/plugin-usage-meters.js +105 -0
- package/src/lib/plugin-manager/plugin-usage-meters.js.map +1 -0
- package/src/lib/plugin-manager/usage-alert-contributors.d.ts +76 -6
- package/src/lib/plugin-manager/usage-alert-contributors.js +23 -10
- package/src/lib/plugin-manager/usage-alert-contributors.js.map +1 -1
- package/src/lib/app-utils/assist-credits.d.ts +0 -482
- package/src/lib/app-utils/assist-credits.js +0 -510
- package/src/lib/app-utils/assist-credits.js.map +0 -1
- package/src/lib/app-utils/campaign-membership.d.ts +0 -121
- package/src/lib/app-utils/campaign-membership.js +0 -189
- package/src/lib/app-utils/campaign-membership.js.map +0 -1
- package/src/lib/app-utils/campaign-touch.js.map +0 -1
- package/src/lib/app-utils/console-record-links.d.ts +0 -84
- package/src/lib/app-utils/console-record-links.js +0 -76
- package/src/lib/app-utils/console-record-links.js.map +0 -1
- package/src/lib/app-utils/crm-booking.d.ts +0 -75
- package/src/lib/app-utils/crm-booking.js +0 -133
- package/src/lib/app-utils/crm-booking.js.map +0 -1
- package/src/lib/app-utils/crm-inbound.d.ts +0 -245
- package/src/lib/app-utils/crm-inbound.js +0 -407
- package/src/lib/app-utils/crm-inbound.js.map +0 -1
- package/src/lib/app-utils/crm-reports.d.ts +0 -479
- package/src/lib/app-utils/crm-reports.js +0 -680
- package/src/lib/app-utils/crm-reports.js.map +0 -1
- package/src/lib/app-utils/dataset-csv.js.map +0 -1
- package/src/lib/app-utils/dataset-query.d.ts +0 -57
- package/src/lib/app-utils/dataset-query.js.map +0 -1
- package/src/lib/app-utils/email-topics.js.map +0 -1
- package/src/lib/app-utils/list-assignment-policy.js.map +0 -1
- package/src/lib/plugin-manager/plugin-metered-lines.d.ts +0 -97
- package/src/lib/plugin-manager/plugin-metered-lines.js +0 -123
- package/src/lib/plugin-manager/plugin-metered-lines.js.map +0 -1
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
/** Where the platform receives mail when the deployment names no domain. */
|
|
18
|
+
export declare const INBOUND_MAIL_DEFAULT_DOMAIN = "in.aglyn.com";
|
|
19
|
+
/**
|
|
20
|
+
* The domain the platform receives mail on for the workspaces it serves —
|
|
21
|
+
* the one its mail provider's inbound route answers, and so one no member
|
|
22
|
+
* may claim an address on (AGL-2657, AGL-2975). `CRM_INBOUND_DOMAIN`, the
|
|
23
|
+
* name the deployment has always set it under, or the platform's default
|
|
24
|
+
* when it names none or names something that is not a hostname. Lowercased,
|
|
25
|
+
* because a domain is.
|
|
26
|
+
*/
|
|
27
|
+
export declare function inboundMailDomain(env?: Record<string, string | undefined>): string;
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
*/ /** Where the platform receives mail when the deployment names no domain. */ export const INBOUND_MAIL_DEFAULT_DOMAIN = 'in.aglyn.com';
|
|
17
|
+
const DOMAIN_PATTERN = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/;
|
|
18
|
+
/**
|
|
19
|
+
* The domain the platform receives mail on for the workspaces it serves —
|
|
20
|
+
* the one its mail provider's inbound route answers, and so one no member
|
|
21
|
+
* may claim an address on (AGL-2657, AGL-2975). `CRM_INBOUND_DOMAIN`, the
|
|
22
|
+
* name the deployment has always set it under, or the platform's default
|
|
23
|
+
* when it names none or names something that is not a hostname. Lowercased,
|
|
24
|
+
* because a domain is.
|
|
25
|
+
*/ export function inboundMailDomain(env = process.env) {
|
|
26
|
+
var _env_CRM_INBOUND_DOMAIN;
|
|
27
|
+
const configured = String((_env_CRM_INBOUND_DOMAIN = env['CRM_INBOUND_DOMAIN']) != null ? _env_CRM_INBOUND_DOMAIN : '').trim().toLowerCase();
|
|
28
|
+
return DOMAIN_PATTERN.test(configured) ? configured : INBOUND_MAIL_DEFAULT_DOMAIN;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=inbound-mail-domain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/inbound-mail-domain.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/** Where the platform receives mail when the deployment names no domain. */\nexport const INBOUND_MAIL_DEFAULT_DOMAIN = 'in.aglyn.com'\n\nconst DOMAIN_PATTERN = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/\n\n/**\n * The domain the platform receives mail on for the workspaces it serves —\n * the one its mail provider's inbound route answers, and so one no member\n * may claim an address on (AGL-2657, AGL-2975). `CRM_INBOUND_DOMAIN`, the\n * name the deployment has always set it under, or the platform's default\n * when it names none or names something that is not a hostname. Lowercased,\n * because a domain is.\n */\nexport function inboundMailDomain(\n env: Record<string, string | undefined> = process.env,\n): string {\n const configured = String(env['CRM_INBOUND_DOMAIN'] ?? '')\n .trim()\n .toLowerCase()\n return DOMAIN_PATTERN.test(configured) ? configured : INBOUND_MAIL_DEFAULT_DOMAIN\n}\n"],"names":["INBOUND_MAIL_DEFAULT_DOMAIN","DOMAIN_PATTERN","inboundMailDomain","env","process","configured","String","trim","toLowerCase","test"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,0EAA0E,GAC1E,OAAO,MAAMA,8BAA8B,eAAc;AAEzD,MAAMC,iBAAiB;AAEvB;;;;;;;CAOC,GACD,OAAO,SAASC,kBACdC,MAA0CC,QAAQD,GAAG;QAE3BA;IAA1B,MAAME,aAAaC,QAAOH,0BAAAA,GAAG,CAAC,qBAAqB,YAAzBA,0BAA6B,IACpDI,IAAI,GACJC,WAAW;IACd,OAAOP,eAAeQ,IAAI,CAACJ,cAAcA,aAAaL;AACxD"}
|
|
@@ -0,0 +1,96 @@
|
|
|
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 first path segment the notice owns on every site (a reserved slug). */
|
|
18
|
+
export declare const LEAVING_NOTICE_ROUTE_SEGMENT = "_aglyn";
|
|
19
|
+
/** Where the notice is served, on the site's own host. */
|
|
20
|
+
export declare const LEAVING_NOTICE_PATH = "/_aglyn/leaving";
|
|
21
|
+
/** The query parameter carrying the destination. */
|
|
22
|
+
export declare const LEAVING_NOTICE_DESTINATION_PARAM = "to";
|
|
23
|
+
/** The query parameter carrying the destination's signature. */
|
|
24
|
+
export declare const LEAVING_NOTICE_SIGNATURE_PARAM = "sig";
|
|
25
|
+
/** What a page in its notice window carries, from the server. */
|
|
26
|
+
export interface LeavingNoticeConfig {
|
|
27
|
+
/**
|
|
28
|
+
* Epoch ms the window closes. For people and specs reading the payload:
|
|
29
|
+
* nothing here compares it to a clock, because the page that carries this
|
|
30
|
+
* config was rendered inside the window and its href has to match the
|
|
31
|
+
* server's byte for byte.
|
|
32
|
+
*/
|
|
33
|
+
until: number;
|
|
34
|
+
/**
|
|
35
|
+
* The hosts a link may go to without the notice: the site's own subdomain
|
|
36
|
+
* and custom domain, and the platform's own. An entry starting with `.` is
|
|
37
|
+
* that domain AND every name under it.
|
|
38
|
+
*/
|
|
39
|
+
hosts: readonly string[];
|
|
40
|
+
/**
|
|
41
|
+
* Destination (as {@link normalizeLeavingDestination} writes it) → its
|
|
42
|
+
* signature, for every outside address the server found on this page. A
|
|
43
|
+
* destination with none still goes through the notice, which refuses it.
|
|
44
|
+
*/
|
|
45
|
+
sigs: Readonly<Record<string, string>>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The absolute http(s) address an href navigates to when it names another
|
|
49
|
+
* host, or `null` for anything that stays on the page's own origin or is not
|
|
50
|
+
* a navigation at all.
|
|
51
|
+
*
|
|
52
|
+
* Resolved the way a browser resolves it rather than pattern-matched, because
|
|
53
|
+
* a pattern is exactly what a disguised link is built to get past: `//evil`
|
|
54
|
+
* and `/\evil` are both protocol-relative to a browser while reading as
|
|
55
|
+
* site paths to a `^/` test. `mailto:`, `tel:` and `sms:` hand off to another
|
|
56
|
+
* app and are not navigations, so they answer `null` with every other scheme.
|
|
57
|
+
*
|
|
58
|
+
* The answer is the URL's `href` — lowercased host, default port dropped,
|
|
59
|
+
* root path filled in — so the server that signs a destination and the
|
|
60
|
+
* client that looks the signature up agree on one spelling.
|
|
61
|
+
*/
|
|
62
|
+
export declare function normalizeLeavingDestination(raw: unknown): string | null;
|
|
63
|
+
/**
|
|
64
|
+
* Whether a hostname is one a link may reach without the notice: listed in
|
|
65
|
+
* `hosts` (exactly, or under a `.domain` entry), or a development machine's
|
|
66
|
+
* own `localhost`, which sends a visitor nowhere.
|
|
67
|
+
*/
|
|
68
|
+
export declare function isLeavingNoticeExempt(hostname: string, hosts: readonly string[]): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* The normalized destination of an href that leaves for a host outside
|
|
71
|
+
* `hosts`, or `null` when the href stays, is not a navigation, or is exempt.
|
|
72
|
+
*/
|
|
73
|
+
export declare function leavingDestination(raw: unknown, hosts: readonly string[]): string | null;
|
|
74
|
+
/** The notice's own address for a destination, signed when a signature is known. */
|
|
75
|
+
export declare function leavingNoticeHref(destination: string, signature?: string | null): string;
|
|
76
|
+
/**
|
|
77
|
+
* The href a link should carry instead of `raw` — the notice for its
|
|
78
|
+
* destination — or `undefined` to leave it as it is: no config (the site is
|
|
79
|
+
* not in its window), a destination on an exempt host, or not a navigation.
|
|
80
|
+
*/
|
|
81
|
+
export declare function routeThroughLeavingNotice(raw: unknown, config: LeavingNoticeConfig | null | undefined): string | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* Points the anchor an event landed in at the notice, when it leaves.
|
|
84
|
+
*
|
|
85
|
+
* The catch-all for every link `useLinkTarget` never saw — Markdown and rich
|
|
86
|
+
* text bodies, collection blocks, a plugin's own anchors. The href is
|
|
87
|
+
* rewritten IN PLACE, before the browser acts on the event, so the visitor's
|
|
88
|
+
* own gesture still decides how it opens: a modified click, a middle click
|
|
89
|
+
* and "Open in new tab" all take the notice with them. Next's `Link` leaves
|
|
90
|
+
* a non-local href to the browser, so the browser is what reads the change.
|
|
91
|
+
*
|
|
92
|
+
* Returns whether it rewrote anything.
|
|
93
|
+
*/
|
|
94
|
+
export declare function rerouteLeavingAnchor(target: EventTarget | null, config: LeavingNoticeConfig | null | undefined): boolean;
|
|
95
|
+
/** Installs {@link rerouteLeavingAnchor} on a document; returns the uninstall. */
|
|
96
|
+
export declare function installLeavingNoticeInterceptor(doc: Document, config: LeavingNoticeConfig): () => void;
|
|
@@ -0,0 +1,157 @@
|
|
|
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 "YOU'RE LEAVING THIS SITE" NOTICE, CLIENT HALF (AGL-3452).
|
|
18
|
+
*
|
|
19
|
+
* Both phishing pages found on free sites on 2026-10-01 had one button, and it
|
|
20
|
+
* went straight to an outside credential harvester: the site's address was
|
|
21
|
+
* only the trusted-looking hop. So for a FREE workspace in its first 14 days,
|
|
22
|
+
* a link to another domain goes through a notice on the site's own host
|
|
23
|
+
* first, which names the destination in plain text and says the platform
|
|
24
|
+
* does not operate it.
|
|
25
|
+
*
|
|
26
|
+
* The server decides whether a page is in that window and hands the page a
|
|
27
|
+
* {@link LeavingNoticeConfig}; this module only applies it. Nothing here
|
|
28
|
+
* reads the clock, so the server render and the hydrating client always
|
|
29
|
+
* produce the same href.
|
|
30
|
+
*
|
|
31
|
+
* A leaf with no imports: it is on every published page through
|
|
32
|
+
* `useLinkTarget`, and the hosts and signatures it needs arrive as data.
|
|
33
|
+
*=========================================*/ /** The first path segment the notice owns on every site (a reserved slug). */ export const LEAVING_NOTICE_ROUTE_SEGMENT = '_aglyn';
|
|
34
|
+
/** Where the notice is served, on the site's own host. */ export const LEAVING_NOTICE_PATH = `/${LEAVING_NOTICE_ROUTE_SEGMENT}/leaving`;
|
|
35
|
+
/** The query parameter carrying the destination. */ export const LEAVING_NOTICE_DESTINATION_PARAM = 'to';
|
|
36
|
+
/** The query parameter carrying the destination's signature. */ export const LEAVING_NOTICE_SIGNATURE_PARAM = 'sig';
|
|
37
|
+
/** The origin a site-relative href is resolved against to tell it apart. */ const SITE_RELATIVE_BASE = 'https://site-relative.invalid';
|
|
38
|
+
const SITE_RELATIVE_HOSTNAME = 'site-relative.invalid';
|
|
39
|
+
/**
|
|
40
|
+
* The absolute http(s) address an href navigates to when it names another
|
|
41
|
+
* host, or `null` for anything that stays on the page's own origin or is not
|
|
42
|
+
* a navigation at all.
|
|
43
|
+
*
|
|
44
|
+
* Resolved the way a browser resolves it rather than pattern-matched, because
|
|
45
|
+
* a pattern is exactly what a disguised link is built to get past: `//evil`
|
|
46
|
+
* and `/\evil` are both protocol-relative to a browser while reading as
|
|
47
|
+
* site paths to a `^/` test. `mailto:`, `tel:` and `sms:` hand off to another
|
|
48
|
+
* app and are not navigations, so they answer `null` with every other scheme.
|
|
49
|
+
*
|
|
50
|
+
* The answer is the URL's `href` — lowercased host, default port dropped,
|
|
51
|
+
* root path filled in — so the server that signs a destination and the
|
|
52
|
+
* client that looks the signature up agree on one spelling.
|
|
53
|
+
*/ export function normalizeLeavingDestination(raw) {
|
|
54
|
+
if (typeof raw !== 'string') return null;
|
|
55
|
+
const trimmed = raw.trim();
|
|
56
|
+
if (!trimmed) return null;
|
|
57
|
+
let url;
|
|
58
|
+
try {
|
|
59
|
+
url = new URL(trimmed, SITE_RELATIVE_BASE);
|
|
60
|
+
} catch (unused) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') return null;
|
|
64
|
+
if (!url.hostname || url.hostname === SITE_RELATIVE_HOSTNAME) return null;
|
|
65
|
+
return url.href;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Whether a hostname is one a link may reach without the notice: listed in
|
|
69
|
+
* `hosts` (exactly, or under a `.domain` entry), or a development machine's
|
|
70
|
+
* own `localhost`, which sends a visitor nowhere.
|
|
71
|
+
*/ export function isLeavingNoticeExempt(hostname, hosts) {
|
|
72
|
+
const name = String(hostname != null ? hostname : '').toLowerCase().replace(/\.+$/, '');
|
|
73
|
+
if (!name) return true;
|
|
74
|
+
if (name === 'localhost' || name.endsWith('.localhost')) return true;
|
|
75
|
+
for (const entry of hosts){
|
|
76
|
+
if (entry.startsWith('.')) {
|
|
77
|
+
if (name === entry.slice(1) || name.endsWith(entry)) return true;
|
|
78
|
+
} else if (name === entry) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The normalized destination of an href that leaves for a host outside
|
|
86
|
+
* `hosts`, or `null` when the href stays, is not a navigation, or is exempt.
|
|
87
|
+
*/ export function leavingDestination(raw, hosts) {
|
|
88
|
+
const destination = normalizeLeavingDestination(raw);
|
|
89
|
+
if (!destination) return null;
|
|
90
|
+
return isLeavingNoticeExempt(new URL(destination).hostname, hosts) ? null : destination;
|
|
91
|
+
}
|
|
92
|
+
/** The notice's own address for a destination, signed when a signature is known. */ export function leavingNoticeHref(destination, signature) {
|
|
93
|
+
const params = new URLSearchParams();
|
|
94
|
+
params.set(LEAVING_NOTICE_DESTINATION_PARAM, destination);
|
|
95
|
+
if (signature) params.set(LEAVING_NOTICE_SIGNATURE_PARAM, signature);
|
|
96
|
+
return `${LEAVING_NOTICE_PATH}?${params.toString()}`;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The href a link should carry instead of `raw` — the notice for its
|
|
100
|
+
* destination — or `undefined` to leave it as it is: no config (the site is
|
|
101
|
+
* not in its window), a destination on an exempt host, or not a navigation.
|
|
102
|
+
*/ export function routeThroughLeavingNotice(raw, config) {
|
|
103
|
+
var _config_sigs;
|
|
104
|
+
if (!config) return undefined;
|
|
105
|
+
const destination = leavingDestination(raw, config.hosts);
|
|
106
|
+
if (!destination) return undefined;
|
|
107
|
+
return leavingNoticeHref(destination, (_config_sigs = config.sigs) == null ? void 0 : _config_sigs[destination]);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Points the anchor an event landed in at the notice, when it leaves.
|
|
111
|
+
*
|
|
112
|
+
* The catch-all for every link `useLinkTarget` never saw — Markdown and rich
|
|
113
|
+
* text bodies, collection blocks, a plugin's own anchors. The href is
|
|
114
|
+
* rewritten IN PLACE, before the browser acts on the event, so the visitor's
|
|
115
|
+
* own gesture still decides how it opens: a modified click, a middle click
|
|
116
|
+
* and "Open in new tab" all take the notice with them. Next's `Link` leaves
|
|
117
|
+
* a non-local href to the browser, so the browser is what reads the change.
|
|
118
|
+
*
|
|
119
|
+
* Returns whether it rewrote anything.
|
|
120
|
+
*/ export function rerouteLeavingAnchor(target, config) {
|
|
121
|
+
var _node_parentElement;
|
|
122
|
+
var _element_closest;
|
|
123
|
+
if (!config || !target) return false;
|
|
124
|
+
const node = target;
|
|
125
|
+
const element = node.nodeType === 1 ? node : (_node_parentElement = node.parentElement) != null ? _node_parentElement : null;
|
|
126
|
+
const anchor = element == null ? void 0 : (_element_closest = element.closest) == null ? void 0 : _element_closest.call(element, 'a[href], area[href]');
|
|
127
|
+
if (!anchor) return false;
|
|
128
|
+
const rerouted = routeThroughLeavingNotice(anchor.getAttribute('href'), config);
|
|
129
|
+
if (!rerouted) return false;
|
|
130
|
+
anchor.setAttribute('href', rerouted);
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Every gesture that can follow, open or copy a link, caught on the way
|
|
135
|
+
* down so the href is the notice's before anything reads it.
|
|
136
|
+
*/ const LEAVING_NOTICE_EVENTS = [
|
|
137
|
+
'pointerdown',
|
|
138
|
+
'click',
|
|
139
|
+
'auxclick',
|
|
140
|
+
'contextmenu',
|
|
141
|
+
'focusin'
|
|
142
|
+
];
|
|
143
|
+
/** Installs {@link rerouteLeavingAnchor} on a document; returns the uninstall. */ export function installLeavingNoticeInterceptor(doc, config) {
|
|
144
|
+
const handler = (event)=>{
|
|
145
|
+
rerouteLeavingAnchor(event.target, config);
|
|
146
|
+
};
|
|
147
|
+
for (const type of LEAVING_NOTICE_EVENTS){
|
|
148
|
+
doc.addEventListener(type, handler, true);
|
|
149
|
+
}
|
|
150
|
+
return ()=>{
|
|
151
|
+
for (const type of LEAVING_NOTICE_EVENTS){
|
|
152
|
+
doc.removeEventListener(type, handler, true);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
//# sourceMappingURL=leaving-notice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/leaving-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\n/*==========================================\n * THE \"YOU'RE LEAVING THIS SITE\" NOTICE, CLIENT HALF (AGL-3452).\n *\n * Both phishing pages found on free sites on 2026-10-01 had one button, and it\n * went straight to an outside credential harvester: the site's address was\n * only the trusted-looking hop. So for a FREE workspace in its first 14 days,\n * a link to another domain goes through a notice on the site's own host\n * first, which names the destination in plain text and says the platform\n * does not operate it.\n *\n * The server decides whether a page is in that window and hands the page a\n * {@link LeavingNoticeConfig}; this module only applies it. Nothing here\n * reads the clock, so the server render and the hydrating client always\n * produce the same href.\n *\n * A leaf with no imports: it is on every published page through\n * `useLinkTarget`, and the hosts and signatures it needs arrive as data.\n *=========================================*/\n\n/** The first path segment the notice owns on every site (a reserved slug). */\nexport const LEAVING_NOTICE_ROUTE_SEGMENT = '_aglyn'\n\n/** Where the notice is served, on the site's own host. */\nexport const LEAVING_NOTICE_PATH = `/${LEAVING_NOTICE_ROUTE_SEGMENT}/leaving`\n\n/** The query parameter carrying the destination. */\nexport const LEAVING_NOTICE_DESTINATION_PARAM = 'to'\n\n/** The query parameter carrying the destination's signature. */\nexport const LEAVING_NOTICE_SIGNATURE_PARAM = 'sig'\n\n/** What a page in its notice window carries, from the server. */\nexport interface LeavingNoticeConfig {\n /**\n * Epoch ms the window closes. For people and specs reading the payload:\n * nothing here compares it to a clock, because the page that carries this\n * config was rendered inside the window and its href has to match the\n * server's byte for byte.\n */\n until: number\n /**\n * The hosts a link may go to without the notice: the site's own subdomain\n * and custom domain, and the platform's own. An entry starting with `.` is\n * that domain AND every name under it.\n */\n hosts: readonly string[]\n /**\n * Destination (as {@link normalizeLeavingDestination} writes it) → its\n * signature, for every outside address the server found on this page. A\n * destination with none still goes through the notice, which refuses it.\n */\n sigs: Readonly<Record<string, string>>\n}\n\n/** The origin a site-relative href is resolved against to tell it apart. */\nconst SITE_RELATIVE_BASE = 'https://site-relative.invalid'\nconst SITE_RELATIVE_HOSTNAME = 'site-relative.invalid'\n\n/**\n * The absolute http(s) address an href navigates to when it names another\n * host, or `null` for anything that stays on the page's own origin or is not\n * a navigation at all.\n *\n * Resolved the way a browser resolves it rather than pattern-matched, because\n * a pattern is exactly what a disguised link is built to get past: `//evil`\n * and `/\\evil` are both protocol-relative to a browser while reading as\n * site paths to a `^/` test. `mailto:`, `tel:` and `sms:` hand off to another\n * app and are not navigations, so they answer `null` with every other scheme.\n *\n * The answer is the URL's `href` — lowercased host, default port dropped,\n * root path filled in — so the server that signs a destination and the\n * client that looks the signature up agree on one spelling.\n */\nexport function normalizeLeavingDestination(raw: unknown): string | null {\n if (typeof raw !== 'string') return null\n const trimmed = raw.trim()\n if (!trimmed) return null\n let url: URL\n try {\n url = new URL(trimmed, SITE_RELATIVE_BASE)\n } catch {\n return null\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return null\n if (!url.hostname || url.hostname === SITE_RELATIVE_HOSTNAME) return null\n return url.href\n}\n\n/**\n * Whether a hostname is one a link may reach without the notice: listed in\n * `hosts` (exactly, or under a `.domain` entry), or a development machine's\n * own `localhost`, which sends a visitor nowhere.\n */\nexport function isLeavingNoticeExempt(\n hostname: string,\n hosts: readonly string[],\n): boolean {\n const name = String(hostname ?? '')\n .toLowerCase()\n .replace(/\\.+$/, '')\n if (!name) return true\n if (name === 'localhost' || name.endsWith('.localhost')) return true\n for (const entry of hosts) {\n if (entry.startsWith('.')) {\n if (name === entry.slice(1) || name.endsWith(entry)) return true\n } else if (name === entry) {\n return true\n }\n }\n return false\n}\n\n/**\n * The normalized destination of an href that leaves for a host outside\n * `hosts`, or `null` when the href stays, is not a navigation, or is exempt.\n */\nexport function leavingDestination(\n raw: unknown,\n hosts: readonly string[],\n): string | null {\n const destination = normalizeLeavingDestination(raw)\n if (!destination) return null\n return isLeavingNoticeExempt(new URL(destination).hostname, hosts)\n ? null\n : destination\n}\n\n/** The notice's own address for a destination, signed when a signature is known. */\nexport function leavingNoticeHref(\n destination: string,\n signature?: string | null,\n): string {\n const params = new URLSearchParams()\n params.set(LEAVING_NOTICE_DESTINATION_PARAM, destination)\n if (signature) params.set(LEAVING_NOTICE_SIGNATURE_PARAM, signature)\n return `${LEAVING_NOTICE_PATH}?${params.toString()}`\n}\n\n/**\n * The href a link should carry instead of `raw` — the notice for its\n * destination — or `undefined` to leave it as it is: no config (the site is\n * not in its window), a destination on an exempt host, or not a navigation.\n */\nexport function routeThroughLeavingNotice(\n raw: unknown,\n config: LeavingNoticeConfig | null | undefined,\n): string | undefined {\n if (!config) return undefined\n const destination = leavingDestination(raw, config.hosts)\n if (!destination) return undefined\n return leavingNoticeHref(destination, config.sigs?.[destination])\n}\n\n/**\n * Points the anchor an event landed in at the notice, when it leaves.\n *\n * The catch-all for every link `useLinkTarget` never saw — Markdown and rich\n * text bodies, collection blocks, a plugin's own anchors. The href is\n * rewritten IN PLACE, before the browser acts on the event, so the visitor's\n * own gesture still decides how it opens: a modified click, a middle click\n * and \"Open in new tab\" all take the notice with them. Next's `Link` leaves\n * a non-local href to the browser, so the browser is what reads the change.\n *\n * Returns whether it rewrote anything.\n */\nexport function rerouteLeavingAnchor(\n target: EventTarget | null,\n config: LeavingNoticeConfig | null | undefined,\n): boolean {\n if (!config || !target) return false\n const node = target as Node\n const element =\n node.nodeType === 1 ? (node as Element) : (node.parentElement ?? null)\n const anchor = element?.closest?.('a[href], area[href]')\n if (!anchor) return false\n const rerouted = routeThroughLeavingNotice(anchor.getAttribute('href'), config)\n if (!rerouted) return false\n anchor.setAttribute('href', rerouted)\n return true\n}\n\n/**\n * Every gesture that can follow, open or copy a link, caught on the way\n * down so the href is the notice's before anything reads it.\n */\nconst LEAVING_NOTICE_EVENTS = [\n 'pointerdown',\n 'click',\n 'auxclick',\n 'contextmenu',\n 'focusin',\n] as const\n\n/** Installs {@link rerouteLeavingAnchor} on a document; returns the uninstall. */\nexport function installLeavingNoticeInterceptor(\n doc: Document,\n config: LeavingNoticeConfig,\n): () => void {\n const handler = (event: Event) => {\n rerouteLeavingAnchor(event.target, config)\n }\n for (const type of LEAVING_NOTICE_EVENTS) {\n doc.addEventListener(type, handler, true)\n }\n return () => {\n for (const type of LEAVING_NOTICE_EVENTS) {\n doc.removeEventListener(type, handler, true)\n }\n }\n}\n"],"names":["LEAVING_NOTICE_ROUTE_SEGMENT","LEAVING_NOTICE_PATH","LEAVING_NOTICE_DESTINATION_PARAM","LEAVING_NOTICE_SIGNATURE_PARAM","SITE_RELATIVE_BASE","SITE_RELATIVE_HOSTNAME","normalizeLeavingDestination","raw","trimmed","trim","url","URL","protocol","hostname","href","isLeavingNoticeExempt","hosts","name","String","toLowerCase","replace","endsWith","entry","startsWith","slice","leavingDestination","destination","leavingNoticeHref","signature","params","URLSearchParams","set","toString","routeThroughLeavingNotice","config","undefined","sigs","rerouteLeavingAnchor","target","node","element","nodeType","parentElement","anchor","closest","rerouted","getAttribute","setAttribute","LEAVING_NOTICE_EVENTS","installLeavingNoticeInterceptor","doc","handler","event","type","addEventListener","removeEventListener"],"mappings":"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"}
|
|
@@ -53,6 +53,17 @@ export declare function lockdownFeatureDeclaration(key: unknown): PluginLockdown
|
|
|
53
53
|
export declare function isLockdownFeatureKey(value: unknown): value is LockdownFeatureKey;
|
|
54
54
|
/** Staff-surface label; the key stays the wire/API identity. */
|
|
55
55
|
export declare function lockdownFeatureLabel(key: LockdownFeatureKey): string;
|
|
56
|
+
/**
|
|
57
|
+
* What a customer's mail calls a lever (`media uploads`), never the staff
|
|
58
|
+
* label. A key nothing declares, or a declaration without a name, reads as
|
|
59
|
+
* "a feature" rather than leak a key or a staff label.
|
|
60
|
+
*/
|
|
61
|
+
export declare function lockdownFeatureCustomerName(key: LockdownFeatureKey): string;
|
|
62
|
+
/**
|
|
63
|
+
* Several levers named together in a customer's mail: `AI assist and AI
|
|
64
|
+
* generation`. One pause of several levers is one notice.
|
|
65
|
+
*/
|
|
66
|
+
export declare function lockdownFeaturesCustomerText(keys: readonly LockdownFeatureKey[]): string;
|
|
56
67
|
/**
|
|
57
68
|
* Whether a verified staff claim passes this feature's lock. A key nothing
|
|
58
69
|
* declared answers `false`: an undeclared lever has no bypass argument on
|
|
@@ -89,6 +89,7 @@ import { listPluginLockdownFeatures } from "../plugin-manager/plugin-entitlement
|
|
|
89
89
|
{
|
|
90
90
|
key: 'signups',
|
|
91
91
|
label: 'New signups',
|
|
92
|
+
customerName: 'new signups',
|
|
92
93
|
staffBypass: false,
|
|
93
94
|
notice: {
|
|
94
95
|
title: 'New signups are paused',
|
|
@@ -98,6 +99,7 @@ import { listPluginLockdownFeatures } from "../plugin-manager/plugin-entitlement
|
|
|
98
99
|
{
|
|
99
100
|
key: 'uploads',
|
|
100
101
|
label: 'Media uploads',
|
|
102
|
+
customerName: 'media uploads',
|
|
101
103
|
staffBypass: true,
|
|
102
104
|
notice: {
|
|
103
105
|
title: 'Uploads are paused',
|
|
@@ -107,6 +109,9 @@ import { listPluginLockdownFeatures } from "../plugin-manager/plugin-entitlement
|
|
|
107
109
|
{
|
|
108
110
|
key: 'checkout',
|
|
109
111
|
label: 'Checkout (new subscriptions)',
|
|
112
|
+
// Both doors it closes start a purchase: a plan or add-on, and a paid
|
|
113
|
+
// marketplace install.
|
|
114
|
+
customerName: 'new purchases',
|
|
110
115
|
staffBypass: false,
|
|
111
116
|
notice: {
|
|
112
117
|
title: 'Checkout is temporarily unavailable',
|
|
@@ -124,6 +129,7 @@ import { listPluginLockdownFeatures } from "../plugin-manager/plugin-entitlement
|
|
|
124
129
|
{
|
|
125
130
|
key: 'marketplace-installs',
|
|
126
131
|
label: 'Marketplace installs',
|
|
132
|
+
customerName: 'marketplace installs',
|
|
127
133
|
staffBypass: true,
|
|
128
134
|
notice: {
|
|
129
135
|
title: 'Marketplace installs are paused',
|
|
@@ -172,6 +178,26 @@ export function isLockdownFeatureKey(value) {
|
|
|
172
178
|
var _lockdownFeatureDeclaration;
|
|
173
179
|
return (_ref = (_lockdownFeatureDeclaration = lockdownFeatureDeclaration(key)) == null ? void 0 : _lockdownFeatureDeclaration.label) != null ? _ref : key;
|
|
174
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* What a customer's mail calls a lever (`media uploads`), never the staff
|
|
183
|
+
* label. A key nothing declares, or a declaration without a name, reads as
|
|
184
|
+
* "a feature" rather than leak a key or a staff label.
|
|
185
|
+
*/ export function lockdownFeatureCustomerName(key) {
|
|
186
|
+
var _lockdownFeatureDeclaration_customerName, _lockdownFeatureDeclaration;
|
|
187
|
+
return ((_lockdownFeatureDeclaration = lockdownFeatureDeclaration(key)) == null ? void 0 : (_lockdownFeatureDeclaration_customerName = _lockdownFeatureDeclaration.customerName) == null ? void 0 : _lockdownFeatureDeclaration_customerName.trim()) || 'a feature';
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Several levers named together in a customer's mail: `AI assist and AI
|
|
191
|
+
* generation`. One pause of several levers is one notice.
|
|
192
|
+
*/ export function lockdownFeaturesCustomerText(keys) {
|
|
193
|
+
const names = [
|
|
194
|
+
...new Set(keys.map(lockdownFeatureCustomerName))
|
|
195
|
+
];
|
|
196
|
+
return new Intl.ListFormat('en-US', {
|
|
197
|
+
style: 'long',
|
|
198
|
+
type: 'conjunction'
|
|
199
|
+
}).format(names);
|
|
200
|
+
}
|
|
175
201
|
/**
|
|
176
202
|
* Whether a verified staff claim passes this feature's lock. A key nothing
|
|
177
203
|
* declared answers `false`: an undeclared lever has no bypass argument on
|
|
@@ -634,6 +660,10 @@ export const userLockdownDocId = (uid)=>`user--${uid}`;
|
|
|
634
660
|
if (state.scope === 'domain') {
|
|
635
661
|
return domainLockdownNotice(state, custom);
|
|
636
662
|
}
|
|
663
|
+
// A SITE lock closes one site, not the account that owns it: its default
|
|
664
|
+
// copy says "this site", or the owners' notice tells them their account was
|
|
665
|
+
// closed when their other sites are serving (AGL-3432).
|
|
666
|
+
const subject = state.scope === 'host' ? 'site' : 'account';
|
|
637
667
|
switch(state.reason){
|
|
638
668
|
case 'maintenance':
|
|
639
669
|
{
|
|
@@ -647,7 +677,7 @@ export const userLockdownDocId = (uid)=>`user--${uid}`;
|
|
|
647
677
|
var _lockdownSupportEmail;
|
|
648
678
|
return {
|
|
649
679
|
title: 'Account on hold',
|
|
650
|
-
body: custom != null ? custom :
|
|
680
|
+
body: custom != null ? custom : `This ${subject} is on hold over an unresolved billing issue. ` + 'Updating the payment method in workspace billing settings ' + 'restores access.',
|
|
651
681
|
contact: (_lockdownSupportEmail = lockdownSupportEmail()) != null ? _lockdownSupportEmail : undefined
|
|
652
682
|
};
|
|
653
683
|
case 'security':
|
|
@@ -663,7 +693,7 @@ export const userLockdownDocId = (uid)=>`user--${uid}`;
|
|
|
663
693
|
var _lockdownSupportEmail2;
|
|
664
694
|
return _extends({
|
|
665
695
|
title: 'Unavailable',
|
|
666
|
-
body: custom != null ? custom :
|
|
696
|
+
body: custom != null ? custom : `This ${subject} has been closed for a violation of our Terms of ` + 'Service.',
|
|
667
697
|
contact: (_lockdownSupportEmail2 = lockdownSupportEmail()) != null ? _lockdownSupportEmail2 : undefined
|
|
668
698
|
}, state.scope !== 'platform' ? {
|
|
669
699
|
appeal: true
|
|
@@ -673,7 +703,7 @@ export const userLockdownDocId = (uid)=>`user--${uid}`;
|
|
|
673
703
|
var _lockdownSupportEmail3;
|
|
674
704
|
return {
|
|
675
705
|
title: 'Temporarily unavailable',
|
|
676
|
-
body: custom != null ? custom :
|
|
706
|
+
body: custom != null ? custom : `Access to this ${subject} is currently disabled.`,
|
|
677
707
|
contact: (_lockdownSupportEmail3 = lockdownSupportEmail()) != null ? _lockdownSupportEmail3 : undefined
|
|
678
708
|
};
|
|
679
709
|
}
|