@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.
Files changed (68) hide show
  1. package/README.md +124 -0
  2. package/dist/{chunk-4SWOVHZ2.js → chunk-A3DE2IAH.js} +155 -18
  3. package/dist/chunk-A3DE2IAH.js.map +1 -0
  4. package/dist/{chunk-AFCJLUPK.js → chunk-DWFZA5T6.js} +19 -8
  5. package/dist/chunk-DWFZA5T6.js.map +1 -0
  6. package/dist/chunk-FIRSA7EH.js +3 -0
  7. package/dist/{chunk-ZDY2I6KY.js.map → chunk-FIRSA7EH.js.map} +1 -1
  8. package/dist/chunk-JFDQZ3AC.js +3 -0
  9. package/dist/chunk-JFDQZ3AC.js.map +1 -0
  10. package/dist/{chunk-34BQX2KR.js → chunk-KFHVZGX6.js} +61 -6
  11. package/dist/chunk-KFHVZGX6.js.map +1 -0
  12. package/dist/chunk-NDSBIB3N.js +99 -0
  13. package/dist/chunk-NDSBIB3N.js.map +1 -0
  14. package/dist/chunk-OLWR5COK.js +10 -0
  15. package/dist/chunk-OLWR5COK.js.map +1 -0
  16. package/dist/{chunk-SWMIIUXR.js → chunk-OVULPWNZ.js} +917 -158
  17. package/dist/chunk-OVULPWNZ.js.map +1 -0
  18. package/dist/chunk-SEKNVHD3.js +59 -0
  19. package/dist/chunk-SEKNVHD3.js.map +1 -0
  20. package/dist/chunk-T7EWURTF.js +321 -0
  21. package/dist/chunk-T7EWURTF.js.map +1 -0
  22. package/dist/{chunk-BQCTDU5V.js → chunk-UHOFGWI7.js} +90 -15
  23. package/dist/chunk-UHOFGWI7.js.map +1 -0
  24. package/dist/chunk-WKN4U7QF.js +35 -0
  25. package/dist/chunk-WKN4U7QF.js.map +1 -0
  26. package/dist/{chunk-AW7W3HAC.js → chunk-XJTJPBJB.js} +40 -3
  27. package/dist/chunk-XJTJPBJB.js.map +1 -0
  28. package/dist/chunk-ZR6X3GSM.js +186 -0
  29. package/dist/chunk-ZR6X3GSM.js.map +1 -0
  30. package/dist/client-NvqPpfQP.d.ts +86 -0
  31. package/dist/consent/index.d.ts +59 -0
  32. package/dist/consent/index.js +7 -0
  33. package/dist/consent/index.js.map +1 -0
  34. package/dist/controller-BLH3wnxl.d.ts +177 -0
  35. package/dist/dynamic/index.d.ts +65 -6
  36. package/dist/dynamic/index.js +5 -2
  37. package/dist/errors-vJkye4a0.d.ts +110 -0
  38. package/dist/form/index.d.ts +12 -4
  39. package/dist/form/index.js +2 -2
  40. package/dist/index-BMSbQGWP.d.ts +103 -0
  41. package/dist/index.d.ts +13 -7
  42. package/dist/index.js +13 -8
  43. package/dist/model-BI_I-D_V.d.ts +267 -0
  44. package/dist/{model-IEyDI54w.d.ts → model-CEwvHlWA.d.ts} +212 -46
  45. package/dist/render-EWQXF73I.js +5 -0
  46. package/dist/render-EWQXF73I.js.map +1 -0
  47. package/dist/schema-BrtjpXfL.d.ts +257 -0
  48. package/dist/{signup-client-Bl6eUrxR.d.ts → signup-client-CS95nkVO.d.ts} +152 -19
  49. package/dist/spec/index.d.ts +10 -81
  50. package/dist/spec/index.js +3 -2
  51. package/dist/submit/index.d.ts +7 -71
  52. package/dist/submit/index.js +4 -3
  53. package/dist/viewer/index.d.ts +4 -0
  54. package/dist/viewer/index.js +5 -0
  55. package/dist/viewer/index.js.map +1 -0
  56. package/dist/widget/index.d.ts +102 -20
  57. package/dist/widget/index.js +10 -7
  58. package/package.json +21 -2
  59. package/dist/chunk-34BQX2KR.js.map +0 -1
  60. package/dist/chunk-4SWOVHZ2.js.map +0 -1
  61. package/dist/chunk-AFCJLUPK.js.map +0 -1
  62. package/dist/chunk-AW7W3HAC.js.map +0 -1
  63. package/dist/chunk-BIFANV7M.js +0 -22
  64. package/dist/chunk-BIFANV7M.js.map +0 -1
  65. package/dist/chunk-BQCTDU5V.js.map +0 -1
  66. package/dist/chunk-SWMIIUXR.js.map +0 -1
  67. package/dist/chunk-ZDY2I6KY.js +0 -3
  68. package/dist/schema-TfyBU6JW.d.ts +0 -107
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/submit/client.ts","../src/submit/signup-client.ts"],"names":["MAX_ATTEMPTS","RETRY_DELAYS_MS","buildUrl","parseRetryAfter","sleep"],"mappings":"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","file":"chunk-UHOFGWI7.js","sourcesContent":["/**\n * Submit client for POST /api/v1/public/onboarding/leads.\n *\n * Contract per D540 + hub REST interface 8686dd33:\n * - Idempotency-Key header REQUIRED (uuid v4, generated ONCE per logical\n * submission, REUSED on retries — same key = exactly-once delivery).\n * - Outgoing payload validated through leadsWireSchema (STRICT — unknown\n * keys rejected, matching additionalProperties:false) BEFORE any fetch.\n * - botToken in request body (D537).\n * - Response: { accepted: true } on 200/202 (empty/non-JSON 2xx body is\n * treated as acceptance; a JSON body that does not confirm acceptance\n * throws OnboardingServerError — see parseSuccessBody).\n * - 403 bot_challenge_failed → throw OnboardingBotChallengeError (no retry)\n * - 409 spec_stale (D540) → throw OnboardingSpecStaleError (no retry)\n * - 422 → throw OnboardingValidationError (no retry)\n * - 429 → throw OnboardingRateLimitError (no auto-retry)\n * - 5xx → retry with SAME idempotency key, capped backoff\n * - network error → retry with SAME idempotency key, capped backoff\n *\n * Max retries: 3 attempts total (1 initial + 2 retries).\n * Backoff: 1s, 2s (capped at 2 retries).\n */\n\nimport { validateLeadsPayload } from \"../form/schema.js\";\nimport type { ContactFormValues } from \"../form/schema.js\";\nimport {\n OnboardingBotChallengeError,\n OnboardingConflictError,\n OnboardingNetworkError,\n OnboardingRateLimitError,\n OnboardingServerError,\n OnboardingValidationError,\n} from \"./errors.js\";\nimport { DEFAULT_REQUEST_TIMEOUT_MS, fetchWithTimeout } from \"./http.js\";\n\nconst LEADS_PATH = \"/api/v1/public/onboarding/leads\";\nconst MAX_ATTEMPTS = 3;\nconst RETRY_DELAYS_MS = [1000, 2000] as const;\n\n/**\n * Resolve a bot token inside the retry loop, mapping ANY provider failure to\n * a typed OnboardingBotChallengeError. The provider is usually\n * executeRecaptcha, which throws a RecaptchaError if reCAPTCHA is unreachable\n * — that raw error must NOT escape submitLead/submitSignup (the @throws\n * contract is Onboarding* only) and must not silently burn the retry budget.\n */\nexport async function resolveBotTokenGuarded(\n source: BotTokenSource,\n): Promise<string> {\n try {\n return await resolveBotToken(source);\n } catch (err) {\n throw new OnboardingBotChallengeError(\n err instanceof Error\n ? `Could not obtain a bot-challenge token: ${err.message}`\n : \"Could not obtain a bot-challenge token.\",\n );\n }\n}\n\n/**\n * A bot token, or a provider that mints a FRESH one per attempt.\n *\n * reCAPTCHA tokens are SINGLE-USE — the server consumes them at siteverify.\n * If a 5xx happens AFTER the token was consumed, resending the same token on\n * retry gets 403. Pass a provider function to mint a fresh token per attempt;\n * a bare string is back-compat but the caller then OWNS that risk.\n */\nexport type BotTokenSource = string | (() => string | Promise<string>);\n\nexport async function resolveBotToken(source: BotTokenSource): Promise<string> {\n return typeof source === \"function\" ? source() : source;\n}\n\nexport interface LeadsSubmitOptions {\n baseUrl: string;\n /**\n * reCAPTCHA token (D537), or a provider called ONCE PER ATTEMPT to mint a\n * fresh token (recommended — tokens are single-use; a fixed string on a\n * retried request risks 403 after the first attempt consumed it).\n */\n botToken?: BotTokenSource;\n /**\n * Stable idempotency key for this submission. Generate ONCE with\n * `generateIdempotencyKey()` and pass the same key for retries.\n * The server chains this key through to D538 for exactly-once delivery.\n */\n idempotencyKey: string;\n /** Optional custom fetch implementation (defaults to global fetch). */\n fetch?: typeof globalThis.fetch;\n /**\n * Per-attempt request timeout in ms (default 15s). A stalled intake aborts\n * with OnboardingNetworkError so the widget re-enables instead of hanging.\n */\n timeoutMs?: number;\n /** Optional onAttempt callback for observability (attempt number, 1-based). */\n onAttempt?: (attempt: number) => void;\n}\n\nexport interface LeadsSubmitResult {\n accepted: true;\n}\n\n/**\n * Generate a stable idempotency key for one logical submission.\n * Uses the Web Crypto API (available in all modern browsers + Bun/Node >= 19).\n * Call ONCE per user submission, pass the SAME key on retries.\n */\nexport function generateIdempotencyKey(): string {\n // crypto.randomUUID() is available in all modern browsers, Bun, and Node >= 14.17\n return globalThis.crypto.randomUUID();\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\nfunction parseRetryAfter(response: Response): number | null {\n const header = response.headers.get(\"retry-after\");\n if (!header) return null;\n const seconds = Number.parseInt(header, 10);\n return Number.isNaN(seconds) ? null : seconds;\n}\n\nasync function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Compute a stable fingerprint of the outgoing lead payload, EXCLUDING\n * botToken (which is fresh per attempt by design).\n *\n * The widget uses this to decide whether a resubmission is a RETRY of the\n * same logical submission (same fingerprint → reuse the Idempotency-Key)\n * or a NEW logical submission (fingerprint changed because the user edited\n * fields → mint a NEW key). This mirrors the server's D538 payload-match\n * guard: same key + different payload is INVALID_ARGUMENT server-side.\n *\n * FNV-1a 32-bit over a stable JSON serialization of [name, email, phone,\n * message]. Collision risk is irrelevant here — a collision only means an\n * unnecessary key reuse across two submissions from the same browser tab.\n */\nexport function computePayloadFingerprint(values: ContactFormValues): string {\n const canonical = JSON.stringify([\n values.companyName,\n values.contactName,\n values.email,\n values.phone ?? null,\n values.message ?? null,\n [...(values.interestModules ?? [])].sort(),\n values.sizeBand ?? null,\n values.attribution ?? null,\n values.privacyNoticeAcknowledged,\n values.marketingOptIn,\n values.intakeKind,\n ]);\n let hash = 0x811c9dc5;\n for (let i = 0; i < canonical.length; i++) {\n hash ^= canonical.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * Parse a success-status (200/202) response body.\n *\n * Documented behavior (one rule, tested):\n * - EMPTY body or NON-JSON body → treated as acceptance. The locked\n * contract response is `{accepted:true}` and the status is already\n * 2xx; load balancers / CDNs occasionally strip or replace bodies on\n * 202, and a 2xx from the intake IS the acceptance signal.\n * - A PARSEABLE JSON body that is not `{accepted: true}` (e.g.\n * `{accepted:false}` or an unrelated shape) → OnboardingServerError.\n * A server-signaled soft-rejection on a success status must NOT be\n * masked as success. Not auto-retried (the status was 2xx; the server\n * state is ambiguous — the caller decides).\n */\nasync function parseSuccessBody(\n response: Response,\n): Promise<LeadsSubmitResult> {\n let text: string;\n try {\n text = await response.text();\n } catch {\n // Unreadable body on a 2xx — acceptance per the rule above.\n return { accepted: true };\n }\n if (text.trim() === \"\") {\n return { accepted: true };\n }\n let json: unknown;\n try {\n json = JSON.parse(text);\n } catch {\n // Non-JSON body on a 2xx (LB/CDN interstitial) — acceptance.\n return { accepted: true };\n }\n if (\n typeof json === \"object\" &&\n json !== null &&\n (json as { accepted?: unknown }).accepted === true\n ) {\n return { accepted: true };\n }\n throw new OnboardingServerError(\n response.status,\n `Success status ${response.status} but response body did not confirm acceptance.`,\n );\n}\n\n/**\n * Submit a contact-form lead to the onboarding intake.\n *\n * The outgoing payload is validated through leadsWireSchema in STRICT mode\n * before any network I/O — unknown keys are rejected client-side, exactly\n * mirroring the server's additionalProperties:false.\n *\n * Handles retry semantics: 5xx and network errors are retried up to\n * MAX_ATTEMPTS total, always with the SAME idempotencyKey to guarantee\n * exactly-once server-side processing (D538 idempotency chain).\n *\n * 403, 409, 422 and 429 are NOT retried — caller handles them.\n *\n * @throws {OnboardingValidationError} if the payload fails the strict wire\n * schema (unknown keys, constraint violations) — thrown BEFORE any fetch\n * @throws {OnboardingBotChallengeError} on 403, or if the bot-token provider\n * throws (reCAPTCHA unreachable) on any attempt\n * @throws {OnboardingConflictError} on 409 (leads has no spec_stale concept)\n * @throws {OnboardingRateLimitError} on 429\n * @throws {OnboardingServerError} on 5xx after all retries exhausted, or on\n * a 2xx whose JSON body does not confirm acceptance\n * @throws {OnboardingNetworkError} on network failure OR timeout after all\n * retries exhausted\n * @throws {OnboardingValidationError} on 422 (server-side re-validation)\n */\nexport async function submitLead(\n values: ContactFormValues,\n options: LeadsSubmitOptions,\n): Promise<LeadsSubmitResult> {\n const fetchImpl = options.fetch ?? globalThis.fetch;\n const timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n const url = buildUrl(options.baseUrl, LEADS_PATH);\n\n // Normalize non-token fields once (empty optionals → omitted). The token\n // is attached PER ATTEMPT below so a retry can carry a FRESH single-use\n // token; the body is therefore serialized inside the loop.\n const base: Record<string, unknown> = { ...values };\n if (base[\"phone\"] === \"\" || base[\"phone\"] === undefined) {\n delete base[\"phone\"];\n }\n if (base[\"message\"] === \"\" || base[\"message\"] === undefined) {\n delete base[\"message\"];\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"Idempotency-Key\": options.idempotencyKey,\n };\n\n let lastError: unknown = null;\n\n for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n options.onAttempt?.(attempt);\n\n // Mint a fresh token per attempt (single-use tokens; see BotTokenSource).\n // Guarded: a provider failure (reCAPTCHA down) becomes a typed\n // OnboardingBotChallengeError, never a raw RecaptchaError escaping.\n const botToken =\n options.botToken === undefined\n ? undefined\n : await resolveBotTokenGuarded(options.botToken);\n\n // Strict wire-schema validation BEFORE the network I/O of THIS attempt.\n // Extra keys are REJECTED (additionalProperties:false), not stripped.\n const parsed = validateLeadsPayload({ ...base, botToken });\n if (!parsed.success) {\n const detail = parsed.error.issues\n .map((i) => `${i.path.join(\".\") || \"(payload)\"}: ${i.message}`)\n .join(\"; \");\n throw new OnboardingValidationError(\n `Payload failed wire-schema validation: ${detail}`,\n );\n }\n const body = JSON.stringify(parsed.data);\n\n let response: Response;\n\n try {\n response = await fetchWithTimeout(url, {\n method: \"POST\",\n headers,\n body,\n timeoutMs,\n fetchImpl,\n });\n } catch (err) {\n // Timeout (abort) and network failures are both retryable transient.\n lastError = new OnboardingNetworkError(err);\n const delay = RETRY_DELAYS_MS[attempt - 1];\n if (attempt < MAX_ATTEMPTS && delay !== undefined) {\n await sleep(delay);\n continue;\n }\n throw lastError;\n }\n\n // 200/202 → success (body parse guarded; see parseSuccessBody)\n if (response.status === 200 || response.status === 202) {\n return parseSuccessBody(response);\n }\n\n // 403 → bot challenge failed — NOT retryable\n if (response.status === 403) {\n throw new OnboardingBotChallengeError();\n }\n\n // 409 on /leads is NOT spec_stale (the leads payload has no specVersion;\n // spec_stale is a /submit-only concept per D540). A leads 409 is a\n // generic conflict (e.g. a D538 same-key concurrent-processing race) —\n // surface OnboardingConflictError so headless consumers switching on\n // `kind` get the right diagnosis, not a \"form has been updated\" message.\n if (response.status === 409) {\n throw new OnboardingConflictError(409);\n }\n\n // 429 → rate limited — NOT auto-retried (caller decides when to retry)\n if (response.status === 429) {\n const retryAfter = parseRetryAfter(response);\n throw new OnboardingRateLimitError(retryAfter);\n }\n\n // 422 → validation error (client-side should have caught this)\n if (response.status === 422) {\n throw new OnboardingValidationError(\n \"Server rejected the submission (422). Please check your inputs.\",\n );\n }\n\n // 5xx → retryable server error\n if (response.status >= 500) {\n lastError = new OnboardingServerError(response.status);\n const delay = RETRY_DELAYS_MS[attempt - 1];\n if (attempt < MAX_ATTEMPTS && delay !== undefined) {\n await sleep(delay);\n continue;\n }\n throw lastError;\n }\n\n // Any other unexpected status\n throw new OnboardingServerError(\n response.status,\n `Unexpected response status ${response.status}`,\n );\n }\n\n // Should not be reachable; guard for type safety\n throw lastError ?? new OnboardingNetworkError();\n}\n","/**\n * Submit client for POST /api/v1/public/onboarding/submit (D540 §2).\n *\n * Contract per the LOCKED hub REST interface (PublicOnboardingSubmitRequest):\n * - Body: { specVersion, answers, selectedServices?, botToken,\n * consent:{tosAccepted:true, privacyAccepted:true},\n * credentials:{email, displayName?}, organisationName?, subdomain }\n * additionalProperties: false — validated STRICTLY client-side before\n * any fetch (unknown keys are rejected, not stripped).\n * - Idempotency-Key header REQUIRED (same D538 discipline as leads:\n * stable across retries of the SAME payload; the widget mints a new\n * key when the payload fingerprint changes).\n * - specVersion policy is reject-stale: 409 → OnboardingSpecStaleError\n * carrying the server's current version. The CALLER refetches the spec\n * and surfaces re-validation to the user — NEVER silent auto-resubmit.\n * - Response: { accepted:true, terminalStage, verificationEmailSent? }.\n *\n * Success-body rule for /submit (DIFFERENT from /leads, deliberately):\n * the response carries REQUIRED routing data (terminalStage decides what\n * the UI shows next), so a 2xx with an empty/unparseable/garbage body is\n * an OnboardingServerError — the SDK will not fabricate a terminal stage.\n * (/leads tolerates body-less 2xx because its only signal IS the status.)\n */\n\nimport { z } from \"zod/v3\";\nimport type { TerminalStage } from \"../spec/model.js\";\nimport type { ConsentSubmitDocument } from \"../consent/controller.js\";\nimport {\n OnboardingBotChallengeError,\n OnboardingConflictError,\n OnboardingConsentIncompleteError,\n OnboardingConsentStaleError,\n OnboardingNetworkError,\n OnboardingRateLimitError,\n OnboardingServerError,\n OnboardingSpecStaleError,\n OnboardingSubdomainTakenError,\n OnboardingValidationError,\n type StaleConsentDocument,\n} from \"./errors.js\";\nimport { type BotTokenSource, resolveBotTokenGuarded } from \"./client.js\";\nimport { DEFAULT_REQUEST_TIMEOUT_MS, fetchWithTimeout } from \"./http.js\";\nimport { isSubdomainLabelShape, SUBDOMAIN_SHAPE_MESSAGE } from \"./subdomain.js\";\n\nconst SUBMIT_PATH = \"/api/v1/public/onboarding/submit\";\nconst MAX_ATTEMPTS = 3;\nconst RETRY_DELAYS_MS = [1000, 2000] as const;\n\n/**\n * STRICT wire schema for ONE element of the D543 §4 consent.documents array.\n * `accepted` is const-true (an unaccepted doc is never sent — the consent\n * controller omits it), and `evidence` carries only the gate-relevant flags.\n */\nexport const consentSubmitDocumentSchema = z\n .object({\n legalDocumentId: z.string().min(1, \"legalDocumentId is required\"),\n versionNo: z.number().int().nonnegative(),\n accepted: z.literal(true, {\n errorMap: () => ({ message: \"a consent document must be accepted\" }),\n }),\n // evidence keys stay snake_case (the only snake bits in the wire shape).\n evidence: z\n .object({\n scrolled_to_end: z.boolean().optional(),\n opened: z.boolean().optional(),\n })\n .strict(),\n })\n .strict();\n\n/**\n * STRICT wire schema — mirrors PublicOnboardingSubmitRequest exactly\n * (additionalProperties: false; consent booleans are const-true; passwordless\n * per D1010, with the workspace subdomain required by D836).\n *\n * D543 §4: `consent` extends from booleans to a per-document array. The\n * `documents` array is AUTHORITATIVE (the server re-validates it); the\n * `tosAccepted`/`privacyAccepted` booleans are kept as a DERIVED convenience\n * (D543 \"kept as a derived convenience only\"). `documents` is OPTIONAL on the\n * wire for backward-compat — a pre-ConsentPack onboarder serves no consentPack\n * and submits the booleans alone (D543 \"pre-ConsentPack submits still parse\").\n */\nexport const signupWireSchema = z\n .object({\n specVersion: z.string().min(1, \"specVersion is required\"),\n answers: z.record(z.unknown()),\n selectedServices: z.array(z.string()).optional(),\n botToken: z.string().min(1, \"botToken is required\").optional(),\n consent: z\n .object({\n tosAccepted: z.literal(true, {\n errorMap: () => ({ message: \"Terms of Service must be accepted\" }),\n }),\n privacyAccepted: z.literal(true, {\n errorMap: () => ({ message: \"Privacy Policy must be accepted\" }),\n }),\n documents: z.array(consentSubmitDocumentSchema).optional(),\n })\n .strict(),\n credentials: z\n .object({\n email: z.string().email(\"A valid account email is required\"),\n displayName: z.string().trim().min(1).max(200).optional(),\n })\n .strict(),\n organisationName: z.string().trim().min(1).max(200).optional(),\n subdomain: z\n .string()\n .trim()\n .min(1)\n .max(63)\n .refine(isSubdomainLabelShape, SUBDOMAIN_SHAPE_MESSAGE),\n })\n .strict();\n\nexport type SignupWirePayload = z.infer<typeof signupWireSchema>;\n\n/** The payload the caller assembles (botToken attached by the transport). */\nexport interface SignupSubmitBody {\n specVersion: string;\n answers: Record<string, unknown>;\n selectedServices?: string[];\n /**\n * D543 §4 consent. `documents` (the per-doc array) is authoritative when\n * present; the booleans are the derived convenience. `documents` is omitted\n * for a pre-ConsentPack onboarder (backward-compat).\n */\n consent: {\n tosAccepted: true;\n privacyAccepted: true;\n documents?: ConsentSubmitDocument[];\n };\n credentials: { email: string; displayName?: string };\n organisationName?: string;\n subdomain: string;\n}\n\nexport interface SignupSubmitOptions {\n baseUrl: string;\n /**\n * reCAPTCHA token (D537), or a provider called ONCE PER ATTEMPT to mint a\n * fresh token. Tokens are single-use (consumed at siteverify), so a fixed\n * string on a retried request risks 403 after the first attempt consumed\n * it — pass a provider for retry-safe recovery.\n */\n botToken?: BotTokenSource;\n /** Stable idempotency key — same discipline as submitLead. */\n idempotencyKey: string;\n fetch?: typeof globalThis.fetch;\n /**\n * Per-attempt request timeout in ms (default 15s). A stalled intake aborts\n * with OnboardingNetworkError so the widget re-enables instead of hanging.\n */\n timeoutMs?: number;\n onAttempt?: (attempt: number) => void;\n}\n\nexport interface SignupSubmitResult {\n accepted: true;\n /** Stage the existing engine landed the applicant at (D540 §2). */\n terminalStage: TerminalStage;\n verificationEmailSent?: boolean;\n}\n\n/**\n * Stable fingerprint of a signup submission, EXCLUDING botToken (fresh per\n * attempt by design). Same role as computePayloadFingerprint for leads:\n * same fingerprint ⇒ retry (reuse the Idempotency-Key); changed\n * fingerprint ⇒ new logical submission (mint a new key) — mirroring the\n * server's D538 payload-match guard.\n */\nexport function computeSignupFingerprint(body: SignupSubmitBody): string {\n const canonical = JSON.stringify([\n body.specVersion,\n // Sort answer keys for stability regardless of insertion order.\n Object.keys(body.answers)\n .sort()\n .map((k) => [k, body.answers[k]]),\n [...(body.selectedServices ?? [])].sort(),\n body.credentials.email,\n body.credentials.displayName ?? null,\n body.organisationName ?? null,\n body.subdomain,\n body.consent.tosAccepted,\n body.consent.privacyAccepted,\n // Consent documents fold into the fingerprint (sorted by legalDocumentId\n // for order-independence) — re-accepting a changed doc after 409\n // consent_stale is a NEW logical submission and must rotate the key.\n [...(body.consent.documents ?? [])]\n .sort((a, b) => a.legalDocumentId.localeCompare(b.legalDocumentId))\n .map((d) => [\n d.legalDocumentId,\n d.versionNo,\n d.evidence.scrolled_to_end ?? false,\n d.evidence.opened ?? false,\n ]),\n ]);\n let hash = 0x811c9dc5;\n for (let i = 0; i < canonical.length; i++) {\n hash ^= canonical.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0).toString(16).padStart(8, \"0\");\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\nfunction parseRetryAfter(response: Response): number | null {\n const header = response.headers.get(\"retry-after\");\n if (!header) return null;\n const seconds = Number.parseInt(header, 10);\n return Number.isNaN(seconds) ? null : seconds;\n}\n\n/**\n * Parse the OPTIONAL `staleDocuments` array on a consent_stale 409 body\n * (D543 §4). CANONICAL camelCase ONLY — { legalDocumentId, docType?,\n * currentVersionNo? } — NO snake_case hedging (P2b), matching the sibling\n * spec_stale error's \"no field hedging\" discipline: hedging both cases lets a\n * mismatched server shape pass silently and defeats the canonical-camelCase\n * pin. The server may omit the array entirely (caller then refetches + diffs);\n * an entry lacking a string `legalDocumentId` is skipped. Never throws.\n */\nfunction parseStaleConsentDocuments(value: unknown): StaleConsentDocument[] {\n if (!Array.isArray(value)) return [];\n const out: StaleConsentDocument[] = [];\n for (const entry of value) {\n if (entry === null || typeof entry !== \"object\") continue;\n const e = entry as Record<string, unknown>;\n if (typeof e[\"legalDocumentId\"] !== \"string\") continue;\n const doc: StaleConsentDocument = { legalDocumentId: e[\"legalDocumentId\"] };\n if (typeof e[\"docType\"] === \"string\") doc.docType = e[\"docType\"];\n if (typeof e[\"currentVersionNo\"] === \"number\") {\n doc.currentVersionNo = e[\"currentVersionNo\"];\n }\n out.push(doc);\n }\n return out;\n}\n\nasync function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nconst TERMINAL_STAGES: readonly TerminalStage[] = [\n \"prospect\",\n \"inactive\",\n \"active\",\n];\n\n/** Parse the REQUIRED submit response shape; anything else → ServerError. */\nasync function parseSubmitSuccess(\n response: Response,\n): Promise<SignupSubmitResult> {\n let json: unknown;\n try {\n json = await response.json();\n } catch {\n throw new OnboardingServerError(\n response.status,\n `Success status ${response.status} but the submit response body was not JSON — terminalStage is unknown.`,\n );\n }\n if (\n typeof json === \"object\" &&\n json !== null &&\n (json as { accepted?: unknown }).accepted === true\n ) {\n const stage = (json as { terminalStage?: unknown }).terminalStage;\n if (\n typeof stage === \"string\" &&\n (TERMINAL_STAGES as readonly string[]).includes(stage)\n ) {\n const verificationEmailSent = (\n json as { verificationEmailSent?: unknown }\n ).verificationEmailSent;\n return {\n accepted: true,\n terminalStage: stage as TerminalStage,\n ...(typeof verificationEmailSent === \"boolean\"\n ? { verificationEmailSent }\n : {}),\n };\n }\n }\n throw new OnboardingServerError(\n response.status,\n `Success status ${response.status} but the submit response did not carry {accepted:true, terminalStage}.`,\n );\n}\n\n/**\n * Submit a full signup to the onboarding intake.\n *\n * @throws {OnboardingValidationError} strict wire-schema failure — BEFORE any fetch\n * @throws {OnboardingBotChallengeError} on 403, or if the bot-token provider\n * throws (reCAPTCHA unreachable) on any attempt\n * @throws {OnboardingSpecStaleError} on 409 with code:\"spec_stale\" (caller\n * refetches spec); carries the server's currentSpecVersion\n * @throws {OnboardingConsentStaleError} on 409 with code:\"consent_stale\"\n * (D543 §4 — a required legal doc changed; caller refetches spec + requires\n * re-acceptance of the changed docs, NEVER auto-accepts); carries\n * staleDocuments\n * @throws {OnboardingConsentIncompleteError} on 400 with\n * code:\"consent_incomplete\" (D543 §4 — server found a required doc missing/\n * unaccepted/evidence-insufficient)\n * @throws {OnboardingConflictError} on a 409 that is NOT spec_stale/consent_stale\n * @throws {OnboardingRateLimitError} on 429\n * @throws {OnboardingValidationError} on 422 (server re-validation) or a\n * generic 400 that is NOT consent_incomplete\n * @throws {OnboardingServerError} on 5xx after retries, or a 2xx without\n * the required {accepted:true, terminalStage} body\n * @throws {OnboardingNetworkError} on network failure OR timeout after retries\n */\nexport async function submitSignup(\n body: SignupSubmitBody,\n options: SignupSubmitOptions,\n): Promise<SignupSubmitResult> {\n const fetchImpl = options.fetch ?? globalThis.fetch;\n const timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n const url = buildUrl(options.baseUrl, SUBMIT_PATH);\n\n // Non-token fields normalized once; token attached PER ATTEMPT (single-use)\n // so a retry carries a fresh token — the body is serialized inside the loop.\n const base: Record<string, unknown> = { ...body };\n if (base[\"selectedServices\"] === undefined) {\n delete base[\"selectedServices\"];\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"Idempotency-Key\": options.idempotencyKey,\n };\n\n let lastError: unknown = null;\n\n for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n options.onAttempt?.(attempt);\n\n // Guarded: a token-provider failure (reCAPTCHA unreachable on a retry)\n // becomes a typed OnboardingBotChallengeError, never a raw RecaptchaError.\n const botToken =\n options.botToken === undefined\n ? undefined\n : await resolveBotTokenGuarded(options.botToken);\n const parsed = signupWireSchema.safeParse({ ...base, botToken });\n if (!parsed.success) {\n const detail = parsed.error.issues\n .map((i) => `${i.path.join(\".\") || \"(payload)\"}: ${i.message}`)\n .join(\"; \");\n throw new OnboardingValidationError(\n `Signup payload failed wire-schema validation: ${detail}`,\n );\n }\n const wireBody = JSON.stringify(parsed.data);\n\n let response: Response;\n try {\n response = await fetchWithTimeout(url, {\n method: \"POST\",\n headers,\n body: wireBody,\n timeoutMs,\n fetchImpl,\n });\n } catch (err) {\n lastError = new OnboardingNetworkError(err);\n const delay = RETRY_DELAYS_MS[attempt - 1];\n if (attempt < MAX_ATTEMPTS && delay !== undefined) {\n await sleep(delay);\n continue;\n }\n throw lastError;\n }\n\n if (response.status === 200 || response.status === 202) {\n return parseSubmitSuccess(response);\n }\n\n if (response.status === 403) {\n throw new OnboardingBotChallengeError();\n }\n\n // 400 — the D543 §4 PINNED consent_incomplete body is\n // { error: true, code: \"consent_incomplete\", ... }\n // The server's authoritative re-validation found a required live document\n // that was missing / unaccepted / evidence-insufficient. Any OTHER 400 is\n // a generic validation error (routes the user to review inputs).\n if (response.status === 400) {\n let code400: unknown;\n let message400: string | undefined;\n try {\n const body400 = (await response.json()) as Record<string, unknown>;\n code400 = body400[\"code\"];\n const m = body400[\"message\"];\n if (typeof m === \"string\" && m !== \"\") message400 = m;\n } catch {\n // keep defaults; a body-less 400 is a generic validation error\n }\n if (code400 === \"consent_incomplete\") {\n throw new OnboardingConsentIncompleteError(message400);\n }\n throw new OnboardingValidationError(\n message400 ??\n \"The server rejected the submission (400). Please review your inputs.\",\n );\n }\n\n // 409 — TWO pinned bodies (D540 spec_stale + D543 §4 consent_stale, its\n // sibling):\n // { error: true, code: \"spec_stale\", currentSpecVersion: string }\n // { error: true, code: \"consent_stale\", staleDocuments?: [...] }\n // ONLY a 409 carrying one of these codes maps to a typed stale error; any\n // other 409 is a generic conflict.\n if (response.status === 409) {\n let code: unknown;\n let currentSpecVersion = \"unknown\";\n let staleDocuments: StaleConsentDocument[] = [];\n try {\n const body409 = (await response.json()) as Record<string, unknown>;\n code = body409[\"code\"];\n const v = body409[\"currentSpecVersion\"];\n if (typeof v === \"string\" && v !== \"\") currentSpecVersion = v;\n staleDocuments = parseStaleConsentDocuments(body409[\"staleDocuments\"]);\n } catch {\n // keep defaults; a body-less 409 is treated as a generic conflict\n }\n if (code === \"spec_stale\") {\n throw new OnboardingSpecStaleError(currentSpecVersion);\n }\n if (code === \"consent_stale\") {\n throw new OnboardingConsentStaleError(staleDocuments);\n }\n if (code === \"subdomain_taken\") {\n throw new OnboardingSubdomainTakenError();\n }\n throw new OnboardingConflictError(409);\n }\n\n if (response.status === 429) {\n throw new OnboardingRateLimitError(parseRetryAfter(response));\n }\n\n if (response.status === 422) {\n throw new OnboardingValidationError(\n \"The server rejected the submission (422). Please review your inputs.\",\n );\n }\n\n if (response.status >= 500) {\n lastError = new OnboardingServerError(response.status);\n const delay = RETRY_DELAYS_MS[attempt - 1];\n if (attempt < MAX_ATTEMPTS && delay !== undefined) {\n await sleep(delay);\n continue;\n }\n throw lastError;\n }\n\n throw new OnboardingServerError(\n response.status,\n `Unexpected response status ${response.status}`,\n );\n }\n\n throw lastError ?? new OnboardingNetworkError();\n}\n"]}
@@ -0,0 +1,35 @@
1
+ import { z } from 'zod/v3';
2
+
3
+ // src/form/schema.ts
4
+ var contactFormSchema = z.object({
5
+ name: z.string().max(200).optional(),
6
+ companyName: z.string().min(1, "Company name is required").max(200, "Company name must be 200 characters or fewer"),
7
+ contactName: z.string().min(1, "Contact name is required").max(200, "Contact name must be 200 characters or fewer"),
8
+ email: z.string().email("A valid email address is required"),
9
+ phone: z.string().optional(),
10
+ message: z.string().max(4e3, "Message must be 4000 characters or fewer").optional(),
11
+ interestModules: z.array(z.string().min(1)).max(32).default([]),
12
+ sizeBand: z.string().max(80).optional(),
13
+ attribution: z.object({
14
+ campaignTags: z.record(z.string()).optional(),
15
+ referringSite: z.string().max(2048).optional(),
16
+ landingPage: z.string().max(2048).optional(),
17
+ button: z.string().max(120).optional()
18
+ }).strict().optional(),
19
+ privacyNoticeAcknowledged: z.boolean().refine((value) => value, "Privacy notice acknowledgement is required"),
20
+ marketingOptIn: z.boolean(),
21
+ intakeKind: z.enum(["contact", "enquiry"])
22
+ });
23
+ var leadsWireSchema = contactFormSchema.extend({
24
+ botToken: z.string().min(1, "botToken is required").optional()
25
+ }).strict();
26
+ function validateContactForm(values) {
27
+ return contactFormSchema.safeParse(values);
28
+ }
29
+ function validateLeadsPayload(payload) {
30
+ return leadsWireSchema.safeParse(payload);
31
+ }
32
+
33
+ export { contactFormSchema, leadsWireSchema, validateContactForm, validateLeadsPayload };
34
+ //# sourceMappingURL=chunk-WKN4U7QF.js.map
35
+ //# sourceMappingURL=chunk-WKN4U7QF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/form/schema.ts"],"names":[],"mappings":";;;AAyBO,IAAM,iBAAA,GAAoB,EAAE,MAAA,CAAO;AAAA,EACxC,MAAM,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,GAAG,EAAE,QAAA,EAAS;AAAA,EACnC,WAAA,EAAa,CAAA,CACV,MAAA,EAAO,CACP,GAAA,CAAI,GAAG,0BAA0B,CAAA,CACjC,GAAA,CAAI,GAAA,EAAK,8CAA8C,CAAA;AAAA,EAC1D,WAAA,EAAa,CAAA,CACV,MAAA,EAAO,CACP,GAAA,CAAI,GAAG,0BAA0B,CAAA,CACjC,GAAA,CAAI,GAAA,EAAK,8CAA8C,CAAA;AAAA,EAC1D,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,MAAM,mCAAmC,CAAA;AAAA,EAC3D,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC3B,OAAA,EAAS,EACN,MAAA,EAAO,CACP,IAAI,GAAA,EAAM,0CAA0C,EACpD,QAAA,EAAS;AAAA,EACZ,eAAA,EAAiB,CAAA,CAAE,KAAA,CAAM,CAAA,CAAE,QAAO,CAAE,GAAA,CAAI,CAAC,CAAC,EAAE,GAAA,CAAI,EAAE,CAAA,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,EAC9D,UAAU,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,EAAE,EAAE,QAAA,EAAS;AAAA,EACtC,WAAA,EAAa,EACV,MAAA,CAAO;AAAA,IACN,cAAc,CAAA,CAAE,MAAA,CAAO,EAAE,MAAA,EAAQ,EAAE,QAAA,EAAS;AAAA,IAC5C,eAAe,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,IAAI,EAAE,QAAA,EAAS;AAAA,IAC7C,aAAa,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,IAAI,EAAE,QAAA,EAAS;AAAA,IAC3C,QAAQ,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,GAAG,EAAE,QAAA;AAAS,GACtC,CAAA,CACA,MAAA,EAAO,CACP,QAAA,EAAS;AAAA,EACZ,yBAAA,EAA2B,EACxB,OAAA,EAAQ,CACR,OAAO,CAAC,KAAA,KAAU,OAAO,4CAA4C,CAAA;AAAA,EACxE,cAAA,EAAgB,EAAE,OAAA,EAAQ;AAAA,EAC1B,YAAY,CAAA,CAAE,IAAA,CAAK,CAAC,SAAA,EAAW,SAAS,CAAC;AAC3C,CAAC;AAYM,IAAM,eAAA,GAAkB,kBAC5B,MAAA,CAAO;AAAA,EACN,QAAA,EAAU,EAAE,MAAA,EAAO,CAAE,IAAI,CAAA,EAAG,sBAAsB,EAAE,QAAA;AACtD,CAAC,EACA,MAAA;AAQI,SAAS,oBAAoB,MAAA,EAAiB;AACnD,EAAA,OAAO,iBAAA,CAAkB,UAAU,MAAM,CAAA;AAC3C;AAQO,SAAS,qBAAqB,OAAA,EAAkB;AACrD,EAAA,OAAO,eAAA,CAAgB,UAAU,OAAO,CAAA;AAC1C","file":"chunk-WKN4U7QF.js","sourcesContent":["/**\n * Contact-form Zod schema — mirrors the LOCKED hub REST contract exactly:\n * POST /api/v1/public/onboarding/leads\n * Schema: PublicOnboardingLeadsRequest (hub id 8686dd33)\n *\n * Constraints (per hub JSON Schema):\n * name — string, 1-200 chars, required\n * email — email format, required\n * phone — string, optional (no server-side constraint beyond string)\n * message — string, ≤4000 chars, optional\n * botToken — string, required (reCAPTCHA token, D537)\n *\n * additionalProperties: false\n *\n * The schema intentionally does NOT include botToken in the public-facing\n * ContactFormValues type — that field is injected by the submit client right\n * before the POST (the widget/form never exposes it to the user).\n */\n\nimport { z } from \"zod/v3\";\n\n/**\n * The user-visible contact form fields. Validated client-side for UX.\n * Server re-validates authoritatively (D540 §2).\n */\nexport const contactFormSchema = z.object({\n name: z.string().max(200).optional(),\n companyName: z\n .string()\n .min(1, \"Company name is required\")\n .max(200, \"Company name must be 200 characters or fewer\"),\n contactName: z\n .string()\n .min(1, \"Contact name is required\")\n .max(200, \"Contact name must be 200 characters or fewer\"),\n email: z.string().email(\"A valid email address is required\"),\n phone: z.string().optional(),\n message: z\n .string()\n .max(4000, \"Message must be 4000 characters or fewer\")\n .optional(),\n interestModules: z.array(z.string().min(1)).max(32).default([]),\n sizeBand: z.string().max(80).optional(),\n attribution: z\n .object({\n campaignTags: z.record(z.string()).optional(),\n referringSite: z.string().max(2048).optional(),\n landingPage: z.string().max(2048).optional(),\n button: z.string().max(120).optional(),\n })\n .strict()\n .optional(),\n privacyNoticeAcknowledged: z\n .boolean()\n .refine((value) => value, \"Privacy notice acknowledgement is required\"),\n marketingOptIn: z.boolean(),\n intakeKind: z.enum([\"contact\", \"enquiry\"]),\n});\n\nexport type ContactFormValues = z.infer<typeof contactFormSchema>;\n\n/**\n * The full wire payload — contactFormSchema + botToken.\n *\n * STRICT mode: unknown keys are REJECTED (not stripped), mirroring the\n * server's `additionalProperties: false`. `submitLead` runs every outgoing\n * payload through this schema before the POST — an extra key never reaches\n * the wire; it fails fast client-side with OnboardingValidationError.\n */\nexport const leadsWireSchema = contactFormSchema\n .extend({\n botToken: z.string().min(1, \"botToken is required\").optional(),\n })\n .strict();\n\nexport type LeadsWirePayload = z.infer<typeof leadsWireSchema>;\n\n/**\n * Validate user-visible form values (no botToken required here).\n * Returns a Zod SafeParseReturnType (inferred).\n */\nexport function validateContactForm(values: unknown) {\n return contactFormSchema.safeParse(values);\n}\n\n/**\n * Validate the full wire payload (including botToken) before submission.\n * STRICT: unknown keys are rejected, matching additionalProperties:false.\n * This is the exact validation `submitLead` applies on the wire path.\n * Returns a Zod SafeParseReturnType (inferred).\n */\nexport function validateLeadsPayload(payload: unknown) {\n return leadsWireSchema.safeParse(payload);\n}\n"]}
@@ -29,6 +29,16 @@ var OnboardingConflictError = class extends OnboardingError {
29
29
  this.name = "OnboardingConflictError";
30
30
  }
31
31
  };
32
+ var OnboardingSubdomainTakenError = class extends OnboardingError {
33
+ constructor() {
34
+ super(
35
+ "subdomain_taken",
36
+ "That workspace address is already taken. Choose another.",
37
+ 409
38
+ );
39
+ this.name = "OnboardingSubdomainTakenError";
40
+ }
41
+ };
32
42
  var OnboardingRateLimitError = class extends OnboardingError {
33
43
  retryAfterSeconds;
34
44
  constructor(retryAfterSeconds = null) {
@@ -53,6 +63,33 @@ var OnboardingSpecStaleError = class extends OnboardingError {
53
63
  this.currentSpecVersion = currentSpecVersion;
54
64
  }
55
65
  };
66
+ var OnboardingConsentStaleError = class extends OnboardingError {
67
+ /**
68
+ * The documents the server reports as changed (may be empty when the
69
+ * server does not enumerate them — the caller then refetches + diffs).
70
+ */
71
+ staleDocuments;
72
+ constructor(staleDocuments = []) {
73
+ const which = staleDocuments.length > 0 ? ` (${staleDocuments.map((d) => d.docType ?? d.legalDocumentId ?? "a document").join(", ")})` : "";
74
+ super(
75
+ "consent_stale",
76
+ `One or more legal documents were updated${which}. Please review and re-accept the updated documents, then try again.`,
77
+ 409
78
+ );
79
+ this.name = "OnboardingConsentStaleError";
80
+ this.staleDocuments = staleDocuments;
81
+ }
82
+ };
83
+ var OnboardingConsentIncompleteError = class extends OnboardingError {
84
+ constructor(message) {
85
+ super(
86
+ "consent_incomplete",
87
+ message ?? "Consent is incomplete (400 consent_incomplete). One or more required documents were not accepted at their required level.",
88
+ 400
89
+ );
90
+ this.name = "OnboardingConsentIncompleteError";
91
+ }
92
+ };
56
93
  var OnboardingServerError = class extends OnboardingError {
57
94
  constructor(statusCode, message) {
58
95
  super(
@@ -149,6 +186,6 @@ function withTimeout(op, timeoutMs, onTimeout) {
149
186
  });
150
187
  }
151
188
 
152
- export { DEFAULT_REQUEST_TIMEOUT_MS, OnboardingBotChallengeError, OnboardingConflictError, OnboardingError, OnboardingNetworkError, OnboardingRateLimitError, OnboardingServerError, OnboardingSpecInvalidError, OnboardingSpecStaleError, OnboardingValidationError, fetchWithTimeout, withTimeout };
153
- //# sourceMappingURL=chunk-AW7W3HAC.js.map
154
- //# sourceMappingURL=chunk-AW7W3HAC.js.map
189
+ export { DEFAULT_REQUEST_TIMEOUT_MS, OnboardingBotChallengeError, OnboardingConflictError, OnboardingConsentIncompleteError, OnboardingConsentStaleError, OnboardingError, OnboardingNetworkError, OnboardingRateLimitError, OnboardingServerError, OnboardingSpecInvalidError, OnboardingSpecStaleError, OnboardingSubdomainTakenError, OnboardingValidationError, fetchWithTimeout, withTimeout };
190
+ //# sourceMappingURL=chunk-XJTJPBJB.js.map
191
+ //# sourceMappingURL=chunk-XJTJPBJB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/submit/errors.ts","../src/submit/http.ts"],"names":[],"mappings":"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","file":"chunk-XJTJPBJB.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 | \"consent_stale\"\n | \"consent_incomplete\"\n | \"subdomain_taken\"\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 OnboardingSubdomainTakenError extends OnboardingError {\n constructor() {\n super(\n \"subdomain_taken\",\n \"That workspace address is already taken. Choose another.\",\n 409,\n );\n this.name = \"OnboardingSubdomainTakenError\";\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\n/**\n * A /submit 409 whose body carries `code:\"consent_stale\"` (D543 §4 —\n * SIBLING to spec_stale). Raised when a required legal document's live\n * version/content_hash changed between spec-fetch and submit, so the echoed\n * consent no longer matches the live version.\n *\n * PINNED body shape (D543 §4, mirroring spec_stale):\n * { error: true, code: \"consent_stale\", staleDocuments?: [...] }\n * where each staleDocuments entry identifies a changed doc so the caller can\n * tell the user WHICH docs changed. The caller refetches the spec and\n * REQUIRES re-acceptance of the changed docs — the SDK NEVER auto-accepts a\n * changed doc (D543 §4). Submit-endpoint only.\n */\nexport interface StaleConsentDocument {\n /** The server registry id of the changed doc (D543 §3 legal_documents.id). */\n legalDocumentId?: string;\n /** doc_type of the changed doc, when the server includes it. */\n docType?: string;\n /** The new live version_no, when the server includes it. */\n currentVersionNo?: number;\n}\n\nexport class OnboardingConsentStaleError extends OnboardingError {\n /**\n * The documents the server reports as changed (may be empty when the\n * server does not enumerate them — the caller then refetches + diffs).\n */\n public readonly staleDocuments: StaleConsentDocument[];\n\n constructor(staleDocuments: StaleConsentDocument[] = []) {\n const which =\n staleDocuments.length > 0\n ? ` (${staleDocuments\n .map((d) => d.docType ?? d.legalDocumentId ?? \"a document\")\n .join(\", \")})`\n : \"\";\n super(\n \"consent_stale\",\n `One or more legal documents were updated${which}. Please review and re-accept the updated documents, then try again.`,\n 409,\n );\n this.name = \"OnboardingConsentStaleError\";\n this.staleDocuments = staleDocuments;\n }\n}\n\n/**\n * A /submit 400 whose body carries `code:\"consent_incomplete\"` (D543 §4).\n * The server's authoritative re-validation found a required live document\n * that was missing, not accepted, or whose evidence did not satisfy its\n * acceptance_level gate. Distinct from a generic 422 so headless consumers\n * switching on `kind` route the user back to the consent step specifically.\n */\nexport class OnboardingConsentIncompleteError extends OnboardingError {\n constructor(message?: string) {\n super(\n \"consent_incomplete\",\n message ??\n \"Consent is incomplete (400 consent_incomplete). One or more required documents were not accepted at their required level.\",\n 400,\n );\n this.name = \"OnboardingConsentIncompleteError\";\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"]}
@@ -0,0 +1,186 @@
1
+ import { consentDocKey } from './chunk-NDSBIB3N.js';
2
+
3
+ // src/consent/controller.ts
4
+ var SCROLL_END_THRESHOLD = 0.99;
5
+ function initialGateSatisfied(level) {
6
+ return level === "checkbox";
7
+ }
8
+ function gateSatisfiedFor(d) {
9
+ switch (d.doc.acceptanceLevel) {
10
+ case "checkbox":
11
+ return true;
12
+ case "scroll_then_accept":
13
+ return d.scrolledToEnd;
14
+ case "open_then_accept":
15
+ return d.opened;
16
+ default:
17
+ return false;
18
+ }
19
+ }
20
+ function evidenceOf(d) {
21
+ switch (d.doc.acceptanceLevel) {
22
+ case "scroll_then_accept":
23
+ return { scrolled_to_end: d.scrolledToEnd };
24
+ case "open_then_accept":
25
+ return { opened: d.opened };
26
+ default:
27
+ return {};
28
+ }
29
+ }
30
+ function createConsentController(pack) {
31
+ let docs = (pack?.documents ?? []).map((doc) => ({
32
+ doc,
33
+ key: consentDocKey(doc),
34
+ accepted: false,
35
+ scrolledToEnd: false,
36
+ scrollProgress: 0,
37
+ opened: false
38
+ }));
39
+ const listeners = /* @__PURE__ */ new Set();
40
+ function find(key) {
41
+ return docs.find((d) => d.key === key);
42
+ }
43
+ function snapshot() {
44
+ const documents = docs.map((d) => ({
45
+ doc: d.doc,
46
+ key: d.key,
47
+ acceptanceLevel: d.doc.acceptanceLevel,
48
+ required: d.doc.required,
49
+ gateSatisfied: gateSatisfiedFor(d),
50
+ accepted: d.accepted,
51
+ evidence: evidenceOf(d),
52
+ scrollProgress: d.scrollProgress
53
+ }));
54
+ return { documents, complete: computeComplete() };
55
+ }
56
+ function computeComplete() {
57
+ for (const d of docs) {
58
+ if (d.doc.required && !(d.accepted && gateSatisfiedFor(d))) return false;
59
+ }
60
+ return true;
61
+ }
62
+ function emit() {
63
+ const state = snapshot();
64
+ for (const listener of listeners) listener(state);
65
+ }
66
+ return {
67
+ getState() {
68
+ return snapshot();
69
+ },
70
+ isEmpty() {
71
+ return docs.length === 0;
72
+ },
73
+ isComplete() {
74
+ return computeComplete();
75
+ },
76
+ canSubmit() {
77
+ return computeComplete();
78
+ },
79
+ isGateSatisfied(key) {
80
+ const d = find(key);
81
+ return d ? gateSatisfiedFor(d) : false;
82
+ },
83
+ reportScrollProgress(key, ratio) {
84
+ const d = find(key);
85
+ if (!d || d.doc.acceptanceLevel !== "scroll_then_accept") return;
86
+ const clamped = Number.isFinite(ratio) ? Math.min(1, Math.max(0, ratio)) : 0;
87
+ const reachedEnd = clamped >= SCROLL_END_THRESHOLD;
88
+ const nextProgress = Math.max(d.scrollProgress, clamped);
89
+ const nextScrolledToEnd = d.scrolledToEnd || reachedEnd;
90
+ if (nextProgress === d.scrollProgress && nextScrolledToEnd === d.scrolledToEnd) {
91
+ return;
92
+ }
93
+ d.scrollProgress = nextProgress;
94
+ d.scrolledToEnd = nextScrolledToEnd;
95
+ emit();
96
+ },
97
+ markScrolledToEnd(key) {
98
+ const d = find(key);
99
+ if (!d || d.doc.acceptanceLevel !== "scroll_then_accept") return;
100
+ if (d.scrolledToEnd && d.scrollProgress >= 1) return;
101
+ d.scrolledToEnd = true;
102
+ d.scrollProgress = 1;
103
+ emit();
104
+ },
105
+ markOpened(key) {
106
+ const d = find(key);
107
+ if (!d || d.doc.acceptanceLevel !== "open_then_accept") return;
108
+ if (d.opened) return;
109
+ d.opened = true;
110
+ emit();
111
+ },
112
+ setAccepted(key, accepted) {
113
+ const d = find(key);
114
+ if (!d) return;
115
+ if (accepted) {
116
+ if (!gateSatisfiedFor(d)) return;
117
+ if (d.accepted) return;
118
+ d.accepted = true;
119
+ } else {
120
+ if (!d.accepted) return;
121
+ d.accepted = false;
122
+ }
123
+ emit();
124
+ },
125
+ buildConsentPayload() {
126
+ const out = [];
127
+ for (const d of docs) {
128
+ const gate = gateSatisfiedFor(d);
129
+ if (d.doc.required && !(d.accepted && gate)) {
130
+ throw new Error(
131
+ `buildConsentPayload: cannot build a consent payload \u2014 required document "${d.key}" (${d.doc.acceptanceLevel}) is not accepted at its gate. Never fabricate acceptance; surface the consent gate to the user first.`
132
+ );
133
+ }
134
+ if (d.accepted) {
135
+ out.push({
136
+ legalDocumentId: d.doc.legalDocumentId,
137
+ versionNo: d.doc.versionNo,
138
+ accepted: true,
139
+ evidence: evidenceOf(d)
140
+ });
141
+ }
142
+ }
143
+ return out;
144
+ },
145
+ applyNewPack(newPack) {
146
+ const prevByKey = new Map(docs.map((d) => [d.key, d]));
147
+ const resetKeys = [];
148
+ docs = (newPack?.documents ?? []).map((doc) => {
149
+ const key = consentDocKey(doc);
150
+ const prev = prevByKey.get(key);
151
+ const unchanged = prev !== void 0 && prev.doc.versionNo === doc.versionNo && prev.doc.contentHash === doc.contentHash && prev.doc.acceptanceLevel === doc.acceptanceLevel && prev.doc.required === doc.required;
152
+ if (unchanged) {
153
+ return {
154
+ doc,
155
+ key,
156
+ accepted: prev.accepted,
157
+ scrolledToEnd: prev.scrolledToEnd,
158
+ scrollProgress: prev.scrollProgress,
159
+ opened: prev.opened
160
+ };
161
+ }
162
+ if (prev?.accepted) resetKeys.push(key);
163
+ return {
164
+ doc,
165
+ key,
166
+ accepted: false,
167
+ scrolledToEnd: false,
168
+ scrollProgress: 0,
169
+ opened: false
170
+ };
171
+ });
172
+ emit();
173
+ return resetKeys;
174
+ },
175
+ subscribe(listener) {
176
+ listeners.add(listener);
177
+ return () => {
178
+ listeners.delete(listener);
179
+ };
180
+ }
181
+ };
182
+ }
183
+
184
+ export { SCROLL_END_THRESHOLD, createConsentController, initialGateSatisfied };
185
+ //# sourceMappingURL=chunk-ZR6X3GSM.js.map
186
+ //# sourceMappingURL=chunk-ZR6X3GSM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/consent/controller.ts"],"names":[],"mappings":"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","file":"chunk-ZR6X3GSM.js","sourcesContent":["/**\n * Headless ConsentPack controller (D543 §2/§5).\n *\n * Framework-agnostic consent state for the signup flow's consent stage.\n * Exposes, per document: { doc, accepted, gateSatisfied, evidence } and\n * enforces the per-document ACCEPTANCE-LEVEL GATE (D543 §2):\n *\n * - checkbox — accept is enable-able immediately (basic clickwrap).\n * - scroll_then_accept — accept is DISABLED until the rendered doc is\n * scrolled to the end (evidence.scrolled_to_end).\n * - open_then_accept — accept is DISABLED until the doc is opened/viewed\n * (evidence.opened).\n *\n * The gate is a UX guard (D543 §5: \"Client evidence is UX-only; the server\n * re-validates\"). But the controller ENFORCES it locally so a UI cannot\n * present an accept affordance the gate hasn't unlocked, and so a headless\n * caller cannot assemble a payload that claims an unsatisfied gate.\n *\n * COMPLIANCE-CRITICAL (same discipline as the stepper's consent-fabrication\n * fix): `accept(key)` is REFUSED when the gate is not yet satisfied, and\n * `buildConsentPayload()` THROWS if ANY required doc is not accepted at its\n * gate. The controller NEVER fabricates acceptance or evidence — it only\n * records what the user genuinely did. `isComplete()` is true only when every\n * REQUIRED doc is accepted with its gate satisfied.\n *\n * Same subscribe/mutate contract as the dynamic stepper so the default\n * component layer renders it and tenants can build their own UI against the\n * exported control surface (D543 §5 headless flexibility).\n */\n\nimport {\n consentDocKey,\n type AcceptanceLevel,\n type ConsentDocument,\n type ConsentPack,\n} from \"./model.js\";\n\n/**\n * Evidence for a document's gate. Only the field relevant to the doc's\n * acceptanceLevel is ever set true — a checkbox doc carries no evidence.\n * Mirrors the D543 §4 submit `evidence:{scrolled_to_end?, opened?}` — these\n * two keys stay snake_case (the ONLY snake bits in the consent wire shape).\n */\nexport interface ConsentEvidence {\n scrolled_to_end?: boolean;\n opened?: boolean;\n}\n\n/**\n * Per-document view of the consent state — the framework-agnostic \"row model\"\n * a consumer binds their own rendering to (TanStack-style). NO DOM here.\n */\nexport interface ConsentDocState {\n /** The document descriptor (from the spec's consentPack). */\n doc: ConsentDocument;\n /** Stable client-side key (docType | custom:<customKey>). */\n key: string;\n /** The document's acceptance level, surfaced for convenience. */\n acceptanceLevel: AcceptanceLevel;\n /** Whether the document is required, surfaced for convenience. */\n required: boolean;\n /**\n * Whether the acceptance-level GATE is satisfied — i.e. accept is\n * enable-able. checkbox ⇒ always true; scroll ⇒ scrolled-to-end;\n * open ⇒ opened.\n */\n gateSatisfied: boolean;\n /** Whether the user has genuinely accepted (only possible past the gate). */\n accepted: boolean;\n /** The evidence collected so far (drives gateSatisfied for gated levels). */\n evidence: ConsentEvidence;\n /**\n * Scroll progress in [0,1] for a `scroll_then_accept` doc (0 for other\n * levels). A consumer's scroll handler feeds this via reportScrollProgress;\n * reaching ~1 flips gateSatisfied. Purely a UX-progress read (e.g. a\n * progress bar); the gate itself keys on scrolledToEnd evidence.\n */\n scrollProgress: number;\n}\n\nexport interface ConsentState {\n /** All documents in spec order. */\n documents: ConsentDocState[];\n /** True iff every REQUIRED doc is accepted with its gate satisfied. */\n complete: boolean;\n}\n\nexport type ConsentStateListener = (state: ConsentState) => void;\n\n/**\n * One element of the D543 §4 submit `consent.documents` array — CANONICAL\n * camelCase (legalDocumentId, versionNo, accepted); `evidence` keys stay\n * snake_case (scrolled_to_end / opened).\n */\nexport interface ConsentSubmitDocument {\n legalDocumentId: string;\n versionNo: number;\n accepted: true;\n evidence: ConsentEvidence;\n}\n\nexport interface ConsentController {\n getState(): ConsentState;\n /** True iff there are no documents to consent to (stage is skippable). */\n isEmpty(): boolean;\n /** True iff every required doc is accepted with its gate satisfied. */\n isComplete(): boolean;\n /**\n * Alias of isComplete() — TanStack-style ergonomic read: \"can the consumer\n * submit the consent step?\" (all required docs accepted at their gate).\n */\n canSubmit(): boolean;\n /**\n * Whether the given document's gate is satisfied (accept is enable-able).\n * Unknown key ⇒ false.\n */\n isGateSatisfied(key: string): boolean;\n /**\n * Report scroll progress in [0,1] for a `scroll_then_accept` doc — the\n * consumer's OWN scroll handler feeds this (e.g. on its same-origin scroll\n * container). Ratio ≥ SCROLL_END_THRESHOLD marks scrolled-to-end (opens the\n * gate); otherwise it just updates scrollProgress for UX. Clamped to [0,1].\n * No-op for other levels and unknown keys.\n */\n reportScrollProgress(key: string, ratio: number): void;\n /**\n * Mark a `scroll_then_accept` doc as scrolled to the end (evidence). No-op\n * for other levels (their gate isn't scroll-driven) and unknown keys.\n * Equivalent to reportScrollProgress(key, 1).\n */\n markScrolledToEnd(key: string): void;\n /**\n * Mark an `open_then_accept` doc as opened/viewed (evidence). No-op for\n * other levels and unknown keys.\n */\n markOpened(key: string): void;\n /**\n * Set a document's accepted flag. Accepting is REFUSED (accepted stays\n * false) when the gate is not satisfied — a UI cannot record acceptance a\n * user could not legitimately give. Unaccepting is always allowed.\n */\n setAccepted(key: string, accepted: boolean): void;\n /**\n * Build the D543 §4 submit array from the ACTUAL consent state.\n *\n * @throws {Error} if any REQUIRED doc is not accepted with its gate\n * satisfied — never fabricates acceptance/evidence (compliance-critical).\n * Optional docs that were accepted ARE included; optional docs left\n * unaccepted are omitted.\n */\n buildConsentPayload(): ConsentSubmitDocument[];\n /**\n * Swap in a refetched consentPack after 409 consent_stale. For every doc\n * whose (versionNo, contentHash) CHANGED, acceptance + evidence are\n * RESET (re-acceptance required — NEVER auto-accept a changed doc, D543 §4\n * / task §4). Unchanged docs keep their state. Returns the keys whose\n * consent was reset so the UI can highlight what changed.\n */\n applyNewPack(pack: ConsentPack | undefined): string[];\n subscribe(listener: ConsentStateListener): () => void;\n}\n\n/**\n * Scroll ratio at/above which a `scroll_then_accept` doc counts as\n * scrolled-to-end. Slightly below 1 so sub-pixel rounding on the consumer's\n * scroll math (scrollTop+clientHeight vs scrollHeight) still opens the gate.\n */\nexport const SCROLL_END_THRESHOLD = 0.99;\n\n/** checkbox docs have no gate; gated levels start unsatisfied. */\nfunction initialGateSatisfied(level: AcceptanceLevel): boolean {\n return level === \"checkbox\";\n}\n\ninterface InternalDoc {\n doc: ConsentDocument;\n key: string;\n accepted: boolean;\n scrolledToEnd: boolean;\n scrollProgress: number;\n opened: boolean;\n}\n\nfunction gateSatisfiedFor(d: InternalDoc): boolean {\n switch (d.doc.acceptanceLevel) {\n case \"checkbox\":\n return true;\n case \"scroll_then_accept\":\n return d.scrolledToEnd;\n case \"open_then_accept\":\n return d.opened;\n default:\n // Unreachable — the level enum is exhaustive and validated at parse.\n // Fail CLOSED: an unknown level is never auto-satisfied.\n return false;\n }\n}\n\nfunction evidenceOf(d: InternalDoc): ConsentEvidence {\n switch (d.doc.acceptanceLevel) {\n case \"scroll_then_accept\":\n return { scrolled_to_end: d.scrolledToEnd };\n case \"open_then_accept\":\n return { opened: d.opened };\n default:\n return {};\n }\n}\n\n/**\n * Create a headless consent controller for a spec's consentPack.\n *\n * @param pack the spec's `consentPack` (or undefined ⇒ an empty controller;\n * the stage is skipped and `isComplete()` / `isEmpty()` are true).\n *\n * @example\n * ```ts\n * const consent = createConsentController(spec.consentPack);\n * consent.subscribe(render);\n * consent.markScrolledToEnd(\"tos\"); // scroll_then_accept gate\n * consent.setAccepted(\"tos\", true); // now allowed\n * const docs = consent.buildConsentPayload(); // throws if any required unmet\n * ```\n */\nexport function createConsentController(\n pack: ConsentPack | undefined,\n): ConsentController {\n let docs: InternalDoc[] = (pack?.documents ?? []).map((doc) => ({\n doc,\n key: consentDocKey(doc),\n accepted: false,\n scrolledToEnd: false,\n scrollProgress: 0,\n opened: false,\n }));\n\n const listeners = new Set<ConsentStateListener>();\n\n function find(key: string): InternalDoc | undefined {\n return docs.find((d) => d.key === key);\n }\n\n function snapshot(): ConsentState {\n const documents: ConsentDocState[] = docs.map((d) => ({\n doc: d.doc,\n key: d.key,\n acceptanceLevel: d.doc.acceptanceLevel,\n required: d.doc.required,\n gateSatisfied: gateSatisfiedFor(d),\n accepted: d.accepted,\n evidence: evidenceOf(d),\n scrollProgress: d.scrollProgress,\n }));\n return { documents, complete: computeComplete() };\n }\n\n function computeComplete(): boolean {\n for (const d of docs) {\n if (d.doc.required && !(d.accepted && gateSatisfiedFor(d))) return false;\n }\n return true;\n }\n\n function emit(): void {\n const state = snapshot();\n for (const listener of listeners) listener(state);\n }\n\n return {\n getState() {\n return snapshot();\n },\n\n isEmpty() {\n return docs.length === 0;\n },\n\n isComplete() {\n return computeComplete();\n },\n\n canSubmit() {\n return computeComplete();\n },\n\n isGateSatisfied(key) {\n const d = find(key);\n return d ? gateSatisfiedFor(d) : false;\n },\n\n reportScrollProgress(key, ratio) {\n const d = find(key);\n if (!d || d.doc.acceptanceLevel !== \"scroll_then_accept\") return;\n const clamped = Number.isFinite(ratio)\n ? Math.min(1, Math.max(0, ratio))\n : 0;\n const reachedEnd = clamped >= SCROLL_END_THRESHOLD;\n // Progress never regresses below a prior high-water mark that already\n // opened the gate — once scrolled-to-end, a scroll-back must not re-lock\n // acceptance (matches how clickwrap gates latch).\n const nextProgress = Math.max(d.scrollProgress, clamped);\n const nextScrolledToEnd = d.scrolledToEnd || reachedEnd;\n if (\n nextProgress === d.scrollProgress &&\n nextScrolledToEnd === d.scrolledToEnd\n ) {\n return;\n }\n d.scrollProgress = nextProgress;\n d.scrolledToEnd = nextScrolledToEnd;\n emit();\n },\n\n markScrolledToEnd(key) {\n const d = find(key);\n if (!d || d.doc.acceptanceLevel !== \"scroll_then_accept\") return;\n if (d.scrolledToEnd && d.scrollProgress >= 1) return;\n d.scrolledToEnd = true;\n d.scrollProgress = 1;\n emit();\n },\n\n markOpened(key) {\n const d = find(key);\n if (!d || d.doc.acceptanceLevel !== \"open_then_accept\") return;\n if (d.opened) return;\n d.opened = true;\n emit();\n },\n\n setAccepted(key, accepted) {\n const d = find(key);\n if (!d) return;\n if (accepted) {\n // COMPLIANCE: refuse to record acceptance a user could not give —\n // the gate must be satisfied first. A UI that offers accept before\n // the gate is unlocked cannot fabricate consent through this control.\n if (!gateSatisfiedFor(d)) return;\n if (d.accepted) return;\n d.accepted = true;\n } else {\n if (!d.accepted) return;\n d.accepted = false;\n }\n emit();\n },\n\n buildConsentPayload() {\n const out: ConsentSubmitDocument[] = [];\n for (const d of docs) {\n const gate = gateSatisfiedFor(d);\n if (d.doc.required && !(d.accepted && gate)) {\n throw new Error(\n `buildConsentPayload: cannot build a consent payload — required document \"${d.key}\" (${d.doc.acceptanceLevel}) is not accepted at its gate. Never fabricate acceptance; surface the consent gate to the user first.`,\n );\n }\n // Include every ACCEPTED doc (required or optional). An accepted doc\n // always has its gate satisfied (setAccepted refuses otherwise), so\n // its evidence is trustworthy.\n if (d.accepted) {\n out.push({\n legalDocumentId: d.doc.legalDocumentId,\n versionNo: d.doc.versionNo,\n accepted: true,\n evidence: evidenceOf(d),\n });\n }\n }\n return out;\n },\n\n applyNewPack(newPack) {\n const prevByKey = new Map(docs.map((d) => [d.key, d]));\n const resetKeys: string[] = [];\n\n docs = (newPack?.documents ?? []).map((doc) => {\n const key = consentDocKey(doc);\n const prev = prevByKey.get(key);\n // A doc is UNCHANGED iff the same key AND the same live version AND the\n // same content hash AND the same GATE SHAPE (acceptanceLevel + required).\n // Only then may prior acceptance carry over. A gate ESCALATION at the\n // same version/hash (e.g. checkbox → scroll_then_accept, or required\n // flipping true) would otherwise keep accepted:true with an unsatisfied\n // gate — violating the \"an accepted doc always has its gate satisfied\"\n // invariant and making buildConsentPayload emit accepted:true with\n // insufficient evidence → an unrecoverable server-side gate rejection\n // (P2). Treat any gate-shape change as \"changed\" → reset + re-lock.\n const unchanged =\n prev !== undefined &&\n prev.doc.versionNo === doc.versionNo &&\n prev.doc.contentHash === doc.contentHash &&\n prev.doc.acceptanceLevel === doc.acceptanceLevel &&\n prev.doc.required === doc.required;\n if (unchanged) {\n return {\n doc,\n key,\n accepted: prev.accepted,\n scrolledToEnd: prev.scrolledToEnd,\n scrollProgress: prev.scrollProgress,\n opened: prev.opened,\n };\n }\n // Changed (or new) doc → require fresh acceptance. NEVER auto-accept.\n if (prev?.accepted) resetKeys.push(key);\n return {\n doc,\n key,\n accepted: false,\n scrolledToEnd: false,\n scrollProgress: 0,\n opened: false,\n };\n });\n\n emit();\n return resetKeys;\n },\n\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\n/** Re-export the gate-init helper for the widget's initial affordance state. */\nexport { initialGateSatisfied };\n"]}
@@ -0,0 +1,86 @@
1
+ import { a as OnboardingSpec } from './model-CEwvHlWA.js';
2
+
3
+ /**
4
+ * Config-fetch client for GET /api/v1/public/onboarding/spec (D540 §1/§3).
5
+ *
6
+ * - The onboarder is derived SERVER-side from the request Host (D536) —
7
+ * it is NOT sent by the client. The `onboarder` value here is purely the
8
+ * CLIENT-side cache key (D540 §3: "cache key (onboarder, specVersion)").
9
+ * - The spec response is validated against the LOCKED D540 shape before
10
+ * it is returned; a malformed spec throws OnboardingSpecInvalidError.
11
+ * - Cache semantics: every successful fetch is stored under
12
+ * (onboarder, specVersion) and as the onboarder's "latest". The versioned
13
+ * entries let the 409 spec_stale flow diff the previously-served version
14
+ * against the refetched one (see `diffSpecFields` + the signup widget's
15
+ * handleSpecStale, which reads the cached prior version before refetching).
16
+ */
17
+
18
+ /**
19
+ * In-memory spec cache keyed by (onboarder, specVersion), with a "latest"
20
+ * pointer per onboarder. Purely client-side; no TTL — the reject-stale
21
+ * policy (409 spec_stale on submit) is the authoritative staleness signal,
22
+ * and a refetch overwrites "latest".
23
+ */
24
+ declare class OnboardingSpecCache {
25
+ private readonly byVersion;
26
+ private readonly latest;
27
+ /**
28
+ * Collision-proof composite key. Uses JSON.stringify of the tuple so an
29
+ * onboarder or version containing the delimiter can never alias another
30
+ * entry — and, critically, contains NO control characters (an earlier
31
+ * NUL-separator made git treat this whole file as binary/undiffable).
32
+ */
33
+ private static versionKey;
34
+ get(onboarder: string, specVersion: string): OnboardingSpec | undefined;
35
+ getLatest(onboarder: string): OnboardingSpec | undefined;
36
+ set(onboarder: string, spec: OnboardingSpec): void;
37
+ clear(): void;
38
+ }
39
+ /** Module-level default cache (one per page). Injectable for tests/apps. */
40
+ declare const defaultSpecCache: OnboardingSpecCache;
41
+ /**
42
+ * Diff two spec versions' signup field sets — used by the 409 spec_stale
43
+ * flow to tell the user WHAT changed. Compares by field key across all
44
+ * signup steps.
45
+ */
46
+ interface SpecFieldDiff {
47
+ added: string[];
48
+ removed: string[];
49
+ changed: string[];
50
+ }
51
+ declare function diffSpecFields(prev: OnboardingSpec, next: OnboardingSpec): SpecFieldDiff;
52
+ interface SpecFetchOptions {
53
+ baseUrl: string;
54
+ /**
55
+ * Client-side cache key ONLY (never transmitted; trust is host-derived
56
+ * per D536). Use the onboarder slug/id your embed is configured with.
57
+ */
58
+ onboarder: string;
59
+ /** Custom fetch implementation (defaults to globalThis.fetch). */
60
+ fetch?: typeof globalThis.fetch;
61
+ /** Cache to read/write (defaults to the module-level cache). */
62
+ cache?: OnboardingSpecCache;
63
+ /**
64
+ * Request timeout in ms (default 15s). A stalled intake aborts with
65
+ * OnboardingNetworkError so the boot doesn't hang on "Loading signup form…".
66
+ */
67
+ timeoutMs?: number;
68
+ /**
69
+ * When true, return the cached "latest" for this onboarder if present
70
+ * WITHOUT a network round-trip. Default false (spec fetches are cheap
71
+ * and the server bumps specVersion on any change). The signup widget's
72
+ * boot passes preferCached:true so a remount reuses the loaded spec.
73
+ */
74
+ preferCached?: boolean;
75
+ }
76
+ /**
77
+ * Fetch (and validate, and cache) the onboarding spec.
78
+ *
79
+ * @throws {OnboardingSpecInvalidError} response does not match D540 §1
80
+ * @throws {OnboardingRateLimitError} on 429
81
+ * @throws {OnboardingServerError} on any other non-2xx status
82
+ * @throws {OnboardingNetworkError} on network failure OR timeout
83
+ */
84
+ declare function fetchOnboardingSpec(options: SpecFetchOptions): Promise<OnboardingSpec>;
85
+
86
+ export { OnboardingSpecCache as O, type SpecFetchOptions as S, type SpecFieldDiff as a, diffSpecFields as b, defaultSpecCache as d, fetchOnboardingSpec as f };