@owlmeans/client-flow 0.1.2 → 0.1.4
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 +33 -842
- package/build/client.js +4 -4
- package/build/client.js.map +1 -1
- package/build/types.d.ts +1 -1
- package/build/types.d.ts.map +1 -1
- package/package.json +15 -14
- package/src/client.ts +6 -6
- package/src/types.ts +1 -1
- package/tsconfig.json +5 -10
- package/build/helper.d.ts +0 -5
- package/build/helper.d.ts.map +0 -1
- package/build/helper.js +0 -3
- package/build/helper.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,877 +1,68 @@
|
|
|
1
1
|
# @owlmeans/client-flow
|
|
2
2
|
|
|
3
|
-
Client-side
|
|
3
|
+
Client-side flow orchestration service for managing multi-step workflows in browser environments.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
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
|
|
7
|
+
- `makeBasicFlowService(alias?)` — creates a flow service for context registration
|
|
8
|
+
- `createFlowClient(context, nav)` — creates a `FlowClient` bound to the app context and navigator
|
|
9
|
+
- `FlowService` / `FlowClient` — interfaces for flow state management and step transitions
|
|
10
|
+
- Used by `@owlmeans/web-flow` to drive navigation through workflow steps
|
|
23
11
|
|
|
24
12
|
## Installation
|
|
25
13
|
|
|
26
14
|
```bash
|
|
27
|
-
|
|
15
|
+
bun add @owlmeans/client-flow
|
|
28
16
|
```
|
|
29
17
|
|
|
30
|
-
##
|
|
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
|
|
18
|
+
## Usage
|
|
45
19
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
#### `makeBasicFlowService(alias?: string): FlowService`
|
|
49
|
-
|
|
50
|
-
Creates a flow service instance for managing client-side flows.
|
|
20
|
+
Register the flow service in context setup:
|
|
51
21
|
|
|
52
22
|
```typescript
|
|
53
23
|
import { makeBasicFlowService } from '@owlmeans/client-flow'
|
|
54
24
|
|
|
55
|
-
|
|
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
|
-
}
|
|
25
|
+
context.registerService(makeBasicFlowService())
|
|
169
26
|
```
|
|
170
27
|
|
|
171
|
-
|
|
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
|
|
28
|
+
Create a flow client in a component or hook:
|
|
186
29
|
|
|
187
30
|
```typescript
|
|
188
|
-
|
|
31
|
+
import { createFlowClient, DEFAULT_ALIAS as FLOW_ALIAS } from '@owlmeans/client-flow'
|
|
32
|
+
import type { FlowService, FlowClient } from '@owlmeans/client-flow'
|
|
189
33
|
|
|
190
|
-
|
|
191
|
-
|
|
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')
|
|
34
|
+
const service = context.service<FlowService>(FLOW_ALIAS)
|
|
35
|
+
await service.ready()
|
|
214
36
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const restoredFlow = await flowService.load(savedFlowState)
|
|
219
|
-
console.log('Flow restored to step:', restoredFlow.step())
|
|
220
|
-
}
|
|
37
|
+
const client = await createFlowClient(context, nav).boot(target)
|
|
38
|
+
// Navigate to next step
|
|
39
|
+
await client.proceed(transition, request)
|
|
221
40
|
```
|
|
222
41
|
|
|
223
|
-
|
|
42
|
+
## API
|
|
224
43
|
|
|
225
|
-
|
|
44
|
+
### `makeBasicFlowService(alias?): FlowService`
|
|
226
45
|
|
|
227
|
-
|
|
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
|
|
46
|
+
Creates the flow service. `alias` defaults to `DEFAULT_ALIAS` (`'flow'`).
|
|
232
47
|
|
|
233
|
-
|
|
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
|
-
```
|
|
48
|
+
### `createFlowClient(context, nav): FlowClient`
|
|
262
49
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
-
```
|
|
50
|
+
Creates a flow client with methods:
|
|
51
|
+
- `boot(target, from?)` — load and initialize flow for a target slug
|
|
52
|
+
- `setup(flow)` — set the current flow model
|
|
53
|
+
- `proceed(transition, req?)` — advance to the next step
|
|
54
|
+
- `persist()` — save flow state
|
|
850
55
|
|
|
851
|
-
|
|
56
|
+
### `DEFAULT_ALIAS`
|
|
852
57
|
|
|
853
|
-
|
|
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
|
|
58
|
+
`'flow'` — the default service alias.
|
|
860
59
|
|
|
861
|
-
|
|
60
|
+
### `FLOW_STATE`
|
|
862
61
|
|
|
863
|
-
|
|
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)
|
|
62
|
+
`'state:flow'` — key used to store flow state in the client resource layer.
|
|
870
63
|
|
|
871
64
|
## Related Packages
|
|
872
65
|
|
|
873
|
-
- [`@owlmeans/flow`](../flow)
|
|
874
|
-
- [`@owlmeans/web-flow`](../web-flow)
|
|
875
|
-
- [`@owlmeans/
|
|
876
|
-
- [`@owlmeans/client-auth`](../client-auth) - Authentication with flow integration
|
|
877
|
-
- [`@owlmeans/client`](../client) - Base React client library
|
|
66
|
+
- [`@owlmeans/flow`](../flow) — `FlowModel`, `FlowConfig`, `FlowTransition` types
|
|
67
|
+
- [`@owlmeans/web-flow`](../web-flow) — React hook (`useFlow`) built on top of `createFlowClient`
|
|
68
|
+
- [`@owlmeans/client-resource`](../client-resource) — used internally to persist flow state
|
package/build/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { entrypoint, stab } from '@owlmeans/client-entrypoint';
|
|
2
2
|
import { route, frontend } from '@owlmeans/route';
|
|
3
3
|
import { FlowStepMissconfigured, FlowTargetError, TARGET_SERVICE } from '@owlmeans/flow';
|
|
4
4
|
import { ResilientError } from '@owlmeans/error';
|
|
@@ -67,13 +67,13 @@ export const createFlowClient = (context, nav) => {
|
|
|
67
67
|
let redirectTo;
|
|
68
68
|
// @TODO Properly use target service - as a way to build the redirect URL
|
|
69
69
|
if (step.service === TARGET_SERVICE) {
|
|
70
|
-
context.
|
|
71
|
-
redirectTo = context.
|
|
70
|
+
context.registerEntrypoint(entrypoint(route(REHACK_MOD, DISPATCHER_PATH, frontend({ service: model.state().service })), stab));
|
|
71
|
+
redirectTo = context.entrypoint(REHACK_MOD);
|
|
72
72
|
await redirectTo.resolve();
|
|
73
73
|
step.module = REHACK_MOD;
|
|
74
74
|
}
|
|
75
75
|
else {
|
|
76
|
-
redirectTo = context.
|
|
76
|
+
redirectTo = context.entrypoint(step.module);
|
|
77
77
|
}
|
|
78
78
|
const [url] = await redirectTo.call(req);
|
|
79
79
|
if (url.startsWith('http')) {
|
package/build/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/build/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { LazyService } from '@owlmeans/context';
|
|
2
2
|
import type { FlowConfig, FlowModel, FlowProvider, FlowState, FlowTransition } from '@owlmeans/flow';
|
|
3
|
-
import type { AbstractRequest } from '@owlmeans/
|
|
3
|
+
import type { AbstractRequest } from '@owlmeans/entrypoint';
|
|
4
4
|
import type { ResourceRecord } from '@owlmeans/resource';
|
|
5
5
|
import type { ResolvedServiceRoute } from '@owlmeans/route';
|
|
6
6
|
import type { ClientResource } from '@owlmeans/client-resource';
|
package/build/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAE/D,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;IAE1B,IAAI,EAAE,SAAS,GAAG,IAAI,CAAA;IAEtB,KAAK,EAAE,MAAM,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAA;IAEtC,WAAW,EAAE,MAAM,WAAW,CAAA;IAE9B,MAAM,EAAE,MAAM,UAAU,CAAA;IAExB,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAA;IAE3D,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAA;IAE1C,WAAW,EAAE,YAAY,CAAA;IAEzB,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CAC/E;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAChC,MAAM,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;IACnE,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,UAAU,CAAA;IACtC,IAAI,EAAE,MAAM,SAAS,CAAA;IACrB,OAAO,EAAE,MAAM,oBAAoB,CAAA;IACnC,OAAO,EAAE,CAAC,WAAW,EAAE,cAAc,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACvF,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;CAChC;AAED,MAAM,WAAW,WAAY,SAAQ,cAAc,EAAE,SAAS;CAAG;AAEjE,MAAM,WAAW,aAAc,SAAQ,cAAc,CAAC,WAAW,CAAC;CAAG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/client-flow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -21,26 +21,27 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@owlmeans/auth-common": "^0.1.
|
|
25
|
-
"@owlmeans/client": "^0.1.
|
|
26
|
-
"@owlmeans/client-context": "^0.1.
|
|
27
|
-
"@owlmeans/client-
|
|
28
|
-
"@owlmeans/client-resource": "^0.1.
|
|
29
|
-
"@owlmeans/config": "^0.1.
|
|
30
|
-
"@owlmeans/context": "^0.1.
|
|
31
|
-
"@owlmeans/error": "^0.1.
|
|
32
|
-
"@owlmeans/flow": "^0.1.
|
|
33
|
-
"@owlmeans/
|
|
34
|
-
"@owlmeans/resource": "^0.1.
|
|
35
|
-
"@owlmeans/route": "^0.1.
|
|
24
|
+
"@owlmeans/auth-common": "^0.1.4",
|
|
25
|
+
"@owlmeans/client": "^0.1.4",
|
|
26
|
+
"@owlmeans/client-context": "^0.1.4",
|
|
27
|
+
"@owlmeans/client-entrypoint": "^0.1.4",
|
|
28
|
+
"@owlmeans/client-resource": "^0.1.4",
|
|
29
|
+
"@owlmeans/config": "^0.1.4",
|
|
30
|
+
"@owlmeans/context": "^0.1.4",
|
|
31
|
+
"@owlmeans/error": "^0.1.4",
|
|
32
|
+
"@owlmeans/flow": "^0.1.4",
|
|
33
|
+
"@owlmeans/entrypoint": "^0.1.4",
|
|
34
|
+
"@owlmeans/resource": "^0.1.4",
|
|
35
|
+
"@owlmeans/route": "^0.1.4"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"react": "*"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
+
"@owlmeans/dep-config": "workspace:*",
|
|
41
42
|
"nodemon": "^3.1.11",
|
|
42
43
|
"npm-check": "^6.0.1",
|
|
43
|
-
"typescript": "^
|
|
44
|
+
"typescript": "^6.0.2"
|
|
44
45
|
},
|
|
45
46
|
"publishConfig": {
|
|
46
47
|
"access": "public"
|
package/src/client.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ClientContext, Navigator } from '@owlmeans/client'
|
|
2
2
|
import type { ClientConfig } from '@owlmeans/client-context'
|
|
3
|
-
import type {
|
|
4
|
-
import {
|
|
3
|
+
import type { ClientEntrypoint } from '@owlmeans/client-entrypoint'
|
|
4
|
+
import { entrypoint, stab } from '@owlmeans/client-entrypoint'
|
|
5
5
|
import type { ResolvedServiceRoute } from '@owlmeans/route'
|
|
6
6
|
import { route, frontend } from '@owlmeans/route'
|
|
7
7
|
import { FlowStepMissconfigured, FlowTargetError, TARGET_SERVICE } from '@owlmeans/flow'
|
|
@@ -87,17 +87,17 @@ export const createFlowClient = <C extends ClientConfig, T extends ClientContext
|
|
|
87
87
|
{ ...previous, ...req?.params, ...req?.query } as Record<string, string>
|
|
88
88
|
)
|
|
89
89
|
|
|
90
|
-
let redirectTo:
|
|
90
|
+
let redirectTo: ClientEntrypoint<string>
|
|
91
91
|
// @TODO Properly use target service - as a way to build the redirect URL
|
|
92
92
|
if (step.service === TARGET_SERVICE) {
|
|
93
|
-
context.
|
|
93
|
+
context.registerEntrypoint(entrypoint(
|
|
94
94
|
route(REHACK_MOD, DISPATCHER_PATH, frontend({ service: model.state().service })), stab
|
|
95
95
|
))
|
|
96
|
-
redirectTo = context.
|
|
96
|
+
redirectTo = context.entrypoint<ClientEntrypoint<string>>(REHACK_MOD)
|
|
97
97
|
await redirectTo.resolve()
|
|
98
98
|
step.module = REHACK_MOD
|
|
99
99
|
} else {
|
|
100
|
-
redirectTo = context.
|
|
100
|
+
redirectTo = context.entrypoint<ClientEntrypoint>(step.module)
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
const [url] = await redirectTo.call<string>(req)
|
package/src/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import type { LazyService } from '@owlmeans/context'
|
|
3
3
|
import type { FlowConfig, FlowModel, FlowProvider, FlowState, FlowTransition } from '@owlmeans/flow'
|
|
4
|
-
import type { AbstractRequest } from '@owlmeans/
|
|
4
|
+
import type { AbstractRequest } from '@owlmeans/entrypoint'
|
|
5
5
|
import type { ResourceRecord } from '@owlmeans/resource'
|
|
6
6
|
import type { ResolvedServiceRoute } from '@owlmeans/route'
|
|
7
7
|
import type { ClientResource } from '@owlmeans/client-resource'
|
package/tsconfig.json
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": [
|
|
3
|
-
"
|
|
3
|
+
"@owlmeans/dep-config/tsconfig.base.json"
|
|
4
4
|
],
|
|
5
5
|
"compilerOptions": {
|
|
6
|
-
"rootDir": "./src/",
|
|
7
|
-
"outDir": "./build/"
|
|
8
|
-
"moduleResolution": "Bundler"
|
|
6
|
+
"rootDir": "./src/",
|
|
7
|
+
"outDir": "./build/"
|
|
9
8
|
},
|
|
10
|
-
"exclude": [
|
|
11
|
-
|
|
12
|
-
"./build/**/*",
|
|
13
|
-
"./*.ts"
|
|
14
|
-
]
|
|
15
|
-
}
|
|
9
|
+
"exclude": ["./dist/**/*", "./build/**/*", "./*.ts"]
|
|
10
|
+
}
|
package/build/helper.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { ClientContext } from "@owlmeans/client";
|
|
2
|
-
import type { ClientConfig } from "@owlmeans/client-context";
|
|
3
|
-
import type { StateResource } from "./types.js";
|
|
4
|
-
export declare const getFlowResource: <C extends ClientConfig = ClientConfig, T extends ClientContext<C> = ClientContext<C>>(context: T) => StateResource;
|
|
5
|
-
//# sourceMappingURL=helper.d.ts.map
|
package/build/helper.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAE5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,eAAO,MAAM,eAAe,GAC1B,CAAC,SAAS,YAAY,GAAG,YAAY,EACrC,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,EAC7C,SAAS,CAAC,KAAG,aAA6C,CAAA"}
|
package/build/helper.js
DELETED
package/build/helper.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGxC,MAAM,CAAC,MAAM,eAAe,GAAG,CAG7B,OAAU,EAAiB,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA"}
|