@nodii/onboarding 0.1.0-dev.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.
- package/README.md +124 -0
- package/dist/{chunk-4SWOVHZ2.js → chunk-A3DE2IAH.js} +155 -18
- package/dist/chunk-A3DE2IAH.js.map +1 -0
- package/dist/{chunk-AFCJLUPK.js → chunk-DWFZA5T6.js} +19 -8
- package/dist/chunk-DWFZA5T6.js.map +1 -0
- package/dist/chunk-FIRSA7EH.js +3 -0
- package/dist/{chunk-ZDY2I6KY.js.map → chunk-FIRSA7EH.js.map} +1 -1
- package/dist/chunk-JFDQZ3AC.js +3 -0
- package/dist/chunk-JFDQZ3AC.js.map +1 -0
- package/dist/{chunk-34BQX2KR.js → chunk-KFHVZGX6.js} +61 -6
- package/dist/chunk-KFHVZGX6.js.map +1 -0
- package/dist/chunk-NDSBIB3N.js +99 -0
- package/dist/chunk-NDSBIB3N.js.map +1 -0
- package/dist/chunk-OLWR5COK.js +10 -0
- package/dist/chunk-OLWR5COK.js.map +1 -0
- package/dist/{chunk-SWMIIUXR.js → chunk-OVULPWNZ.js} +917 -158
- package/dist/chunk-OVULPWNZ.js.map +1 -0
- package/dist/chunk-SEKNVHD3.js +59 -0
- package/dist/chunk-SEKNVHD3.js.map +1 -0
- package/dist/chunk-T7EWURTF.js +321 -0
- package/dist/chunk-T7EWURTF.js.map +1 -0
- package/dist/{chunk-BQCTDU5V.js → chunk-UHOFGWI7.js} +90 -15
- package/dist/chunk-UHOFGWI7.js.map +1 -0
- package/dist/chunk-WKN4U7QF.js +35 -0
- package/dist/chunk-WKN4U7QF.js.map +1 -0
- package/dist/{chunk-AW7W3HAC.js → chunk-XJTJPBJB.js} +40 -3
- package/dist/chunk-XJTJPBJB.js.map +1 -0
- package/dist/chunk-ZR6X3GSM.js +186 -0
- package/dist/chunk-ZR6X3GSM.js.map +1 -0
- package/dist/client-NvqPpfQP.d.ts +86 -0
- package/dist/consent/index.d.ts +59 -0
- package/dist/consent/index.js +7 -0
- package/dist/consent/index.js.map +1 -0
- package/dist/controller-BLH3wnxl.d.ts +177 -0
- package/dist/dynamic/index.d.ts +65 -6
- package/dist/dynamic/index.js +5 -2
- package/dist/errors-vJkye4a0.d.ts +110 -0
- package/dist/form/index.d.ts +12 -4
- package/dist/form/index.js +2 -2
- package/dist/index-BMSbQGWP.d.ts +103 -0
- package/dist/index.d.ts +13 -7
- package/dist/index.js +13 -8
- package/dist/model-BI_I-D_V.d.ts +267 -0
- package/dist/{model-IEyDI54w.d.ts → model-CEwvHlWA.d.ts} +212 -46
- package/dist/render-EWQXF73I.js +5 -0
- package/dist/render-EWQXF73I.js.map +1 -0
- package/dist/schema-BrtjpXfL.d.ts +257 -0
- package/dist/{signup-client-Bl6eUrxR.d.ts → signup-client-CS95nkVO.d.ts} +152 -19
- package/dist/spec/index.d.ts +10 -81
- package/dist/spec/index.js +3 -2
- package/dist/submit/index.d.ts +7 -71
- package/dist/submit/index.js +4 -3
- package/dist/viewer/index.d.ts +4 -0
- package/dist/viewer/index.js +5 -0
- package/dist/viewer/index.js.map +1 -0
- package/dist/widget/index.d.ts +102 -20
- package/dist/widget/index.js +10 -7
- package/package.json +21 -2
- package/dist/chunk-34BQX2KR.js.map +0 -1
- package/dist/chunk-4SWOVHZ2.js.map +0 -1
- package/dist/chunk-AFCJLUPK.js.map +0 -1
- package/dist/chunk-AW7W3HAC.js.map +0 -1
- package/dist/chunk-BIFANV7M.js +0 -22
- package/dist/chunk-BIFANV7M.js.map +0 -1
- package/dist/chunk-BQCTDU5V.js.map +0 -1
- package/dist/chunk-SWMIIUXR.js.map +0 -1
- package/dist/chunk-ZDY2I6KY.js +0 -3
- package/dist/schema-TfyBU6JW.d.ts +0 -107
package/dist/submit/index.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
export { B as BotTokenSource, L as LeadsSubmitOptions, a as LeadsSubmitResult, S as SignupSubmitBody, b as SignupSubmitOptions, c as SignupSubmitResult, d as SignupWirePayload, e as computePayloadFingerprint, f as computeSignupFingerprint, g as generateIdempotencyKey, r as resolveBotToken, s as signupWireSchema,
|
|
1
|
+
export { B as BotTokenSource, L as LeadsSubmitOptions, a as LeadsSubmitResult, S as SignupSubmitBody, b as SignupSubmitOptions, c as SignupSubmitResult, d as SignupWirePayload, e as computePayloadFingerprint, f as computeSignupFingerprint, g as consentSubmitDocumentSchema, h as generateIdempotencyKey, r as resolveBotToken, s as signupWireSchema, i as submitLead, j as submitSignup } from '../signup-client-CS95nkVO.js';
|
|
2
|
+
export { O as OnboardingBotChallengeError, a as OnboardingConflictError, b as OnboardingConsentIncompleteError, c as OnboardingConsentStaleError, d as OnboardingError, e as OnboardingErrorKind, f as OnboardingNetworkError, g as OnboardingRateLimitError, h as OnboardingServerError, i as OnboardingSpecInvalidError, j as OnboardingSpecStaleError, k as OnboardingSubdomainTakenError, l as OnboardingValidationError, S as StaleConsentDocument } from '../errors-vJkye4a0.js';
|
|
2
3
|
import 'zod/v3';
|
|
3
|
-
import '../model-
|
|
4
|
-
import '../
|
|
4
|
+
import '../model-CEwvHlWA.js';
|
|
5
|
+
import '../controller-BLH3wnxl.js';
|
|
6
|
+
import '../model-BI_I-D_V.js';
|
|
7
|
+
import '../schema-BrtjpXfL.js';
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* Shared HTTP helper: fetch bounded by a timeout via AbortController.
|
|
@@ -36,71 +39,4 @@ declare function fetchWithTimeout(url: string, options?: TimeoutFetchOptions): P
|
|
|
36
39
|
*/
|
|
37
40
|
declare function withTimeout<T>(op: Promise<T>, timeoutMs: number, onTimeout: () => Error): Promise<T>;
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
* Error taxonomy for the onboarding submit client.
|
|
41
|
-
*
|
|
42
|
-
* Maps HTTP status codes and server error bodies to typed errors:
|
|
43
|
-
* 403 bot_challenge_failed → OnboardingBotChallengeError
|
|
44
|
-
* 409 spec_stale (SUBMIT) → OnboardingSpecStaleError (submit endpoint only,
|
|
45
|
-
* body { error:true, code:"spec_stale",
|
|
46
|
-
* currentSpecVersion })
|
|
47
|
-
* 409 (other / LEADS) → OnboardingConflictError (generic conflict)
|
|
48
|
-
* 429 → OnboardingRateLimitError
|
|
49
|
-
* 5xx → OnboardingServerError (retryable)
|
|
50
|
-
* network / timeout → OnboardingNetworkError
|
|
51
|
-
*/
|
|
52
|
-
type OnboardingErrorKind = "bot_challenge_failed" | "spec_stale" | "spec_invalid" | "conflict" | "rate_limited" | "server_error" | "network_error" | "validation_error";
|
|
53
|
-
declare class OnboardingError extends Error {
|
|
54
|
-
readonly kind: OnboardingErrorKind;
|
|
55
|
-
readonly statusCode: number | null;
|
|
56
|
-
constructor(kind: OnboardingErrorKind, message: string, statusCode?: number | null);
|
|
57
|
-
}
|
|
58
|
-
declare class OnboardingBotChallengeError extends OnboardingError {
|
|
59
|
-
constructor(message?: string);
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* A 409 that is NOT spec_stale. On the /leads path there is no spec_stale
|
|
63
|
-
* concept (the payload carries no specVersion); on /submit, a 409 that
|
|
64
|
-
* lacks `code:"spec_stale"` also lands here. Distinct from
|
|
65
|
-
* OnboardingSpecStaleError so headless consumers switching on `kind` get the
|
|
66
|
-
* correct diagnosis (a generic conflict, e.g. a D538 same-key concurrent
|
|
67
|
-
* race) rather than a "form has been updated, refresh" recovery message.
|
|
68
|
-
*/
|
|
69
|
-
declare class OnboardingConflictError extends OnboardingError {
|
|
70
|
-
constructor(statusCode?: number, message?: string);
|
|
71
|
-
}
|
|
72
|
-
declare class OnboardingRateLimitError extends OnboardingError {
|
|
73
|
-
readonly retryAfterSeconds: number | null;
|
|
74
|
-
constructor(retryAfterSeconds?: number | null);
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* A /submit 409 whose body carries `code:"spec_stale"` (D540 reject-stale).
|
|
78
|
-
* The PINNED body shape (confirmed against the crm intake) is exactly:
|
|
79
|
-
* { error: true, code: "spec_stale", currentSpecVersion: string }
|
|
80
|
-
* The version is read ONLY from `currentSpecVersion` — no field hedging.
|
|
81
|
-
* Submit-endpoint only; /leads 409s are OnboardingConflictError.
|
|
82
|
-
*/
|
|
83
|
-
declare class OnboardingSpecStaleError extends OnboardingError {
|
|
84
|
-
readonly currentSpecVersion: string;
|
|
85
|
-
constructor(currentSpecVersion: string);
|
|
86
|
-
}
|
|
87
|
-
declare class OnboardingServerError extends OnboardingError {
|
|
88
|
-
constructor(statusCode: number, message?: string);
|
|
89
|
-
}
|
|
90
|
-
declare class OnboardingNetworkError extends OnboardingError {
|
|
91
|
-
constructor(cause?: unknown);
|
|
92
|
-
}
|
|
93
|
-
declare class OnboardingValidationError extends OnboardingError {
|
|
94
|
-
constructor(message: string);
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* The spec endpoint returned a body that does not conform to the LOCKED
|
|
98
|
-
* D540 §1 response shape. This is a server-side contract violation — the
|
|
99
|
-
* SDK refuses to render a form from a malformed spec (a half-parsed spec
|
|
100
|
-
* could silently drop required fields).
|
|
101
|
-
*/
|
|
102
|
-
declare class OnboardingSpecInvalidError extends OnboardingError {
|
|
103
|
-
constructor(detail: string);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export { DEFAULT_REQUEST_TIMEOUT_MS, OnboardingBotChallengeError, OnboardingConflictError, OnboardingError, type OnboardingErrorKind, OnboardingNetworkError, OnboardingRateLimitError, OnboardingServerError, OnboardingSpecInvalidError, OnboardingSpecStaleError, OnboardingValidationError, fetchWithTimeout, withTimeout };
|
|
42
|
+
export { DEFAULT_REQUEST_TIMEOUT_MS, fetchWithTimeout, withTimeout };
|
package/dist/submit/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import '../chunk-Z4PA6C2Z.js';
|
|
2
|
-
export { computePayloadFingerprint, computeSignupFingerprint, generateIdempotencyKey, resolveBotToken, signupWireSchema, submitLead, submitSignup } from '../chunk-
|
|
3
|
-
import '../chunk-
|
|
4
|
-
|
|
2
|
+
export { computePayloadFingerprint, computeSignupFingerprint, consentSubmitDocumentSchema, generateIdempotencyKey, resolveBotToken, signupWireSchema, submitLead, submitSignup } from '../chunk-UHOFGWI7.js';
|
|
3
|
+
import '../chunk-WKN4U7QF.js';
|
|
4
|
+
import '../chunk-OLWR5COK.js';
|
|
5
|
+
export { DEFAULT_REQUEST_TIMEOUT_MS, OnboardingBotChallengeError, OnboardingConflictError, OnboardingConsentIncompleteError, OnboardingConsentStaleError, OnboardingError, OnboardingNetworkError, OnboardingRateLimitError, OnboardingServerError, OnboardingSpecInvalidError, OnboardingSpecStaleError, OnboardingSubdomainTakenError, OnboardingValidationError, fetchWithTimeout, withTimeout } from '../chunk-XJTJPBJB.js';
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
|
6
7
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
package/dist/widget/index.d.ts
CHANGED
|
@@ -1,22 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { C as ContactFormValues } from '../schema-BrtjpXfL.js';
|
|
2
|
+
import { a as OnboardingSpec } from '../model-CEwvHlWA.js';
|
|
3
|
+
import { O as OnboardingSpecCache } from '../client-NvqPpfQP.js';
|
|
4
|
+
import { c as SignupSubmitResult } from '../signup-client-CS95nkVO.js';
|
|
5
|
+
import { _ as ___viewer_render_js } from '../index-BMSbQGWP.js';
|
|
6
|
+
import { C as ConsentController, c as ConsentState } from '../controller-BLH3wnxl.js';
|
|
4
7
|
import 'zod/v3';
|
|
5
|
-
import '../
|
|
8
|
+
import '../model-BI_I-D_V.js';
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* Default vanilla-TS contact form widget.
|
|
9
12
|
*
|
|
10
13
|
* Renders a form into a host element and handles the full lifecycle:
|
|
11
|
-
* 1. Render fields
|
|
14
|
+
* 1. Render the D1165 lead fields: company, contact name, email, phone,
|
|
15
|
+
* message, the interest and team-size choices, the privacy-notice
|
|
16
|
+
* acknowledgement and the marketing opt-in
|
|
12
17
|
* 2. Client-side validation on submit
|
|
13
|
-
* 3. reCAPTCHA token acquisition (D537)
|
|
18
|
+
* 3. reCAPTCHA token acquisition (D537) — only while a challenge is in
|
|
19
|
+
* force (D1444: an onboarder can switch the bot check off)
|
|
14
20
|
* 4. POST to /api/v1/public/onboarding/leads via submit client
|
|
15
21
|
* 5. Retry on 5xx with SAME idempotency key
|
|
16
22
|
* 6. Show success or error state
|
|
17
23
|
*
|
|
24
|
+
* The field SET is fixed (it is the `/leads` wire contract); the choices
|
|
25
|
+
* inside it are the onboarder's. Pass the spec from `fetchOnboardingSpec`
|
|
26
|
+
* and the interests, team sizes, privacy-notice link and bot-challenge
|
|
27
|
+
* siteKey all come from it.
|
|
28
|
+
*
|
|
18
29
|
* No React or framework required. CSS via injectWidgetStyles().
|
|
19
30
|
*/
|
|
31
|
+
|
|
20
32
|
interface ContactFormMountOptions {
|
|
21
33
|
/**
|
|
22
34
|
* The onboarder identifier (slug or UUID).
|
|
@@ -25,10 +37,7 @@ interface ContactFormMountOptions {
|
|
|
25
37
|
* trust/scoping is derived server-side from the request Host at the edge
|
|
26
38
|
* ("onboarder derived from the bundle, never client-supplied", D540 §1);
|
|
27
39
|
* a client-supplied identifier is never trusted and therefore never sent.
|
|
28
|
-
*
|
|
29
|
-
* This option is reserved for Phase 2, where it becomes the client-side
|
|
30
|
-
* cache key for the spec-fetch (`(onboarder, specVersion)` per D540 §3).
|
|
31
|
-
* In Phase 1 it has no runtime effect.
|
|
40
|
+
* It is the client-side spec-cache key (`fetchOnboardingSpec`).
|
|
32
41
|
*/
|
|
33
42
|
onboarder: string;
|
|
34
43
|
/**
|
|
@@ -37,15 +46,45 @@ interface ContactFormMountOptions {
|
|
|
37
46
|
*/
|
|
38
47
|
baseUrl: string;
|
|
39
48
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
49
|
+
* The onboarder's spec, from `fetchOnboardingSpec`. Supplies the interest
|
|
50
|
+
* and team-size choices (`forms.contact`), the field labels, the
|
|
51
|
+
* privacy-notice link (the ConsentPack's privacy document) and the
|
|
52
|
+
* bot-challenge siteKey. Without it the widget renders the text fields and
|
|
53
|
+
* the two checkboxes only.
|
|
54
|
+
*
|
|
55
|
+
* The widget draws a fixed set of fields: company, contact name and email
|
|
56
|
+
* are always required, and the spec's `required` flag is honoured for the
|
|
57
|
+
* interest choice only. Phone, team size and message are always optional
|
|
58
|
+
* here (as the platform spec publishes them), and the address fields are
|
|
59
|
+
* not drawn. An onboarder that requires more builds its own form on the
|
|
60
|
+
* headless `@nodii/onboarding/form` and `/submit` modules.
|
|
42
61
|
*/
|
|
43
|
-
|
|
62
|
+
spec?: OnboardingSpec;
|
|
44
63
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
64
|
+
* reCAPTCHA v3 site key override. Normally taken from `spec.botChallenge`;
|
|
65
|
+
* with neither, no token is requested and Google is never loaded (D1444).
|
|
47
66
|
*/
|
|
67
|
+
siteKey?: string;
|
|
68
|
+
/** Which form to render. "signup" is `mountSignupForm`. */
|
|
48
69
|
form: "contact";
|
|
70
|
+
/**
|
|
71
|
+
* Which door this form is (D1165 §1). Default "enquiry": what every lead
|
|
72
|
+
* from this widget carried before 0.2.0, and the headless form's default.
|
|
73
|
+
*/
|
|
74
|
+
intakeKind?: ContactFormValues["intakeKind"];
|
|
75
|
+
/** Campaign tags, referring site, landing page, button — stamped on the lead. */
|
|
76
|
+
attribution?: ContactFormValues["attribution"];
|
|
77
|
+
/** Privacy-notice link override (defaults to the spec's privacy document). */
|
|
78
|
+
privacyUrl?: string;
|
|
79
|
+
/**
|
|
80
|
+
* How often a mounted form re-reads the spec to keep the privacy-notice link
|
|
81
|
+
* live, in ms (default 5 minutes). The spec's link is the ConsentPack's
|
|
82
|
+
* presigned download URL, which expires (D543 §4: ten minutes on dev), and
|
|
83
|
+
* a visitor can sit on a form longer than that. Applies only when the link
|
|
84
|
+
* comes from `spec`; 0 turns it off. Reads happen only while the form is in
|
|
85
|
+
* the document; call `unmount()` to stop the timer.
|
|
86
|
+
*/
|
|
87
|
+
specRefreshMs?: number;
|
|
49
88
|
/** Called after a successful lead submission. */
|
|
50
89
|
onSuccess?: () => void;
|
|
51
90
|
/** Called on non-retryable errors (bot challenge, rate limit). */
|
|
@@ -73,12 +112,15 @@ interface MountedWidget {
|
|
|
73
112
|
*
|
|
74
113
|
* @example
|
|
75
114
|
* ```ts
|
|
115
|
+
* import { fetchOnboardingSpec } from "@nodii/onboarding/spec";
|
|
76
116
|
* import { OnboardingWidget } from "@nodii/onboarding/widget";
|
|
77
|
-
* const
|
|
117
|
+
* const baseUrl = "https://api.acme.nucleus-cloud.in";
|
|
118
|
+
* const spec = await fetchOnboardingSpec({ baseUrl, onboarder: "acme-corp" });
|
|
119
|
+
* OnboardingWidget.mount(document.getElementById("contact")!, {
|
|
78
120
|
* onboarder: "acme-corp",
|
|
79
121
|
* form: "contact",
|
|
80
|
-
* baseUrl
|
|
81
|
-
*
|
|
122
|
+
* baseUrl,
|
|
123
|
+
* spec,
|
|
82
124
|
* });
|
|
83
125
|
* ```
|
|
84
126
|
*/
|
|
@@ -162,7 +204,7 @@ declare function mountSignupForm(host: HTMLElement, options: SignupFormMountOpti
|
|
|
162
204
|
* The widget scopes all styles under `.nodii-onboarding-widget` to avoid
|
|
163
205
|
* leaking into the host page.
|
|
164
206
|
*/
|
|
165
|
-
declare const DEFAULT_WIDGET_CSS = "\n.nodii-onboarding-widget {\n --_font: var(--nodii-onboarding-font-family, system-ui, -apple-system, sans-serif);\n --_primary: var(--nodii-onboarding-primary-color, #2563eb);\n --_primary-hover: var(--nodii-onboarding-primary-hover, #1d4ed8);\n --_error: var(--nodii-onboarding-error-color, #dc2626);\n --_border: var(--nodii-onboarding-border-color, #d1d5db);\n --_input-bg: var(--nodii-onboarding-input-bg, #fff);\n --_label: var(--nodii-onboarding-label-color, #374151);\n --_radius: var(--nodii-onboarding-radius, 6px);\n --_spacing: var(--nodii-onboarding-spacing, 16px);\n\n font-family: var(--_font);\n box-sizing: border-box;\n}\n\n.nodii-onboarding-widget *,\n.nodii-onboarding-widget *::before,\n.nodii-onboarding-widget *::after {\n box-sizing: inherit;\n}\n\n.nodii-onboarding-field {\n display: flex;\n flex-direction: column;\n gap: calc(var(--_spacing) * 0.25);\n margin-bottom: var(--_spacing);\n}\n\n.nodii-onboarding-label {\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--_label);\n}\n\n.nodii-onboarding-label.required::after {\n content: \" *\";\n color: var(--_error);\n}\n\n.nodii-onboarding-input,\n.nodii-onboarding-textarea {\n width: 100%;\n padding: calc(var(--_spacing) * 0.5) calc(var(--_spacing) * 0.75);\n border: 1px solid var(--_border);\n border-radius: var(--_radius);\n background: var(--_input-bg);\n font-family: var(--_font);\n font-size: 1rem;\n line-height: 1.5;\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.nodii-onboarding-input:focus,\n.nodii-onboarding-textarea:focus {\n border-color: var(--_primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--_primary) 20%, transparent);\n}\n\n.nodii-onboarding-input.has-error,\n.nodii-onboarding-textarea.has-error {\n border-color: var(--_error);\n}\n\n.nodii-onboarding-textarea {\n min-height: 100px;\n resize: vertical;\n}\n\n.nodii-onboarding-field-error {\n font-size: 0.75rem;\n color: var(--_error);\n margin-top: 2px;\n}\n\n.nodii-onboarding-server-error {\n padding: calc(var(--_spacing) * 0.75);\n border-radius: var(--_radius);\n background: color-mix(in srgb, var(--_error) 8%, transparent);\n border: 1px solid color-mix(in srgb, var(--_error) 30%, transparent);\n color: var(--_error);\n font-size: 0.875rem;\n margin-bottom: var(--_spacing);\n}\n\n.nodii-onboarding-success {\n padding: var(--_spacing);\n border-radius: var(--_radius);\n background: #f0fdf4;\n border: 1px solid #bbf7d0;\n color: #15803d;\n font-size: 0.9375rem;\n text-align: center;\n}\n\n.nodii-onboarding-submit-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n padding: calc(var(--_spacing) * 0.625) var(--_spacing);\n background: var(--_primary);\n color: #fff;\n border: none;\n border-radius: var(--_radius);\n font-family: var(--_font);\n font-size: 1rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.nodii-onboarding-submit-btn:hover:not(:disabled) {\n background: var(--_primary-hover);\n}\n\n.nodii-onboarding-submit-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.nodii-onboarding-spinner {\n display: inline-block;\n width: 1em;\n height: 1em;\n border: 2px solid rgba(255,255,255,0.4);\n border-top-color: #fff;\n border-radius: 50%;\n animation: nodii-spin 0.6s linear infinite;\n margin-right: 0.5em;\n}\n\n@keyframes nodii-spin {\n to { transform: rotate(360deg); }\n}\n\n.nodii-onboarding-recaptcha-notice {\n font-size: 0.7rem;\n color: #6b7280;\n margin-top: calc(var(--_spacing) * 0.5);\n text-align: center;\n}\n\n.nodii-onboarding-recaptcha-notice a {\n color: inherit;\n}\n\n/* \u2500\u2500 dynamic stepper (signup form) \u2500\u2500 */\n\n.nodii-onboarding-steps {\n display: flex;\n gap: calc(var(--_spacing) * 0.5);\n margin-bottom: var(--_spacing);\n}\n\n.nodii-onboarding-step-dot {\n flex: 1;\n height: 4px;\n border-radius: 2px;\n background: var(--_border);\n}\n\n.nodii-onboarding-step-dot.active {\n background: var(--_primary);\n}\n\n.nodii-onboarding-stage-title {\n font-size: 1.0625rem;\n font-weight: 600;\n color: var(--_label);\n margin: 0 0 var(--_spacing) 0;\n}\n\n.nodii-onboarding-nav {\n display: flex;\n gap: calc(var(--_spacing) * 0.5);\n margin-top: var(--_spacing);\n}\n\n.nodii-onboarding-back-btn {\n padding: calc(var(--_spacing) * 0.625) var(--_spacing);\n background: transparent;\n color: var(--_label);\n border: 1px solid var(--_border);\n border-radius: var(--_radius);\n font-family: var(--_font);\n font-size: 1rem;\n cursor: pointer;\n}\n\n.nodii-onboarding-stale-banner {\n padding: calc(var(--_spacing) * 0.75);\n border-radius: var(--_radius);\n background: #fffbeb;\n border: 1px solid #fcd34d;\n color: #92400e;\n font-size: 0.875rem;\n margin-bottom: var(--_spacing);\n}\n\n.nodii-onboarding-consent-row {\n display: flex;\n align-items: flex-start;\n gap: calc(var(--_spacing) * 0.5);\n margin-bottom: calc(var(--_spacing) * 0.5);\n font-size: 0.875rem;\n color: var(--_label);\n}\n\n.nodii-onboarding-consent-row input[type=\"checkbox\"] {\n margin-top: 3px;\n}\n\n.nodii-onboarding-account-section {\n border-top: 1px solid var(--_border);\n margin-top: var(--_spacing);\n padding-top: var(--_spacing);\n}\n\n.nodii-onboarding-loading {\n padding: var(--_spacing);\n text-align: center;\n color: var(--_label);\n}\n";
|
|
207
|
+
declare const DEFAULT_WIDGET_CSS = "\n.nodii-onboarding-widget {\n --_font: var(--nodii-onboarding-font-family, system-ui, -apple-system, sans-serif);\n --_primary: var(--nodii-onboarding-primary-color, #2563eb);\n --_primary-hover: var(--nodii-onboarding-primary-hover, #1d4ed8);\n --_error: var(--nodii-onboarding-error-color, #dc2626);\n --_border: var(--nodii-onboarding-border-color, #d1d5db);\n --_input-bg: var(--nodii-onboarding-input-bg, #fff);\n --_label: var(--nodii-onboarding-label-color, #374151);\n --_radius: var(--nodii-onboarding-radius, 6px);\n --_spacing: var(--nodii-onboarding-spacing, 16px);\n\n font-family: var(--_font);\n box-sizing: border-box;\n}\n\n.nodii-onboarding-widget *,\n.nodii-onboarding-widget *::before,\n.nodii-onboarding-widget *::after {\n box-sizing: inherit;\n}\n\n.nodii-onboarding-field {\n display: flex;\n flex-direction: column;\n gap: calc(var(--_spacing) * 0.25);\n margin-bottom: var(--_spacing);\n}\n\n.nodii-onboarding-label {\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--_label);\n}\n\n.nodii-onboarding-label.required::after {\n content: \" *\";\n color: var(--_error);\n}\n\n.nodii-onboarding-input,\n.nodii-onboarding-textarea {\n width: 100%;\n padding: calc(var(--_spacing) * 0.5) calc(var(--_spacing) * 0.75);\n border: 1px solid var(--_border);\n border-radius: var(--_radius);\n background: var(--_input-bg);\n font-family: var(--_font);\n font-size: 1rem;\n line-height: 1.5;\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.nodii-onboarding-input:focus,\n.nodii-onboarding-textarea:focus {\n border-color: var(--_primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--_primary) 20%, transparent);\n}\n\n.nodii-onboarding-input.has-error,\n.nodii-onboarding-textarea.has-error {\n border-color: var(--_error);\n}\n\n.nodii-onboarding-textarea {\n min-height: 100px;\n resize: vertical;\n}\n\n.nodii-onboarding-field-error {\n font-size: 0.75rem;\n color: var(--_error);\n margin-top: 2px;\n}\n\n.nodii-onboarding-server-error {\n padding: calc(var(--_spacing) * 0.75);\n border-radius: var(--_radius);\n background: color-mix(in srgb, var(--_error) 8%, transparent);\n border: 1px solid color-mix(in srgb, var(--_error) 30%, transparent);\n color: var(--_error);\n font-size: 0.875rem;\n margin-bottom: var(--_spacing);\n}\n\n.nodii-onboarding-success {\n padding: var(--_spacing);\n border-radius: var(--_radius);\n background: #f0fdf4;\n border: 1px solid #bbf7d0;\n color: #15803d;\n font-size: 0.9375rem;\n text-align: center;\n}\n\n.nodii-onboarding-submit-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n padding: calc(var(--_spacing) * 0.625) var(--_spacing);\n background: var(--_primary);\n color: #fff;\n border: none;\n border-radius: var(--_radius);\n font-family: var(--_font);\n font-size: 1rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.nodii-onboarding-submit-btn:hover:not(:disabled) {\n background: var(--_primary-hover);\n}\n\n.nodii-onboarding-submit-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.nodii-onboarding-spinner {\n display: inline-block;\n width: 1em;\n height: 1em;\n border: 2px solid rgba(255,255,255,0.4);\n border-top-color: #fff;\n border-radius: 50%;\n animation: nodii-spin 0.6s linear infinite;\n margin-right: 0.5em;\n}\n\n@keyframes nodii-spin {\n to { transform: rotate(360deg); }\n}\n\n.nodii-onboarding-recaptcha-notice {\n font-size: 0.7rem;\n color: #6b7280;\n margin-top: calc(var(--_spacing) * 0.5);\n text-align: center;\n}\n\n.nodii-onboarding-recaptcha-notice a {\n color: inherit;\n}\n\n/* \u2500\u2500 dynamic stepper (signup form) \u2500\u2500 */\n\n.nodii-onboarding-steps {\n display: flex;\n gap: calc(var(--_spacing) * 0.5);\n margin-bottom: var(--_spacing);\n}\n\n.nodii-onboarding-step-dot {\n flex: 1;\n height: 4px;\n border-radius: 2px;\n background: var(--_border);\n}\n\n.nodii-onboarding-step-dot.active {\n background: var(--_primary);\n}\n\n.nodii-onboarding-stage-title {\n font-size: 1.0625rem;\n font-weight: 600;\n color: var(--_label);\n margin: 0 0 var(--_spacing) 0;\n}\n\n.nodii-onboarding-nav {\n display: flex;\n gap: calc(var(--_spacing) * 0.5);\n margin-top: var(--_spacing);\n}\n\n.nodii-onboarding-back-btn {\n padding: calc(var(--_spacing) * 0.625) var(--_spacing);\n background: transparent;\n color: var(--_label);\n border: 1px solid var(--_border);\n border-radius: var(--_radius);\n font-family: var(--_font);\n font-size: 1rem;\n cursor: pointer;\n}\n\n.nodii-onboarding-stale-banner {\n padding: calc(var(--_spacing) * 0.75);\n border-radius: var(--_radius);\n background: #fffbeb;\n border: 1px solid #fcd34d;\n color: #92400e;\n font-size: 0.875rem;\n margin-bottom: var(--_spacing);\n}\n\nfieldset.nodii-onboarding-field {\n border: none;\n padding: 0;\n margin-inline: 0;\n}\n\n.nodii-onboarding-choices {\n display: flex;\n flex-wrap: wrap;\n gap: calc(var(--_spacing) * 0.5);\n}\n\n.nodii-onboarding-choice {\n display: inline-flex;\n align-items: center;\n gap: calc(var(--_spacing) * 0.375);\n padding: calc(var(--_spacing) * 0.375) calc(var(--_spacing) * 0.75);\n border: 1px solid var(--_border);\n border-radius: 999px;\n background: var(--_input-bg);\n font-size: 0.875rem;\n color: var(--_label);\n cursor: pointer;\n}\n\nfieldset.nodii-onboarding-field.has-error .nodii-onboarding-choice {\n border-color: var(--_error);\n}\n\n.nodii-onboarding-consent-row {\n display: flex;\n align-items: flex-start;\n gap: calc(var(--_spacing) * 0.5);\n margin-bottom: calc(var(--_spacing) * 0.5);\n font-size: 0.875rem;\n color: var(--_label);\n}\n\n.nodii-onboarding-consent-row input[type=\"checkbox\"] {\n margin-top: 3px;\n}\n\n.nodii-onboarding-account-section {\n border-top: 1px solid var(--_border);\n margin-top: var(--_spacing);\n padding-top: var(--_spacing);\n}\n\n.nodii-onboarding-loading {\n padding: var(--_spacing);\n text-align: center;\n color: var(--_label);\n}\n\n/* \u2500\u2500 D543 ConsentPack step (full-width, generous spacing) \u2500\u2500 */\n\n.nodii-onboarding-consent-stage {\n /* Full-bleed within the widget host \u2014 consent cards may span the page. */\n width: 100%;\n display: flex;\n flex-direction: column;\n gap: calc(var(--_spacing) * 2);\n margin: calc(var(--_spacing) * 1.5) 0;\n}\n\n.nodii-onboarding-consent-intro {\n font-size: 0.9375rem;\n color: var(--_label);\n margin: 0 0 calc(var(--_spacing) * 0.5) 0;\n line-height: 1.6;\n}\n\n.nodii-onboarding-consent-card {\n width: 100%;\n border: 1px solid var(--_border);\n border-radius: var(--_radius);\n background: var(--_input-bg);\n padding: calc(var(--_spacing) * 1.5);\n display: flex;\n flex-direction: column;\n gap: var(--_spacing);\n}\n\n.nodii-onboarding-consent-card.is-stale {\n border-color: #fcd34d;\n background: #fffbeb;\n}\n\n.nodii-onboarding-consent-card.has-error {\n border-color: var(--_error);\n}\n\n.nodii-onboarding-consent-title {\n font-size: 1.125rem;\n font-weight: 600;\n color: var(--_label);\n margin: 0;\n}\n\n.nodii-onboarding-consent-stale-tag {\n font-size: 0.8125rem;\n font-weight: 500;\n color: #92400e;\n}\n\n.nodii-onboarding-consent-gate {\n width: 100%;\n}\n\n.nodii-onboarding-consent-scrollbox {\n width: 100%;\n height: 360px;\n overflow-y: auto;\n border: 1px solid var(--_border);\n border-radius: var(--_radius);\n background: #fff;\n position: relative;\n}\n\n.nodii-onboarding-consent-scrollbox:focus {\n outline: none;\n border-color: var(--_primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--_primary) 20%, transparent);\n}\n\n/* Inline, same-origin document content (no iframe). Rendered by the default\n viewer: sanitized HTML/markdown, escaped plain text, or PDF.js canvases. */\n.nodii-onboarding-consent-doc-body {\n padding: var(--_spacing);\n font-size: 0.9375rem;\n line-height: 1.7;\n color: var(--_label);\n word-break: break-word;\n}\n\n.nodii-onboarding-consent-doc-body:not(.nodii-consent-pdf) {\n white-space: pre-wrap;\n}\n\n.nodii-onboarding-consent-doc-loading {\n padding: var(--_spacing);\n color: #6b7280;\n font-size: 0.875rem;\n}\n\n.nodii-consent-pdf {\n display: flex;\n flex-direction: column;\n gap: var(--_spacing);\n white-space: normal;\n}\n\n.nodii-consent-pdf-page {\n max-width: 100%;\n height: auto;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);\n}\n\n.nodii-onboarding-consent-note {\n font-size: 0.8125rem;\n color: #6b7280;\n margin: calc(var(--_spacing) * 0.5) 0 0 0;\n}\n\n/* A gated doc that could not be rendered inline: the gate stays unsatisfiable\n (accept disabled); we surface a clear error + Retry. NO click-to-satisfy. */\n.nodii-onboarding-consent-unrenderable {\n display: flex;\n flex-direction: column;\n gap: calc(var(--_spacing) * 0.5);\n padding: var(--_spacing);\n border: 1px solid color-mix(in srgb, var(--_error) 30%, transparent);\n border-radius: var(--_radius);\n background: color-mix(in srgb, var(--_error) 6%, transparent);\n}\n\n.nodii-onboarding-consent-retry {\n align-self: flex-start;\n padding: calc(var(--_spacing) * 0.5) var(--_spacing);\n background: transparent;\n color: var(--_primary);\n border: 1px solid var(--_primary);\n border-radius: var(--_radius);\n font-family: var(--_font);\n font-size: 0.875rem;\n cursor: pointer;\n}\n\n.nodii-onboarding-consent-open-row,\n.nodii-onboarding-consent-preview-row {\n display: flex;\n}\n\n.nodii-onboarding-consent-open {\n display: inline-flex;\n align-items: center;\n padding: calc(var(--_spacing) * 0.625) var(--_spacing);\n border: 1px solid var(--_primary);\n border-radius: var(--_radius);\n color: var(--_primary);\n text-decoration: none;\n font-size: 0.9375rem;\n font-weight: 500;\n}\n\n.nodii-onboarding-consent-open:hover {\n background: color-mix(in srgb, var(--_primary) 8%, transparent);\n}\n\n.nodii-onboarding-consent-preview {\n font-size: 0.875rem;\n color: var(--_primary);\n}\n\n.nodii-onboarding-consent-accept-row {\n display: flex;\n align-items: flex-start;\n gap: calc(var(--_spacing) * 0.5);\n}\n\n.nodii-onboarding-consent-accept {\n margin-top: 3px;\n width: 1.125rem;\n height: 1.125rem;\n}\n\n.nodii-onboarding-consent-accept:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.nodii-onboarding-consent-accept-label {\n font-size: 0.9375rem;\n color: var(--_label);\n line-height: 1.5;\n}\n\n.nodii-onboarding-consent-required {\n color: var(--_error);\n}\n";
|
|
166
208
|
/**
|
|
167
209
|
* Inject the default widget CSS once into the document head.
|
|
168
210
|
* Idempotent — will not add a duplicate style tag.
|
|
@@ -189,6 +231,46 @@ declare function cssEscape(value: string): string;
|
|
|
189
231
|
/** Build a safe `#id` selector with the id CSS-escaped. */
|
|
190
232
|
declare function idSelector(id: string): string;
|
|
191
233
|
|
|
234
|
+
/** Render-time options for the default consent step. */
|
|
235
|
+
interface ConsentStepRenderOptions {
|
|
236
|
+
/** Injectable fetch for doc-content load (defaults to global). */
|
|
237
|
+
fetch?: typeof globalThis.fetch;
|
|
238
|
+
timeoutMs?: number;
|
|
239
|
+
/**
|
|
240
|
+
* Injectable viewer-module importer (defaults to the real dynamic
|
|
241
|
+
* `import("../viewer/render.js")`). Exposed so a consumer can control code-
|
|
242
|
+
* splitting AND so tests can drive the module-load-FAILURE branch WITHOUT
|
|
243
|
+
* `mock.module` (which leaks across bun tests). Must resolve to an object
|
|
244
|
+
* exposing `renderConsentDocument`; rejecting simulates a chunk-load failure.
|
|
245
|
+
*/
|
|
246
|
+
importViewer?: () => Promise<typeof ___viewer_render_js>;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Turn a consent doc's stable key into a DOM-id-safe token. `doc_type` is a
|
|
250
|
+
* fixed enum (safe); a custom key arrives as `custom:<key>` — the `:` and any
|
|
251
|
+
* residual metachar are replaced so the raw token is valid inside an id AND
|
|
252
|
+
* inside a `#id` selector. (The key is already `[A-Za-z0-9_-]+` past the `:`
|
|
253
|
+
* per model.ts; this only neutralizes the `custom:` prefix separator.)
|
|
254
|
+
*/
|
|
255
|
+
declare function consentDocElementId(key: string): string;
|
|
256
|
+
/**
|
|
257
|
+
* Render (or re-render) the consent stage into a host container from the given
|
|
258
|
+
* consent state. Full re-render (the doc set is small); disabled/checked/error
|
|
259
|
+
* state is refreshed in place via updateConsentStep so scroll position and
|
|
260
|
+
* inline doc content are not lost on unrelated state changes.
|
|
261
|
+
*/
|
|
262
|
+
declare function renderConsentStep(host: HTMLElement, control: ConsentController, state: ConsentState, staleKeys?: string[], opts?: ConsentStepRenderOptions): void;
|
|
263
|
+
/**
|
|
264
|
+
* Refresh the consent cards' disabled/checked/error state in place (no full
|
|
265
|
+
* re-render) — called on every stepper state change so gate unlocks and
|
|
266
|
+
* live-clearing errors reflect immediately without losing scroll position or
|
|
267
|
+
* re-fetching inline doc content.
|
|
268
|
+
*/
|
|
269
|
+
declare function updateConsentStep(host: HTMLElement, state: ConsentState, consentErrors: Array<{
|
|
270
|
+
path: string;
|
|
271
|
+
message: string;
|
|
272
|
+
}>): void;
|
|
273
|
+
|
|
192
274
|
type WidgetMountOptions = ContactFormMountOptions | SignupFormMountOptions;
|
|
193
275
|
declare const OnboardingWidget: {
|
|
194
276
|
/**
|
|
@@ -216,4 +298,4 @@ declare const OnboardingWidget: {
|
|
|
216
298
|
readonly mount: (el: HTMLElement, options: WidgetMountOptions) => MountedWidget;
|
|
217
299
|
};
|
|
218
300
|
|
|
219
|
-
export { type ContactFormMountOptions, DEFAULT_WIDGET_CSS, type MountedWidget, OnboardingWidget, type SignupFormMountOptions, type WidgetMountOptions, cssEscape, idSelector, injectWidgetStyles, mountContactForm, mountSignupForm };
|
|
301
|
+
export { type ConsentStepRenderOptions, type ContactFormMountOptions, DEFAULT_WIDGET_CSS, type MountedWidget, OnboardingWidget, type SignupFormMountOptions, type WidgetMountOptions, consentDocElementId, cssEscape, idSelector, injectWidgetStyles, mountContactForm, mountSignupForm, renderConsentStep, updateConsentStep };
|
package/dist/widget/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
export { DEFAULT_WIDGET_CSS, OnboardingWidget, cssEscape, idSelector, injectWidgetStyles, mountContactForm, mountSignupForm } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import '../chunk-
|
|
4
|
-
import '../chunk-
|
|
5
|
-
import '../chunk-
|
|
6
|
-
import '../chunk-
|
|
7
|
-
import '../chunk-
|
|
1
|
+
export { DEFAULT_WIDGET_CSS, OnboardingWidget, consentDocElementId, cssEscape, idSelector, injectWidgetStyles, mountContactForm, mountSignupForm, renderConsentStep, updateConsentStep } from '../chunk-OVULPWNZ.js';
|
|
2
|
+
import '../chunk-DWFZA5T6.js';
|
|
3
|
+
import '../chunk-KFHVZGX6.js';
|
|
4
|
+
import '../chunk-A3DE2IAH.js';
|
|
5
|
+
import '../chunk-ZR6X3GSM.js';
|
|
6
|
+
import '../chunk-NDSBIB3N.js';
|
|
7
|
+
import '../chunk-UHOFGWI7.js';
|
|
8
|
+
import '../chunk-WKN4U7QF.js';
|
|
9
|
+
import '../chunk-OLWR5COK.js';
|
|
10
|
+
import '../chunk-XJTJPBJB.js';
|
|
8
11
|
import '../chunk-7NS7ENJV.js';
|
|
9
12
|
//# sourceMappingURL=index.js.map
|
|
10
13
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodii/onboarding",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Browser SDK for the Nodii self-signup onboarding flow. Headless form state, reCAPTCHA integration, and a default themeable vanilla-TS widget. D540/D537 contract client.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -33,6 +33,12 @@
|
|
|
33
33
|
"default": "./dist/dynamic/index.js"
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
|
+
"./consent": {
|
|
37
|
+
"import": {
|
|
38
|
+
"types": "./dist/consent/index.d.ts",
|
|
39
|
+
"default": "./dist/consent/index.js"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
36
42
|
"./submit": {
|
|
37
43
|
"import": {
|
|
38
44
|
"types": "./dist/submit/index.d.ts",
|
|
@@ -50,6 +56,12 @@
|
|
|
50
56
|
"types": "./dist/widget/index.d.ts",
|
|
51
57
|
"default": "./dist/widget/index.js"
|
|
52
58
|
}
|
|
59
|
+
},
|
|
60
|
+
"./viewer": {
|
|
61
|
+
"import": {
|
|
62
|
+
"types": "./dist/viewer/index.d.ts",
|
|
63
|
+
"default": "./dist/viewer/index.js"
|
|
64
|
+
}
|
|
53
65
|
}
|
|
54
66
|
},
|
|
55
67
|
"files": [
|
|
@@ -62,13 +74,20 @@
|
|
|
62
74
|
"dev": "tsup --watch",
|
|
63
75
|
"typecheck": "tsc --noEmit",
|
|
64
76
|
"test": "bun test",
|
|
65
|
-
"
|
|
77
|
+
"typecheck:browser": "tsc --noEmit -p browser-tests/tsconfig.json",
|
|
78
|
+
"test:browser:bundle": "bun build browser-tests/fixtures/harness-entry.ts --target=browser --format=esm --outfile=browser-tests/.bundle/sdk.js",
|
|
79
|
+
"test:browser": "bun run test:browser:bundle && playwright test",
|
|
80
|
+
"clean": "rm -rf dist browser-tests/.bundle test-results playwright-report"
|
|
66
81
|
},
|
|
67
82
|
"dependencies": {
|
|
83
|
+
"dompurify": "^3.4.12",
|
|
84
|
+
"marked": "^18.0.6",
|
|
85
|
+
"pdfjs-dist": "^6.1.200",
|
|
68
86
|
"zod": "^3.25.76"
|
|
69
87
|
},
|
|
70
88
|
"devDependencies": {
|
|
71
89
|
"@happy-dom/global-registrator": "^20.10.6",
|
|
90
|
+
"@playwright/test": "^1.61.1",
|
|
72
91
|
"tsup": "^8.3.5",
|
|
73
92
|
"typescript": "^5.9.3"
|
|
74
93
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/spec/model.ts","../src/spec/client.ts"],"names":[],"mappings":"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","file":"chunk-34BQX2KR.js","sourcesContent":["/**\n * D540 §1 onboarding-spec model — mirrors the LOCKED hub REST contract:\n * GET /api/v1/public/onboarding/spec (PublicOnboardingSpecResponse)\n *\n * Response shape (hub JSON Schema, both tiers identical):\n * {\n * specVersion: string (monotonic; echo on submit; stale ⇒ 409),\n * onboarderTier: \"platform\" | \"tenant\",\n * botChallenge: { provider: \"recaptcha\", siteKey: string },\n * forms: {\n * contact: { fields: SpecField[] },\n * signup: { steps: [{ stage: \"lead\"|\"prospect\"|\"activation\",\n * fields: SpecField[] }] }\n * }\n * }\n *\n * SpecField (required: key, type, label, required):\n * { key, type, label, required, validation?, options?, serviceSelection?,\n * piiClass?: \"none\"|\"context\"|\"always\"|\"reference\" }\n *\n * The Zod schema below validates a fetched spec BEFORE the SDK renders\n * anything from it — a malformed spec throws OnboardingSpecInvalidError\n * (a half-parsed spec could silently drop required fields).\n */\n\nimport { z } from \"zod/v3\";\n\nexport type OnboarderTier = \"platform\" | \"tenant\";\nexport type PiiClass = \"none\" | \"context\" | \"always\" | \"reference\";\nexport type SpecStage = \"lead\" | \"prospect\" | \"activation\";\nexport type TerminalStage = \"prospect\" | \"inactive\" | \"active\";\n\n/**\n * Field keys are interpolated into DOM element ids and (historically) CSS\n * selectors by the widget layer. Constrain them to a strict identifier\n * alphabet so a MALICIOUS or compromised onboarder spec cannot smuggle CSS\n * metacharacters or selector-breakout payloads (e.g. `evil], [onclick=…`)\n * through the fetched spec. A spec with an out-of-alphabet key is REJECTED\n * at parse time → OnboardingSpecInvalidError (fail-closed; never rendered).\n * This is layer 1; the widget also CSS.escapes ids as defense-in-depth.\n */\nexport const FIELD_KEY_PATTERN = /^[A-Za-z0-9_-]+$/;\n\nexport const specFieldSchema = z.object({\n key: z\n .string()\n .min(1)\n .regex(\n FIELD_KEY_PATTERN,\n \"field key must contain only letters, digits, underscore, or hyphen\",\n ),\n type: z.string().min(1),\n label: z.string(),\n required: z.boolean(),\n validation: z.record(z.unknown()).optional(),\n options: z.array(z.unknown()).optional(),\n serviceSelection: z.boolean().optional(),\n piiClass: z.enum([\"none\", \"context\", \"always\", \"reference\"]).optional(),\n});\n\nexport type SpecField = z.infer<typeof specFieldSchema>;\n\nexport const specStepSchema = z.object({\n stage: z.enum([\"lead\", \"prospect\", \"activation\"]),\n fields: z.array(specFieldSchema),\n});\n\nexport type SpecStep = z.infer<typeof specStepSchema>;\n\nexport const onboardingSpecSchema = z.object({\n specVersion: z.string().min(1),\n onboarderTier: z.enum([\"platform\", \"tenant\"]),\n botChallenge: z.object({\n provider: z.literal(\"recaptcha\"),\n siteKey: z.string(),\n }),\n forms: z.object({\n // ADVISORY-ONLY (by design, plan §12.2 / D540): the Contact-Us form is\n // INTENTIONALLY a fixed barebones shape (name/email/phone/message) and is\n // NOT onboarder-configurable. `mountContactForm` renders that fixed set\n // directly and does NOT read forms.contact.fields — the field is modeled\n // (and validated) so the SDK can round-trip the full spec, but it drives\n // no rendering. Only forms.signup is spec-driven (the dynamic stepper).\n contact: z.object({ fields: z.array(specFieldSchema) }),\n signup: z.object({ steps: z.array(specStepSchema) }),\n }),\n});\n\nexport type OnboardingSpec = z.infer<typeof onboardingSpecSchema>;\n\n/**\n * Parse an unknown value as an OnboardingSpec.\n * Returns a SafeParseReturnType (inferred).\n */\nexport function validateOnboardingSpec(value: unknown) {\n return onboardingSpecSchema.safeParse(value);\n}\n","/**\n * Config-fetch client for GET /api/v1/public/onboarding/spec (D540 §1/§3).\n *\n * - The onboarder is derived SERVER-side from the request Host (D536) —\n * it is NOT sent by the client. The `onboarder` value here is purely the\n * CLIENT-side cache key (D540 §3: \"cache key (onboarder, specVersion)\").\n * - The spec response is validated against the LOCKED D540 shape before\n * it is returned; a malformed spec throws OnboardingSpecInvalidError.\n * - Cache semantics: every successful fetch is stored under\n * (onboarder, specVersion) and as the onboarder's \"latest\". The versioned\n * entries let the 409 spec_stale flow diff the previously-served version\n * against the refetched one (see `diffSpecFields` + the signup widget's\n * handleSpecStale, which reads the cached prior version before refetching).\n */\n\nimport {\n OnboardingNetworkError,\n OnboardingRateLimitError,\n OnboardingServerError,\n OnboardingSpecInvalidError,\n} from \"../submit/errors.js\";\nimport {\n DEFAULT_REQUEST_TIMEOUT_MS,\n fetchWithTimeout,\n} from \"../submit/http.js\";\nimport {\n validateOnboardingSpec,\n type OnboardingSpec,\n type SpecField,\n} from \"./model.js\";\n\nconst SPEC_PATH = \"/api/v1/public/onboarding/spec\";\n\n/**\n * In-memory spec cache keyed by (onboarder, specVersion), with a \"latest\"\n * pointer per onboarder. Purely client-side; no TTL — the reject-stale\n * policy (409 spec_stale on submit) is the authoritative staleness signal,\n * and a refetch overwrites \"latest\".\n */\nexport class OnboardingSpecCache {\n private readonly byVersion = new Map<string, OnboardingSpec>();\n private readonly latest = new Map<string, OnboardingSpec>();\n\n /**\n * Collision-proof composite key. Uses JSON.stringify of the tuple so an\n * onboarder or version containing the delimiter can never alias another\n * entry — and, critically, contains NO control characters (an earlier\n * NUL-separator made git treat this whole file as binary/undiffable).\n */\n private static versionKey(onboarder: string, specVersion: string): string {\n return JSON.stringify([onboarder, specVersion]);\n }\n\n get(onboarder: string, specVersion: string): OnboardingSpec | undefined {\n return this.byVersion.get(\n OnboardingSpecCache.versionKey(onboarder, specVersion),\n );\n }\n\n getLatest(onboarder: string): OnboardingSpec | undefined {\n return this.latest.get(onboarder);\n }\n\n set(onboarder: string, spec: OnboardingSpec): void {\n this.byVersion.set(\n OnboardingSpecCache.versionKey(onboarder, spec.specVersion),\n spec,\n );\n this.latest.set(onboarder, spec);\n }\n\n clear(): void {\n this.byVersion.clear();\n this.latest.clear();\n }\n}\n\n/** Module-level default cache (one per page). Injectable for tests/apps. */\nexport const defaultSpecCache = new OnboardingSpecCache();\n\n/**\n * Diff two spec versions' signup field sets — used by the 409 spec_stale\n * flow to tell the user WHAT changed. Compares by field key across all\n * signup steps.\n */\nexport interface SpecFieldDiff {\n added: string[];\n removed: string[];\n changed: string[];\n}\n\nfunction signupFieldMap(spec: OnboardingSpec): Map<string, SpecField> {\n const map = new Map<string, SpecField>();\n for (const step of spec.forms.signup.steps) {\n for (const field of step.fields) map.set(field.key, field);\n }\n return map;\n}\n\nexport function diffSpecFields(\n prev: OnboardingSpec,\n next: OnboardingSpec,\n): SpecFieldDiff {\n const prevMap = signupFieldMap(prev);\n const nextMap = signupFieldMap(next);\n const added: string[] = [];\n const removed: string[] = [];\n const changed: string[] = [];\n for (const key of nextMap.keys()) {\n if (!prevMap.has(key)) added.push(key);\n }\n for (const [key, prevField] of prevMap) {\n const nextField = nextMap.get(key);\n if (!nextField) {\n removed.push(key);\n continue;\n }\n if (JSON.stringify(prevField) !== JSON.stringify(nextField)) {\n changed.push(key);\n }\n }\n return {\n added: added.sort(),\n removed: removed.sort(),\n changed: changed.sort(),\n };\n}\n\nexport interface SpecFetchOptions {\n baseUrl: string;\n /**\n * Client-side cache key ONLY (never transmitted; trust is host-derived\n * per D536). Use the onboarder slug/id your embed is configured with.\n */\n onboarder: string;\n /** Custom fetch implementation (defaults to globalThis.fetch). */\n fetch?: typeof globalThis.fetch;\n /** Cache to read/write (defaults to the module-level cache). */\n cache?: OnboardingSpecCache;\n /**\n * Request timeout in ms (default 15s). A stalled intake aborts with\n * OnboardingNetworkError so the boot doesn't hang on \"Loading signup form…\".\n */\n timeoutMs?: number;\n /**\n * When true, return the cached \"latest\" for this onboarder if present\n * WITHOUT a network round-trip. Default false (spec fetches are cheap\n * and the server bumps specVersion on any change). The signup widget's\n * boot passes preferCached:true so a remount reuses the loaded spec.\n */\n preferCached?: boolean;\n}\n\nfunction buildUrl(baseUrl: string, path: string): string {\n const base = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n return `${base}${path}`;\n}\n\n/**\n * Fetch (and validate, and cache) the onboarding spec.\n *\n * @throws {OnboardingSpecInvalidError} response does not match D540 §1\n * @throws {OnboardingRateLimitError} on 429\n * @throws {OnboardingServerError} on any other non-2xx status\n * @throws {OnboardingNetworkError} on network failure OR timeout\n */\nexport async function fetchOnboardingSpec(\n options: SpecFetchOptions,\n): Promise<OnboardingSpec> {\n const cache = options.cache ?? defaultSpecCache;\n\n if (options.preferCached) {\n const cached = cache.getLatest(options.onboarder);\n if (cached) return cached;\n }\n\n const fetchImpl = options.fetch ?? globalThis.fetch;\n const timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n const url = buildUrl(options.baseUrl, SPEC_PATH);\n\n let response: Response;\n try {\n response = await fetchWithTimeout(url, {\n method: \"GET\",\n timeoutMs,\n fetchImpl,\n });\n } catch (err) {\n throw new OnboardingNetworkError(err);\n }\n\n if (response.status === 429) {\n const retryAfterHeader = response.headers.get(\"retry-after\");\n const retryAfter = retryAfterHeader\n ? Number.parseInt(retryAfterHeader, 10)\n : Number.NaN;\n throw new OnboardingRateLimitError(\n Number.isNaN(retryAfter) ? null : retryAfter,\n );\n }\n if (response.status < 200 || response.status >= 300) {\n throw new OnboardingServerError(\n response.status,\n `Spec fetch failed with status ${response.status}`,\n );\n }\n\n let body: unknown;\n try {\n body = await response.json();\n } catch {\n throw new OnboardingSpecInvalidError(\"response body is not JSON\");\n }\n\n const parsed = validateOnboardingSpec(body);\n if (!parsed.success) {\n const detail = parsed.error.issues\n .slice(0, 5)\n .map((i) => `${i.path.join(\".\") || \"(root)\"}: ${i.message}`)\n .join(\"; \");\n throw new OnboardingSpecInvalidError(detail);\n }\n\n cache.set(options.onboarder, parsed.data);\n return parsed.data;\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dynamic/derive.ts","../src/dynamic/stepper.ts"],"names":["z"],"mappings":"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","file":"chunk-4SWOVHZ2.js","sourcesContent":["/**\n * Spec → Zod validator derivation (D540 §3).\n *\n * Turns arbitrary SpecField descriptors from the served spec into Zod\n * schemas for CLIENT-side UX validation. The server re-validates\n * authoritatively (D540 §2) — client validation is a convenience layer,\n * so the derivation is deliberately conservative:\n *\n * - Known `type`s map to typed schemas (see FIELD TYPE MAP below).\n * - An UNKNOWN type falls back to z.string() (never blocks a field the\n * server knows how to validate).\n * - Known `validation` keys are applied (minLength, maxLength, min, max,\n * pattern, format:\"email\"). Unknown validation keys are IGNORED\n * client-side — the server enforces them.\n * - `required:false` fields accept undefined AND \"\" (an empty input is\n * \"not provided\").\n * - select/radio values must be one of `options`; multiselect values\n * must be a subset.\n *\n * FIELD TYPE MAP:\n * text | textarea | tel | (unknown) → string\n * email → string().email()\n * number → coerce.number (DOM inputs are strings)\n * checkbox | boolean → boolean\n * select | radio → string ∈ options\n * multiselect → string[] ⊆ options\n * date → string, parseable as a date\n */\n\nimport { z } from \"zod/v3\";\nimport type { SpecField, SpecStep } from \"../spec/model.js\";\n\n/** Extract the comparable value of an option entry ({value}, {key}, or scalar). */\nexport function optionValue(option: unknown): string {\n if (option !== null && typeof option === \"object\") {\n const o = option as Record<string, unknown>;\n if (\"value\" in o) return String(o[\"value\"]);\n if (\"key\" in o) return String(o[\"key\"]);\n }\n return String(option);\n}\n\ninterface KnownValidation {\n minLength?: number;\n maxLength?: number;\n min?: number;\n max?: number;\n pattern?: string;\n format?: string;\n}\n\nfunction knownValidation(field: SpecField): KnownValidation {\n const v = field.validation ?? {};\n const out: KnownValidation = {};\n if (typeof v[\"minLength\"] === \"number\") out.minLength = v[\"minLength\"];\n if (typeof v[\"maxLength\"] === \"number\") out.maxLength = v[\"maxLength\"];\n if (typeof v[\"min\"] === \"number\") out.min = v[\"min\"];\n if (typeof v[\"max\"] === \"number\") out.max = v[\"max\"];\n if (typeof v[\"pattern\"] === \"string\") out.pattern = v[\"pattern\"];\n if (typeof v[\"format\"] === \"string\") out.format = v[\"format\"];\n return out;\n}\n\nfunction stringSchema(field: SpecField): z.ZodTypeAny {\n const v = knownValidation(field);\n let s = z.string();\n if (field.type === \"email\" || v.format === \"email\") {\n s = s.email(`${field.label} must be a valid email`);\n }\n if (v.minLength !== undefined) {\n s = s.min(\n v.minLength,\n `${field.label} must be at least ${v.minLength} characters`,\n );\n }\n if (v.maxLength !== undefined) {\n s = s.max(\n v.maxLength,\n `${field.label} must be at most ${v.maxLength} characters`,\n );\n }\n if (v.pattern !== undefined) {\n try {\n s = s.regex(\n new RegExp(v.pattern),\n `${field.label} has an invalid format`,\n );\n } catch {\n // Invalid regex in the spec — skip client-side; server enforces.\n }\n }\n return s;\n}\n\nfunction numberSchema(field: SpecField): z.ZodTypeAny {\n const v = knownValidation(field);\n let s = z.coerce.number({\n invalid_type_error: `${field.label} must be a number`,\n });\n if (v.min !== undefined) {\n s = s.min(v.min, `${field.label} must be at least ${v.min}`);\n }\n if (v.max !== undefined) {\n s = s.max(v.max, `${field.label} must be at most ${v.max}`);\n }\n return s;\n}\n\nfunction enumSchema(field: SpecField): z.ZodTypeAny {\n const allowed = (field.options ?? []).map(optionValue);\n if (allowed.length === 0) return z.string();\n return z.string().refine((val) => allowed.includes(val), {\n message: `${field.label} must be one of the offered options`,\n });\n}\n\nfunction multiEnumSchema(field: SpecField): z.ZodTypeAny {\n const allowed = (field.options ?? []).map(optionValue);\n const item =\n allowed.length === 0\n ? z.string()\n : z.string().refine((val) => allowed.includes(val), {\n message: `${field.label} contains a value outside the offered options`,\n });\n return z.array(item);\n}\n\nfunction dateSchema(field: SpecField): z.ZodTypeAny {\n return z.string().refine((val) => !Number.isNaN(Date.parse(val)), {\n message: `${field.label} must be a valid date`,\n });\n}\n\n/** Derive the value schema for one field, BEFORE the required/optional wrap. */\nfunction baseSchema(field: SpecField): z.ZodTypeAny {\n switch (field.type) {\n case \"email\":\n return stringSchema(field);\n case \"number\":\n return numberSchema(field);\n case \"checkbox\":\n case \"boolean\":\n return z.boolean();\n case \"select\":\n case \"radio\":\n return enumSchema(field);\n case \"multiselect\":\n return multiEnumSchema(field);\n case \"date\":\n return dateSchema(field);\n default:\n // text | textarea | tel | any unknown type → string with validation\n return stringSchema(field);\n }\n}\n\n/**\n * Derive the full Zod schema for one SpecField.\n * Optional fields accept undefined and \"\" (\"not provided\" in a form UI).\n * Required fields additionally reject \"\" for string-typed inputs.\n */\nexport function deriveFieldSchema(field: SpecField): z.ZodTypeAny {\n const base = baseSchema(field);\n if (field.required) {\n // Required string inputs must be non-empty.\n if (\n field.type !== \"checkbox\" &&\n field.type !== \"boolean\" &&\n field.type !== \"number\" &&\n field.type !== \"multiselect\"\n ) {\n return z\n .string({ required_error: `${field.label} is required` })\n .min(1, `${field.label} is required`)\n .pipe(base);\n }\n if (field.type === \"number\") {\n // A required number must be PRESENT. Without this guard,\n // z.coerce.number(\"\") coerces the empty DOM value to 0 and passes —\n // then buildSubmitPayload drops the \"\" answer, so the client declares\n // the form valid while the server 422s on a missing required field.\n // Reject empty/missing BEFORE coercion; a provided value flows to\n // the coercing number schema (min/max still apply).\n return z\n .any()\n .refine(\n (val) => val !== \"\" && val !== undefined && val !== null,\n `${field.label} is required`,\n )\n .pipe(base);\n }\n if (field.type === \"multiselect\") {\n return base.refine(\n (val) => Array.isArray(val) && val.length > 0,\n `${field.label} requires at least one selection`,\n );\n }\n return base;\n }\n // Optional: \"\" normalizes to undefined before validation.\n return z.preprocess(\n (val) =>\n val === \"\" || val === undefined || val === null ? undefined : val,\n base.optional(),\n );\n}\n\n/**\n * Derive a STRICT Zod object schema for one step's fields — unknown answer\n * keys within the step are rejected (mirrors the server's authoritative\n * re-validation posture; a stale client must not silently carry fields the\n * spec no longer defines).\n */\nexport function deriveStepSchema(step: SpecStep) {\n const shape: Record<string, z.ZodTypeAny> = {};\n for (const field of step.fields) {\n shape[field.key] = deriveFieldSchema(field);\n }\n return z.object(shape).strict();\n}\n\n/**\n * Derive the STRICT schema for the complete answers object across all\n * steps (used at submit time and by the 409-stale re-validation).\n */\nexport function deriveAnswersSchema(steps: SpecStep[]) {\n const shape: Record<string, z.ZodTypeAny> = {};\n for (const step of steps) {\n for (const field of step.fields) {\n shape[field.key] = deriveFieldSchema(field);\n }\n }\n return z.object(shape).strict();\n}\n","/**\n * Headless dynamic signup stepper (D540 §1/§3).\n *\n * The steps are GENERATED from the served spec: fields bucket by `stage`\n * server-side → the spec's forms.signup.steps array. More configured\n * fields ⇒ more steps. The final `activation` stage carries the\n * service-selection inputs feeding the existing terminal-stage guards.\n *\n * Framework-agnostic: same subscribe/mutate contract as the Phase-1\n * contact-form state. The default component layer renders it; framework\n * integrations can wrap it.\n *\n * 409 spec_stale flow (D540 reject-stale): the CALLER (widget/submit\n * orchestration) refetches the spec on OnboardingSpecStaleError and calls\n * `applyNewSpec(newSpec)`. The stepper re-validates every answer against\n * the NEW derived schema, DROPS answers whose fields no longer exist,\n * marks `specStale: true`, and repositions to the first step with errors —\n * the user re-confirms; the SDK NEVER silently auto-resubmits.\n */\n\nimport { z, type ZodIssue } from \"zod/v3\";\nimport type { OnboardingSpec, SpecStep, TerminalStage } from \"../spec/model.js\";\nimport { deriveAnswersSchema, deriveStepSchema } from \"./derive.js\";\nimport type { FieldError } from \"../form/state.js\";\n\nexport type StepperStatus = \"editing\" | \"submitting\" | \"success\" | \"error\";\n\nexport interface SignupConsent {\n tosAccepted: boolean;\n privacyAccepted: boolean;\n}\n\nexport interface SignupCredentials {\n email: string;\n password: string;\n}\n\nexport interface SignupStepperState {\n /** Zero-based index of the current step. */\n stepIndex: number;\n stepCount: number;\n currentStep: SpecStep | null;\n /** Answers keyed by field key (only keys defined by the active spec). */\n answers: Record<string, unknown>;\n /** Derived from serviceSelection-flagged answers (see buildSubmitPayload). */\n selectedServices: string[];\n /** Validation errors for the CURRENT step (or full-form at submit). */\n errors: FieldError[];\n /** Credential/consent errors (submit-time). */\n accountErrors: FieldError[];\n consent: SignupConsent;\n credentials: SignupCredentials;\n status: StepperStatus;\n serverError: string | null;\n /** True after a 409-stale spec swap until the user re-validates. */\n specStale: boolean;\n /** The specVersion currently driving the form (echoed on submit). */\n specVersion: string;\n /** Set on success from the submit response. */\n terminalStage: TerminalStage | null;\n}\n\nexport type SignupStepperListener = (state: SignupStepperState) => void;\n\n/** The locked credential constraints (PublicOnboardingSubmitRequest). */\nconst credentialsSchema = z.object({\n email: z.string().email(\"A valid account email is required\"),\n password: z\n .string()\n .min(12, \"Password must be at least 12 characters\")\n .max(256, \"Password must be at most 256 characters\"),\n});\n\nfunction zodIssuesToFieldErrors(issues: ZodIssue[]): FieldError[] {\n return issues.map((issue) => ({\n path: issue.path.join(\".\"),\n message: issue.message,\n }));\n}\n\n/** Flatten a serviceSelection field's answer into service ids. */\nfunction serviceIdsOf(key: string, value: unknown): string[] {\n if (typeof value === \"string\" && value !== \"\") return [value];\n if (Array.isArray(value)) return value.map((v) => String(v));\n if (value === true) return [key];\n return [];\n}\n\nexport interface SignupStepperControl {\n getState(): SignupStepperState;\n setAnswer(key: string, value: unknown): void;\n setConsent(consent: Partial<SignupConsent>): void;\n setCredentials(credentials: Partial<SignupCredentials>): void;\n /** Validate ONLY the current step. Returns true when clean. */\n validateStep(): boolean;\n /** Validate current step; advance when clean. Returns true if advanced. */\n next(): boolean;\n back(): void;\n isFirstStep(): boolean;\n isLastStep(): boolean;\n /**\n * Full-form validation: every step's answers + credentials + consent.\n * Populates errors/accountErrors. Returns true when submittable.\n */\n validateAll(): boolean;\n /**\n * The submit payload body (minus botToken, which the transport attaches).\n * Call ONLY after validateAll() returns true.\n */\n buildSubmitPayload(): {\n specVersion: string;\n answers: Record<string, unknown>;\n selectedServices?: string[];\n consent: { tosAccepted: true; privacyAccepted: true };\n credentials: SignupCredentials;\n };\n /**\n * Swap in a refetched spec after 409 spec_stale. Drops answers for\n * removed fields, re-validates the rest, marks specStale, repositions\n * to the first step with errors. NEVER auto-resubmits.\n */\n applyNewSpec(spec: OnboardingSpec): void;\n /** Clear the specStale flag once the user has re-confirmed. */\n acknowledgeSpecStale(): void;\n setSubmitting(): void;\n setSuccess(terminalStage: TerminalStage): void;\n setError(message: string): void;\n /**\n * Reset to the pristine initial state against the CURRENT spec: step 0,\n * status \"editing\", answers/credentials/consent/errors cleared. This is\n * the only transition back OUT of \"success\"/\"error\"; the widget's reset()\n * relies on it (otherwise a post-success keystroke would re-emit\n * status \"success\" and re-hide the form).\n */\n reset(): void;\n subscribe(listener: SignupStepperListener): () => void;\n}\n\n/**\n * Create a headless stepper for the spec's signup form.\n *\n * @example\n * ```ts\n * const spec = await fetchOnboardingSpec({ baseUrl, onboarder });\n * const stepper = createSignupStepper(spec);\n * stepper.subscribe(render);\n * stepper.setAnswer(\"company_name\", \"ACME\");\n * if (stepper.next()) { ... }\n * ```\n */\nexport function createSignupStepper(\n spec: OnboardingSpec,\n): SignupStepperControl {\n let steps: SpecStep[] = spec.forms.signup.steps;\n let specVersion = spec.specVersion;\n\n let state: SignupStepperState = {\n stepIndex: 0,\n stepCount: steps.length,\n currentStep: steps[0] ?? null,\n answers: {},\n selectedServices: [],\n errors: [],\n accountErrors: [],\n consent: { tosAccepted: false, privacyAccepted: false },\n credentials: { email: \"\", password: \"\" },\n status: \"editing\",\n serverError: null,\n specStale: false,\n specVersion,\n terminalStage: null,\n };\n\n // Which step indices have been validated at least once. Once a step has\n // been validated, EVERY subsequent setAnswer revalidates it — so a\n // fix-then-rebreak on that step re-shows the error immediately (matching\n // the Phase-1 contact-form `hasValidated` fix). Before a step is validated,\n // typing does not spray errors. Cleared on reset()/applyNewSpec.\n let validatedSteps = new Set<number>();\n\n const listeners = new Set<SignupStepperListener>();\n\n function emit(): void {\n for (const listener of listeners) {\n listener(structuredClone(state));\n }\n }\n\n function knownFieldKeys(): Set<string> {\n const keys = new Set<string>();\n for (const step of steps) {\n for (const field of step.fields) keys.add(field.key);\n }\n return keys;\n }\n\n /** Answers restricted to the current step's fields. */\n function stepAnswers(step: SpecStep): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const field of step.fields) {\n if (field.key in state.answers) out[field.key] = state.answers[field.key];\n }\n return out;\n }\n\n function deriveSelectedServices(): string[] {\n const services: string[] = [];\n for (const step of steps) {\n for (const field of step.fields) {\n if (field.serviceSelection === true) {\n services.push(...serviceIdsOf(field.key, state.answers[field.key]));\n }\n }\n }\n return [...new Set(services)];\n }\n\n function runStepValidation(step: SpecStep): FieldError[] {\n const result = deriveStepSchema(step).safeParse(stepAnswers(step));\n return result.success ? [] : zodIssuesToFieldErrors(result.error.issues);\n }\n\n function firstStepWithErrors(): number {\n for (let i = 0; i < steps.length; i++) {\n const step = steps[i];\n if (step && runStepValidation(step).length > 0) return i;\n }\n return 0;\n }\n\n return {\n getState() {\n return structuredClone(state);\n },\n\n setAnswer(key, value) {\n state = {\n ...state,\n answers: { ...state.answers, [key]: value },\n };\n state.selectedServices = deriveSelectedServices();\n // Live-revalidate once THIS step has been validated at least once —\n // regardless of whether errors are currently showing. This is the\n // fix-then-rebreak fix: after fixing a field (errors → 0), breaking it\n // again must re-show the error without a fresh next()/validateStep().\n if (validatedSteps.has(state.stepIndex) && state.currentStep) {\n state = { ...state, errors: runStepValidation(state.currentStep) };\n }\n emit();\n },\n\n setConsent(consent) {\n state = { ...state, consent: { ...state.consent, ...consent } };\n emit();\n },\n\n setCredentials(credentials) {\n state = {\n ...state,\n credentials: { ...state.credentials, ...credentials },\n };\n emit();\n },\n\n validateStep() {\n if (!state.currentStep) return true;\n validatedSteps.add(state.stepIndex);\n const errors = runStepValidation(state.currentStep);\n state = { ...state, errors };\n emit();\n return errors.length === 0;\n },\n\n next() {\n if (!state.currentStep) return false;\n validatedSteps.add(state.stepIndex);\n const errors = runStepValidation(state.currentStep);\n if (errors.length > 0) {\n state = { ...state, errors };\n emit();\n return false;\n }\n if (state.stepIndex >= steps.length - 1) return false;\n const stepIndex = state.stepIndex + 1;\n state = {\n ...state,\n stepIndex,\n currentStep: steps[stepIndex] ?? null,\n errors: [],\n };\n emit();\n return true;\n },\n\n back() {\n if (state.stepIndex === 0) return;\n const stepIndex = state.stepIndex - 1;\n state = {\n ...state,\n stepIndex,\n currentStep: steps[stepIndex] ?? null,\n errors: [],\n };\n emit();\n },\n\n isFirstStep() {\n return state.stepIndex === 0;\n },\n\n isLastStep() {\n return state.stepIndex >= steps.length - 1;\n },\n\n validateAll() {\n // A full validation counts as validating every step.\n for (let i = 0; i < steps.length; i++) validatedSteps.add(i);\n // Answers across ALL steps (strict — unknown keys rejected).\n const answersResult = deriveAnswersSchema(steps).safeParse(state.answers);\n const errors = answersResult.success\n ? []\n : zodIssuesToFieldErrors(answersResult.error.issues);\n\n // Credentials + consent (locked submit contract).\n const accountErrors: FieldError[] = [];\n const credResult = credentialsSchema.safeParse(state.credentials);\n if (!credResult.success) {\n accountErrors.push(...zodIssuesToFieldErrors(credResult.error.issues));\n }\n if (state.consent.tosAccepted !== true) {\n accountErrors.push({\n path: \"tosAccepted\",\n message: \"You must accept the Terms of Service\",\n });\n }\n if (state.consent.privacyAccepted !== true) {\n accountErrors.push({\n path: \"privacyAccepted\",\n message: \"You must accept the Privacy Policy\",\n });\n }\n\n state = { ...state, errors, accountErrors };\n emit();\n return errors.length === 0 && accountErrors.length === 0;\n },\n\n buildSubmitPayload() {\n // COMPLIANCE-CRITICAL: never fabricate legal consent. Read the ACTUAL\n // consent state and refuse to build a payload unless the user genuinely\n // accepted both. A headless caller that skips validateAll() must not be\n // able to transmit consent:true for a user who never consented.\n if (\n state.consent.tosAccepted !== true ||\n state.consent.privacyAccepted !== true\n ) {\n throw new Error(\n \"buildSubmitPayload: cannot build a submit payload without genuine \" +\n \"user consent (tosAccepted && privacyAccepted must both be true). \" +\n \"Call validateAll() and surface the consent errors to the user first.\",\n );\n }\n\n // Run answers through the derived schema so the wire carries COERCED\n // typed values (e.g. number fields as numbers, not raw DOM strings) —\n // this is the exact validation validateAll() applies, so a payload can\n // only be built from a state that would pass. A required field that is\n // empty/missing FAILS here (see deriveFieldSchema's required wrap),\n // so buildSubmitPayload throws rather than silently dropping it.\n const parsed = deriveAnswersSchema(steps).safeParse(state.answers);\n if (!parsed.success) {\n const detail = parsed.error.issues\n .map((i) => `${i.path.join(\".\") || \"(answers)\"}: ${i.message}`)\n .join(\"; \");\n throw new Error(\n `buildSubmitPayload: answers failed validation — call validateAll() first (${detail}).`,\n );\n }\n\n // Only keys the active spec defines, with empty optionals omitted.\n const known = knownFieldKeys();\n const answers: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(parsed.data)) {\n if (known.has(key) && value !== undefined && value !== \"\") {\n answers[key] = value;\n }\n }\n const selectedServices = deriveSelectedServices();\n return {\n specVersion,\n answers,\n ...(selectedServices.length > 0 ? { selectedServices } : {}),\n consent: {\n tosAccepted: state.consent.tosAccepted,\n privacyAccepted: state.consent.privacyAccepted,\n } as { tosAccepted: true; privacyAccepted: true },\n credentials: { ...state.credentials },\n };\n },\n\n applyNewSpec(newSpec) {\n steps = newSpec.forms.signup.steps;\n specVersion = newSpec.specVersion;\n\n // Drop answers for fields the new spec no longer defines.\n const known = new Set<string>();\n for (const step of steps) {\n for (const field of step.fields) known.add(field.key);\n }\n const answers: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(state.answers)) {\n if (known.has(key)) answers[key] = value;\n }\n\n state = {\n ...state,\n answers,\n stepCount: steps.length,\n specVersion,\n specStale: true,\n status: \"editing\",\n serverError: null,\n };\n state.selectedServices = deriveSelectedServices();\n\n // Reposition to the first step with errors under the NEW schema so\n // the user reviews exactly what changed; errors render immediately.\n const stepIndex = Math.min(firstStepWithErrors(), steps.length - 1);\n const currentStep = steps[Math.max(stepIndex, 0)] ?? null;\n // The repositioned step is being shown with errors → treat it as\n // validated so live-revalidation is active there immediately.\n validatedSteps = new Set<number>([Math.max(stepIndex, 0)]);\n state = {\n ...state,\n stepIndex: Math.max(stepIndex, 0),\n currentStep,\n errors: currentStep ? runStepValidation(currentStep) : [],\n };\n emit();\n },\n\n acknowledgeSpecStale() {\n state = { ...state, specStale: false };\n emit();\n },\n\n setSubmitting() {\n state = {\n ...state,\n status: \"submitting\",\n serverError: null,\n };\n emit();\n },\n\n setSuccess(terminalStage) {\n state = { ...state, status: \"success\", terminalStage, serverError: null };\n emit();\n },\n\n setError(message) {\n state = { ...state, status: \"error\", serverError: message };\n emit();\n },\n\n reset() {\n validatedSteps = new Set<number>();\n state = {\n stepIndex: 0,\n stepCount: steps.length,\n currentStep: steps[0] ?? null,\n answers: {},\n selectedServices: [],\n errors: [],\n accountErrors: [],\n consent: { tosAccepted: false, privacyAccepted: false },\n credentials: { email: \"\", password: \"\" },\n status: \"editing\",\n serverError: null,\n specStale: false,\n specVersion,\n terminalStage: null,\n };\n emit();\n },\n\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/form/state.ts"],"names":[],"mappings":"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","file":"chunk-AFCJLUPK.js","sourcesContent":["/**\n * Headless form state manager for the contact form.\n *\n * Designed to be framework-agnostic — callers supply a change callback\n * and call the returned mutators. The widget layer uses this internally;\n * framework integrations (React, Vue, etc.) can wrap it in their own hooks.\n */\n\nimport type { ZodIssue } from \"zod/v3\";\nimport { contactFormSchema, type ContactFormValues } from \"./schema.js\";\n\nexport interface FieldError {\n path: string;\n message: string;\n}\n\nexport type FormStatus = \"idle\" | \"submitting\" | \"success\" | \"error\";\n\nexport interface ContactFormState {\n values: ContactFormValues;\n /** Per-field validation errors from the last validate() call. */\n errors: FieldError[];\n /** Cross-field / server error message. */\n serverError: string | null;\n status: FormStatus;\n /** True if values have been touched at least once. */\n dirty: boolean;\n}\n\nexport type ContactFormStateListener = (state: ContactFormState) => void;\n\nexport interface ContactFormControl {\n /** Current snapshot of form state. */\n getState(): ContactFormState;\n /** Update a single field value and re-validate if dirty. */\n setValue<K extends keyof ContactFormValues>(\n field: K,\n value: ContactFormValues[K],\n ): void;\n /**\n * Run full client-side validation. Returns true if valid.\n * Also marks the form dirty so future setValue calls auto-validate.\n */\n validate(): boolean;\n /** Mark the form as submitting. Called by the submit client. */\n setSubmitting(): void;\n /** Mark the form as successfully submitted. */\n setSuccess(): void;\n /** Set a server-side error (e.g. 429 rate limited). */\n setError(message: string): void;\n /** Reset to initial idle state. */\n reset(): void;\n /** Subscribe to state changes. Returns unsubscribe function. */\n subscribe(listener: ContactFormStateListener): () => void;\n}\n\nfunction zodIssuesToFieldErrors(issues: ZodIssue[]): FieldError[] {\n return issues.map((issue) => ({\n path: issue.path.join(\".\"),\n message: issue.message,\n }));\n}\n\nconst INITIAL_VALUES: ContactFormValues = {\n name: \"\",\n email: \"\",\n phone: undefined,\n message: undefined,\n};\n\n/**\n * Create a new headless contact form state controller.\n *\n * @example\n * ```ts\n * const form = createContactFormState();\n * form.subscribe((state) => renderUI(state));\n * form.setValue(\"name\", \"Alice\");\n * const valid = form.validate();\n * if (valid) await submitContact(form.getState().values, botToken);\n * ```\n */\nexport function createContactFormState(): ContactFormControl {\n let state: ContactFormState = {\n values: { ...INITIAL_VALUES },\n errors: [],\n serverError: null,\n status: \"idle\",\n dirty: false,\n };\n\n const listeners = new Set<ContactFormStateListener>();\n\n function emit(): void {\n for (const listener of listeners) {\n listener({ ...state, values: { ...state.values } });\n }\n }\n\n function runValidation(): FieldError[] {\n const result = contactFormSchema.safeParse(state.values);\n if (result.success) return [];\n return zodIssuesToFieldErrors(result.error.issues);\n }\n\n // Once validate() has run at least once, EVERY subsequent setValue\n // revalidates — errors both clear when a field is fixed AND reappear\n // when a fixed field is broken again. (Previously the revalidation was\n // gated on errors.length > 0, so fix-then-rebreak silently showed no\n // error until the next explicit validate().)\n let hasValidated = false;\n\n return {\n getState() {\n return { ...state, values: { ...state.values } };\n },\n\n setValue(field, value) {\n state = {\n ...state,\n values: { ...state.values, [field]: value },\n dirty: true,\n };\n if (hasValidated) {\n state = { ...state, errors: runValidation() };\n }\n emit();\n },\n\n validate() {\n hasValidated = true;\n const errors = runValidation();\n state = { ...state, errors, dirty: true };\n emit();\n return errors.length === 0;\n },\n\n setSubmitting() {\n state = { ...state, status: \"submitting\", serverError: null, errors: [] };\n emit();\n },\n\n setSuccess() {\n state = { ...state, status: \"success\", serverError: null };\n emit();\n },\n\n setError(message) {\n state = { ...state, status: \"error\", serverError: message };\n emit();\n },\n\n reset() {\n hasValidated = false;\n state = {\n values: { ...INITIAL_VALUES },\n errors: [],\n serverError: null,\n status: \"idle\",\n dirty: false,\n };\n emit();\n },\n\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/submit/errors.ts","../src/submit/http.ts"],"names":[],"mappings":";AAwBO,IAAM,eAAA,GAAN,cAA8B,KAAA,CAAM;AAAA,EACzB,IAAA;AAAA,EACA,UAAA;AAAA,EAEhB,WAAA,CACE,IAAA,EACA,OAAA,EACA,UAAA,GAA4B,IAAA,EAC5B;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAAA,EACpB;AACF;AAEO,IAAM,2BAAA,GAAN,cAA0C,eAAA,CAAgB;AAAA,EAC/D,YAAY,OAAA,EAAkB;AAC5B,IAAA,KAAA;AAAA,MACE,sBAAA;AAAA,MACA,OAAA,IACE,kGAAA;AAAA,MACF;AAAA,KACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,6BAAA;AAAA,EACd;AACF;AAUO,IAAM,uBAAA,GAAN,cAAsC,eAAA,CAAgB;AAAA,EAC3D,WAAA,CAAY,UAAA,GAAa,GAAA,EAAK,OAAA,EAAkB;AAC9C,IAAA,KAAA;AAAA,MACE,UAAA;AAAA,MACA,OAAA,IACE,qDAAqD,UAAU,CAAA,EAAA,CAAA;AAAA,MACjE;AAAA,KACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,yBAAA;AAAA,EACd;AACF;AAEO,IAAM,wBAAA,GAAN,cAAuC,eAAA,CAAgB;AAAA,EAC5C,iBAAA;AAAA,EAEhB,WAAA,CAAY,oBAAmC,IAAA,EAAM;AACnD,IAAA,KAAA;AAAA,MACE,cAAA;AAAA,MACA,iBAAA,KAAsB,IAAA,GAClB,CAAA,0BAAA,EAA6B,iBAAiB,CAAA,EAAA,CAAA,GAC9C,4CAAA;AAAA,MACJ;AAAA,KACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,0BAAA;AACZ,IAAA,IAAA,CAAK,iBAAA,GAAoB,iBAAA;AAAA,EAC3B;AACF;AASO,IAAM,wBAAA,GAAN,cAAuC,eAAA,CAAgB;AAAA,EAC5C,kBAAA;AAAA,EAEhB,YAAY,kBAAA,EAA4B;AACtC,IAAA,KAAA;AAAA,MACE,YAAA;AAAA,MACA,sDAAsD,kBAAkB,CAAA,gCAAA,CAAA;AAAA,MACxE;AAAA,KACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,0BAAA;AACZ,IAAA,IAAA,CAAK,kBAAA,GAAqB,kBAAA;AAAA,EAC5B;AACF;AAEO,IAAM,qBAAA,GAAN,cAAoC,eAAA,CAAgB;AAAA,EACzD,WAAA,CAAY,YAAoB,OAAA,EAAkB;AAChD,IAAA,KAAA;AAAA,MACE,cAAA;AAAA,MACA,OAAA,IAAW,iBAAiB,UAAU,CAAA,+BAAA,CAAA;AAAA,MACtC;AAAA,KACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,uBAAA;AAAA,EACd;AACF;AAEO,IAAM,sBAAA,GAAN,cAAqC,eAAA,CAAgB;AAAA,EAC1D,YAAY,KAAA,EAAiB;AAC3B,IAAA,KAAA;AAAA,MACE,eAAA;AAAA,MACA,2DAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,wBAAA;AACZ,IAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,MAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AAAA,IACf;AAAA,EACF;AACF;AAEO,IAAM,yBAAA,GAAN,cAAwC,eAAA,CAAgB;AAAA,EAC7D,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,kBAAA,EAAoB,SAAS,GAAG,CAAA;AACtC,IAAA,IAAA,CAAK,IAAA,GAAO,2BAAA;AAAA,EACd;AACF;AAQO,IAAM,0BAAA,GAAN,cAAyC,eAAA,CAAgB;AAAA,EAC9D,YAAY,MAAA,EAAgB;AAC1B,IAAA,KAAA;AAAA,MACE,cAAA;AAAA,MACA,kEAAkE,MAAM,CAAA,CAAA;AAAA,MACxE;AAAA,KACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,4BAAA;AAAA,EACd;AACF;;;AChJO,IAAM,0BAAA,GAA6B;AAe1C,eAAsB,gBAAA,CACpB,GAAA,EACA,OAAA,GAA+B,EAAC,EACb;AACnB,EAAA,MAAM;AAAA,IACJ,SAAA,GAAY,0BAAA;AAAA,IACZ,YAAY,UAAA,CAAW,KAAA;AAAA,IACvB,MAAA,EAAQ,YAAA;AAAA,IACR,GAAG;AAAA,GACL,GAAI,OAAA;AAEJ,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,MAAM,KAAA,GAAQ,WAAW,MAAM;AAC7B,IAAA,UAAA,CAAW,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,SAAS,IAAI,CAAC,CAAA;AAAA,EACtE,GAAG,SAAS,CAAA;AAGZ,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA,IAAI,aAAa,OAAA,EAAS;AACxB,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,UAAA,CAAW,KAAA,CAAM,aAAa,MAAM,CAAA;AAAA,IACtC,CAAA,MAAO;AACL,MAAA,YAAA,CAAa,gBAAA;AAAA,QACX,OAAA;AAAA,QACA,MAAM,UAAA,CAAW,KAAA,CAAM,YAAA,CAAa,MAAM,CAAA;AAAA,QAC1C,EAAE,MAAM,IAAA;AAAK,OACf;AAAA,IACF;AAAA,EACF;AAEA,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,UAAU,GAAA,EAAK,EAAE,GAAG,IAAA,EAAM,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAQ,CAAA;AAAA,EACpE,CAAA,SAAE;AACA,IAAA,YAAA,CAAa,KAAK,CAAA;AAAA,EACpB;AACF;AAQO,SAAS,WAAA,CACd,EAAA,EACA,SAAA,EACA,SAAA,EACY;AACZ,EAAA,OAAO,IAAI,OAAA,CAAW,CAAC,OAAA,EAAS,MAAA,KAAW;AACzC,IAAA,IAAI,OAAA,GAAU,KAAA;AACd,IAAA,MAAM,KAAA,GAAQ,WAAW,MAAM;AAC7B,MAAA,IAAI,OAAA,EAAS;AACb,MAAA,OAAA,GAAU,IAAA;AACV,MAAA,MAAA,CAAO,WAAW,CAAA;AAAA,IACpB,GAAG,SAAS,CAAA;AACZ,IAAA,EAAA,CAAG,IAAA;AAAA,MACD,CAAC,CAAA,KAAM;AACL,QAAA,IAAI,OAAA,EAAS;AACb,QAAA,OAAA,GAAU,IAAA;AACV,QAAA,YAAA,CAAa,KAAK,CAAA;AAClB,QAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,MACX,CAAA;AAAA,MACA,CAAC,GAAA,KAAQ;AACP,QAAA,IAAI,OAAA,EAAS;AACb,QAAA,OAAA,GAAU,IAAA;AACV,QAAA,YAAA,CAAa,KAAK,CAAA;AAClB,QAAA,MAAA,CAAO,GAAG,CAAA;AAAA,MACZ;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH","file":"chunk-AW7W3HAC.js","sourcesContent":["/**\n * Error taxonomy for the onboarding submit client.\n *\n * Maps HTTP status codes and server error bodies to typed errors:\n * 403 bot_challenge_failed → OnboardingBotChallengeError\n * 409 spec_stale (SUBMIT) → OnboardingSpecStaleError (submit endpoint only,\n * body { error:true, code:\"spec_stale\",\n * currentSpecVersion })\n * 409 (other / LEADS) → OnboardingConflictError (generic conflict)\n * 429 → OnboardingRateLimitError\n * 5xx → OnboardingServerError (retryable)\n * network / timeout → OnboardingNetworkError\n */\n\nexport type OnboardingErrorKind =\n | \"bot_challenge_failed\"\n | \"spec_stale\"\n | \"spec_invalid\"\n | \"conflict\"\n | \"rate_limited\"\n | \"server_error\"\n | \"network_error\"\n | \"validation_error\";\n\nexport class OnboardingError extends Error {\n public readonly kind: OnboardingErrorKind;\n public readonly statusCode: number | null;\n\n constructor(\n kind: OnboardingErrorKind,\n message: string,\n statusCode: number | null = null,\n ) {\n super(message);\n this.name = \"OnboardingError\";\n this.kind = kind;\n this.statusCode = statusCode;\n }\n}\n\nexport class OnboardingBotChallengeError extends OnboardingError {\n constructor(message?: string) {\n super(\n \"bot_challenge_failed\",\n message ??\n \"Bot challenge verification failed (403 bot_challenge_failed). Refresh the CAPTCHA and try again.\",\n 403,\n );\n this.name = \"OnboardingBotChallengeError\";\n }\n}\n\n/**\n * A 409 that is NOT spec_stale. On the /leads path there is no spec_stale\n * concept (the payload carries no specVersion); on /submit, a 409 that\n * lacks `code:\"spec_stale\"` also lands here. Distinct from\n * OnboardingSpecStaleError so headless consumers switching on `kind` get the\n * correct diagnosis (a generic conflict, e.g. a D538 same-key concurrent\n * race) rather than a \"form has been updated, refresh\" recovery message.\n */\nexport class OnboardingConflictError extends OnboardingError {\n constructor(statusCode = 409, message?: string) {\n super(\n \"conflict\",\n message ??\n `Request conflicted with the current server state (${statusCode}).`,\n statusCode,\n );\n this.name = \"OnboardingConflictError\";\n }\n}\n\nexport class OnboardingRateLimitError extends OnboardingError {\n public readonly retryAfterSeconds: number | null;\n\n constructor(retryAfterSeconds: number | null = null) {\n super(\n \"rate_limited\",\n retryAfterSeconds !== null\n ? `Rate limited. Retry after ${retryAfterSeconds}s.`\n : \"Rate limited. Please wait before retrying.\",\n 429,\n );\n this.name = \"OnboardingRateLimitError\";\n this.retryAfterSeconds = retryAfterSeconds;\n }\n}\n\n/**\n * A /submit 409 whose body carries `code:\"spec_stale\"` (D540 reject-stale).\n * The PINNED body shape (confirmed against the crm intake) is exactly:\n * { error: true, code: \"spec_stale\", currentSpecVersion: string }\n * The version is read ONLY from `currentSpecVersion` — no field hedging.\n * Submit-endpoint only; /leads 409s are OnboardingConflictError.\n */\nexport class OnboardingSpecStaleError extends OnboardingError {\n public readonly currentSpecVersion: string;\n\n constructor(currentSpecVersion: string) {\n super(\n \"spec_stale\",\n `The onboarding form has been updated (new version: ${currentSpecVersion}). Please refresh and try again.`,\n 409,\n );\n this.name = \"OnboardingSpecStaleError\";\n this.currentSpecVersion = currentSpecVersion;\n }\n}\n\nexport class OnboardingServerError extends OnboardingError {\n constructor(statusCode: number, message?: string) {\n super(\n \"server_error\",\n message ?? `Server error (${statusCode}). The request will be retried.`,\n statusCode,\n );\n this.name = \"OnboardingServerError\";\n }\n}\n\nexport class OnboardingNetworkError extends OnboardingError {\n constructor(cause?: unknown) {\n super(\n \"network_error\",\n \"Network error — check your connection and try again.\",\n null,\n );\n this.name = \"OnboardingNetworkError\";\n if (cause instanceof Error) {\n this.cause = cause;\n }\n }\n}\n\nexport class OnboardingValidationError extends OnboardingError {\n constructor(message: string) {\n super(\"validation_error\", message, 422);\n this.name = \"OnboardingValidationError\";\n }\n}\n\n/**\n * The spec endpoint returned a body that does not conform to the LOCKED\n * D540 §1 response shape. This is a server-side contract violation — the\n * SDK refuses to render a form from a malformed spec (a half-parsed spec\n * could silently drop required fields).\n */\nexport class OnboardingSpecInvalidError extends OnboardingError {\n constructor(detail: string) {\n super(\n \"spec_invalid\",\n `The onboarding spec response does not match the D540 contract: ${detail}`,\n null,\n );\n this.name = \"OnboardingSpecInvalidError\";\n }\n}\n","/**\n * Shared HTTP helper: fetch bounded by a timeout via AbortController.\n *\n * Every SDK wire call (spec fetch, leads submit, signup submit) MUST be\n * bounded — the widgets flip to a disabled/\"Sending…\"/\"Loading…\" state\n * BEFORE the await, so an intake that accepts the connection and then stalls\n * would otherwise hang the UI forever. On timeout the fetch is aborted and a\n * typed OnboardingNetworkError is raised by the caller (which maps abort/\n * network failures uniformly).\n */\n\n/** Default per-request timeout. Generous enough for a slow-but-alive intake. */\nexport const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;\n\nexport interface TimeoutFetchOptions extends RequestInit {\n /** Per-request timeout in ms. Default DEFAULT_REQUEST_TIMEOUT_MS. */\n timeoutMs?: number;\n /** The fetch implementation to use (defaults to globalThis.fetch). */\n fetchImpl?: typeof globalThis.fetch;\n}\n\n/**\n * fetch() with a hard timeout. Aborts the request when the deadline elapses;\n * the resulting AbortError propagates to the caller (who wraps it as an\n * OnboardingNetworkError). A caller-supplied `signal` is respected too — if\n * either the caller aborts OR the timeout fires, the request is aborted.\n */\nexport async function fetchWithTimeout(\n url: string,\n options: TimeoutFetchOptions = {},\n): Promise<Response> {\n const {\n timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS,\n fetchImpl = globalThis.fetch,\n signal: callerSignal,\n ...init\n } = options;\n\n const controller = new AbortController();\n const timer = setTimeout(() => {\n controller.abort(new Error(`request timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n // Forward a caller abort into our controller.\n if (callerSignal) {\n if (callerSignal.aborted) {\n clearTimeout(timer);\n controller.abort(callerSignal.reason);\n } else {\n callerSignal.addEventListener(\n \"abort\",\n () => controller.abort(callerSignal.reason),\n { once: true },\n );\n }\n }\n\n try {\n return await fetchImpl(url, { ...init, signal: controller.signal });\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Run a promise with a timeout. On timeout, rejects with `onTimeout()`'s\n * error. Used to bound calls that don't take an AbortSignal (grecaptcha\n * .execute()). The underlying op is NOT cancelled (grecaptcha has no abort),\n * but the caller no longer waits on it — the widget re-enables.\n */\nexport function withTimeout<T>(\n op: Promise<T>,\n timeoutMs: number,\n onTimeout: () => Error,\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n let settled = false;\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n reject(onTimeout());\n }, timeoutMs);\n op.then(\n (v) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(v);\n },\n (err) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n reject(err);\n },\n );\n });\n}\n"]}
|