@owlmeans/client-flow 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.
package/README.md ADDED
@@ -0,0 +1,877 @@
1
+ # @owlmeans/client-flow
2
+
3
+ Client-side configurable user flow management library for OwlMeans Common applications. This package provides a comprehensive system for implementing complex user workflows, authentication flows, and multi-step processes in React applications with state persistence and navigation integration.
4
+
5
+ ## Overview
6
+
7
+ The `@owlmeans/client-flow` package extends the base `@owlmeans/flow` package with client-specific functionality. It provides:
8
+
9
+ - **Client Flow Management**: Complete client-side implementation of configurable user flows
10
+ - **State Persistence**: Automatic flow state persistence across browser sessions
11
+ - **Navigation Integration**: Seamless integration with React Router navigation
12
+ - **Authentication Flows**: Pre-built authentication and authorization workflows
13
+ - **Service Integration**: Integration with OwlMeans service and module systems
14
+ - **React Components**: Flow-aware components and hooks for React applications
15
+ - **Configuration Management**: Dynamic flow configuration from external sources
16
+ - **Error Handling**: Robust error handling and recovery mechanisms
17
+
18
+ This package is part of the OwlMeans flow management quadra:
19
+ - **@owlmeans/flow**: Common flow interfaces and utilities
20
+ - **@owlmeans/client-flow**: Client-side flow implementation *(this package)*
21
+ - **@owlmeans/web-flow**: Web-specific flow components and utilities
22
+ - **@owlmeans/server-flow**: Server-side flow processing
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install @owlmeans/client-flow react
28
+ ```
29
+
30
+ ## Core Concepts
31
+
32
+ ### Flow Models
33
+ Flows are state machines that guide users through multi-step processes. Each flow consists of steps, transitions, and conditions that determine the user's path.
34
+
35
+ ### Flow States
36
+ Flow states represent the current position in a workflow and are automatically persisted to survive browser sessions and page reloads.
37
+
38
+ ### Flow Service
39
+ The central service that manages flow lifecycle, state transitions, and integration with other application services.
40
+
41
+ ### Flow Client
42
+ A client-side wrapper that provides an easy-to-use interface for flow operations with navigation integration.
43
+
44
+ ## API Reference
45
+
46
+ ### Factory Functions
47
+
48
+ #### `makeBasicFlowService(alias?: string): FlowService`
49
+
50
+ Creates a flow service instance for managing client-side flows.
51
+
52
+ ```typescript
53
+ import { makeBasicFlowService } from '@owlmeans/client-flow'
54
+
55
+ const flowService = makeBasicFlowService('user-flows')
56
+ ```
57
+
58
+ **Parameters:**
59
+ - `alias`: string (optional) - Service alias for registration, defaults to 'flow'
60
+
61
+ **Returns:** FlowService instance ready for registration with context
62
+
63
+ #### `createFlowClient<C, T>(context: T, nav: Navigator): FlowClient`
64
+
65
+ Creates a flow client that integrates flows with navigation and context.
66
+
67
+ ```typescript
68
+ import { createFlowClient } from '@owlmeans/client-flow'
69
+ import { useContext, useNavigate } from '@owlmeans/client'
70
+
71
+ const context = useContext()
72
+ const navigator = useNavigate()
73
+ const flowClient = createFlowClient(context, navigator)
74
+ ```
75
+
76
+ **Parameters:**
77
+ - `context`: T - Application context with flow service
78
+ - `nav`: Navigator - Navigation service for routing integration
79
+
80
+ **Returns:** FlowClient instance for flow operations
81
+
82
+ ### Core Interfaces
83
+
84
+ #### `FlowService`
85
+
86
+ Main service interface for flow management and lifecycle operations.
87
+
88
+ ```typescript
89
+ interface FlowService extends LazyService {
90
+ supplied: Promise<boolean> // Flow availability promise
91
+ flow: FlowModel | null // Current active flow
92
+
93
+ // Flow operations
94
+ state(): Promise<FlowModel | null> // Get current flow state
95
+ begin(slug?: string, from?: string): Promise<FlowModel> // Start new flow
96
+ load(flow: string): Promise<FlowModel> // Load existing flow
97
+ proceed(req?, dryRun?): Promise<string> // Proceed to next step
98
+
99
+ // Configuration and providers
100
+ config(): FlowConfig // Get flow configuration
101
+ provideFlow: FlowProvider // Flow provider function
102
+ resolvePair(): ResolvePair // Promise resolution pair
103
+ }
104
+ ```
105
+
106
+ #### `FlowClient`
107
+
108
+ Client-side flow wrapper with navigation and persistence integration.
109
+
110
+ ```typescript
111
+ interface FlowClient {
112
+ boot(target?: string, from?: string): Promise<FlowClient> // Initialize flow client
113
+ setup(flow: FlowModel): FlowClient // Setup with existing flow
114
+ flow(): FlowModel // Get current flow model
115
+ service(): ResolvedServiceRoute // Get target service route
116
+ proceed(transition: FlowTransition, req?): Promise<void> // Execute transition
117
+ persist(): Promise<boolean> // Persist current state
118
+ }
119
+ ```
120
+
121
+ #### `StateRecord`
122
+
123
+ Interface for flow state persistence records.
124
+
125
+ ```typescript
126
+ interface StateRecord extends ResourceRecord, FlowState {
127
+ id: string // Record identifier
128
+ flow: string // Flow type identifier
129
+ step: string // Current step identifier
130
+ service: string // Target service
131
+ data: any // Flow-specific data
132
+ // ... other FlowState properties
133
+ }
134
+ ```
135
+
136
+ #### `StateResource`
137
+
138
+ Resource interface for managing flow state persistence.
139
+
140
+ ```typescript
141
+ interface StateResource extends ClientResource<StateRecord> {
142
+ // Inherits all standard resource methods for CRUD operations
143
+ }
144
+ ```
145
+
146
+ ### Flow Service Methods Detailed Reference
147
+
148
+ #### `state(): Promise<FlowModel | null>`
149
+
150
+ **Purpose**: Retrieves the current active flow model
151
+
152
+ **Behavior**:
153
+ - Waits for flow service to be supplied/ready
154
+ - Returns the currently active flow or null if no flow is active
155
+ - Used to check flow status before operations
156
+
157
+ **Usage**: Checking if a flow is currently active
158
+
159
+ ```typescript
160
+ const flowService = context.service<FlowService>('flow')
161
+
162
+ const currentFlow = await flowService.state()
163
+ if (currentFlow) {
164
+ console.log('Active flow:', currentFlow.name)
165
+ console.log('Current step:', currentFlow.step())
166
+ } else {
167
+ console.log('No active flow')
168
+ }
169
+ ```
170
+
171
+ #### `begin(slug?: string, from?: string): Promise<FlowModel>`
172
+
173
+ **Purpose**: Starts a new flow with the specified configuration
174
+
175
+ **Behavior**:
176
+ - Creates a new flow model based on the slug
177
+ - Sets the service as supplied/ready
178
+ - Enters the flow from the specified step or initial step
179
+ - Returns the active flow model
180
+
181
+ **Usage**: Starting authentication, registration, or other workflows
182
+
183
+ **Parameters**:
184
+ - `slug`: string (optional) - Flow identifier, defaults to config.defaultFlow or STD_AUTH_FLOW
185
+ - `from`: string (optional) - Starting step, defaults to flow's initial step
186
+
187
+ ```typescript
188
+ const flowService = context.service<FlowService>('flow')
189
+
190
+ // Start default authentication flow
191
+ const authFlow = await flowService.begin()
192
+
193
+ // Start specific flow from specific step
194
+ const registrationFlow = await flowService.begin('user-registration', 'email-verification')
195
+
196
+ // Start flow with default slug
197
+ const defaultFlow = await flowService.begin('custom-onboarding')
198
+ ```
199
+
200
+ #### `load(flow: string): Promise<FlowModel>`
201
+
202
+ **Purpose**: Loads and activates a flow from serialized state
203
+
204
+ **Behavior**:
205
+ - Recreates flow model from serialized state string
206
+ - Sets the service as supplied/ready
207
+ - Restores flow to its previous state
208
+ - Returns the restored flow model
209
+
210
+ **Usage**: Restoring flows from persistent storage
211
+
212
+ ```typescript
213
+ const flowService = context.service<FlowService>('flow')
214
+
215
+ // Load flow from saved state
216
+ const savedFlowState = localStorage.getItem('flow-state')
217
+ if (savedFlowState) {
218
+ const restoredFlow = await flowService.load(savedFlowState)
219
+ console.log('Flow restored to step:', restoredFlow.step())
220
+ }
221
+ ```
222
+
223
+ #### `proceed(req?: Partial<AbstractRequest>, dryRun?: boolean): Promise<string>`
224
+
225
+ **Purpose**: Advances the flow to the next step based on conditions
226
+
227
+ **Behavior**:
228
+ - Evaluates current step's transitions and conditions
229
+ - Moves to the next appropriate step
230
+ - Throws FlowUnsupported error (not implemented in basic service)
231
+ - Should be overridden in specific implementations
232
+
233
+ **Usage**: Advancing through flow steps
234
+
235
+ **Note**: The basic service throws `FlowUnsupported` - this should be implemented by specific flow implementations
236
+
237
+ ```typescript
238
+ // This would be implemented in a specific flow service
239
+ const nextStep = await flowService.proceed({
240
+ body: { userInput: 'some-data' }
241
+ })
242
+ ```
243
+
244
+ #### `config(): FlowConfig`
245
+
246
+ **Purpose**: Retrieves the current flow configuration
247
+
248
+ **Behavior**:
249
+ - Accesses flow configuration from context
250
+ - Returns default empty config if not configured
251
+ - Used for flow behavior customization
252
+
253
+ **Usage**: Accessing flow settings and configuration
254
+
255
+ ```typescript
256
+ const flowService = context.service<FlowService>('flow')
257
+
258
+ const config = flowService.config()
259
+ console.log('Default flow:', config.defaultFlow)
260
+ console.log('Available services:', config.services)
261
+ ```
262
+
263
+ ### Flow Client Methods Detailed Reference
264
+
265
+ #### `boot(target?: string, from?: string): Promise<FlowClient>`
266
+
267
+ **Purpose**: Initializes the flow client with optional target service and starting point
268
+
269
+ **Behavior**:
270
+ - Waits for flow service to be ready
271
+ - Attempts to restore flow from persistent storage
272
+ - Creates new flow if no saved state exists
273
+ - Sets target service if provided
274
+ - Returns the initialized client
275
+
276
+ **Usage**: Starting the flow system in your application
277
+
278
+ ```typescript
279
+ const flowClient = createFlowClient(context, navigator)
280
+
281
+ // Boot with specific target service
282
+ await flowClient.boot('user-dashboard', 'login-step')
283
+
284
+ // Boot with default behavior
285
+ await flowClient.boot()
286
+
287
+ // Boot without target, from saved state
288
+ await flowClient.boot(null)
289
+ ```
290
+
291
+ #### `setup(flow: FlowModel): FlowClient`
292
+
293
+ **Purpose**: Configures the client with an existing flow model
294
+
295
+ **Behavior**:
296
+ - Sets the provided flow as the active flow
297
+ - Returns the client for method chaining
298
+ - Used when you already have a flow model
299
+
300
+ **Usage**: Using pre-configured flow models
301
+
302
+ ```typescript
303
+ const existingFlow = await flowService.begin('checkout-flow')
304
+ const flowClient = createFlowClient(context, navigator)
305
+ .setup(existingFlow)
306
+ ```
307
+
308
+ #### `flow(): FlowModel`
309
+
310
+ **Purpose**: Returns the current active flow model
311
+
312
+ **Behavior**: Direct access to the underlying flow model
313
+
314
+ **Usage**: Accessing flow state and methods
315
+
316
+ ```typescript
317
+ const currentFlow = flowClient.flow()
318
+ console.log('Current step:', currentFlow.step())
319
+ console.log('Flow data:', currentFlow.data())
320
+ ```
321
+
322
+ #### `service(): ResolvedServiceRoute`
323
+
324
+ **Purpose**: Returns the resolved service route for the target service
325
+
326
+ **Behavior**:
327
+ - Resolves the target service from the context
328
+ - Returns route information for navigation
329
+
330
+ **Usage**: Getting service routing information
331
+
332
+ ```typescript
333
+ const serviceRoute = flowClient.service()
334
+ console.log('Target service:', serviceRoute.service)
335
+ console.log('Service path:', serviceRoute.path)
336
+ ```
337
+
338
+ #### `proceed(transition: FlowTransition, req?: Partial<AbstractRequest>): Promise<void>`
339
+
340
+ **Purpose**: Executes a flow transition with optional request data
341
+
342
+ **Behavior**:
343
+ - Executes the specified transition
344
+ - Passes request data to the transition handler
345
+ - Updates flow state based on transition result
346
+ - Handles navigation if required
347
+
348
+ **Usage**: Moving between flow steps
349
+
350
+ ```typescript
351
+ await flowClient.proceed('next', {
352
+ body: { formData: userData }
353
+ })
354
+
355
+ await flowClient.proceed('back')
356
+
357
+ await flowClient.proceed('submit', {
358
+ params: { id: userId }
359
+ })
360
+ ```
361
+
362
+ #### `persist(): Promise<boolean>`
363
+
364
+ **Purpose**: Saves the current flow state to persistent storage
365
+
366
+ **Behavior**:
367
+ - Serializes current flow state
368
+ - Saves to flow state resource
369
+ - Returns success status
370
+
371
+ **Usage**: Saving flow progress
372
+
373
+ ```typescript
374
+ const saved = await flowClient.persist()
375
+ if (saved) {
376
+ console.log('Flow state saved successfully')
377
+ } else {
378
+ console.log('Failed to save flow state')
379
+ }
380
+ ```
381
+
382
+ ### Constants
383
+
384
+ #### `DEFAULT_ALIAS`
385
+ Default flow service alias (`'flow'`).
386
+
387
+ #### `FLOW_STATE`
388
+ Resource identifier for flow state persistence (`'state:flow'`).
389
+
390
+ #### `REHACK_MOD`
391
+ Internal module identifier for redirects (`'__redirect'`).
392
+
393
+ ## Usage Examples
394
+
395
+ ### Basic Flow Setup
396
+
397
+ ```typescript
398
+ import { makeBasicFlowService, createFlowClient } from '@owlmeans/client-flow'
399
+ import { makeClientContext, useContext } from '@owlmeans/client'
400
+
401
+ // Create context with flow service
402
+ const context = makeClientContext(config)
403
+ const flowService = makeBasicFlowService()
404
+ context.registerService(flowService)
405
+
406
+ // Initialize context
407
+ await context.configure().init()
408
+
409
+ // Create flow client
410
+ function MyFlowComponent() {
411
+ const context = useContext()
412
+ const navigator = useNavigate()
413
+
414
+ const [flowClient, setFlowClient] = useState(null)
415
+
416
+ useEffect(() => {
417
+ const initFlow = async () => {
418
+ const client = createFlowClient(context, navigator)
419
+ await client.boot('user-onboarding')
420
+ setFlowClient(client)
421
+ }
422
+
423
+ initFlow()
424
+ }, [])
425
+
426
+ return flowClient ? <FlowRenderer client={flowClient} /> : <div>Loading...</div>
427
+ }
428
+ ```
429
+
430
+ ### Authentication Flow Implementation
431
+
432
+ ```typescript
433
+ import { makeBasicFlowService } from '@owlmeans/client-flow'
434
+ import { STD_AUTH_FLOW } from '@owlmeans/flow'
435
+
436
+ // Setup authentication flow
437
+ const context = makeClientContext({
438
+ service: 'my-app',
439
+ // ... other config
440
+ flowConfig: {
441
+ defaultFlow: STD_AUTH_FLOW,
442
+ services: {
443
+ auth: 'authentication-service',
444
+ dashboard: 'user-dashboard'
445
+ },
446
+ modules: {
447
+ login: 'auth-login-module',
448
+ register: 'auth-register-module'
449
+ }
450
+ }
451
+ })
452
+
453
+ const flowService = makeBasicFlowService('auth-flow')
454
+ context.registerService(flowService)
455
+
456
+ // Use in component
457
+ function AuthFlow() {
458
+ const context = useContext()
459
+ const navigator = useNavigate()
460
+ const [currentStep, setCurrentStep] = useState(null)
461
+
462
+ useEffect(() => {
463
+ const startAuthFlow = async () => {
464
+ const client = createFlowClient(context, navigator)
465
+ await client.boot('dashboard', 'login')
466
+
467
+ const flow = client.flow()
468
+ setCurrentStep(flow.step())
469
+ }
470
+
471
+ startAuthFlow()
472
+ }, [])
473
+
474
+ const handleLogin = async (credentials) => {
475
+ const client = createFlowClient(context, navigator)
476
+ await client.proceed('authenticate', {
477
+ body: credentials
478
+ })
479
+
480
+ // Persist progress
481
+ await client.persist()
482
+
483
+ // Update current step
484
+ setCurrentStep(client.flow().step())
485
+ }
486
+
487
+ return (
488
+ <div>
489
+ <h2>Authentication Flow</h2>
490
+ <p>Current Step: {currentStep}</p>
491
+ {currentStep === 'login' && <LoginForm onSubmit={handleLogin} />}
492
+ {currentStep === 'verified' && <Navigate to="/dashboard" />}
493
+ </div>
494
+ )
495
+ }
496
+ ```
497
+
498
+ ### Multi-Step Registration Flow
499
+
500
+ ```typescript
501
+ interface RegistrationData {
502
+ personalInfo: { name: string; email: string }
503
+ preferences: { theme: string; notifications: boolean }
504
+ verification: { code: string }
505
+ }
506
+
507
+ function RegistrationFlow() {
508
+ const context = useContext()
509
+ const navigator = useNavigate()
510
+ const [flowClient, setFlowClient] = useState<FlowClient | null>(null)
511
+ const [registrationData, setRegistrationData] = useState<Partial<RegistrationData>>({})
512
+
513
+ useEffect(() => {
514
+ const initRegistration = async () => {
515
+ const client = createFlowClient(context, navigator)
516
+ await client.boot('user-registration', 'personal-info')
517
+ setFlowClient(client)
518
+ }
519
+
520
+ initRegistration()
521
+ }, [])
522
+
523
+ const proceedToNext = async (stepData: any) => {
524
+ if (!flowClient) return
525
+
526
+ // Update registration data
527
+ const updatedData = { ...registrationData, ...stepData }
528
+ setRegistrationData(updatedData)
529
+
530
+ // Proceed to next step
531
+ await flowClient.proceed('next', {
532
+ body: updatedData
533
+ })
534
+
535
+ // Persist state
536
+ await flowClient.persist()
537
+ }
538
+
539
+ const getCurrentStep = () => {
540
+ return flowClient?.flow().step() || 'loading'
541
+ }
542
+
543
+ const renderCurrentStep = () => {
544
+ const step = getCurrentStep()
545
+
546
+ switch (step) {
547
+ case 'personal-info':
548
+ return <PersonalInfoForm onSubmit={proceedToNext} />
549
+ case 'preferences':
550
+ return <PreferencesForm onSubmit={proceedToNext} />
551
+ case 'verification':
552
+ return <VerificationForm onSubmit={proceedToNext} />
553
+ case 'complete':
554
+ return <RegistrationComplete />
555
+ default:
556
+ return <div>Loading...</div>
557
+ }
558
+ }
559
+
560
+ return (
561
+ <div>
562
+ <h2>Registration Flow</h2>
563
+ <div>Step: {getCurrentStep()}</div>
564
+ {renderCurrentStep()}
565
+ </div>
566
+ )
567
+ }
568
+ ```
569
+
570
+ ### Flow State Persistence
571
+
572
+ ```typescript
573
+ import { appendClientResource } from '@owlmeans/client-resource'
574
+ import { FLOW_STATE } from '@owlmeans/client-flow'
575
+
576
+ // Setup flow state resource
577
+ appendClientResource(context, FLOW_STATE)
578
+
579
+ class FlowStateManager {
580
+ constructor(private context: ClientContext) {}
581
+
582
+ async saveFlowState(flowClient: FlowClient): Promise<boolean> {
583
+ try {
584
+ const flow = flowClient.flow()
585
+ const stateResource = this.context.resource<StateResource>(FLOW_STATE)
586
+
587
+ await stateResource.save({
588
+ id: FLOW_STATE,
589
+ flow: flow.name,
590
+ step: flow.step(),
591
+ service: flowClient.service().service,
592
+ data: flow.data(),
593
+ timestamp: new Date()
594
+ })
595
+
596
+ return true
597
+ } catch (error) {
598
+ console.error('Failed to save flow state:', error)
599
+ return false
600
+ }
601
+ }
602
+
603
+ async loadFlowState(): Promise<StateRecord | null> {
604
+ try {
605
+ const stateResource = this.context.resource<StateResource>(FLOW_STATE)
606
+ return await stateResource.load(FLOW_STATE)
607
+ } catch (error) {
608
+ console.error('Failed to load flow state:', error)
609
+ return null
610
+ }
611
+ }
612
+
613
+ async clearFlowState(): Promise<void> {
614
+ try {
615
+ const stateResource = this.context.resource<StateResource>(FLOW_STATE)
616
+ await stateResource.delete(FLOW_STATE)
617
+ } catch (error) {
618
+ console.error('Failed to clear flow state:', error)
619
+ }
620
+ }
621
+ }
622
+
623
+ // Usage
624
+ const stateManager = new FlowStateManager(context)
625
+
626
+ // Auto-save flow state
627
+ const flowClient = createFlowClient(context, navigator)
628
+ await flowClient.boot()
629
+
630
+ // Save state after each step
631
+ await stateManager.saveFlowState(flowClient)
632
+
633
+ // Restore on application start
634
+ const savedState = await stateManager.loadFlowState()
635
+ if (savedState) {
636
+ const flowService = context.service<FlowService>('flow')
637
+ const restoredFlow = await flowService.begin(savedState.flow)
638
+ restoredFlow.setState(savedState)
639
+ }
640
+ ```
641
+
642
+ ### Custom Flow Service
643
+
644
+ ```typescript
645
+ import { makeBasicFlowService } from '@owlmeans/client-flow'
646
+
647
+ class CustomFlowService extends makeBasicFlowService('custom') {
648
+ async proceed(req?: Partial<AbstractRequest>, dryRun?: boolean): Promise<string> {
649
+ if (!this.flow) {
650
+ throw new Error('No active flow')
651
+ }
652
+
653
+ const currentStep = this.flow.step()
654
+ const transitions = this.flow.transitions()
655
+
656
+ // Custom logic for determining next step
657
+ let nextStep = 'default'
658
+
659
+ if (req?.body?.action === 'skip') {
660
+ nextStep = this.findSkipTransition(transitions)
661
+ } else if (req?.body?.action === 'back') {
662
+ nextStep = this.findBackTransition(transitions)
663
+ } else {
664
+ nextStep = this.findNextTransition(transitions, req)
665
+ }
666
+
667
+ if (!dryRun) {
668
+ await this.flow.transition(nextStep, req)
669
+ }
670
+
671
+ return nextStep
672
+ }
673
+
674
+ private findNextTransition(transitions: any[], req?: any): string {
675
+ // Implement custom transition logic
676
+ return transitions[0]?.target || 'end'
677
+ }
678
+
679
+ private findSkipTransition(transitions: any[]): string {
680
+ return transitions.find(t => t.type === 'skip')?.target || 'end'
681
+ }
682
+
683
+ private findBackTransition(transitions: any[]): string {
684
+ return transitions.find(t => t.type === 'back')?.target || 'start'
685
+ }
686
+ }
687
+
688
+ // Register custom service
689
+ const customFlowService = new CustomFlowService()
690
+ context.registerService(customFlowService)
691
+ ```
692
+
693
+ ### Flow-Aware React Hook
694
+
695
+ ```typescript
696
+ import { useState, useEffect } from 'react'
697
+ import { useContext } from '@owlmeans/client'
698
+
699
+ interface UseFlowResult {
700
+ flowClient: FlowClient | null
701
+ currentStep: string | null
702
+ isLoading: boolean
703
+ proceed: (action: string, data?: any) => Promise<void>
704
+ persist: () => Promise<boolean>
705
+ }
706
+
707
+ export function useFlow(flowType?: string, targetService?: string): UseFlowResult {
708
+ const context = useContext()
709
+ const navigator = useNavigate()
710
+ const [flowClient, setFlowClient] = useState<FlowClient | null>(null)
711
+ const [currentStep, setCurrentStep] = useState<string | null>(null)
712
+ const [isLoading, setIsLoading] = useState(true)
713
+
714
+ useEffect(() => {
715
+ const initFlow = async () => {
716
+ try {
717
+ const client = createFlowClient(context, navigator)
718
+ await client.boot(targetService)
719
+
720
+ if (flowType) {
721
+ const flowService = context.service<FlowService>('flow')
722
+ const flow = await flowService.begin(flowType)
723
+ client.setup(flow)
724
+ }
725
+
726
+ setFlowClient(client)
727
+ setCurrentStep(client.flow().step())
728
+ } catch (error) {
729
+ console.error('Flow initialization failed:', error)
730
+ } finally {
731
+ setIsLoading(false)
732
+ }
733
+ }
734
+
735
+ initFlow()
736
+ }, [flowType, targetService])
737
+
738
+ const proceed = async (action: string, data?: any) => {
739
+ if (!flowClient) return
740
+
741
+ await flowClient.proceed(action, { body: data })
742
+ setCurrentStep(flowClient.flow().step())
743
+ }
744
+
745
+ const persist = async (): Promise<boolean> => {
746
+ if (!flowClient) return false
747
+ return await flowClient.persist()
748
+ }
749
+
750
+ return {
751
+ flowClient,
752
+ currentStep,
753
+ isLoading,
754
+ proceed,
755
+ persist
756
+ }
757
+ }
758
+
759
+ // Usage in component
760
+ function FlowBasedComponent() {
761
+ const { currentStep, isLoading, proceed, persist } = useFlow('onboarding', 'dashboard')
762
+
763
+ if (isLoading) {
764
+ return <div>Initializing flow...</div>
765
+ }
766
+
767
+ const handleNext = async (data: any) => {
768
+ await proceed('next', data)
769
+ await persist()
770
+ }
771
+
772
+ return (
773
+ <div>
774
+ <h3>Current Step: {currentStep}</h3>
775
+ {currentStep === 'welcome' && <WelcomeStep onNext={handleNext} />}
776
+ {currentStep === 'setup' && <SetupStep onNext={handleNext} />}
777
+ {currentStep === 'complete' && <CompleteStep />}
778
+ </div>
779
+ )
780
+ }
781
+ ```
782
+
783
+ ## Error Handling
784
+
785
+ The package integrates with the OwlMeans error system and may throw the following errors:
786
+
787
+ ### `FlowUnsupported`
788
+ Thrown when an unsupported flow operation is attempted.
789
+
790
+ ### `UnknownFlow`
791
+ Thrown when trying to access a flow that hasn't been registered.
792
+
793
+ ### `FlowTargetError`
794
+ Thrown when the target service for a flow cannot be resolved.
795
+
796
+ ### `FlowStepMissconfigured`
797
+ Thrown when flow steps are not properly configured.
798
+
799
+ ```typescript
800
+ import { FlowUnsupported, UnknownFlow, FlowTargetError } from '@owlmeans/flow'
801
+
802
+ const handleFlowError = async () => {
803
+ try {
804
+ await flowClient.proceed('invalid-transition')
805
+ } catch (error) {
806
+ if (error instanceof FlowUnsupported) {
807
+ console.error('Flow operation not supported')
808
+ } else if (error instanceof UnknownFlow) {
809
+ console.error('Flow not found:', error.message)
810
+ } else if (error instanceof FlowTargetError) {
811
+ console.error('Invalid flow target:', error.message)
812
+ }
813
+ }
814
+ }
815
+ ```
816
+
817
+ ## Integration with Other Packages
818
+
819
+ ### Authentication Integration
820
+ ```typescript
821
+ import { makeAuthService } from '@owlmeans/client-auth'
822
+ import { makeBasicFlowService } from '@owlmeans/client-flow'
823
+
824
+ // Setup authentication flow
825
+ const context = makeClientContext(config)
826
+ const authService = makeAuthService()
827
+ const flowService = makeBasicFlowService()
828
+
829
+ context.registerService(authService)
830
+ context.registerService(flowService)
831
+ ```
832
+
833
+ ### Resource Integration
834
+ ```typescript
835
+ import { appendClientResource } from '@owlmeans/client-resource'
836
+ import { FLOW_STATE } from '@owlmeans/client-flow'
837
+
838
+ // Setup flow state persistence
839
+ appendClientResource(context, FLOW_STATE)
840
+ ```
841
+
842
+ ### Navigation Integration
843
+ ```typescript
844
+ import { useNavigate } from '@owlmeans/client'
845
+ import { createFlowClient } from '@owlmeans/client-flow'
846
+
847
+ const navigator = useNavigate()
848
+ const flowClient = createFlowClient(context, navigator)
849
+ ```
850
+
851
+ ## Best Practices
852
+
853
+ 1. **State Persistence**: Always persist flow state for long-running processes
854
+ 2. **Error Handling**: Implement comprehensive error handling for flow operations
855
+ 3. **Flow Design**: Design flows with clear steps and transition conditions
856
+ 4. **Service Integration**: Properly configure target services for flows
857
+ 5. **React Integration**: Use hooks for clean React component integration
858
+ 6. **Testing**: Test flow transitions and error conditions thoroughly
859
+ 7. **Configuration**: Use external configuration for flexible flow behavior
860
+
861
+ ## Dependencies
862
+
863
+ This package depends on:
864
+ - `@owlmeans/flow` - Core flow interfaces and utilities
865
+ - `@owlmeans/client-context` - Client context management
866
+ - `@owlmeans/client-module` - Client module system
867
+ - `@owlmeans/client-resource` - Client resource management
868
+ - `@owlmeans/context` - Core context system
869
+ - `react` - React library (peer dependency)
870
+
871
+ ## Related Packages
872
+
873
+ - [`@owlmeans/flow`](../flow) - Core flow system
874
+ - [`@owlmeans/web-flow`](../web-flow) - Web-specific flow components
875
+ - [`@owlmeans/server-flow`](../server-flow) - Server-side flow processing
876
+ - [`@owlmeans/client-auth`](../client-auth) - Authentication with flow integration
877
+ - [`@owlmeans/client`](../client) - Base React client library