@flamingo-stack/openframe-frontend-core 0.0.672 → 0.0.673-2406.7238.1

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 (160) hide show
  1. package/dist/{chunk-PSL2WBEO.cjs → chunk-26NSIJMT.cjs} +33 -33
  2. package/dist/{chunk-PSL2WBEO.cjs.map → chunk-26NSIJMT.cjs.map} +1 -1
  3. package/dist/{chunk-UOQRNZIB.cjs → chunk-6QUKYN4K.cjs} +17 -17
  4. package/dist/{chunk-UOQRNZIB.cjs.map → chunk-6QUKYN4K.cjs.map} +1 -1
  5. package/dist/{chunk-AL4I5HHB.js → chunk-BF7GZ6BS.js} +4 -4
  6. package/dist/{chunk-CBCETEB4.cjs → chunk-DRURJCEY.cjs} +6 -6
  7. package/dist/{chunk-CBCETEB4.cjs.map → chunk-DRURJCEY.cjs.map} +1 -1
  8. package/dist/{chunk-GV7R2NTS.cjs → chunk-G262S3Q5.cjs} +23 -23
  9. package/dist/{chunk-GV7R2NTS.cjs.map → chunk-G262S3Q5.cjs.map} +1 -1
  10. package/dist/{chunk-HVEX4BSC.cjs → chunk-HJ532KQR.cjs} +60 -45
  11. package/dist/chunk-HJ532KQR.cjs.map +1 -0
  12. package/dist/{chunk-TQPKNG2E.js → chunk-HRM6242Y.js} +3 -3
  13. package/dist/{chunk-HOEVEB7N.cjs → chunk-IFQMIYFO.cjs} +50 -23
  14. package/dist/chunk-IFQMIYFO.cjs.map +1 -0
  15. package/dist/{chunk-Z6E45TJM.js → chunk-IG7M3XKX.js} +5 -5
  16. package/dist/{chunk-EKABOXNB.js → chunk-II6ABVLK.js} +2 -2
  17. package/dist/{chunk-ZRIVCLPR.js → chunk-JD5TKRCU.js} +1 -1
  18. package/dist/chunk-JD5TKRCU.js.map +1 -0
  19. package/dist/{chunk-SNWAN52K.cjs → chunk-K365MY6G.cjs} +3 -3
  20. package/dist/{chunk-SNWAN52K.cjs.map → chunk-K365MY6G.cjs.map} +1 -1
  21. package/dist/{chunk-NETIHUEY.cjs → chunk-KV7W5T6U.cjs} +10 -10
  22. package/dist/{chunk-NETIHUEY.cjs.map → chunk-KV7W5T6U.cjs.map} +1 -1
  23. package/dist/{chunk-H54F476P.cjs → chunk-KYF5EONP.cjs} +3 -3
  24. package/dist/{chunk-H54F476P.cjs.map → chunk-KYF5EONP.cjs.map} +1 -1
  25. package/dist/{chunk-D55IENZJ.cjs → chunk-LCMT2GZK.cjs} +1 -1
  26. package/dist/chunk-LCMT2GZK.cjs.map +1 -0
  27. package/dist/{chunk-TFSGYKWG.js → chunk-LMUZZUYC.js} +34 -7
  28. package/dist/chunk-LMUZZUYC.js.map +1 -0
  29. package/dist/{chunk-XLJNYTUW.cjs → chunk-N6TC6IPV.cjs} +56 -55
  30. package/dist/chunk-N6TC6IPV.cjs.map +1 -0
  31. package/dist/{chunk-IBGHJLNT.js → chunk-OGOMAK7B.js} +2 -2
  32. package/dist/{chunk-PWPYCJ33.js → chunk-P4GUIOLP.js} +2 -2
  33. package/dist/{chunk-7KM34VWL.js → chunk-RRU46J3N.js} +3 -3
  34. package/dist/{chunk-RD3I5PBB.cjs → chunk-THSIFQGS.cjs} +15 -15
  35. package/dist/{chunk-RD3I5PBB.cjs.map → chunk-THSIFQGS.cjs.map} +1 -1
  36. package/dist/{chunk-4AZWVS7F.js → chunk-TTHBFT7Q.js} +7 -6
  37. package/dist/{chunk-4AZWVS7F.js.map → chunk-TTHBFT7Q.js.map} +1 -1
  38. package/dist/{chunk-YNGAJGYB.cjs → chunk-TZP2OXHG.cjs} +346 -6
  39. package/dist/chunk-TZP2OXHG.cjs.map +1 -0
  40. package/dist/{chunk-VZRVQEMT.cjs → chunk-U2H3C4NT.cjs} +7 -7
  41. package/dist/{chunk-VZRVQEMT.cjs.map → chunk-U2H3C4NT.cjs.map} +1 -1
  42. package/dist/{chunk-KSBDARII.js → chunk-UFMXQKVT.js} +5 -5
  43. package/dist/{chunk-42OVTTOM.js → chunk-UMTXGEPU.js} +18 -3
  44. package/dist/{chunk-42OVTTOM.js.map → chunk-UMTXGEPU.js.map} +1 -1
  45. package/dist/{chunk-3PVOF3IB.js → chunk-UTHZ7DLN.js} +343 -3
  46. package/dist/{chunk-3PVOF3IB.js.map → chunk-UTHZ7DLN.js.map} +1 -1
  47. package/dist/{chunk-UUOBCUHW.cjs → chunk-UXT5EK27.cjs} +3 -3
  48. package/dist/{chunk-UUOBCUHW.cjs.map → chunk-UXT5EK27.cjs.map} +1 -1
  49. package/dist/{chunk-T3KXCOB2.js → chunk-VK5737GE.js} +2 -2
  50. package/dist/{chunk-O5PYXQ6H.js → chunk-VKLM5UKG.js} +3 -3
  51. package/dist/{chunk-6S2KLT7O.js → chunk-X2DR3XNN.js} +4 -4
  52. package/dist/{chunk-JREG4T6K.cjs → chunk-YCYOZAB5.cjs} +3 -3
  53. package/dist/{chunk-JREG4T6K.cjs.map → chunk-YCYOZAB5.cjs.map} +1 -1
  54. package/dist/{chunk-XDTU23P4.js → chunk-YWQVL5VJ.js} +2 -2
  55. package/dist/{chunk-BODMICIH.cjs → chunk-Z5K4IQZU.cjs} +40 -40
  56. package/dist/{chunk-BODMICIH.cjs.map → chunk-Z5K4IQZU.cjs.map} +1 -1
  57. package/dist/components/case-studies/index.cjs +15 -14
  58. package/dist/components/case-studies/index.cjs.map +1 -1
  59. package/dist/components/case-studies/index.js +6 -5
  60. package/dist/components/case-studies/index.js.map +1 -1
  61. package/dist/components/case-studies/share-experience-section.d.ts.map +1 -1
  62. package/dist/components/contact/contact-form.d.ts +6 -1
  63. package/dist/components/contact/contact-form.d.ts.map +1 -1
  64. package/dist/components/contact/index.cjs +5 -5
  65. package/dist/components/contact/index.js +4 -4
  66. package/dist/components/docs/index.cjs +8 -8
  67. package/dist/components/docs/index.js +7 -7
  68. package/dist/components/embeds/index.cjs +5 -5
  69. package/dist/components/embeds/index.js +4 -4
  70. package/dist/components/faq/index.cjs +5 -5
  71. package/dist/components/faq/index.js +4 -4
  72. package/dist/components/features/index.cjs +4 -4
  73. package/dist/components/features/index.js +3 -3
  74. package/dist/components/features/waitlist-form.d.ts +7 -2
  75. package/dist/components/features/waitlist-form.d.ts.map +1 -1
  76. package/dist/components/help-center-pages/index.cjs +53 -52
  77. package/dist/components/help-center-pages/index.cjs.map +1 -1
  78. package/dist/components/help-center-pages/index.js +16 -15
  79. package/dist/components/help-center-pages/index.js.map +1 -1
  80. package/dist/components/help-center-pages/trust-center-sections.d.ts.map +1 -1
  81. package/dist/components/index.cjs +35 -35
  82. package/dist/components/index.js +14 -14
  83. package/dist/components/meeting-scheduler/booking-form.d.ts +4 -1
  84. package/dist/components/meeting-scheduler/booking-form.d.ts.map +1 -1
  85. package/dist/components/meeting-scheduler/index.cjs +72 -37
  86. package/dist/components/meeting-scheduler/index.cjs.map +1 -1
  87. package/dist/components/meeting-scheduler/index.d.ts +5 -1
  88. package/dist/components/meeting-scheduler/index.d.ts.map +1 -1
  89. package/dist/components/meeting-scheduler/index.js +45 -10
  90. package/dist/components/meeting-scheduler/index.js.map +1 -1
  91. package/dist/components/navigation/index.cjs +7 -7
  92. package/dist/components/navigation/index.js +6 -6
  93. package/dist/components/onboarding-guides/index.cjs +7 -7
  94. package/dist/components/onboarding-guides/index.js +6 -6
  95. package/dist/components/tickets/help-center-create-form.d.ts.map +1 -1
  96. package/dist/components/tickets/index.cjs +8 -8
  97. package/dist/components/tickets/index.js +7 -7
  98. package/dist/components/ui/index.cjs +4 -4
  99. package/dist/components/ui/index.js +3 -3
  100. package/dist/contexts/endpoints-runtime-context.d.ts +5 -0
  101. package/dist/contexts/endpoints-runtime-context.d.ts.map +1 -1
  102. package/dist/contexts/index.cjs +2 -2
  103. package/dist/contexts/index.js +1 -1
  104. package/dist/hooks/index.cjs +5 -3
  105. package/dist/hooks/index.cjs.map +1 -1
  106. package/dist/hooks/index.d.ts +1 -0
  107. package/dist/hooks/index.d.ts.map +1 -1
  108. package/dist/hooks/index.js +4 -2
  109. package/dist/hooks/use-form-rescue.d.ts +37 -0
  110. package/dist/hooks/use-form-rescue.d.ts.map +1 -0
  111. package/dist/index.cjs +52 -8
  112. package/dist/index.cjs.map +1 -1
  113. package/dist/index.js +51 -7
  114. package/dist/index.js.map +1 -1
  115. package/dist/utils/form-rescue.cjs +123 -0
  116. package/dist/utils/form-rescue.cjs.map +1 -0
  117. package/dist/utils/form-rescue.d.ts +105 -0
  118. package/dist/utils/form-rescue.d.ts.map +1 -0
  119. package/dist/utils/form-rescue.js +101 -0
  120. package/dist/utils/form-rescue.js.map +1 -0
  121. package/dist/utils/index.cjs +119 -0
  122. package/dist/utils/index.cjs.map +1 -1
  123. package/dist/utils/index.d.ts +1 -0
  124. package/dist/utils/index.d.ts.map +1 -1
  125. package/dist/utils/index.js +99 -1
  126. package/dist/utils/index.js.map +1 -1
  127. package/package.json +7 -1
  128. package/src/components/case-studies/share-experience-section.tsx +1 -0
  129. package/src/components/contact/contact-form.tsx +38 -2
  130. package/src/components/features/waitlist-form.tsx +27 -2
  131. package/src/components/help-center-pages/trust-center-sections.tsx +1 -0
  132. package/src/components/meeting-scheduler/booking-form.tsx +14 -0
  133. package/src/components/meeting-scheduler/index.tsx +38 -3
  134. package/src/components/tickets/help-center-create-form.tsx +2 -0
  135. package/src/contexts/endpoints-runtime-context.tsx +5 -0
  136. package/src/hooks/__tests__/use-form-rescue.test.tsx +189 -0
  137. package/src/hooks/index.ts +3 -0
  138. package/src/hooks/use-form-rescue.ts +315 -0
  139. package/src/utils/__tests__/form-rescue.test.ts +106 -0
  140. package/src/utils/form-rescue.ts +198 -0
  141. package/src/utils/index.ts +5 -0
  142. package/dist/chunk-D55IENZJ.cjs.map +0 -1
  143. package/dist/chunk-HOEVEB7N.cjs.map +0 -1
  144. package/dist/chunk-HVEX4BSC.cjs.map +0 -1
  145. package/dist/chunk-TFSGYKWG.js.map +0 -1
  146. package/dist/chunk-XLJNYTUW.cjs.map +0 -1
  147. package/dist/chunk-YNGAJGYB.cjs.map +0 -1
  148. package/dist/chunk-ZRIVCLPR.js.map +0 -1
  149. /package/dist/{chunk-AL4I5HHB.js.map → chunk-BF7GZ6BS.js.map} +0 -0
  150. /package/dist/{chunk-TQPKNG2E.js.map → chunk-HRM6242Y.js.map} +0 -0
  151. /package/dist/{chunk-Z6E45TJM.js.map → chunk-IG7M3XKX.js.map} +0 -0
  152. /package/dist/{chunk-EKABOXNB.js.map → chunk-II6ABVLK.js.map} +0 -0
  153. /package/dist/{chunk-IBGHJLNT.js.map → chunk-OGOMAK7B.js.map} +0 -0
  154. /package/dist/{chunk-PWPYCJ33.js.map → chunk-P4GUIOLP.js.map} +0 -0
  155. /package/dist/{chunk-7KM34VWL.js.map → chunk-RRU46J3N.js.map} +0 -0
  156. /package/dist/{chunk-KSBDARII.js.map → chunk-UFMXQKVT.js.map} +0 -0
  157. /package/dist/{chunk-T3KXCOB2.js.map → chunk-VK5737GE.js.map} +0 -0
  158. /package/dist/{chunk-O5PYXQ6H.js.map → chunk-VKLM5UKG.js.map} +0 -0
  159. /package/dist/{chunk-6S2KLT7O.js.map → chunk-X2DR3XNN.js.map} +0 -0
  160. /package/dist/{chunk-XDTU23P4.js.map → chunk-YWQVL5VJ.js.map} +0 -0
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  useEndpointsRuntime,
4
4
  useRequiredEndpointsRuntime
5
- } from "./chunk-ZRIVCLPR.js";
5
+ } from "./chunk-JD5TKRCU.js";
6
6
  import {
7
7
  contentFetch,
8
8
  createSearchParams,
@@ -3059,6 +3059,324 @@ function useHumanitySignals() {
3059
3059
  return { honeypotInputProps: { ref, name: HONEYPOT_FIELD }, getSignals, resetSignals };
3060
3060
  }
3061
3061
 
3062
+ // src/hooks/use-form-rescue.ts
3063
+ import { useCallback as useCallback14, useEffect as useEffect20, useMemo as useMemo7, useRef as useRef14 } from "react";
3064
+
3065
+ // src/utils/form-rescue.ts
3066
+ var FORM_RESCUE_FORM_IDS = [
3067
+ "contact",
3068
+ "case_study_pitch",
3069
+ "data_room_request",
3070
+ "trust_center_request",
3071
+ "tmcg_join",
3072
+ "meeting_booking",
3073
+ "waitlist"
3074
+ ];
3075
+ function isFormRescueFormId(value) {
3076
+ return typeof value === "string" && FORM_RESCUE_FORM_IDS.includes(value);
3077
+ }
3078
+ var FORM_RESCUE_VALUE_FIELDS = [
3079
+ "email",
3080
+ "name",
3081
+ "firstName",
3082
+ "lastName",
3083
+ "company",
3084
+ "jobtitle",
3085
+ "companySize",
3086
+ "linkedin_url"
3087
+ ];
3088
+ var EXCLUDED_FIELD_PATTERN = /pass(word|code)?|card|cvv|cvc|ssn|social.?security|iban|bank|routing|token|secret|otp|pin$/i;
3089
+ function isExcludedRescueField(name) {
3090
+ return EXCLUDED_FIELD_PATTERN.test(name);
3091
+ }
3092
+ var FORM_RESCUE_MAX_VALUE_CHARS = 200;
3093
+ var FORM_RESCUE_DEBOUNCE_MS = 1500;
3094
+ var FORM_RESCUE_MAX_FIELDS = 40;
3095
+ var FORM_RESCUE_ATTEMPT_FIELD = "form_attempt_id";
3096
+ var FORM_RESCUE_RESUME_FIELD = "form_resume_token";
3097
+ var FORM_RESCUE_SUBMIT_KEYS = [FORM_RESCUE_ATTEMPT_FIELD, FORM_RESCUE_RESUME_FIELD];
3098
+ var FORM_RESCUE_RESUME_PARAM = "resume";
3099
+ var FORM_DRAFT_STATUSES = ["started", "rescuable", "rescued", "submitted"];
3100
+ var FORM_RESCUE_SKIP_REASONS = {
3101
+ finishedElsewhere: "finished_elsewhere",
3102
+ alreadyRescued: "already_rescued",
3103
+ internal: "internal_email"
3104
+ };
3105
+ var EMAIL_PATTERN = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
3106
+ function isRescueEmail(value) {
3107
+ return typeof value === "string" && value.length <= FORM_RESCUE_MAX_VALUE_CHARS && EMAIL_PATTERN.test(value.trim());
3108
+ }
3109
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
3110
+ function isFormAttemptId(value) {
3111
+ return typeof value === "string" && UUID_PATTERN.test(value);
3112
+ }
3113
+ function isFilled(value) {
3114
+ if (value == null) return false;
3115
+ if (typeof value === "string") return value.trim().length > 0;
3116
+ if (typeof value === "boolean") return value;
3117
+ if (Array.isArray(value)) return value.length > 0;
3118
+ if (typeof value === "object") return Object.values(value).some(isFilled);
3119
+ return true;
3120
+ }
3121
+ function sanitizeRescueValues(values) {
3122
+ const out = {};
3123
+ if (!values || typeof values !== "object") return out;
3124
+ for (const key of FORM_RESCUE_VALUE_FIELDS) {
3125
+ if (isExcludedRescueField(key)) continue;
3126
+ const raw = values[key];
3127
+ if (typeof raw !== "string") continue;
3128
+ const trimmed = raw.trim().slice(0, FORM_RESCUE_MAX_VALUE_CHARS);
3129
+ if (trimmed) out[key] = trimmed;
3130
+ }
3131
+ return out;
3132
+ }
3133
+ function filledRescueFields(values, fieldNames) {
3134
+ return fieldNames.filter((name) => !isExcludedRescueField(name) && isFilled(values[name])).slice(0, FORM_RESCUE_MAX_FIELDS);
3135
+ }
3136
+ function rescueCompletionPct(filledCount, totalCount) {
3137
+ if (totalCount <= 0) return 0;
3138
+ return Math.max(0, Math.min(100, Math.round(filledCount / totalCount * 100)));
3139
+ }
3140
+ function sanitizeRescueFieldName(value) {
3141
+ if (typeof value !== "string") return null;
3142
+ const trimmed = value.trim();
3143
+ return /^[A-Za-z0-9_.-]{1,64}$/.test(trimmed) && !isExcludedRescueField(trimmed) ? trimmed : null;
3144
+ }
3145
+ var FORM_RESCUE_EVENTS = {
3146
+ started: "form_started",
3147
+ progress: "form_progress",
3148
+ leadCaptured: "form_lead_captured",
3149
+ abandoned: "form_abandoned",
3150
+ submitted: "form_submitted",
3151
+ rescued: "form_rescued",
3152
+ resumed: "form_resumed"
3153
+ };
3154
+ function captureFormRescueEvent(event, properties) {
3155
+ try {
3156
+ if (typeof window === "undefined") return;
3157
+ const ph = window.posthog;
3158
+ if (typeof ph?.capture === "function") ph.capture(event, properties);
3159
+ } catch {
3160
+ }
3161
+ }
3162
+
3163
+ // src/hooks/use-form-rescue.ts
3164
+ var LOCAL_DRAFT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
3165
+ var STORAGE_PREFIX = "form-rescue:v1:";
3166
+ var UTM_KEYS = ["source", "medium", "campaign", "content", "term"];
3167
+ function newAttemptId() {
3168
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
3169
+ const bytes = new Uint8Array(16);
3170
+ crypto.getRandomValues(bytes);
3171
+ bytes[6] = bytes[6] & 15 | 64;
3172
+ bytes[8] = bytes[8] & 63 | 128;
3173
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
3174
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
3175
+ }
3176
+ function readLocalDraft(formId) {
3177
+ try {
3178
+ const raw = window.localStorage.getItem(STORAGE_PREFIX + formId);
3179
+ if (!raw) return null;
3180
+ const parsed = JSON.parse(raw);
3181
+ if (!isFormAttemptId(parsed.attemptId) || typeof parsed.savedAt !== "number") return null;
3182
+ if (Date.now() - parsed.savedAt > LOCAL_DRAFT_TTL_MS) return null;
3183
+ return { attemptId: parsed.attemptId, values: sanitizeRescueValues(parsed.values), savedAt: parsed.savedAt };
3184
+ } catch {
3185
+ return null;
3186
+ }
3187
+ }
3188
+ function writeLocalDraft(formId, draft) {
3189
+ try {
3190
+ window.localStorage.setItem(STORAGE_PREFIX + formId, JSON.stringify(draft));
3191
+ } catch {
3192
+ }
3193
+ }
3194
+ function clearLocalDraft(formId) {
3195
+ try {
3196
+ window.localStorage.removeItem(STORAGE_PREFIX + formId);
3197
+ } catch {
3198
+ }
3199
+ }
3200
+ function readUtm() {
3201
+ try {
3202
+ const params = new URLSearchParams(window.location.search);
3203
+ const utm = {};
3204
+ for (const key of UTM_KEYS) {
3205
+ const value = params.get(`utm_${key}`);
3206
+ if (value) utm[key] = value.slice(0, 200);
3207
+ }
3208
+ return Object.keys(utm).length ? utm : void 0;
3209
+ } catch {
3210
+ return void 0;
3211
+ }
3212
+ }
3213
+ function useFormRescue({ formId, fieldNames, onRestore, getSignals }) {
3214
+ const draftsUrl = useEndpointsRuntime()?.formDraftsUrl;
3215
+ const attemptIdRef = useRef14(null);
3216
+ const resumeTokenRef = useRef14(null);
3217
+ const latestRef = useRef14(null);
3218
+ const dirtyRef = useRef14(false);
3219
+ const startedRef = useRef14(false);
3220
+ const leadCapturedRef = useRef14(false);
3221
+ const completedRef = useRef14(false);
3222
+ const timerRef = useRef14(null);
3223
+ const fieldNamesRef = useRef14(fieldNames);
3224
+ const onRestoreRef = useRef14(onRestore);
3225
+ const getSignalsRef = useRef14(getSignals);
3226
+ useEffect20(() => {
3227
+ fieldNamesRef.current = fieldNames;
3228
+ onRestoreRef.current = onRestore;
3229
+ getSignalsRef.current = getSignals;
3230
+ });
3231
+ const attemptId = useCallback14(() => {
3232
+ if (!attemptIdRef.current) attemptIdRef.current = newAttemptId();
3233
+ return attemptIdRef.current;
3234
+ }, []);
3235
+ const send = useCallback14(
3236
+ (keepalive) => {
3237
+ if (!formId || !draftsUrl || !dirtyRef.current || completedRef.current || !latestRef.current) return;
3238
+ dirtyRef.current = false;
3239
+ const body = {
3240
+ attempt_id: attemptId(),
3241
+ form_id: formId,
3242
+ ...latestRef.current,
3243
+ source_path: typeof window !== "undefined" ? window.location.pathname : "/",
3244
+ utm: readUtm()
3245
+ };
3246
+ try {
3247
+ void contentFetch(draftsUrl, {
3248
+ method: "POST",
3249
+ headers: { "Content-Type": "application/json" },
3250
+ body: JSON.stringify({ ...body, ...getSignalsRef.current?.() ?? {} }),
3251
+ keepalive
3252
+ }).catch(() => void 0);
3253
+ } catch {
3254
+ }
3255
+ captureFormRescueEvent(FORM_RESCUE_EVENTS.progress, {
3256
+ form_id: formId,
3257
+ attempt_id: attemptId(),
3258
+ fields_filled: body.fields_filled,
3259
+ last_field: body.last_field,
3260
+ completion_pct: body.completion_pct
3261
+ });
3262
+ },
3263
+ [attemptId, draftsUrl, formId]
3264
+ );
3265
+ useEffect20(() => {
3266
+ if (!formId || typeof window === "undefined") return void 0;
3267
+ let cancelled = false;
3268
+ const token = new URLSearchParams(window.location.search).get(FORM_RESCUE_RESUME_PARAM);
3269
+ const restoreLocal = () => {
3270
+ const local = readLocalDraft(formId);
3271
+ if (!local) return;
3272
+ attemptIdRef.current = local.attemptId;
3273
+ if (Object.keys(local.values).length) onRestoreRef.current?.(local.values);
3274
+ };
3275
+ if (token && draftsUrl && /^[A-Za-z0-9_-]{16,128}$/.test(token)) {
3276
+ contentFetch(`${draftsUrl}/resume/${encodeURIComponent(token)}`).then(async (res) => res.ok ? await res.json() : null).then((data) => {
3277
+ if (cancelled) return;
3278
+ if (!data || data.form_id !== formId) {
3279
+ restoreLocal();
3280
+ return;
3281
+ }
3282
+ resumeTokenRef.current = token;
3283
+ onRestoreRef.current?.(sanitizeRescueValues(data.values));
3284
+ captureFormRescueEvent(FORM_RESCUE_EVENTS.resumed, { form_id: formId, attempt_id: attemptId() });
3285
+ }).catch(() => {
3286
+ if (!cancelled) restoreLocal();
3287
+ });
3288
+ } else {
3289
+ restoreLocal();
3290
+ }
3291
+ return () => {
3292
+ cancelled = true;
3293
+ };
3294
+ }, [attemptId, draftsUrl, formId]);
3295
+ useEffect20(() => {
3296
+ if (!formId || typeof document === "undefined") return void 0;
3297
+ const onHide = () => {
3298
+ if (completedRef.current || !startedRef.current) return;
3299
+ if (timerRef.current) {
3300
+ clearTimeout(timerRef.current);
3301
+ timerRef.current = null;
3302
+ }
3303
+ send(true);
3304
+ captureFormRescueEvent(FORM_RESCUE_EVENTS.abandoned, {
3305
+ form_id: formId,
3306
+ attempt_id: attemptId(),
3307
+ last_field: latestRef.current?.last_field ?? null,
3308
+ completion_pct: latestRef.current?.completion_pct ?? 0
3309
+ });
3310
+ };
3311
+ const onVisibility = () => {
3312
+ if (document.visibilityState === "hidden") onHide();
3313
+ };
3314
+ document.addEventListener("visibilitychange", onVisibility);
3315
+ window.addEventListener("pagehide", onHide);
3316
+ return () => {
3317
+ document.removeEventListener("visibilitychange", onVisibility);
3318
+ window.removeEventListener("pagehide", onHide);
3319
+ if (timerRef.current) clearTimeout(timerRef.current);
3320
+ };
3321
+ }, [attemptId, formId, send]);
3322
+ const track = useCallback14(
3323
+ (values, lastField) => {
3324
+ if (!formId || completedRef.current) return;
3325
+ const names = fieldNamesRef.current;
3326
+ const visible = {};
3327
+ for (const name of names) visible[name] = values[name];
3328
+ const filled = filledRescueFields(visible, names);
3329
+ if (!filled.length) return;
3330
+ if (!startedRef.current) {
3331
+ startedRef.current = true;
3332
+ captureFormRescueEvent(FORM_RESCUE_EVENTS.started, { form_id: formId, attempt_id: attemptId() });
3333
+ }
3334
+ const clean = sanitizeRescueValues(visible);
3335
+ if (!leadCapturedRef.current && isRescueEmail(clean.email)) {
3336
+ leadCapturedRef.current = true;
3337
+ captureFormRescueEvent(FORM_RESCUE_EVENTS.leadCaptured, { form_id: formId, attempt_id: attemptId() });
3338
+ }
3339
+ latestRef.current = {
3340
+ values: clean,
3341
+ fields_filled: filled,
3342
+ last_field: sanitizeRescueFieldName(lastField),
3343
+ completion_pct: rescueCompletionPct(filled.length, names.length)
3344
+ };
3345
+ dirtyRef.current = true;
3346
+ writeLocalDraft(formId, { attemptId: attemptId(), values: clean, savedAt: Date.now() });
3347
+ if (timerRef.current) clearTimeout(timerRef.current);
3348
+ timerRef.current = setTimeout(() => {
3349
+ timerRef.current = null;
3350
+ send(false);
3351
+ }, FORM_RESCUE_DEBOUNCE_MS);
3352
+ },
3353
+ [attemptId, formId, send]
3354
+ );
3355
+ const submitFields = useCallback14(() => {
3356
+ if (!formId) return {};
3357
+ return {
3358
+ [FORM_RESCUE_ATTEMPT_FIELD]: attemptId(),
3359
+ ...resumeTokenRef.current ? { [FORM_RESCUE_RESUME_FIELD]: resumeTokenRef.current } : {}
3360
+ };
3361
+ }, [attemptId, formId]);
3362
+ const complete = useCallback14(() => {
3363
+ if (!formId) return;
3364
+ if (timerRef.current) {
3365
+ clearTimeout(timerRef.current);
3366
+ timerRef.current = null;
3367
+ }
3368
+ captureFormRescueEvent(FORM_RESCUE_EVENTS.submitted, { form_id: formId, attempt_id: attemptId() });
3369
+ clearLocalDraft(formId);
3370
+ attemptIdRef.current = null;
3371
+ resumeTokenRef.current = null;
3372
+ latestRef.current = null;
3373
+ dirtyRef.current = false;
3374
+ startedRef.current = false;
3375
+ leadCapturedRef.current = false;
3376
+ }, [attemptId, formId]);
3377
+ return useMemo7(() => ({ track, submitFields, complete }), [track, submitFields, complete]);
3378
+ }
3379
+
3062
3380
  export {
3063
3381
  useAutoLimitTags,
3064
3382
  useKeyboardCollisionPadding,
@@ -3144,6 +3462,28 @@ export {
3144
3462
  findHoneypotCopySource,
3145
3463
  evaluateHumanitySignals,
3146
3464
  splitCsvEnv,
3147
- useHumanitySignals
3465
+ useHumanitySignals,
3466
+ FORM_RESCUE_FORM_IDS,
3467
+ isFormRescueFormId,
3468
+ FORM_RESCUE_VALUE_FIELDS,
3469
+ isExcludedRescueField,
3470
+ FORM_RESCUE_MAX_VALUE_CHARS,
3471
+ FORM_RESCUE_DEBOUNCE_MS,
3472
+ FORM_RESCUE_MAX_FIELDS,
3473
+ FORM_RESCUE_ATTEMPT_FIELD,
3474
+ FORM_RESCUE_RESUME_FIELD,
3475
+ FORM_RESCUE_SUBMIT_KEYS,
3476
+ FORM_RESCUE_RESUME_PARAM,
3477
+ FORM_DRAFT_STATUSES,
3478
+ FORM_RESCUE_SKIP_REASONS,
3479
+ isRescueEmail,
3480
+ isFormAttemptId,
3481
+ sanitizeRescueValues,
3482
+ filledRescueFields,
3483
+ rescueCompletionPct,
3484
+ sanitizeRescueFieldName,
3485
+ FORM_RESCUE_EVENTS,
3486
+ captureFormRescueEvent,
3487
+ useFormRescue
3148
3488
  };
3149
- //# sourceMappingURL=chunk-3PVOF3IB.js.map
3489
+ //# sourceMappingURL=chunk-UTHZ7DLN.js.map