@secmia/openui-flow 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1073 @@
1
+ "use client";
2
+
3
+ // src/AdaptiveFlow.tsx
4
+ import * as React from "react";
5
+
6
+ // src/engine.ts
7
+ var DefaultAppRequirements = {
8
+ HAS_EMAIL: "has_email",
9
+ EMAIL_VERIFIED: "email_verified",
10
+ HAS_PASSWORD: "has_password",
11
+ HAS_FIRST_NAME: "has_first_name",
12
+ HAS_LAST_NAME: "has_last_name",
13
+ ACCEPTED_TOS: "accepted_tos",
14
+ HAS_JOB_TITLE: "has_job_title"
15
+ };
16
+ var DefaultAdaptiveSteps = {
17
+ COLLECT_EMAIL: "COLLECT_EMAIL",
18
+ VERIFY_OTP: "VERIFY_OTP",
19
+ COLLECT_PASSWORD: "COLLECT_PASSWORD",
20
+ COLLECT_PROFILE: "COLLECT_PROFILE",
21
+ COLLECT_TOS: "COLLECT_TOS",
22
+ COMPLETE: "COMPLETE"
23
+ };
24
+ var defaultRequirements = [
25
+ DefaultAppRequirements.HAS_EMAIL,
26
+ DefaultAppRequirements.EMAIL_VERIFIED,
27
+ DefaultAppRequirements.HAS_PASSWORD,
28
+ DefaultAppRequirements.HAS_FIRST_NAME,
29
+ DefaultAppRequirements.ACCEPTED_TOS
30
+ ];
31
+ var initialContext = {
32
+ email: null,
33
+ isVerified: false,
34
+ hasPassword: false,
35
+ profile: {
36
+ firstName: null,
37
+ lastName: null,
38
+ jobTitle: null
39
+ },
40
+ agreedToTos: false
41
+ };
42
+ var defaultRequirementResolvers = {
43
+ has_email: {
44
+ isMet: (context) => Boolean(context.email),
45
+ step: DefaultAdaptiveSteps.COLLECT_EMAIL
46
+ },
47
+ email_verified: {
48
+ isMet: (context) => context.isVerified,
49
+ step: DefaultAdaptiveSteps.VERIFY_OTP
50
+ },
51
+ has_password: {
52
+ isMet: (context) => context.hasPassword,
53
+ step: DefaultAdaptiveSteps.COLLECT_PASSWORD
54
+ },
55
+ has_first_name: {
56
+ isMet: (context) => Boolean(context.profile.firstName),
57
+ step: DefaultAdaptiveSteps.COLLECT_PROFILE
58
+ },
59
+ has_last_name: {
60
+ isMet: (context) => Boolean(context.profile.lastName),
61
+ step: DefaultAdaptiveSteps.COLLECT_PROFILE
62
+ },
63
+ accepted_tos: {
64
+ isMet: (context) => context.agreedToTos,
65
+ step: DefaultAdaptiveSteps.COLLECT_TOS
66
+ },
67
+ has_job_title: {
68
+ isMet: (context) => Boolean(context.profile.jobTitle),
69
+ step: DefaultAdaptiveSteps.COLLECT_PROFILE
70
+ }
71
+ };
72
+ function createRequirementGraph(requirements, resolvers, options) {
73
+ const graph = [];
74
+ for (const requirement of requirements) {
75
+ const resolver = resolvers[requirement];
76
+ if (!resolver) {
77
+ continue;
78
+ }
79
+ graph.push({
80
+ requirement,
81
+ step: resolver.step,
82
+ isMet: resolver.isMet,
83
+ when: options?.conditions?.[requirement],
84
+ priority: options?.priorities?.[requirement] ?? 0,
85
+ dependsOn: options?.dependencies?.[requirement] ?? []
86
+ });
87
+ }
88
+ return graph;
89
+ }
90
+ function createDefaultRequirementGraph(options) {
91
+ const requirements = options?.requirements ?? defaultRequirements;
92
+ const resolvers = {
93
+ ...defaultRequirementResolvers,
94
+ ...options?.resolvers ?? {}
95
+ };
96
+ return createRequirementGraph(requirements, resolvers, {
97
+ priorities: options?.priorities,
98
+ conditions: options?.conditions,
99
+ dependencies: options?.dependencies
100
+ });
101
+ }
102
+ function sortGraph(graph) {
103
+ return [...graph].sort((left, right) => {
104
+ const byPriority = (right.priority ?? 0) - (left.priority ?? 0);
105
+ if (byPriority !== 0) {
106
+ return byPriority;
107
+ }
108
+ return 0;
109
+ });
110
+ }
111
+ async function evaluateNextStep(context, graph, completeStep) {
112
+ for (const node of sortGraph(graph)) {
113
+ if (node.when && !await node.when(context)) {
114
+ continue;
115
+ }
116
+ if (node.dependsOn && node.dependsOn.length > 0) {
117
+ const prerequisiteNodes = graph.filter(
118
+ (candidate) => node.dependsOn?.includes(candidate.requirement)
119
+ );
120
+ if (prerequisiteNodes.length > 0) {
121
+ const prerequisitesMet = await Promise.all(
122
+ prerequisiteNodes.map(async (candidate) => candidate.isMet(context))
123
+ );
124
+ if (prerequisitesMet.includes(false)) {
125
+ continue;
126
+ }
127
+ }
128
+ }
129
+ if (!await node.isMet(context)) {
130
+ return node.step;
131
+ }
132
+ }
133
+ return completeStep;
134
+ }
135
+ async function getMissingRequirements(context, graph) {
136
+ const missing = [];
137
+ for (const node of sortGraph(graph)) {
138
+ if (node.when && !await node.when(context)) {
139
+ continue;
140
+ }
141
+ if (!await node.isMet(context)) {
142
+ missing.push(node.requirement);
143
+ }
144
+ }
145
+ return missing;
146
+ }
147
+
148
+ // src/AdaptiveFlow.tsx
149
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
150
+ var defaultStepTitle = {
151
+ COLLECT_EMAIL: "Enter your email",
152
+ VERIFY_OTP: "Verify your email",
153
+ COLLECT_PASSWORD: "Set up your password",
154
+ COLLECT_PROFILE: "Complete your profile",
155
+ COLLECT_TOS: "Accept terms",
156
+ COMPLETE: "Done"
157
+ };
158
+ var styleSlots = [
159
+ "shell",
160
+ "headerRow",
161
+ "eyebrow",
162
+ "title",
163
+ "badge",
164
+ "success",
165
+ "error",
166
+ "info",
167
+ "caption",
168
+ "formRow",
169
+ "formColumn",
170
+ "input",
171
+ "button",
172
+ "checkboxRow",
173
+ "complete",
174
+ "footer",
175
+ "oauthButton"
176
+ ];
177
+ function mergeContext(current, patch) {
178
+ return {
179
+ ...current,
180
+ ...patch,
181
+ profile: {
182
+ ...current.profile,
183
+ ...patch.profile
184
+ }
185
+ };
186
+ }
187
+ function withDefaults(initialValue) {
188
+ if (!initialValue) {
189
+ return initialContext;
190
+ }
191
+ return mergeContext(initialContext, initialValue);
192
+ }
193
+ function toError(error) {
194
+ if (error instanceof Error) {
195
+ return error;
196
+ }
197
+ return new Error("Unknown error while processing adaptive flow");
198
+ }
199
+ function cx(...names) {
200
+ const value = names.filter(Boolean).join(" ").trim();
201
+ return value.length > 0 ? value : void 0;
202
+ }
203
+ function pickStorage(storage) {
204
+ if (typeof window === "undefined") {
205
+ return null;
206
+ }
207
+ if (!storage || storage === "session") {
208
+ return window.sessionStorage;
209
+ }
210
+ if (storage === "local") {
211
+ return window.localStorage;
212
+ }
213
+ return storage;
214
+ }
215
+ function readPersistedState(persistence) {
216
+ if (!persistence) {
217
+ return null;
218
+ }
219
+ const storage = pickStorage(persistence.storage);
220
+ if (!storage) {
221
+ return null;
222
+ }
223
+ const raw = storage.getItem(persistence.key);
224
+ if (!raw) {
225
+ return null;
226
+ }
227
+ const deserialize = persistence.deserialize ?? ((value) => JSON.parse(value));
228
+ return deserialize(raw);
229
+ }
230
+ function writePersistedState(persistence, state) {
231
+ if (!persistence) {
232
+ return;
233
+ }
234
+ const storage = pickStorage(persistence.storage);
235
+ if (!storage) {
236
+ return;
237
+ }
238
+ const serialize = persistence.serialize ?? ((value) => JSON.stringify(value));
239
+ storage.setItem(persistence.key, serialize(state));
240
+ }
241
+ function clearPersistedState(persistence) {
242
+ if (!persistence) {
243
+ return;
244
+ }
245
+ const storage = pickStorage(persistence.storage);
246
+ if (!storage) {
247
+ return;
248
+ }
249
+ storage.removeItem(persistence.key);
250
+ }
251
+ async function assertValid(result) {
252
+ const message = await result;
253
+ if (typeof message === "string" && message.trim().length > 0) {
254
+ throw new Error(message);
255
+ }
256
+ }
257
+ function resolveStyles(unstyled, styleOverrides) {
258
+ const resolved = {};
259
+ for (const slot of styleSlots) {
260
+ if (unstyled) {
261
+ resolved[slot] = styleOverrides?.[slot];
262
+ continue;
263
+ }
264
+ resolved[slot] = {
265
+ ...defaultStyles[slot],
266
+ ...styleOverrides?.[slot] ?? {}
267
+ };
268
+ }
269
+ return resolved;
270
+ }
271
+ function AdaptiveFlow({
272
+ adapter,
273
+ requirements,
274
+ requirementGraph,
275
+ requirementGraphConfig,
276
+ requirementResolvers,
277
+ completeStep,
278
+ stepTitles,
279
+ renderStep,
280
+ stepRegistry,
281
+ initialValue,
282
+ onComplete,
283
+ onError,
284
+ onStepTransition,
285
+ className,
286
+ classNames,
287
+ styles,
288
+ unstyled = false,
289
+ persistence,
290
+ validators
291
+ }) {
292
+ const normalizedRequirements = React.useMemo(
293
+ () => requirements ?? defaultRequirements,
294
+ [requirements]
295
+ );
296
+ const normalizedCompleteStep = React.useMemo(
297
+ () => completeStep ?? "COMPLETE",
298
+ [completeStep]
299
+ );
300
+ const resolvers = React.useMemo(
301
+ () => ({
302
+ ...defaultRequirementResolvers,
303
+ ...requirementResolvers ?? {}
304
+ }),
305
+ [requirementResolvers]
306
+ );
307
+ const graph = React.useMemo(
308
+ () => requirementGraph ?? createDefaultRequirementGraph({
309
+ requirements: normalizedRequirements,
310
+ resolvers,
311
+ priorities: requirementGraphConfig?.priorities,
312
+ conditions: requirementGraphConfig?.conditions,
313
+ dependencies: requirementGraphConfig?.dependencies
314
+ }),
315
+ [
316
+ requirementGraph,
317
+ normalizedRequirements,
318
+ resolvers,
319
+ requirementGraphConfig?.priorities,
320
+ requirementGraphConfig?.conditions,
321
+ requirementGraphConfig?.dependencies
322
+ ]
323
+ );
324
+ const uiStyles = React.useMemo(() => resolveStyles(unstyled, styles), [unstyled, styles]);
325
+ const [context, setContext] = React.useState(() => withDefaults(initialValue));
326
+ const [step, setStep] = React.useState(normalizedCompleteStep);
327
+ const [missingRequirements, setMissingRequirements] = React.useState([]);
328
+ const [transitions, setTransitions] = React.useState([]);
329
+ const [busy, setBusy] = React.useState(false);
330
+ const [message, setMessage] = React.useState(null);
331
+ const [errorMessage, setErrorMessage] = React.useState(null);
332
+ const [oauthPendingProvider, setOauthPendingProvider] = React.useState(null);
333
+ const [persistenceHydrated, setPersistenceHydrated] = React.useState(!persistence);
334
+ const attemptByStepRef = React.useRef({});
335
+ const previousStepRef = React.useRef(null);
336
+ const evaluationRef = React.useRef(0);
337
+ const completed = React.useRef(false);
338
+ React.useEffect(() => {
339
+ if (!persistence) {
340
+ return;
341
+ }
342
+ try {
343
+ const persisted = readPersistedState(persistence);
344
+ if (persisted?.context) {
345
+ setContext(mergeContext(withDefaults(initialValue), persisted.context));
346
+ }
347
+ if (persisted?.oauthPendingProvider) {
348
+ setOauthPendingProvider(persisted.oauthPendingProvider);
349
+ }
350
+ } catch {
351
+ } finally {
352
+ setPersistenceHydrated(true);
353
+ }
354
+ }, [initialValue, persistence]);
355
+ React.useEffect(() => {
356
+ let isCancelled = false;
357
+ const currentEvaluation = ++evaluationRef.current;
358
+ void (async () => {
359
+ const [missing2, next] = await Promise.all([
360
+ getMissingRequirements(context, graph),
361
+ evaluateNextStep(context, graph, normalizedCompleteStep)
362
+ ]);
363
+ if (isCancelled || currentEvaluation !== evaluationRef.current) {
364
+ return;
365
+ }
366
+ setMissingRequirements(missing2);
367
+ setStep(next);
368
+ const from = previousStepRef.current;
369
+ const attemptKey = String(next);
370
+ const nextAttempt = from === next ? (attemptByStepRef.current[attemptKey] ?? 0) + 1 : 1;
371
+ attemptByStepRef.current[attemptKey] = nextAttempt;
372
+ const transition = {
373
+ from,
374
+ to: next,
375
+ at: Date.now(),
376
+ attempt: nextAttempt
377
+ };
378
+ setTransitions((previous) => [...previous, transition].slice(-100));
379
+ previousStepRef.current = next;
380
+ onStepTransition?.(transition, context);
381
+ })().catch((error) => {
382
+ if (isCancelled || currentEvaluation !== evaluationRef.current) {
383
+ return;
384
+ }
385
+ const normalized = toError(error);
386
+ setErrorMessage(normalized.message);
387
+ onError?.(normalized);
388
+ });
389
+ return () => {
390
+ isCancelled = true;
391
+ };
392
+ }, [context, graph, normalizedCompleteStep, onError, onStepTransition]);
393
+ React.useEffect(() => {
394
+ if (step === normalizedCompleteStep) {
395
+ if (!completed.current) {
396
+ completed.current = true;
397
+ onComplete?.(context);
398
+ const shouldClearPersistence = persistence?.clearOnComplete ?? true;
399
+ if (shouldClearPersistence) {
400
+ clearPersistedState(persistence);
401
+ }
402
+ }
403
+ } else {
404
+ completed.current = false;
405
+ }
406
+ }, [context, normalizedCompleteStep, onComplete, persistence, step]);
407
+ React.useEffect(() => {
408
+ if (!persistence || !persistenceHydrated) {
409
+ return;
410
+ }
411
+ try {
412
+ writePersistedState(persistence, { context, oauthPendingProvider });
413
+ } catch {
414
+ }
415
+ }, [context, oauthPendingProvider, persistence, persistenceHydrated]);
416
+ const run = React.useCallback(
417
+ async (job) => {
418
+ setBusy(true);
419
+ setErrorMessage(null);
420
+ try {
421
+ await job();
422
+ } catch (error) {
423
+ const normalized = toError(error);
424
+ setErrorMessage(normalized.message);
425
+ onError?.(normalized);
426
+ } finally {
427
+ setBusy(false);
428
+ }
429
+ },
430
+ [onError]
431
+ );
432
+ const patchContext = React.useCallback((patch) => {
433
+ setContext((prev) => mergeContext(prev, patch));
434
+ }, []);
435
+ const patchBaseContext = React.useCallback(
436
+ (patch) => {
437
+ patchContext(patch);
438
+ },
439
+ [patchContext]
440
+ );
441
+ React.useEffect(() => {
442
+ const completeOAuth = adapter?.completeOAuth;
443
+ if (!oauthPendingProvider || !completeOAuth) {
444
+ return;
445
+ }
446
+ void run(async () => {
447
+ const patch = await completeOAuth(oauthPendingProvider, context);
448
+ if (patch) {
449
+ patchContext(patch);
450
+ }
451
+ setOauthPendingProvider(null);
452
+ setMessage("OAuth sign-in completed.");
453
+ });
454
+ }, [adapter, context, oauthPendingProvider, patchContext, run]);
455
+ const handleEmail = (emailInput) => {
456
+ const email = emailInput.trim().toLowerCase();
457
+ if (!email) {
458
+ return;
459
+ }
460
+ void run(async () => {
461
+ if (validators?.email) {
462
+ await assertValid(validators.email(email, { context }));
463
+ }
464
+ const identity = await adapter?.lookupByEmail?.(email) ?? null;
465
+ patchBaseContext({
466
+ email,
467
+ hasPassword: Boolean(identity?.hasPassword),
468
+ isVerified: Boolean(identity?.isVerified),
469
+ agreedToTos: Boolean(identity?.agreedToTos),
470
+ profile: {
471
+ firstName: identity?.profile?.firstName ?? null,
472
+ lastName: identity?.profile?.lastName ?? null,
473
+ jobTitle: identity?.profile?.jobTitle ?? null
474
+ }
475
+ });
476
+ if (identity?.accountExists && identity.hasPassword) {
477
+ setMessage("Welcome back. Enter your password to continue.");
478
+ return;
479
+ }
480
+ if (adapter?.requestOtp) {
481
+ await adapter.requestOtp(email);
482
+ setMessage("We sent a 6-digit code to your inbox.");
483
+ } else {
484
+ patchBaseContext({ isVerified: true });
485
+ setMessage("No OTP adapter configured. Email verification was skipped.");
486
+ }
487
+ });
488
+ };
489
+ const handleOtp = (code) => {
490
+ if (!context.email) {
491
+ return;
492
+ }
493
+ void run(async () => {
494
+ if (validators?.otp) {
495
+ await assertValid(validators.otp(code, { context, email: context.email }));
496
+ }
497
+ if (adapter?.verifyOtp) {
498
+ await adapter.verifyOtp(context.email, code);
499
+ }
500
+ patchBaseContext({ isVerified: true });
501
+ setMessage("Email verified.");
502
+ });
503
+ };
504
+ const handlePassword = (password) => {
505
+ if (!context.email) {
506
+ return;
507
+ }
508
+ void run(async () => {
509
+ if (validators?.password) {
510
+ await assertValid(validators.password(password, { context, hasPassword: context.hasPassword }));
511
+ }
512
+ if (context.hasPassword) {
513
+ if (adapter?.signInWithPassword) {
514
+ await adapter.signInWithPassword(context.email, password);
515
+ }
516
+ } else {
517
+ if (adapter?.createPassword) {
518
+ await adapter.createPassword(password);
519
+ }
520
+ patchBaseContext({ hasPassword: true });
521
+ }
522
+ setMessage("Password step complete.");
523
+ });
524
+ };
525
+ const handleProfile = (profile) => {
526
+ void run(async () => {
527
+ if (validators?.profile) {
528
+ await assertValid(validators.profile(profile, { context }));
529
+ }
530
+ const next = mergeContext(context, {
531
+ profile: {
532
+ firstName: profile.firstName || null,
533
+ lastName: profile.lastName || null,
534
+ jobTitle: profile.jobTitle || null
535
+ }
536
+ });
537
+ if (adapter?.saveProfile) {
538
+ await adapter.saveProfile(next);
539
+ }
540
+ patchContext({ profile: next.profile });
541
+ setMessage("Profile saved.");
542
+ });
543
+ };
544
+ const handleTos = () => {
545
+ void run(async () => {
546
+ if (validators?.tos) {
547
+ await assertValid(validators.tos(true, { context }));
548
+ }
549
+ const next = mergeContext(context, { agreedToTos: true });
550
+ if (adapter?.acceptTos) {
551
+ await adapter.acceptTos(next);
552
+ }
553
+ patchBaseContext({ agreedToTos: true });
554
+ setMessage("Terms accepted.");
555
+ });
556
+ };
557
+ const handleOAuth = (provider) => {
558
+ const startOAuth = adapter?.startOAuth;
559
+ if (!startOAuth) {
560
+ return;
561
+ }
562
+ void run(async () => {
563
+ setOauthPendingProvider(provider);
564
+ setMessage(`Starting ${provider} sign-in...`);
565
+ await startOAuth(provider, context);
566
+ });
567
+ };
568
+ const missing = missingRequirements;
569
+ const needsJobTitle = normalizedRequirements.includes("has_job_title");
570
+ const stepLabel = stepTitles?.[step] ?? defaultStepTitle[step] ?? step;
571
+ const defaultView = /* @__PURE__ */ jsxs(Fragment, { children: [
572
+ step === "COLLECT_EMAIL" ? /* @__PURE__ */ jsx(
573
+ EmailBlock,
574
+ {
575
+ onSubmit: handleEmail,
576
+ disabled: busy,
577
+ styles: uiStyles,
578
+ classNames
579
+ }
580
+ ) : null,
581
+ step === "VERIFY_OTP" ? /* @__PURE__ */ jsx(
582
+ OtpBlock,
583
+ {
584
+ onSubmit: handleOtp,
585
+ email: context.email ?? "your email",
586
+ disabled: busy,
587
+ styles: uiStyles,
588
+ classNames
589
+ }
590
+ ) : null,
591
+ step === "COLLECT_PASSWORD" ? /* @__PURE__ */ jsx(
592
+ PasswordBlock,
593
+ {
594
+ onSubmit: handlePassword,
595
+ disabled: busy,
596
+ hasPassword: context.hasPassword,
597
+ styles: uiStyles,
598
+ classNames
599
+ }
600
+ ) : null,
601
+ step === "COLLECT_PROFILE" ? /* @__PURE__ */ jsx(
602
+ ProfileBlock,
603
+ {
604
+ defaultValue: context.profile,
605
+ requireJobTitle: needsJobTitle,
606
+ onSubmit: handleProfile,
607
+ disabled: busy,
608
+ styles: uiStyles,
609
+ classNames
610
+ }
611
+ ) : null,
612
+ step === "COLLECT_TOS" ? /* @__PURE__ */ jsx(
613
+ TosBlock,
614
+ {
615
+ onSubmit: handleTos,
616
+ disabled: busy,
617
+ styles: uiStyles,
618
+ classNames
619
+ }
620
+ ) : null,
621
+ step === normalizedCompleteStep ? /* @__PURE__ */ jsx(CompleteBlock, { styles: uiStyles, classNames }) : null,
622
+ step !== "COLLECT_EMAIL" && step !== "VERIFY_OTP" && step !== "COLLECT_PASSWORD" && step !== "COLLECT_PROFILE" && step !== "COLLECT_TOS" && step !== normalizedCompleteStep ? /* @__PURE__ */ jsxs("div", { className: classNames?.info, style: uiStyles.info, children: [
623
+ 'No default renderer for step "',
624
+ step,
625
+ '". Provide renderStep to handle custom steps.'
626
+ ] }) : null
627
+ ] });
628
+ const customView = renderStep ? renderStep({
629
+ step,
630
+ context,
631
+ busy,
632
+ message,
633
+ errorMessage,
634
+ missingRequirements: missing,
635
+ requirements: normalizedRequirements,
636
+ defaultView,
637
+ setContextPatch: patchContext,
638
+ run,
639
+ adapter,
640
+ transitions
641
+ }) : null;
642
+ const registryView = stepRegistry?.[step] ? stepRegistry[step]?.({
643
+ step,
644
+ context,
645
+ busy,
646
+ message,
647
+ errorMessage,
648
+ missingRequirements: missing,
649
+ requirements: normalizedRequirements,
650
+ setContextPatch: patchContext,
651
+ run,
652
+ adapter,
653
+ transitions
654
+ }) : null;
655
+ return /* @__PURE__ */ jsxs("div", { className: cx(className, classNames?.shell), style: uiStyles.shell, children: [
656
+ /* @__PURE__ */ jsxs("div", { className: classNames?.headerRow, style: uiStyles.headerRow, children: [
657
+ /* @__PURE__ */ jsxs("div", { children: [
658
+ /* @__PURE__ */ jsx("div", { className: classNames?.eyebrow, style: uiStyles.eyebrow, children: "Adaptive flow" }),
659
+ /* @__PURE__ */ jsx("h2", { className: classNames?.title, style: uiStyles.title, children: stepLabel })
660
+ ] }),
661
+ /* @__PURE__ */ jsxs("div", { className: classNames?.badge, style: uiStyles.badge, children: [
662
+ normalizedRequirements.length - missing.length,
663
+ "/",
664
+ normalizedRequirements.length
665
+ ] })
666
+ ] }),
667
+ message ? /* @__PURE__ */ jsx("div", { className: classNames?.success, style: uiStyles.success, children: message }) : null,
668
+ errorMessage ? /* @__PURE__ */ jsx("div", { className: classNames?.error, style: uiStyles.error, children: errorMessage }) : null,
669
+ customView ?? registryView ?? defaultView,
670
+ /* @__PURE__ */ jsxs("div", { className: classNames?.footer, style: uiStyles.footer, children: [
671
+ /* @__PURE__ */ jsx(
672
+ "button",
673
+ {
674
+ type: "button",
675
+ className: classNames?.oauthButton,
676
+ style: uiStyles.oauthButton,
677
+ disabled: busy || !adapter?.startOAuth,
678
+ onClick: () => {
679
+ handleOAuth("google");
680
+ },
681
+ children: "Continue with Google"
682
+ }
683
+ ),
684
+ /* @__PURE__ */ jsx(
685
+ "button",
686
+ {
687
+ type: "button",
688
+ className: classNames?.oauthButton,
689
+ style: uiStyles.oauthButton,
690
+ disabled: busy || !adapter?.startOAuth,
691
+ onClick: () => {
692
+ handleOAuth("apple");
693
+ },
694
+ children: "Continue with Apple"
695
+ }
696
+ )
697
+ ] })
698
+ ] });
699
+ }
700
+ function EmailBlock({ onSubmit, disabled, styles, classNames }) {
701
+ const [email, setEmail] = React.useState("");
702
+ return /* @__PURE__ */ jsxs(
703
+ "form",
704
+ {
705
+ className: classNames?.formRow,
706
+ style: styles.formRow,
707
+ onSubmit: (event) => {
708
+ event.preventDefault();
709
+ onSubmit(email);
710
+ },
711
+ children: [
712
+ /* @__PURE__ */ jsx(
713
+ "input",
714
+ {
715
+ className: classNames?.input,
716
+ style: styles.input,
717
+ type: "email",
718
+ autoComplete: "email",
719
+ placeholder: "Enter your email",
720
+ value: email,
721
+ onChange: (event) => setEmail(event.target.value),
722
+ required: true
723
+ }
724
+ ),
725
+ /* @__PURE__ */ jsx("button", { className: classNames?.button, style: styles.button, disabled, type: "submit", children: "Continue" })
726
+ ]
727
+ }
728
+ );
729
+ }
730
+ function OtpBlock({
731
+ onSubmit,
732
+ disabled,
733
+ email,
734
+ styles,
735
+ classNames
736
+ }) {
737
+ const [code, setCode] = React.useState("");
738
+ return /* @__PURE__ */ jsxs(
739
+ "form",
740
+ {
741
+ className: classNames?.formColumn,
742
+ style: styles.formColumn,
743
+ onSubmit: (event) => {
744
+ event.preventDefault();
745
+ onSubmit(code);
746
+ },
747
+ children: [
748
+ /* @__PURE__ */ jsxs("p", { className: classNames?.caption, style: styles.caption, children: [
749
+ "Use the verification code sent to ",
750
+ email,
751
+ "."
752
+ ] }),
753
+ /* @__PURE__ */ jsxs("div", { className: classNames?.formRow, style: styles.formRow, children: [
754
+ /* @__PURE__ */ jsx(
755
+ "input",
756
+ {
757
+ className: classNames?.input,
758
+ style: styles.input,
759
+ inputMode: "numeric",
760
+ placeholder: "Enter 6-digit code",
761
+ value: code,
762
+ onChange: (event) => setCode(event.target.value.replace(/\D/g, "").slice(0, 6)),
763
+ required: true,
764
+ maxLength: 6,
765
+ pattern: "[0-9]{6}"
766
+ }
767
+ ),
768
+ /* @__PURE__ */ jsx(
769
+ "button",
770
+ {
771
+ className: classNames?.button,
772
+ style: styles.button,
773
+ disabled: disabled || code.length !== 6,
774
+ type: "submit",
775
+ children: "Verify"
776
+ }
777
+ )
778
+ ] })
779
+ ]
780
+ }
781
+ );
782
+ }
783
+ function PasswordBlock({
784
+ onSubmit,
785
+ disabled,
786
+ hasPassword,
787
+ styles,
788
+ classNames
789
+ }) {
790
+ const [password, setPassword] = React.useState("");
791
+ return /* @__PURE__ */ jsxs(
792
+ "form",
793
+ {
794
+ className: classNames?.formRow,
795
+ style: styles.formRow,
796
+ onSubmit: (event) => {
797
+ event.preventDefault();
798
+ onSubmit(password);
799
+ },
800
+ children: [
801
+ /* @__PURE__ */ jsx(
802
+ "input",
803
+ {
804
+ className: classNames?.input,
805
+ style: styles.input,
806
+ type: "password",
807
+ autoComplete: hasPassword ? "current-password" : "new-password",
808
+ placeholder: hasPassword ? "Enter your password" : "Create a password",
809
+ value: password,
810
+ onChange: (event) => setPassword(event.target.value),
811
+ required: true,
812
+ minLength: 8
813
+ }
814
+ ),
815
+ /* @__PURE__ */ jsx("button", { className: classNames?.button, style: styles.button, disabled, type: "submit", children: "Continue" })
816
+ ]
817
+ }
818
+ );
819
+ }
820
+ function ProfileBlock({
821
+ onSubmit,
822
+ disabled,
823
+ defaultValue,
824
+ requireJobTitle,
825
+ styles,
826
+ classNames
827
+ }) {
828
+ const [firstName, setFirstName] = React.useState(defaultValue.firstName ?? "");
829
+ const [lastName, setLastName] = React.useState(defaultValue.lastName ?? "");
830
+ const [jobTitle, setJobTitle] = React.useState(defaultValue.jobTitle ?? "");
831
+ return /* @__PURE__ */ jsxs(
832
+ "form",
833
+ {
834
+ className: classNames?.formColumn,
835
+ style: styles.formColumn,
836
+ onSubmit: (event) => {
837
+ event.preventDefault();
838
+ onSubmit({
839
+ firstName: firstName || null,
840
+ lastName: lastName || null,
841
+ jobTitle: jobTitle || null
842
+ });
843
+ },
844
+ children: [
845
+ /* @__PURE__ */ jsxs("div", { className: classNames?.formRow, style: styles.formRow, children: [
846
+ /* @__PURE__ */ jsx(
847
+ "input",
848
+ {
849
+ className: classNames?.input,
850
+ style: styles.input,
851
+ type: "text",
852
+ autoComplete: "given-name",
853
+ placeholder: "First name",
854
+ value: firstName,
855
+ onChange: (event) => setFirstName(event.target.value),
856
+ required: true
857
+ }
858
+ ),
859
+ /* @__PURE__ */ jsx(
860
+ "input",
861
+ {
862
+ className: classNames?.input,
863
+ style: styles.input,
864
+ type: "text",
865
+ autoComplete: "family-name",
866
+ placeholder: "Last name",
867
+ value: lastName,
868
+ onChange: (event) => setLastName(event.target.value),
869
+ required: true
870
+ }
871
+ )
872
+ ] }),
873
+ requireJobTitle ? /* @__PURE__ */ jsx(
874
+ "input",
875
+ {
876
+ className: classNames?.input,
877
+ style: styles.input,
878
+ type: "text",
879
+ autoComplete: "organization-title",
880
+ placeholder: "Job title",
881
+ value: jobTitle,
882
+ onChange: (event) => setJobTitle(event.target.value),
883
+ required: true
884
+ }
885
+ ) : null,
886
+ /* @__PURE__ */ jsx("button", { className: classNames?.button, style: styles.button, disabled, type: "submit", children: "Continue" })
887
+ ]
888
+ }
889
+ );
890
+ }
891
+ function TosBlock({
892
+ onSubmit,
893
+ disabled,
894
+ styles,
895
+ classNames
896
+ }) {
897
+ const [checked, setChecked] = React.useState(false);
898
+ return /* @__PURE__ */ jsxs(
899
+ "form",
900
+ {
901
+ className: classNames?.formColumn,
902
+ style: styles.formColumn,
903
+ onSubmit: (event) => {
904
+ event.preventDefault();
905
+ if (checked) {
906
+ onSubmit();
907
+ }
908
+ },
909
+ children: [
910
+ /* @__PURE__ */ jsxs("label", { className: classNames?.checkboxRow, style: styles.checkboxRow, children: [
911
+ /* @__PURE__ */ jsx("input", { type: "checkbox", checked, onChange: (event) => setChecked(event.target.checked) }),
912
+ /* @__PURE__ */ jsx("span", { children: "I agree to the Terms of Service and Privacy Policy." })
913
+ ] }),
914
+ /* @__PURE__ */ jsx(
915
+ "button",
916
+ {
917
+ className: classNames?.button,
918
+ style: styles.button,
919
+ disabled: disabled || !checked,
920
+ type: "submit",
921
+ children: "Accept and Continue"
922
+ }
923
+ )
924
+ ]
925
+ }
926
+ );
927
+ }
928
+ function CompleteBlock({
929
+ styles,
930
+ classNames
931
+ }) {
932
+ return /* @__PURE__ */ jsxs("div", { className: classNames?.complete, style: styles.complete, children: [
933
+ /* @__PURE__ */ jsx("strong", { children: "You are fully authenticated." }),
934
+ /* @__PURE__ */ jsx("p", { className: classNames?.caption, style: styles.caption, children: "All requirements are complete." })
935
+ ] });
936
+ }
937
+ var defaultStyles = {
938
+ shell: {
939
+ display: "flex",
940
+ flexDirection: "column",
941
+ gap: 12,
942
+ width: "100%",
943
+ maxWidth: 560,
944
+ border: "1px solid #e5e7eb",
945
+ borderRadius: 14,
946
+ padding: 16,
947
+ background: "#ffffff",
948
+ fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, sans-serif"
949
+ },
950
+ headerRow: {
951
+ display: "flex",
952
+ justifyContent: "space-between",
953
+ alignItems: "center",
954
+ gap: 12
955
+ },
956
+ eyebrow: {
957
+ fontSize: 12,
958
+ textTransform: "uppercase",
959
+ letterSpacing: 1.2,
960
+ color: "#6b7280",
961
+ marginBottom: 4
962
+ },
963
+ title: {
964
+ margin: 0,
965
+ fontSize: 22,
966
+ lineHeight: "28px"
967
+ },
968
+ badge: {
969
+ border: "1px solid #d1d5db",
970
+ borderRadius: 999,
971
+ padding: "4px 10px",
972
+ fontSize: 12,
973
+ fontWeight: 600,
974
+ color: "#374151"
975
+ },
976
+ success: {
977
+ border: "1px solid #bbf7d0",
978
+ background: "#f0fdf4",
979
+ color: "#166534",
980
+ borderRadius: 10,
981
+ padding: "8px 10px",
982
+ fontSize: 14
983
+ },
984
+ error: {
985
+ border: "1px solid #fecaca",
986
+ background: "#fef2f2",
987
+ color: "#991b1b",
988
+ borderRadius: 10,
989
+ padding: "8px 10px",
990
+ fontSize: 14
991
+ },
992
+ info: {
993
+ border: "1px solid #bfdbfe",
994
+ background: "#eff6ff",
995
+ color: "#1e3a8a",
996
+ borderRadius: 10,
997
+ padding: "8px 10px",
998
+ fontSize: 14
999
+ },
1000
+ caption: {
1001
+ margin: 0,
1002
+ color: "#4b5563",
1003
+ fontSize: 14
1004
+ },
1005
+ formRow: {
1006
+ display: "flex",
1007
+ gap: 8,
1008
+ alignItems: "center"
1009
+ },
1010
+ formColumn: {
1011
+ display: "flex",
1012
+ flexDirection: "column",
1013
+ gap: 10
1014
+ },
1015
+ input: {
1016
+ width: "100%",
1017
+ border: "1px solid #d1d5db",
1018
+ borderRadius: 8,
1019
+ height: 40,
1020
+ padding: "0 12px",
1021
+ fontSize: 14
1022
+ },
1023
+ button: {
1024
+ border: "1px solid #d1d5db",
1025
+ borderRadius: 8,
1026
+ height: 40,
1027
+ padding: "0 14px",
1028
+ fontWeight: 600,
1029
+ background: "#ffffff",
1030
+ whiteSpace: "nowrap",
1031
+ cursor: "pointer"
1032
+ },
1033
+ checkboxRow: {
1034
+ display: "flex",
1035
+ gap: 8,
1036
+ alignItems: "flex-start",
1037
+ fontSize: 14,
1038
+ color: "#374151"
1039
+ },
1040
+ complete: {
1041
+ border: "1px solid #bbf7d0",
1042
+ background: "#f0fdf4",
1043
+ color: "#166534",
1044
+ borderRadius: 12,
1045
+ padding: 12
1046
+ },
1047
+ footer: {
1048
+ display: "flex",
1049
+ gap: 8,
1050
+ marginTop: 4
1051
+ },
1052
+ oauthButton: {
1053
+ flex: 1,
1054
+ border: "1px solid #d1d5db",
1055
+ borderRadius: 8,
1056
+ padding: "10px 12px",
1057
+ background: "#f9fafb",
1058
+ cursor: "pointer"
1059
+ }
1060
+ };
1061
+ export {
1062
+ AdaptiveFlow,
1063
+ DefaultAdaptiveSteps,
1064
+ DefaultAppRequirements,
1065
+ createDefaultRequirementGraph,
1066
+ createRequirementGraph,
1067
+ defaultRequirementResolvers,
1068
+ defaultRequirements,
1069
+ evaluateNextStep,
1070
+ getMissingRequirements,
1071
+ initialContext
1072
+ };
1073
+ //# sourceMappingURL=index.mjs.map