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