@nodii/onboarding 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +46 -0
  2. package/dist/{chunk-KQTS47FC.js → chunk-A3DE2IAH.js} +30 -5
  3. package/dist/chunk-A3DE2IAH.js.map +1 -0
  4. package/dist/{chunk-U6AXKGYQ.js → chunk-DWFZA5T6.js} +10 -8
  5. package/dist/chunk-DWFZA5T6.js.map +1 -0
  6. package/dist/{chunk-F75ZJERM.js → chunk-KFHVZGX6.js} +34 -14
  7. package/dist/chunk-KFHVZGX6.js.map +1 -0
  8. package/dist/{chunk-U56GQITG.js → chunk-NDSBIB3N.js} +25 -5
  9. package/dist/chunk-NDSBIB3N.js.map +1 -0
  10. package/dist/chunk-OLWR5COK.js +10 -0
  11. package/dist/chunk-OLWR5COK.js.map +1 -0
  12. package/dist/{chunk-IPAKPLIF.js → chunk-OVULPWNZ.js} +341 -80
  13. package/dist/chunk-OVULPWNZ.js.map +1 -0
  14. package/dist/{chunk-LVJM4CTG.js → chunk-T7EWURTF.js} +34 -19
  15. package/dist/chunk-T7EWURTF.js.map +1 -0
  16. package/dist/{chunk-BMQINH6O.js → chunk-UHOFGWI7.js} +5 -4
  17. package/dist/chunk-UHOFGWI7.js.map +1 -0
  18. package/dist/{chunk-SIMLMQPT.js → chunk-WKN4U7QF.js} +3 -5
  19. package/dist/chunk-WKN4U7QF.js.map +1 -0
  20. package/dist/{chunk-AZE3NPSY.js → chunk-ZR6X3GSM.js} +3 -3
  21. package/dist/{chunk-AZE3NPSY.js.map → chunk-ZR6X3GSM.js.map} +1 -1
  22. package/dist/{client-CqE2dF9A.d.ts → client-NvqPpfQP.d.ts} +1 -1
  23. package/dist/consent/index.d.ts +3 -3
  24. package/dist/consent/index.js +2 -2
  25. package/dist/{controller-BQb62D0N.d.ts → controller-BLH3wnxl.d.ts} +1 -1
  26. package/dist/dynamic/index.d.ts +13 -5
  27. package/dist/dynamic/index.js +4 -3
  28. package/dist/form/index.d.ts +12 -4
  29. package/dist/form/index.js +2 -2
  30. package/dist/{index-Dqfa890U.d.ts → index-BMSbQGWP.d.ts} +23 -3
  31. package/dist/index.d.ts +8 -8
  32. package/dist/index.js +9 -8
  33. package/dist/{model-mnjAEtKe.d.ts → model-BI_I-D_V.d.ts} +16 -2
  34. package/dist/{model-ESLSEbi0.d.ts → model-CEwvHlWA.d.ts} +14 -1
  35. package/dist/render-EWQXF73I.js +5 -0
  36. package/dist/{render-DC7P2BGQ.js.map → render-EWQXF73I.js.map} +1 -1
  37. package/dist/{schema-BrXb9gnh.d.ts → schema-BrtjpXfL.d.ts} +0 -10
  38. package/dist/{signup-client-BL5DfarJ.d.ts → signup-client-CS95nkVO.d.ts} +4 -4
  39. package/dist/spec/index.d.ts +3 -3
  40. package/dist/spec/index.js +2 -2
  41. package/dist/submit/index.d.ts +5 -5
  42. package/dist/submit/index.js +3 -2
  43. package/dist/viewer/index.d.ts +3 -3
  44. package/dist/viewer/index.js +1 -1
  45. package/dist/widget/index.d.ts +61 -22
  46. package/dist/widget/index.js +9 -8
  47. package/package.json +1 -1
  48. package/dist/chunk-BMQINH6O.js.map +0 -1
  49. package/dist/chunk-F75ZJERM.js.map +0 -1
  50. package/dist/chunk-IPAKPLIF.js.map +0 -1
  51. package/dist/chunk-KQTS47FC.js.map +0 -1
  52. package/dist/chunk-LVJM4CTG.js.map +0 -1
  53. package/dist/chunk-SIMLMQPT.js.map +0 -1
  54. package/dist/chunk-U56GQITG.js.map +0 -1
  55. package/dist/chunk-U6AXKGYQ.js.map +0 -1
  56. package/dist/render-DC7P2BGQ.js +0 -5
@@ -1,8 +1,10 @@
1
- import { createContactFormState } from './chunk-U6AXKGYQ.js';
2
- import { defaultSpecCache, fetchOnboardingSpec, diffSpecFields } from './chunk-F75ZJERM.js';
3
- import { createSignupStepper, optionValue } from './chunk-KQTS47FC.js';
4
- import { computePayloadFingerprint, generateIdempotencyKey, submitLead, computeSignupFingerprint, submitSignup } from './chunk-BMQINH6O.js';
5
- import { OnboardingBotChallengeError, OnboardingRateLimitError, OnboardingSpecStaleError, OnboardingConsentStaleError, OnboardingConsentIncompleteError } from './chunk-XJTJPBJB.js';
1
+ import { createContactFormState } from './chunk-DWFZA5T6.js';
2
+ import { fetchOnboardingSpec, OnboardingSpecCache, defaultSpecCache, diffSpecFields } from './chunk-KFHVZGX6.js';
3
+ import { createSignupStepper, optionValue } from './chunk-A3DE2IAH.js';
4
+ import { isNavigableUrl } from './chunk-NDSBIB3N.js';
5
+ import { computePayloadFingerprint, generateIdempotencyKey, submitLead, computeSignupFingerprint, submitSignup } from './chunk-UHOFGWI7.js';
6
+ import { contactFormSchema } from './chunk-WKN4U7QF.js';
7
+ import { OnboardingBotChallengeError, OnboardingRateLimitError, OnboardingSpecStaleError, OnboardingConsentStaleError, OnboardingConsentIncompleteError, OnboardingSubdomainTakenError, OnboardingValidationError } from './chunk-XJTJPBJB.js';
6
8
  import { loadRecaptcha, executeRecaptcha } from './chunk-7NS7ENJV.js';
7
9
 
8
10
  // src/widget/dom-utils.ts
@@ -219,6 +221,35 @@ var DEFAULT_WIDGET_CSS = `
219
221
  margin-bottom: var(--_spacing);
220
222
  }
221
223
 
224
+ fieldset.nodii-onboarding-field {
225
+ border: none;
226
+ padding: 0;
227
+ margin-inline: 0;
228
+ }
229
+
230
+ .nodii-onboarding-choices {
231
+ display: flex;
232
+ flex-wrap: wrap;
233
+ gap: calc(var(--_spacing) * 0.5);
234
+ }
235
+
236
+ .nodii-onboarding-choice {
237
+ display: inline-flex;
238
+ align-items: center;
239
+ gap: calc(var(--_spacing) * 0.375);
240
+ padding: calc(var(--_spacing) * 0.375) calc(var(--_spacing) * 0.75);
241
+ border: 1px solid var(--_border);
242
+ border-radius: 999px;
243
+ background: var(--_input-bg);
244
+ font-size: 0.875rem;
245
+ color: var(--_label);
246
+ cursor: pointer;
247
+ }
248
+
249
+ fieldset.nodii-onboarding-field.has-error .nodii-onboarding-choice {
250
+ border-color: var(--_error);
251
+ }
252
+
222
253
  .nodii-onboarding-consent-row {
223
254
  display: flex;
224
255
  align-items: flex-start;
@@ -456,30 +487,32 @@ function el(tag, attrs = {}, children = []) {
456
487
  }
457
488
  return element;
458
489
  }
490
+ function errorSlot(id) {
491
+ return el("div", {
492
+ class: "nodii-onboarding-field-error",
493
+ id: `${id}-error`,
494
+ role: "alert",
495
+ "aria-live": "polite"
496
+ });
497
+ }
459
498
  function createFieldEl(id, label, input, required) {
460
499
  const labelEl = el(
461
500
  "label",
462
501
  { for: id, class: `nodii-onboarding-label${required ? " required" : ""}` },
463
502
  [label]
464
503
  );
465
- const errorEl = el("div", {
466
- class: "nodii-onboarding-field-error",
467
- id: `${id}-error`,
468
- role: "alert",
469
- "aria-live": "polite"
470
- });
471
- const field = el("div", { class: "nodii-onboarding-field" }, [
504
+ return el("div", { class: "nodii-onboarding-field" }, [
472
505
  labelEl,
473
506
  input,
474
- errorEl
507
+ errorSlot(id)
475
508
  ]);
476
- return field;
477
509
  }
478
510
  function setFieldError(container, fieldId, message) {
479
511
  const errorEl = container.querySelector(idSelector(`${fieldId}-error`));
480
512
  if (errorEl) errorEl.textContent = message;
481
513
  const inputEl = container.querySelector(idSelector(fieldId));
482
514
  if (inputEl) inputEl.classList.add("has-error");
515
+ return errorEl !== null;
483
516
  }
484
517
  function clearFieldErrors(container) {
485
518
  for (const el4 of container.querySelectorAll(
@@ -493,24 +526,91 @@ function clearFieldErrors(container) {
493
526
  }
494
527
  function applyErrors(container, errors) {
495
528
  clearFieldErrors(container);
529
+ let unplaced = 0;
496
530
  for (const err of errors) {
497
- setFieldError(container, `nodii-${err.path}`, err.message);
531
+ const field = err.path.split(".")[0] ?? err.path;
532
+ if (!setFieldError(container, `nodii-${field}`, err.message)) unplaced++;
498
533
  }
534
+ return unplaced;
535
+ }
536
+ function assertHostValues(options) {
537
+ if (options.privacyUrl !== void 0 && !isNavigableUrl(options.privacyUrl)) {
538
+ throw new OnboardingValidationError(
539
+ "mountContactForm: invalid privacyUrl: must be an http(s) URL, absolute or relative"
540
+ );
541
+ }
542
+ const shape = contactFormSchema.shape;
543
+ for (const [name, schema, value] of [
544
+ ["intakeKind", shape.intakeKind, options.intakeKind],
545
+ ["attribution", shape.attribution, options.attribution]
546
+ ]) {
547
+ if (value === void 0) continue;
548
+ const parsed = schema.safeParse(value);
549
+ if (!parsed.success) {
550
+ const issue = parsed.error.issues[0];
551
+ throw new OnboardingValidationError(
552
+ `mountContactForm: invalid ${[name, ...issue?.path ?? []].join(".")}: ${issue?.message ?? "invalid"}`
553
+ );
554
+ }
555
+ }
556
+ }
557
+ var DEFAULT_SPEC_REFRESH_MS = 5 * 60 * 1e3;
558
+ function privacyDocumentUrl(spec) {
559
+ const url = spec?.consentPack?.documents.find(
560
+ (d) => d.docType === "privacy"
561
+ )?.downloadUrl;
562
+ return url && isNavigableUrl(url) ? url : void 0;
563
+ }
564
+ function choicesOf(spec, key) {
565
+ const field = spec?.forms.contact.fields.find((f) => f.key === key);
566
+ return (field?.options ?? []).map((option) => {
567
+ const value = optionValue(option);
568
+ const label = option !== null && typeof option === "object" && typeof option.label === "string" ? option.label : value;
569
+ return { value, label };
570
+ });
571
+ }
572
+ function isRequired(spec, key) {
573
+ return spec?.forms.contact.fields.find((f) => f.key === key)?.required === true;
574
+ }
575
+ function labelOf(spec, key, fallback) {
576
+ const label = spec?.forms.contact.fields.find((f) => f.key === key)?.label;
577
+ return label?.trim() ? label : fallback;
499
578
  }
500
579
  function mountContactForm(host, options) {
580
+ assertHostValues(options);
501
581
  injectWidgetStyles();
502
- const formControl = createContactFormState();
503
- if (options.siteKey) {
504
- loadRecaptcha(options.siteKey).catch(() => {
582
+ const spec = options.spec;
583
+ const interests = choicesOf(spec, "interestModules");
584
+ const sizes = choicesOf(spec, "sizeBand");
585
+ const interestRequired = interests.length > 0 && isRequired(spec, "interestModules");
586
+ const privacyUrl = options.privacyUrl ?? privacyDocumentUrl(spec);
587
+ const siteKey = options.siteKey ?? spec?.botChallenge?.siteKey;
588
+ const formControl = createContactFormState({
589
+ requireInterest: interestRequired
590
+ });
591
+ formControl.setValue("intakeKind", options.intakeKind ?? "enquiry");
592
+ if (options.attribution) {
593
+ formControl.setValue("attribution", options.attribution);
594
+ }
595
+ if (siteKey) {
596
+ loadRecaptcha(siteKey).catch(() => {
505
597
  });
506
598
  }
507
599
  const wrapper = el("div", { class: "nodii-onboarding-widget" });
508
- const nameInput = el("input", {
600
+ const companyInput = el("input", {
601
+ type: "text",
602
+ id: "nodii-companyName",
603
+ name: "companyName",
604
+ class: "nodii-onboarding-input",
605
+ autocomplete: "organization",
606
+ maxlength: "200",
607
+ required: "required"
608
+ });
609
+ const contactInput = el("input", {
509
610
  type: "text",
510
- id: "nodii-name",
511
- name: "name",
611
+ id: "nodii-contactName",
612
+ name: "contactName",
512
613
  class: "nodii-onboarding-input",
513
- placeholder: "Your name",
514
614
  autocomplete: "name",
515
615
  maxlength: "200",
516
616
  required: "required"
@@ -529,8 +629,8 @@ function mountContactForm(host, options) {
529
629
  id: "nodii-phone",
530
630
  name: "phone",
531
631
  class: "nodii-onboarding-input",
532
- placeholder: "+1 555 000 0000",
533
- autocomplete: "tel"
632
+ autocomplete: "tel",
633
+ maxlength: "40"
534
634
  });
535
635
  const messageInput = el("textarea", {
536
636
  id: "nodii-message",
@@ -540,6 +640,34 @@ function mountContactForm(host, options) {
540
640
  maxlength: "4000",
541
641
  rows: "4"
542
642
  });
643
+ const interestBoxes = interests.map(
644
+ (choice) => el("input", {
645
+ type: "checkbox",
646
+ name: "interestModules",
647
+ value: choice.value
648
+ })
649
+ );
650
+ const sizeSelect = el(
651
+ "select",
652
+ { id: "nodii-sizeBand", name: "sizeBand", class: "nodii-onboarding-input" },
653
+ [
654
+ el("option", { value: "" }, ["Choose\u2026"]),
655
+ ...sizes.map(
656
+ (choice) => el("option", { value: choice.value }, [choice.label])
657
+ )
658
+ ]
659
+ );
660
+ const privacyBox = el("input", {
661
+ type: "checkbox",
662
+ id: "nodii-privacyNoticeAcknowledged",
663
+ name: "privacyNoticeAcknowledged",
664
+ required: "required"
665
+ });
666
+ const marketingBox = el("input", {
667
+ type: "checkbox",
668
+ id: "nodii-marketingOptIn",
669
+ name: "marketingOptIn"
670
+ });
543
671
  const serverErrorEl = el("div", {
544
672
  class: "nodii-onboarding-server-error",
545
673
  role: "alert",
@@ -563,60 +691,165 @@ function mountContactForm(host, options) {
563
691
  },
564
692
  [options.submitLabel ?? "Get in touch"]
565
693
  );
566
- const recaptchaNotice = el(
567
- "p",
568
- { class: "nodii-onboarding-recaptcha-notice" },
569
- [
570
- "Protected by reCAPTCHA. ",
571
- Object.assign(
572
- el("a", {
573
- href: "https://policies.google.com/privacy",
574
- target: "_blank",
575
- rel: "noopener noreferrer"
576
- }),
577
- { textContent: "Privacy" }
578
- ),
579
- " & ",
580
- Object.assign(
581
- el("a", {
582
- href: "https://policies.google.com/terms",
583
- target: "_blank",
584
- rel: "noopener noreferrer"
585
- }),
586
- { textContent: "Terms" }
587
- )
588
- ]
589
- );
590
694
  const form = el("form", {
591
695
  novalidate: "true",
592
696
  "aria-label": "Contact us"
593
697
  });
594
- form.appendChild(createFieldEl("nodii-name", "Name", nameInput, true));
595
- form.appendChild(createFieldEl("nodii-email", "Email", emailInput, true));
596
698
  form.appendChild(
597
- createFieldEl("nodii-phone", "Phone (optional)", phoneInput, false)
699
+ createFieldEl(
700
+ "nodii-companyName",
701
+ labelOf(spec, "companyName", "Company name"),
702
+ companyInput,
703
+ true
704
+ )
705
+ );
706
+ form.appendChild(
707
+ createFieldEl(
708
+ "nodii-contactName",
709
+ labelOf(spec, "contactName", "Your name"),
710
+ contactInput,
711
+ true
712
+ )
713
+ );
714
+ form.appendChild(
715
+ createFieldEl(
716
+ "nodii-email",
717
+ labelOf(spec, "email", "Email"),
718
+ emailInput,
719
+ true
720
+ )
721
+ );
722
+ form.appendChild(
723
+ createFieldEl(
724
+ "nodii-phone",
725
+ `${labelOf(spec, "phone", "Phone")} (optional)`,
726
+ phoneInput,
727
+ false
728
+ )
729
+ );
730
+ if (interests.length > 0) {
731
+ form.appendChild(
732
+ el(
733
+ "fieldset",
734
+ { id: "nodii-interestModules", class: "nodii-onboarding-field" },
735
+ [
736
+ el(
737
+ "legend",
738
+ {
739
+ class: `nodii-onboarding-label${interestRequired ? " required" : ""}`
740
+ },
741
+ [labelOf(spec, "interestModules", "What are you interested in?")]
742
+ ),
743
+ el(
744
+ "div",
745
+ { class: "nodii-onboarding-choices" },
746
+ interests.map(
747
+ (choice, i) => el("label", { class: "nodii-onboarding-choice" }, [
748
+ interestBoxes[i],
749
+ choice.label
750
+ ])
751
+ )
752
+ ),
753
+ errorSlot("nodii-interestModules")
754
+ ]
755
+ )
756
+ );
757
+ }
758
+ if (sizes.length > 0) {
759
+ form.appendChild(
760
+ createFieldEl(
761
+ "nodii-sizeBand",
762
+ `${labelOf(spec, "sizeBand", "Team size")} (optional)`,
763
+ sizeSelect,
764
+ false
765
+ )
766
+ );
767
+ }
768
+ form.appendChild(
769
+ createFieldEl(
770
+ "nodii-message",
771
+ `${labelOf(spec, "message", "Message")} (optional)`,
772
+ messageInput,
773
+ false
774
+ )
598
775
  );
776
+ const privacyLabel = ["I have read the "];
777
+ let privacyLink = null;
778
+ if (privacyUrl) {
779
+ privacyLink = Object.assign(
780
+ el("a", {
781
+ href: privacyUrl,
782
+ target: "_blank",
783
+ rel: "noopener noreferrer"
784
+ }),
785
+ { textContent: "privacy notice" }
786
+ );
787
+ privacyLabel.push(privacyLink);
788
+ } else {
789
+ privacyLabel.push("privacy notice");
790
+ }
599
791
  form.appendChild(
600
- createFieldEl("nodii-message", "Message (optional)", messageInput, false)
792
+ el("div", { class: "nodii-onboarding-field" }, [
793
+ el("label", { class: "nodii-onboarding-consent-row" }, [
794
+ privacyBox,
795
+ el("span", {}, privacyLabel)
796
+ ]),
797
+ errorSlot("nodii-privacyNoticeAcknowledged")
798
+ ])
799
+ );
800
+ form.appendChild(
801
+ el("label", { class: "nodii-onboarding-consent-row" }, [
802
+ marketingBox,
803
+ el("span", {}, [
804
+ labelOf(spec, "marketingOptIn", "Send me occasional product updates")
805
+ ])
806
+ ])
601
807
  );
602
808
  form.appendChild(serverErrorEl);
603
809
  form.appendChild(submitBtn);
604
- form.appendChild(recaptchaNotice);
810
+ if (siteKey) {
811
+ form.appendChild(
812
+ el("p", { class: "nodii-onboarding-recaptcha-notice" }, [
813
+ "Protected by reCAPTCHA. ",
814
+ Object.assign(
815
+ el("a", {
816
+ href: "https://policies.google.com/privacy",
817
+ target: "_blank",
818
+ rel: "noopener noreferrer"
819
+ }),
820
+ { textContent: "Privacy" }
821
+ ),
822
+ " & ",
823
+ Object.assign(
824
+ el("a", {
825
+ href: "https://policies.google.com/terms",
826
+ target: "_blank",
827
+ rel: "noopener noreferrer"
828
+ }),
829
+ { textContent: "Terms" }
830
+ )
831
+ ])
832
+ );
833
+ }
605
834
  wrapper.appendChild(successEl);
606
835
  wrapper.appendChild(form);
607
836
  host.appendChild(wrapper);
608
- nameInput.addEventListener("input", () => {
609
- formControl.setValue("name", nameInput.value);
610
- });
611
- emailInput.addEventListener("input", () => {
837
+ function syncValues() {
838
+ formControl.setValue("companyName", companyInput.value);
839
+ formControl.setValue("contactName", contactInput.value);
612
840
  formControl.setValue("email", emailInput.value);
613
- });
614
- phoneInput.addEventListener("input", () => {
615
841
  formControl.setValue("phone", phoneInput.value || void 0);
616
- });
617
- messageInput.addEventListener("input", () => {
618
842
  formControl.setValue("message", messageInput.value || void 0);
619
- });
843
+ formControl.setValue(
844
+ "interestModules",
845
+ interestBoxes.filter((box) => box.checked).map((box) => box.value)
846
+ );
847
+ formControl.setValue("sizeBand", sizeSelect.value || void 0);
848
+ formControl.setValue("privacyNoticeAcknowledged", privacyBox.checked);
849
+ formControl.setValue("marketingOptIn", marketingBox.checked);
850
+ }
851
+ form.addEventListener("input", syncValues);
852
+ form.addEventListener("change", syncValues);
620
853
  const submitLabel = options.submitLabel ?? "Get in touch";
621
854
  function renderSubmitButton(submitting) {
622
855
  while (submitBtn.firstChild) {
@@ -634,9 +867,10 @@ function mountContactForm(host, options) {
634
867
  const unsubscribe = formControl.subscribe((state) => {
635
868
  submitBtn.disabled = state.status === "submitting";
636
869
  renderSubmitButton(state.status === "submitting");
637
- applyErrors(wrapper, state.errors);
638
- if (state.serverError) {
639
- serverErrorEl.textContent = state.serverError;
870
+ const unplaced = applyErrors(wrapper, state.errors);
871
+ const formError = state.serverError ?? (unplaced > 0 ? "This form can\u2019t be sent right now." : null);
872
+ if (formError) {
873
+ serverErrorEl.textContent = formError;
640
874
  serverErrorEl.style.display = "";
641
875
  } else {
642
876
  serverErrorEl.style.display = "none";
@@ -650,10 +884,7 @@ function mountContactForm(host, options) {
650
884
  let lastFingerprint = null;
651
885
  form.addEventListener("submit", async (event) => {
652
886
  event.preventDefault();
653
- formControl.setValue("name", nameInput.value);
654
- formControl.setValue("email", emailInput.value);
655
- formControl.setValue("phone", phoneInput.value || void 0);
656
- formControl.setValue("message", messageInput.value || void 0);
887
+ syncValues();
657
888
  const valid = formControl.validate();
658
889
  if (!valid) return;
659
890
  formControl.setSubmitting();
@@ -664,9 +895,9 @@ function mountContactForm(host, options) {
664
895
  lastFingerprint = fingerprint;
665
896
  }
666
897
  let firstToken;
667
- if (options.siteKey) {
898
+ if (siteKey) {
668
899
  try {
669
- firstToken = await executeRecaptcha(options.siteKey, "contact_submit", {
900
+ firstToken = await executeRecaptcha(siteKey, "contact_submit", {
670
901
  timeoutMs: options.timeoutMs
671
902
  });
672
903
  } catch (recaptchaErr) {
@@ -683,14 +914,14 @@ function mountContactForm(host, options) {
683
914
  usedFirst = true;
684
915
  return firstToken;
685
916
  }
686
- return executeRecaptcha(options.siteKey, "contact_submit", {
917
+ return executeRecaptcha(siteKey, "contact_submit", {
687
918
  timeoutMs: options.timeoutMs
688
919
  });
689
920
  };
690
921
  try {
691
922
  await submitLead(values, {
692
923
  baseUrl: options.baseUrl,
693
- ...options.siteKey ? { botToken: tokenProvider } : {},
924
+ ...siteKey ? { botToken: tokenProvider } : {},
694
925
  idempotencyKey,
695
926
  fetch: options.fetch,
696
927
  timeoutMs: options.timeoutMs
@@ -717,21 +948,45 @@ function mountContactForm(host, options) {
717
948
  }
718
949
  }
719
950
  });
951
+ const refreshMs = options.specRefreshMs ?? DEFAULT_SPEC_REFRESH_MS;
952
+ let refreshing = false;
953
+ const specRefresh = privacyLink && !options.privacyUrl && refreshMs > 0 ? setInterval(() => {
954
+ if (refreshing || !wrapper.isConnected) return;
955
+ refreshing = true;
956
+ fetchOnboardingSpec({
957
+ baseUrl: options.baseUrl,
958
+ onboarder: options.onboarder,
959
+ fetch: options.fetch,
960
+ timeoutMs: options.timeoutMs,
961
+ cache: new OnboardingSpecCache()
962
+ }).then(
963
+ (fresh) => {
964
+ const url = privacyDocumentUrl(fresh);
965
+ if (url && privacyLink) privacyLink.href = url;
966
+ },
967
+ () => {
968
+ }
969
+ ).finally(() => {
970
+ refreshing = false;
971
+ });
972
+ }, refreshMs) : void 0;
720
973
  return {
721
974
  unmount() {
975
+ clearInterval(specRefresh);
722
976
  unsubscribe();
723
977
  host.removeChild(wrapper);
724
978
  },
725
979
  reset() {
726
980
  idempotencyKey = null;
727
981
  lastFingerprint = null;
982
+ form.reset();
728
983
  formControl.reset();
984
+ formControl.setValue("intakeKind", options.intakeKind ?? "enquiry");
985
+ if (options.attribution) {
986
+ formControl.setValue("attribution", options.attribution);
987
+ }
729
988
  form.style.display = "";
730
989
  successEl.style.display = "none";
731
- nameInput.value = "";
732
- emailInput.value = "";
733
- phoneInput.value = "";
734
- messageInput.value = "";
735
990
  }
736
991
  };
737
992
  }
@@ -840,7 +1095,7 @@ function buildGateAffordance(control, doc, opts) {
840
1095
  "Loading document\u2026"
841
1096
  ])
842
1097
  );
843
- const importViewer = opts.importViewer ?? (() => import('./render-DC7P2BGQ.js'));
1098
+ const importViewer = opts.importViewer ?? (() => import('./render-EWQXF73I.js'));
844
1099
  const loadViewer = () => {
845
1100
  void (async () => {
846
1101
  try {
@@ -1491,6 +1746,12 @@ function mountSignupForm(host, options) {
1491
1746
  control.validateAll();
1492
1747
  control.setError("Please accept all required documents to continue.");
1493
1748
  options.onError?.(err);
1749
+ } else if (err instanceof OnboardingSubdomainTakenError) {
1750
+ control.setAccountError(
1751
+ "subdomain",
1752
+ "This workspace address is already taken. Choose another one."
1753
+ );
1754
+ options.onError?.(err);
1494
1755
  } else if (err instanceof OnboardingBotChallengeError) {
1495
1756
  control.setError(
1496
1757
  "Bot protection check failed. Please refresh the page and try again."
@@ -1656,5 +1917,5 @@ var OnboardingWidget = {
1656
1917
  };
1657
1918
 
1658
1919
  export { DEFAULT_WIDGET_CSS, OnboardingWidget, consentDocElementId, cssEscape, idSelector, injectWidgetStyles, mountContactForm, mountSignupForm, renderConsentStep, updateConsentStep };
1659
- //# sourceMappingURL=chunk-IPAKPLIF.js.map
1660
- //# sourceMappingURL=chunk-IPAKPLIF.js.map
1920
+ //# sourceMappingURL=chunk-OVULPWNZ.js.map
1921
+ //# sourceMappingURL=chunk-OVULPWNZ.js.map