@nodii/onboarding 0.0.1 → 0.1.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 (73) hide show
  1. package/README.md +75 -2
  2. package/dist/chunk-7NS7ENJV.js +232 -0
  3. package/dist/chunk-7NS7ENJV.js.map +1 -0
  4. package/dist/chunk-AZE3NPSY.js +186 -0
  5. package/dist/chunk-AZE3NPSY.js.map +1 -0
  6. package/dist/chunk-BMQINH6O.js +401 -0
  7. package/dist/chunk-BMQINH6O.js.map +1 -0
  8. package/dist/chunk-F75ZJERM.js +199 -0
  9. package/dist/chunk-F75ZJERM.js.map +1 -0
  10. package/dist/chunk-FIRSA7EH.js +3 -0
  11. package/dist/chunk-FIRSA7EH.js.map +1 -0
  12. package/dist/chunk-IPAKPLIF.js +1660 -0
  13. package/dist/chunk-IPAKPLIF.js.map +1 -0
  14. package/dist/chunk-JFDQZ3AC.js +3 -0
  15. package/dist/chunk-JFDQZ3AC.js.map +1 -0
  16. package/dist/chunk-JFIONONR.js +3 -0
  17. package/dist/chunk-JFIONONR.js.map +1 -0
  18. package/dist/chunk-KQTS47FC.js +546 -0
  19. package/dist/chunk-KQTS47FC.js.map +1 -0
  20. package/dist/chunk-LVJM4CTG.js +306 -0
  21. package/dist/chunk-LVJM4CTG.js.map +1 -0
  22. package/dist/chunk-SEKNVHD3.js +59 -0
  23. package/dist/chunk-SEKNVHD3.js.map +1 -0
  24. package/dist/chunk-SIMLMQPT.js +37 -0
  25. package/dist/chunk-SIMLMQPT.js.map +1 -0
  26. package/dist/chunk-U56GQITG.js +79 -0
  27. package/dist/chunk-U56GQITG.js.map +1 -0
  28. package/dist/chunk-U6AXKGYQ.js +101 -0
  29. package/dist/chunk-U6AXKGYQ.js.map +1 -0
  30. package/dist/chunk-VH6FWDWI.js +3 -0
  31. package/dist/chunk-VH6FWDWI.js.map +1 -0
  32. package/dist/chunk-XJTJPBJB.js +191 -0
  33. package/dist/chunk-XJTJPBJB.js.map +1 -0
  34. package/dist/chunk-Z4PA6C2Z.js +3 -0
  35. package/dist/chunk-Z4PA6C2Z.js.map +1 -0
  36. package/dist/client-CqE2dF9A.d.ts +86 -0
  37. package/dist/consent/index.d.ts +59 -0
  38. package/dist/consent/index.js +7 -0
  39. package/dist/consent/index.js.map +1 -0
  40. package/dist/controller-BQb62D0N.d.ts +177 -0
  41. package/dist/dynamic/index.d.ts +235 -0
  42. package/dist/dynamic/index.js +6 -0
  43. package/dist/dynamic/index.js.map +1 -0
  44. package/dist/errors-vJkye4a0.d.ts +110 -0
  45. package/dist/form/index.d.ts +64 -0
  46. package/dist/form/index.js +5 -0
  47. package/dist/form/index.js.map +1 -0
  48. package/dist/index-Dqfa890U.d.ts +83 -0
  49. package/dist/index.d.ts +16 -2
  50. package/dist/index.js +18 -2
  51. package/dist/index.js.map +1 -0
  52. package/dist/model-ESLSEbi0.d.ts +640 -0
  53. package/dist/model-mnjAEtKe.d.ts +253 -0
  54. package/dist/recaptcha/index.d.ts +102 -0
  55. package/dist/recaptcha/index.js +4 -0
  56. package/dist/recaptcha/index.js.map +1 -0
  57. package/dist/render-DC7P2BGQ.js +5 -0
  58. package/dist/render-DC7P2BGQ.js.map +1 -0
  59. package/dist/schema-BrXb9gnh.d.ts +267 -0
  60. package/dist/signup-client-BL5DfarJ.d.ts +392 -0
  61. package/dist/spec/index.d.ts +17 -0
  62. package/dist/spec/index.js +5 -0
  63. package/dist/spec/index.js.map +1 -0
  64. package/dist/submit/index.d.ts +42 -0
  65. package/dist/submit/index.js +6 -0
  66. package/dist/submit/index.js.map +1 -0
  67. package/dist/viewer/index.d.ts +4 -0
  68. package/dist/viewer/index.js +5 -0
  69. package/dist/viewer/index.js.map +1 -0
  70. package/dist/widget/index.d.ts +262 -0
  71. package/dist/widget/index.js +12 -0
  72. package/dist/widget/index.js.map +1 -0
  73. package/package.json +96 -6
@@ -0,0 +1,262 @@
1
+ import { a as OnboardingSpec } from '../model-ESLSEbi0.js';
2
+ import { O as OnboardingSpecCache } from '../client-CqE2dF9A.js';
3
+ import { c as SignupSubmitResult } from '../signup-client-BL5DfarJ.js';
4
+ import { _ as ___viewer_render_js } from '../index-Dqfa890U.js';
5
+ import { C as ConsentController, c as ConsentState } from '../controller-BQb62D0N.js';
6
+ import 'zod/v3';
7
+ import '../schema-BrXb9gnh.js';
8
+ import '../model-mnjAEtKe.js';
9
+
10
+ /**
11
+ * Default vanilla-TS contact form widget.
12
+ *
13
+ * Renders a form into a host element and handles the full lifecycle:
14
+ * 1. Render fields (name, email, phone, message)
15
+ * 2. Client-side validation on submit
16
+ * 3. reCAPTCHA token acquisition (D537)
17
+ * 4. POST to /api/v1/public/onboarding/leads via submit client
18
+ * 5. Retry on 5xx with SAME idempotency key
19
+ * 6. Show success or error state
20
+ *
21
+ * No React or framework required. CSS via injectWidgetStyles().
22
+ */
23
+ interface ContactFormMountOptions {
24
+ /**
25
+ * The onboarder identifier (slug or UUID).
26
+ *
27
+ * NOT transmitted on the wire — by design. Per LOCKED D536, onboarder
28
+ * trust/scoping is derived server-side from the request Host at the edge
29
+ * ("onboarder derived from the bundle, never client-supplied", D540 §1);
30
+ * a client-supplied identifier is never trusted and therefore never sent.
31
+ *
32
+ * This option is reserved for Phase 2, where it becomes the client-side
33
+ * cache key for the spec-fetch (`(onboarder, specVersion)` per D540 §3).
34
+ * In Phase 1 it has no runtime effect.
35
+ */
36
+ onboarder: string;
37
+ /**
38
+ * Base URL for the intake service.
39
+ * e.g. "https://api.acme.nucleus-cloud.in"
40
+ */
41
+ baseUrl: string;
42
+ /**
43
+ * reCAPTCHA v3 site key. Obtained from the spec endpoint (D540 §1)
44
+ * or passed directly in Phase 1 before spec-fetch is wired.
45
+ */
46
+ siteKey?: string;
47
+ /**
48
+ * Which form to render. Phase 1 supports "contact" only.
49
+ * Phase 2 will add "signup" (dynamic stepper from spec).
50
+ */
51
+ form: "contact";
52
+ /** Called after a successful lead submission. */
53
+ onSuccess?: () => void;
54
+ /** Called on non-retryable errors (bot challenge, rate limit). */
55
+ onError?: (error: unknown) => void;
56
+ /** Custom fetch implementation (defaults to globalThis.fetch). */
57
+ fetch?: typeof globalThis.fetch;
58
+ /**
59
+ * Per-request timeout in ms (default 15s), applied to BOTH the reCAPTCHA
60
+ * execute() and the submit POST so a hung dependency re-enables the form.
61
+ */
62
+ timeoutMs?: number;
63
+ /** Success message override. */
64
+ successMessage?: string;
65
+ /** Submit button label override. */
66
+ submitLabel?: string;
67
+ }
68
+ interface MountedWidget {
69
+ /** Unmount and clean up the widget from the host element. */
70
+ unmount(): void;
71
+ /** Reset the form to its initial state. */
72
+ reset(): void;
73
+ }
74
+ /**
75
+ * Mount the contact form widget into a host element.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * import { OnboardingWidget } from "@nodii/onboarding/widget";
80
+ * const widget = OnboardingWidget.mount(document.getElementById("signup"), {
81
+ * onboarder: "acme-corp",
82
+ * form: "contact",
83
+ * baseUrl: "https://api.acme.nucleus-cloud.in",
84
+ * siteKey: "6Lc...",
85
+ * });
86
+ * ```
87
+ */
88
+ declare function mountContactForm(host: HTMLElement, options: ContactFormMountOptions): MountedWidget;
89
+
90
+ /**
91
+ * Default vanilla-TS dynamic signup stepper widget (D540 Phase 2).
92
+ *
93
+ * Mount flow:
94
+ * 1. Fetch + validate the spec (GET /api/v1/public/onboarding/spec);
95
+ * the reCAPTCHA siteKey comes FROM THE SPEC (botChallenge.siteKey) —
96
+ * the mount-option siteKey is only an override/fallback.
97
+ * 2. Build the headless stepper from spec.forms.signup.steps.
98
+ * 3. Render the current step's fields per descriptor type; Back/Next
99
+ * navigate with per-step validation.
100
+ * 4. The FINAL step carries the account section (credentials + consent
101
+ * clickwrap) and the submit button.
102
+ * 5. Submit: validateAll → payload-fingerprint key management (same
103
+ * D538 discipline as the contact widget) → reCAPTCHA token →
104
+ * submitSignup.
105
+ * 6. 409 spec_stale: refetch the spec, applyNewSpec (drops removed
106
+ * fields, re-validates), show a "form updated — please review"
107
+ * banner. The SDK NEVER auto-resubmits a stale submission.
108
+ */
109
+
110
+ interface SignupFormMountOptions {
111
+ /**
112
+ * The onboarder identifier — the CLIENT-side spec-cache key
113
+ * (D540 §3 `(onboarder, specVersion)`). NOT transmitted: per LOCKED
114
+ * D536, onboarder trust/scoping is host-derived at the edge.
115
+ */
116
+ onboarder: string;
117
+ baseUrl: string;
118
+ /**
119
+ * OPTIONAL siteKey override. Normally the widget uses the siteKey the
120
+ * spec serves (botChallenge.siteKey, D537/D540); pass this only for
121
+ * environments where the spec cannot carry it yet.
122
+ */
123
+ siteKey?: string;
124
+ form: "signup";
125
+ onSuccess?: (result: SignupSubmitResult) => void;
126
+ onError?: (error: unknown) => void;
127
+ /** Called after the spec loads and the stepper renders. */
128
+ onReady?: (spec: OnboardingSpec) => void;
129
+ fetch?: typeof globalThis.fetch;
130
+ /**
131
+ * Per-request timeout in ms (default 15s), applied to the boot spec fetch,
132
+ * the reCAPTCHA execute(), the submit POST, and the 409 refetch — so a hung
133
+ * dependency never leaves "Loading signup form…"/"Sending…" stuck.
134
+ */
135
+ timeoutMs?: number;
136
+ submitLabel?: string;
137
+ tosUrl?: string;
138
+ privacyUrl?: string;
139
+ /** Spec cache override (defaults to the module-level cache). */
140
+ cache?: OnboardingSpecCache;
141
+ }
142
+ /**
143
+ * Mount the dynamic signup stepper into a host element.
144
+ * Returns synchronously; the spec loads in the background (loading state
145
+ * rendered meanwhile; onReady fires when interactive).
146
+ */
147
+ declare function mountSignupForm(host: HTMLElement, options: SignupFormMountOptions): MountedWidget;
148
+
149
+ /**
150
+ * Default CSS for the onboarding contact form widget.
151
+ *
152
+ * Uses CSS custom properties (variables) for theming. Consumers override
153
+ * these on the host element or globally:
154
+ *
155
+ * --nodii-onboarding-font-family
156
+ * --nodii-onboarding-primary-color
157
+ * --nodii-onboarding-primary-hover
158
+ * --nodii-onboarding-error-color
159
+ * --nodii-onboarding-border-color
160
+ * --nodii-onboarding-input-bg
161
+ * --nodii-onboarding-label-color
162
+ * --nodii-onboarding-radius
163
+ * --nodii-onboarding-spacing
164
+ *
165
+ * The widget scopes all styles under `.nodii-onboarding-widget` to avoid
166
+ * leaking into the host page.
167
+ */
168
+ 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\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";
169
+ /**
170
+ * Inject the default widget CSS once into the document head.
171
+ * Idempotent — will not add a duplicate style tag.
172
+ */
173
+ declare function injectWidgetStyles(): void;
174
+
175
+ /**
176
+ * Shared DOM helpers for the widget layer.
177
+ *
178
+ * `idSelector` builds a `#id` CSS selector with the id CSS-escaped, so a field
179
+ * key (which originates in the fetched onboarder spec) can never break out of
180
+ * the selector or match unintended nodes. This is defense-in-depth: the spec
181
+ * model already constrains field keys to `[A-Za-z0-9_-]+` (FIELD_KEY_PATTERN),
182
+ * but the escaping guarantees selector safety even for the widget's own
183
+ * internally-composed ids and for any future relaxation of that alphabet.
184
+ */
185
+ /**
186
+ * CSS-escape a string for safe use as an identifier in a selector.
187
+ * Uses the standard `CSS.escape` when available (all modern browsers), with a
188
+ * spec-compliant-enough fallback for non-DOM/test runtimes: escape everything
189
+ * that is not an ASCII identifier char.
190
+ */
191
+ declare function cssEscape(value: string): string;
192
+ /** Build a safe `#id` selector with the id CSS-escaped. */
193
+ declare function idSelector(id: string): string;
194
+
195
+ /** Render-time options for the default consent step. */
196
+ interface ConsentStepRenderOptions {
197
+ /** Injectable fetch for doc-content load (defaults to global). */
198
+ fetch?: typeof globalThis.fetch;
199
+ timeoutMs?: number;
200
+ /**
201
+ * Injectable viewer-module importer (defaults to the real dynamic
202
+ * `import("../viewer/render.js")`). Exposed so a consumer can control code-
203
+ * splitting AND so tests can drive the module-load-FAILURE branch WITHOUT
204
+ * `mock.module` (which leaks across bun tests). Must resolve to an object
205
+ * exposing `renderConsentDocument`; rejecting simulates a chunk-load failure.
206
+ */
207
+ importViewer?: () => Promise<typeof ___viewer_render_js>;
208
+ }
209
+ /**
210
+ * Turn a consent doc's stable key into a DOM-id-safe token. `doc_type` is a
211
+ * fixed enum (safe); a custom key arrives as `custom:<key>` — the `:` and any
212
+ * residual metachar are replaced so the raw token is valid inside an id AND
213
+ * inside a `#id` selector. (The key is already `[A-Za-z0-9_-]+` past the `:`
214
+ * per model.ts; this only neutralizes the `custom:` prefix separator.)
215
+ */
216
+ declare function consentDocElementId(key: string): string;
217
+ /**
218
+ * Render (or re-render) the consent stage into a host container from the given
219
+ * consent state. Full re-render (the doc set is small); disabled/checked/error
220
+ * state is refreshed in place via updateConsentStep so scroll position and
221
+ * inline doc content are not lost on unrelated state changes.
222
+ */
223
+ declare function renderConsentStep(host: HTMLElement, control: ConsentController, state: ConsentState, staleKeys?: string[], opts?: ConsentStepRenderOptions): void;
224
+ /**
225
+ * Refresh the consent cards' disabled/checked/error state in place (no full
226
+ * re-render) — called on every stepper state change so gate unlocks and
227
+ * live-clearing errors reflect immediately without losing scroll position or
228
+ * re-fetching inline doc content.
229
+ */
230
+ declare function updateConsentStep(host: HTMLElement, state: ConsentState, consentErrors: Array<{
231
+ path: string;
232
+ message: string;
233
+ }>): void;
234
+
235
+ type WidgetMountOptions = ContactFormMountOptions | SignupFormMountOptions;
236
+ declare const OnboardingWidget: {
237
+ /**
238
+ * Mount the onboarding widget into a host element.
239
+ *
240
+ * @param el - The DOM element to render into.
241
+ * @param options - Widget configuration. `form: "contact"` renders the
242
+ * fixed contact form (Phase 1); `form: "signup"` fetches the D540 spec
243
+ * and renders the dynamic stepper (Phase 2 — siteKey comes from the
244
+ * spec's botChallenge unless overridden).
245
+ * @returns A mounted widget control (unmount / reset).
246
+ *
247
+ * @example
248
+ * ```ts
249
+ * import { OnboardingWidget } from "@nodii/onboarding/widget";
250
+ *
251
+ * OnboardingWidget.mount(document.getElementById("signup-form")!, {
252
+ * onboarder: "acme-corp",
253
+ * form: "signup",
254
+ * baseUrl: "https://api.acme.nucleus-cloud.in",
255
+ * onSuccess: (r) => console.log("landed at", r.terminalStage),
256
+ * });
257
+ * ```
258
+ */
259
+ readonly mount: (el: HTMLElement, options: WidgetMountOptions) => MountedWidget;
260
+ };
261
+
262
+ export { type ConsentStepRenderOptions, type ContactFormMountOptions, DEFAULT_WIDGET_CSS, type MountedWidget, OnboardingWidget, type SignupFormMountOptions, type WidgetMountOptions, consentDocElementId, cssEscape, idSelector, injectWidgetStyles, mountContactForm, mountSignupForm, renderConsentStep, updateConsentStep };
@@ -0,0 +1,12 @@
1
+ export { DEFAULT_WIDGET_CSS, OnboardingWidget, consentDocElementId, cssEscape, idSelector, injectWidgetStyles, mountContactForm, mountSignupForm, renderConsentStep, updateConsentStep } from '../chunk-IPAKPLIF.js';
2
+ import '../chunk-U6AXKGYQ.js';
3
+ import '../chunk-F75ZJERM.js';
4
+ import '../chunk-KQTS47FC.js';
5
+ import '../chunk-AZE3NPSY.js';
6
+ import '../chunk-U56GQITG.js';
7
+ import '../chunk-BMQINH6O.js';
8
+ import '../chunk-SIMLMQPT.js';
9
+ import '../chunk-XJTJPBJB.js';
10
+ import '../chunk-7NS7ENJV.js';
11
+ //# sourceMappingURL=index.js.map
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
package/package.json CHANGED
@@ -1,24 +1,114 @@
1
1
  {
2
2
  "name": "@nodii/onboarding",
3
- "version": "0.0.1",
4
- "description": "Placeholder for @nodii/onboarding — implementation lands at v0.1.0",
3
+ "version": "0.1.0",
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",
7
+ "browser": true,
7
8
  "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
8
10
  "types": "./dist/index.d.ts",
9
11
  "exports": {
10
12
  ".": {
11
- "types": "./dist/index.d.ts",
12
- "default": "./dist/index.js"
13
+ "import": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ }
17
+ },
18
+ "./form": {
19
+ "import": {
20
+ "types": "./dist/form/index.d.ts",
21
+ "default": "./dist/form/index.js"
22
+ }
23
+ },
24
+ "./spec": {
25
+ "import": {
26
+ "types": "./dist/spec/index.d.ts",
27
+ "default": "./dist/spec/index.js"
28
+ }
29
+ },
30
+ "./dynamic": {
31
+ "import": {
32
+ "types": "./dist/dynamic/index.d.ts",
33
+ "default": "./dist/dynamic/index.js"
34
+ }
35
+ },
36
+ "./consent": {
37
+ "import": {
38
+ "types": "./dist/consent/index.d.ts",
39
+ "default": "./dist/consent/index.js"
40
+ }
41
+ },
42
+ "./submit": {
43
+ "import": {
44
+ "types": "./dist/submit/index.d.ts",
45
+ "default": "./dist/submit/index.js"
46
+ }
47
+ },
48
+ "./recaptcha": {
49
+ "import": {
50
+ "types": "./dist/recaptcha/index.d.ts",
51
+ "default": "./dist/recaptcha/index.js"
52
+ }
53
+ },
54
+ "./widget": {
55
+ "import": {
56
+ "types": "./dist/widget/index.d.ts",
57
+ "default": "./dist/widget/index.js"
58
+ }
59
+ },
60
+ "./viewer": {
61
+ "import": {
62
+ "types": "./dist/viewer/index.d.ts",
63
+ "default": "./dist/viewer/index.js"
64
+ }
13
65
  }
14
66
  },
15
- "files": ["dist"],
67
+ "files": [
68
+ "dist",
69
+ "README.md",
70
+ "LICENSE"
71
+ ],
72
+ "scripts": {
73
+ "build": "tsup",
74
+ "dev": "tsup --watch",
75
+ "typecheck": "tsc --noEmit",
76
+ "test": "bun test",
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"
81
+ },
82
+ "dependencies": {
83
+ "dompurify": "^3.4.12",
84
+ "marked": "^18.0.6",
85
+ "pdfjs-dist": "^6.1.200",
86
+ "zod": "^3.25.76"
87
+ },
88
+ "devDependencies": {
89
+ "@happy-dom/global-registrator": "^20.10.6",
90
+ "@playwright/test": "^1.61.1",
91
+ "tsup": "^8.3.5",
92
+ "typescript": "^5.9.3"
93
+ },
16
94
  "publishConfig": {
17
- "access": "public"
95
+ "access": "public",
96
+ "registry": "https://registry.npmjs.org"
18
97
  },
19
98
  "repository": {
20
99
  "type": "git",
21
100
  "url": "git+https://github.com/cognion-nucleus/nodii-libs.git",
22
101
  "directory": "ts/onboarding"
102
+ },
103
+ "keywords": [
104
+ "onboarding",
105
+ "self-signup",
106
+ "recaptcha",
107
+ "browser",
108
+ "sdk",
109
+ "nodii"
110
+ ],
111
+ "engines": {
112
+ "node": ">=20"
23
113
  }
24
114
  }