Skip to content

Commit f2501c6

Browse files
committed
merge main and rebuild
2 parents 6c0b875 + 591c9c9 commit f2501c6

43 files changed

Lines changed: 693 additions & 265 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/copilot-instructions.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
- Tip: when debugging, `?debug=true` sets body `data-engrid-debug`, unlocking debug panels and conditional UI.
2727

2828
## When in Doubt
29-
- Search in `packages/scripts/src` before creating new utilities; most EN behaviours already exist (currency, remember-me, plaid, vgs).
29+
- Search in `packages/scripts/src` before creating new utilities; most EN behaviours already exist (currency, remember-me, stripe financial connections, vgs).
3030
- Do not hand-edit files under any `dist/` directory—run the relevant build instead.
3131
- Follow repo Prettier config (`.prettierrc.js`); formatting is enforced even though there are no automated tests.
32-
- Document new options by updating the interfaces in `packages/scripts/src/interfaces` and exposing them through `OptionsDefaults`.
32+
- Document new options by updating the interfaces in `packages/scripts/src/interfaces` and exposing them through `OptionsDefaults`.

README.md

Lines changed: 183 additions & 129 deletions
Large diffs are not rendered by default.

lerna.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"packages": [
33
"packages/*"
44
],
5-
"version": "0.23.7"
5+
"version": "0.24.4"
66
}

packages/scripts/dist/app.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DonationAmount, DonationFrequency, EnForm, ProcessingFees, Country, } from "./events";
2-
import { AmountLabel, Loader, ProgressBar, UpsellLightbox, ENGrid, OptionsDefaults, setRecurrFreq, PageBackground, MediaAttribution, ApplePay, A11y, CapitalizeFields, Ecard, ClickToExpand, Advocacy, DataAttributes, LiveVariables, iFrame, InputPlaceholders, InputHasValueAndFocus, ShowHideRadioCheckboxes, AutoCountrySelect, SkipToMainContentLink, SrcDefer, NeverBounce, AutoYear, Autocomplete, RememberMe, TranslateFields, ShowIfAmount, EngridLogger, OtherAmount, MinMaxAmount, Ticker, DataReplace, DataHide, AddNameToMessage, ExpandRegionName, AppVersion, UrlToForm, RequiredIfVisible, TidyContact, DataLayer, LiveCurrency, Autosubmit, EventTickets, SwapAmounts, DebugPanel, DebugHiddenFields, FreshAddress, BrandingHtml, CountryDisable, PremiumGift, DigitalWallets, MobileCTA, LiveFrequency, UniversalOptIn, Plaid, GiveBySelect, UrlParamsToBodyAttrs, ExitIntentLightbox, SupporterHub, FastFormFill, SetAttr, ShowIfPresent, ENValidators, CustomCurrency, VGS, PostalCodeValidator, CountryRedirect, WelcomeBack, EcardToTarget, UsOnlyForm, ThankYouPageConditionalContent, EmbeddedEcard, CheckboxLabel, UpsellCheckbox, PostDonationEmbed, FrequencyUpsell, CustomPremium, StickyNSG, StickyPrepopulation, PreferredPaymentMethod, } from ".";
2+
import { AmountLabel, Loader, ProgressBar, UpsellLightbox, ENGrid, OptionsDefaults, setRecurrFreq, PageBackground, MediaAttribution, ApplePay, A11y, CapitalizeFields, Ecard, ClickToExpand, Advocacy, DataAttributes, LiveVariables, iFrame, InputPlaceholders, InputHasValueAndFocus, ShowHideRadioCheckboxes, AutoCountrySelect, SkipToMainContentLink, SrcDefer, NeverBounce, AutoYear, Autocomplete, RememberMe, TranslateFields, ShowIfAmount, EngridLogger, OtherAmount, MinMaxAmount, Ticker, DataReplace, DataHide, AddNameToMessage, ExpandRegionName, AppVersion, UrlToForm, RequiredIfVisible, TidyContact, DataLayer, LiveCurrency, Autosubmit, EventTickets, SwapAmounts, DebugPanel, DebugHiddenFields, FreshAddress, BrandingHtml, CountryDisable, PremiumGift, DigitalWallets, MobileCTA, LiveFrequency, UniversalOptIn, StripeFinancialConnections, GiveBySelect, UrlParamsToBodyAttrs, ExitIntentLightbox, SupporterHub, FastFormFill, SetAttr, ShowIfPresent, ENValidators, CustomCurrency, VGS, PostalCodeValidator, CountryRedirect, WelcomeBack, EcardToTarget, UsOnlyForm, ThankYouPageConditionalContent, EmbeddedEcard, CheckboxLabel, UpsellCheckbox, PostDonationEmbed, FrequencyUpsell, CustomPremium, StickyNSG, StickyPrepopulation, PreferredPaymentMethod, } from ".";
33
export class App extends ENGrid {
44
constructor(options) {
55
super();
@@ -264,9 +264,7 @@ export class App extends ENGrid {
264264
new LiveFrequency();
265265
// Universal Opt In
266266
new UniversalOptIn();
267-
// Plaid
268-
if (this.options.Plaid)
269-
new Plaid();
267+
new StripeFinancialConnections();
270268
//Exit Intent Lightbox
271269
new ExitIntentLightbox();
272270
new UrlParamsToBodyAttrs();

packages/scripts/dist/data-layer.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ export declare class DataLayer {
2020
private handleFieldValueChange;
2121
private geRetainedFieldsValue;
2222
private hash;
23-
private shaHash;
2423
private getFieldLabel;
2524
addEndOfGiftProcessEvent(eventName: string, eventProperties?: object): void;
2625
addEndOfGiftProcessVariable(variableName: string, variableValue?: any): void;

packages/scripts/dist/data-layer.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ export class DataLayer {
188188
if (el.value === "" || this.excludedFields.includes(el.name))
189189
return;
190190
const value = this.hashedFields.includes(el.name)
191-
? this.hash(el.value)
191+
? yield this.hash(el.value)
192192
: el.value;
193193
if (["checkbox", "radio"].includes(el.type)) {
194194
if (el.checked) {
@@ -216,7 +216,7 @@ export class DataLayer {
216216
}
217217
if (el.name === this.retainedEmailField) {
218218
const retainedEmailValue = this.geRetainedFieldsValue("email");
219-
const sha256value = yield this.shaHash(retainedEmailValue);
219+
const sha256value = yield this.hash(retainedEmailValue);
220220
localStorage.setItem(`EN_HASH_EMAIL`, sha256value);
221221
this.dataLayer.push({
222222
event: "EN_HASH_VALUE_UPDATED",
@@ -228,7 +228,7 @@ export class DataLayer {
228228
}
229229
else if (this.retainedAddressFields.includes(el.name)) {
230230
const retainedAddressValue = this.geRetainedFieldsValue("address");
231-
const sha256value = yield this.shaHash(retainedAddressValue);
231+
const sha256value = yield this.hash(retainedAddressValue);
232232
localStorage.setItem(`EN_HASH_ADDRESS`, sha256value);
233233
this.dataLayer.push({
234234
event: "EN_HASH_VALUE_UPDATED",
@@ -239,7 +239,7 @@ export class DataLayer {
239239
}
240240
else if (this.retainedPhoneFields.includes(el.name)) {
241241
const retainedPhoneValue = this.geRetainedFieldsValue("phone");
242-
const sha256value = yield this.shaHash(retainedPhoneValue);
242+
const sha256value = yield this.hash(retainedPhoneValue);
243243
localStorage.setItem(`EN_HASH_PHONE`, sha256value);
244244
this.dataLayer.push({
245245
event: "EN_HASH_VALUE_UPDATED",
@@ -281,10 +281,6 @@ export class DataLayer {
281281
}
282282
}
283283
hash(value) {
284-
return btoa(value);
285-
}
286-
// TODO: Replace the hash function with this secure SHA-256 implementation later
287-
shaHash(value) {
288284
return __awaiter(this, void 0, void 0, function* () {
289285
const data = this.encoder.encode(value);
290286
const hashBuffer = yield crypto.subtle.digest("SHA-256", data);

packages/scripts/dist/freshaddress.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ export declare class FreshAddress {
1515
private callAPI;
1616
private validateResponse;
1717
private validate;
18+
private callProxy;
19+
private validateProxyResponse;
1820
}

packages/scripts/dist/freshaddress.js

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ENGrid, EngridLogger } from ".";
99
import { EnForm } from "./events";
1010
export class FreshAddress {
1111
constructor() {
12+
var _a;
1213
this.form = EnForm.getInstance();
1314
this.emailField = null;
1415
this.emailWrapper = document.querySelector(".en__field--emailAddress");
@@ -18,8 +19,10 @@ export class FreshAddress {
1819
this.logger = new EngridLogger("FreshAddress", "#039bc4", "#dfdfdf", "📧");
1920
this.shouldRun = true;
2021
this.options = ENGrid.getOption("FreshAddress");
21-
if (this.options === false || !window.FreshAddress)
22+
if (this.options === false ||
23+
(!window.FreshAddress && !((_a = this.options) === null || _a === void 0 ? void 0 : _a.proxyUrl))) {
2224
return;
25+
}
2326
this.emailField = document.getElementById("en__field_supporter_emailAddress");
2427
if (this.emailField) {
2528
this.createFields();
@@ -89,7 +92,12 @@ export class FreshAddress {
8992
return;
9093
}
9194
this.logger.log("Validating " + ((_b = this.emailField) === null || _b === void 0 ? void 0 : _b.value));
92-
this.callAPI();
95+
if (this.options && this.options.proxyUrl) {
96+
this.callProxy();
97+
}
98+
else {
99+
this.callAPI();
100+
}
93101
});
94102
// Add event listener to submit
95103
this.form.onValidate.subscribe(this.validate.bind(this));
@@ -192,7 +200,7 @@ export class FreshAddress {
192200
else if (this.faStatus.value === "Invalid") {
193201
this.form.validate = false;
194202
window.setTimeout(() => {
195-
ENGrid.setError(this.emailWrapper, this.faMessage.value);
203+
ENGrid.setError(this.emailWrapper, "This email address is not valid.");
196204
}, 100);
197205
(_a = this.emailField) === null || _a === void 0 ? void 0 : _a.focus();
198206
ENGrid.enableSubmit();
@@ -201,4 +209,87 @@ export class FreshAddress {
201209
this.form.validate = true;
202210
return true;
203211
}
212+
callProxy() {
213+
var _a, _b;
214+
if (!this.options || !this.shouldRun)
215+
return;
216+
window.FreshAddressStatus = "validating";
217+
ENGrid.disableSubmit("Validating Email Address...");
218+
// Before calling the API, do a basic check to see if the email is in a valid format.
219+
// This is to prevent unnecessary API calls.
220+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
221+
if (!emailRegex.test(this.emailField.value)) {
222+
this.logger.log("Invalid Email Format from Basic Check");
223+
this.writeToFields("Invalid", "Invalid Email Format");
224+
ENGrid.setError(this.emailWrapper, "This email address is not valid.");
225+
(_a = this.emailField) === null || _a === void 0 ? void 0 : _a.focus();
226+
ENGrid.enableSubmit();
227+
return;
228+
}
229+
fetch(this.options.proxyUrl, {
230+
method: "POST",
231+
headers: {
232+
"Content-Type": "application/json",
233+
},
234+
body: JSON.stringify({ email: (_b = this.emailField) === null || _b === void 0 ? void 0 : _b.value }),
235+
signal: AbortSignal.timeout(5000),
236+
})
237+
.then((response) => {
238+
if (!response.ok) {
239+
throw new Error(`HTTP error ${response.status}`);
240+
}
241+
return response.json();
242+
})
243+
.then((data) => {
244+
this.logger.log("Proxy API Response", data);
245+
this.validateProxyResponse(data);
246+
})
247+
.catch((error) => {
248+
// 422 (Unprocessable Content) - This means the email is in an invalid format.
249+
if (error.message.includes("422")) {
250+
this.logger.log("Invalid Email Format");
251+
this.writeToFields("Invalid", "Invalid Email Format");
252+
ENGrid.setError(this.emailWrapper, "This email address is not valid.");
253+
return;
254+
}
255+
if (error.name === "AbortError") {
256+
this.logger.log("Proxy API request timed out");
257+
this.writeToFields("Request Timeout", "The request took too long.");
258+
return;
259+
}
260+
this.logger.log("Proxy API Error", error);
261+
this.writeToFields("Service Error", error.toString());
262+
})
263+
.finally(() => {
264+
window.FreshAddressStatus = "idle";
265+
ENGrid.enableSubmit();
266+
});
267+
}
268+
/*
269+
* Validate a request proxied to AtData's Safe To Send API.
270+
* https://docs.atdata.com/reference/safe-to-send
271+
* https://docs.atdata.com/reference/email-status
272+
* https://docs.atdata.com/reference/status-codes-safe-to-send
273+
*/
274+
validateProxyResponse(data) {
275+
var _a;
276+
// If response is not in expected format, log error and let through.
277+
if (!data.safe_to_send) {
278+
this.logger.log("Invalid Proxy Response");
279+
this.writeToFields("Service Error", "Invalid Proxy Response");
280+
return true;
281+
}
282+
const res = data.safe_to_send;
283+
ENGrid.removeError(this.emailWrapper);
284+
this.writeToFields(res.status, res.status_code);
285+
if (["invalid", "trap"].includes(res.status)) {
286+
this.writeToFields("Invalid", res.status_code); // Must be "Invalid" to trigger validation error on submit
287+
ENGrid.setError(this.emailWrapper, "This email address is not valid.");
288+
(_a = this.emailField) === null || _a === void 0 ? void 0 : _a.focus();
289+
if (res.email_corrections && res.email_corrections.length > 0) {
290+
this.emailField.value = res.email_corrections[0];
291+
ENGrid.setError(this.emailWrapper, `This email address is not valid. Did you mean ${res.email_corrections[0]}?`);
292+
}
293+
}
294+
}
204295
}

packages/scripts/dist/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export * from "./digital-wallets";
6363
export * from "./mobile-cta";
6464
export * from "./live-frequency";
6565
export * from "./universal-opt-in";
66-
export * from "./plaid";
66+
export * from "./stripe-financial-connections";
6767
export * from "./give-by-select";
6868
export * from "./url-params-to-body-attrs";
6969
export * from "./exit-intent-lightbox";

packages/scripts/dist/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export * from "./digital-wallets";
6363
export * from "./mobile-cta";
6464
export * from "./live-frequency";
6565
export * from "./universal-opt-in";
66-
export * from "./plaid";
66+
export * from "./stripe-financial-connections";
6767
export * from "./give-by-select";
6868
export * from "./url-params-to-body-attrs";
6969
export * from "./exit-intent-lightbox";

0 commit comments

Comments
 (0)