@owlmeans/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/LICENSE +21 -0
- package/README.md +514 -0
- package/build/.gitkeep +0 -0
- package/build/consts.d.ts +10 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +10 -0
- package/build/consts.js.map +1 -0
- package/build/errors.d.ts +34 -0
- package/build/errors.d.ts.map +1 -0
- package/build/errors.js +65 -0
- package/build/errors.js.map +1 -0
- package/build/flows/auth.d.ts +19 -0
- package/build/flows/auth.d.ts.map +1 -0
- package/build/flows/auth.js +146 -0
- package/build/flows/auth.js.map +1 -0
- package/build/flows/index.d.ts +5 -0
- package/build/flows/index.d.ts.map +1 -0
- package/build/flows/index.js +5 -0
- package/build/flows/index.js.map +1 -0
- package/build/flows/oidc.d.ts +14 -0
- package/build/flows/oidc.d.ts.map +1 -0
- package/build/flows/oidc.js +136 -0
- package/build/flows/oidc.js.map +1 -0
- package/build/flows/oidp.d.ts +13 -0
- package/build/flows/oidp.d.ts.map +1 -0
- package/build/flows/oidp.js +189 -0
- package/build/flows/oidp.js.map +1 -0
- package/build/flows/stab.d.ts +3 -0
- package/build/flows/stab.d.ts.map +1 -0
- package/build/flows/stab.js +16 -0
- package/build/flows/stab.js.map +1 -0
- package/build/helper.d.ts +5 -0
- package/build/helper.d.ts.map +1 -0
- package/build/helper.js +14 -0
- package/build/helper.js.map +1 -0
- package/build/i18n/en.json +8 -0
- package/build/i18n.d.ts +2 -0
- package/build/i18n.d.ts.map +1 -0
- package/build/i18n.js +4 -0
- package/build/i18n.js.map +1 -0
- package/build/index.d.ts +8 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +7 -0
- package/build/index.js.map +1 -0
- package/build/state.d.ts +3 -0
- package/build/state.d.ts.map +1 -0
- package/build/state.js +118 -0
- package/build/state.js.map +1 -0
- package/build/types.d.ts +100 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/utils/flow.d.ts +4 -0
- package/build/utils/flow.d.ts.map +1 -0
- package/build/utils/flow.js +65 -0
- package/build/utils/flow.js.map +1 -0
- package/build/utils/index.d.ts +2 -0
- package/build/utils/index.d.ts.map +1 -0
- package/build/utils/index.js +2 -0
- package/build/utils/index.js.map +1 -0
- package/package.json +39 -0
- package/src/consts.ts +18 -0
- package/src/errors.ts +82 -0
- package/src/flows/auth.ts +157 -0
- package/src/flows/index.ts +5 -0
- package/src/flows/oidc.ts +148 -0
- package/src/flows/oidp.ts +205 -0
- package/src/flows/stab.ts +17 -0
- package/src/helper.ts +19 -0
- package/src/i18n/en.json +8 -0
- package/src/i18n.ts +5 -0
- package/src/index.ts +8 -0
- package/src/state.ts +138 -0
- package/src/types.ts +103 -0
- package/src/utils/flow.ts +74 -0
- package/src/utils/index.ts +2 -0
- package/tsconfig.json +15 -0
- package/tsconfig.tsbuildinfo +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 OwlMeans Common — Fullstack typescript framework
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
# @owlmeans/flow
|
|
2
|
+
|
|
3
|
+
A configurable user flow management system for OwlMeans Common applications. This package provides a comprehensive framework for defining, managing, and executing multi-step user workflows with state management, transitions, and cross-service coordination.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `@owlmeans/flow` package is a workflow orchestration library in the OwlMeans Common ecosystem that enables:
|
|
8
|
+
|
|
9
|
+
- **Multi-Step Workflows**: Define complex user journeys with multiple steps and decision points
|
|
10
|
+
- **State Management**: Maintain flow state across steps and services
|
|
11
|
+
- **Cross-Service Coordination**: Coordinate workflows across different microservices
|
|
12
|
+
- **Configurable Transitions**: Define conditional transitions between workflow steps
|
|
13
|
+
- **Payload Management**: Handle data passing between workflow steps
|
|
14
|
+
- **Reversible Steps**: Support for backward navigation in workflows
|
|
15
|
+
- **Serialization**: Persistent workflow state through URL parameters or storage
|
|
16
|
+
- **Internationalization**: Built-in i18n support for workflow content
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @owlmeans/flow
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Core Concepts
|
|
25
|
+
|
|
26
|
+
### Flow Definition
|
|
27
|
+
|
|
28
|
+
A flow is a structured definition of a multi-step process with:
|
|
29
|
+
- **Steps**: Individual stages in the workflow
|
|
30
|
+
- **Transitions**: Connections between steps with conditions
|
|
31
|
+
- **Configuration**: Global settings and service mappings
|
|
32
|
+
- **Payload Mapping**: Data transformation rules between steps
|
|
33
|
+
|
|
34
|
+
### Flow State
|
|
35
|
+
|
|
36
|
+
The current state of a workflow execution including:
|
|
37
|
+
- Current step
|
|
38
|
+
- Previous step (for reversible flows)
|
|
39
|
+
- Payload data
|
|
40
|
+
- Target service
|
|
41
|
+
- Status information
|
|
42
|
+
|
|
43
|
+
### Flow Model
|
|
44
|
+
|
|
45
|
+
A runtime instance that provides methods to:
|
|
46
|
+
- Navigate between steps
|
|
47
|
+
- Execute transitions
|
|
48
|
+
- Manage state and payload
|
|
49
|
+
- Serialize/deserialize flow state
|
|
50
|
+
|
|
51
|
+
## API Reference
|
|
52
|
+
|
|
53
|
+
### Types
|
|
54
|
+
|
|
55
|
+
#### `Flow`
|
|
56
|
+
Complete flow definition with configuration and prefabs.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
interface Flow extends ShallowFlow {
|
|
60
|
+
config: FlowConfig
|
|
61
|
+
prefabs: { [step: string]: string }
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### `ShallowFlow`
|
|
66
|
+
Basic flow structure without configuration.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
interface ShallowFlow {
|
|
70
|
+
flow: string
|
|
71
|
+
initialStep: string
|
|
72
|
+
steps: { [key: string]: FlowStep }
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
#### `FlowStep`
|
|
77
|
+
Individual step definition in a workflow.
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
interface FlowStep {
|
|
81
|
+
index: number
|
|
82
|
+
step: string
|
|
83
|
+
service: string
|
|
84
|
+
path?: string
|
|
85
|
+
module?: string
|
|
86
|
+
initial?: boolean
|
|
87
|
+
payloadMap?: { [key: string]: number }
|
|
88
|
+
transitions: { [key: string]: FlowTransition }
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### `FlowTransition`
|
|
93
|
+
Transition between workflow steps.
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
interface FlowTransition {
|
|
97
|
+
transition: string
|
|
98
|
+
step: string
|
|
99
|
+
explicit?: boolean // User can choose this transition
|
|
100
|
+
reversible?: boolean // User can go back
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### `FlowState`
|
|
105
|
+
Current state of workflow execution.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
interface FlowState {
|
|
109
|
+
flow: string
|
|
110
|
+
step: string
|
|
111
|
+
previous?: string
|
|
112
|
+
entityId?: string | null
|
|
113
|
+
service: string
|
|
114
|
+
payload?: FlowPayload
|
|
115
|
+
message?: string
|
|
116
|
+
ok: boolean
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### `FlowConfig`
|
|
121
|
+
Global configuration for flow behavior.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
interface FlowConfig {
|
|
125
|
+
queryParam?: string // URL parameter name for flow state
|
|
126
|
+
services?: { [ref: string]: string } // Service reference mappings
|
|
127
|
+
modules?: { [ref: string]: string } // Module reference mappings
|
|
128
|
+
pathes?: { [ref: string]: string } // Path reference mappings
|
|
129
|
+
defaultFlow?: string // Default flow to use
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### `FlowModel`
|
|
134
|
+
Runtime workflow management interface.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
interface FlowModel {
|
|
138
|
+
target: (service: string) => FlowModel
|
|
139
|
+
entity: (entityId: string) => FlowModel
|
|
140
|
+
enter: (step?: string) => FlowModel
|
|
141
|
+
steps: (enter?: boolean) => FlowStep[]
|
|
142
|
+
state: () => FlowState
|
|
143
|
+
setState: (state: FlowState | null) => FlowModel
|
|
144
|
+
payload: <T extends FlowPayload>() => T
|
|
145
|
+
step: (step?: string) => FlowStep
|
|
146
|
+
transitions: (explicit?: boolean) => FlowTransition[]
|
|
147
|
+
transition: (transition: string) => FlowTransition
|
|
148
|
+
next: () => FlowTransition
|
|
149
|
+
transit: (transition: string, ok: boolean, message?: string | FlowPayload, payload?: FlowPayload) => string
|
|
150
|
+
serialize: () => string
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Factory Functions
|
|
155
|
+
|
|
156
|
+
#### `makeFlowModel(flow: string | ShallowFlow, provider?: FlowProvider): Promise<FlowModel>`
|
|
157
|
+
Creates a flow model instance from flow definition or serialized state.
|
|
158
|
+
|
|
159
|
+
**Parameters:**
|
|
160
|
+
- `flow`: Flow name, definition, or serialized state
|
|
161
|
+
- `provider`: Optional flow provider function
|
|
162
|
+
|
|
163
|
+
**Returns:** Promise resolving to FlowModel instance
|
|
164
|
+
|
|
165
|
+
### Helper Functions
|
|
166
|
+
|
|
167
|
+
#### `flow<C>(cfg: C, flow: ShallowFlow): C`
|
|
168
|
+
Adds a flow definition to configuration.
|
|
169
|
+
|
|
170
|
+
#### `configureFlows<C>(cfg: C & WithFlowConfig, config: FlowConfig): C & WithFlowConfig`
|
|
171
|
+
Adds flow configuration to application config.
|
|
172
|
+
|
|
173
|
+
### Error Types
|
|
174
|
+
|
|
175
|
+
The package provides specialized error types for flow operations:
|
|
176
|
+
|
|
177
|
+
#### `FlowError`
|
|
178
|
+
Base error class for all flow-related errors.
|
|
179
|
+
|
|
180
|
+
#### `FlowUnsupported`
|
|
181
|
+
Error for unsupported flow operations.
|
|
182
|
+
|
|
183
|
+
#### `FlowTargetError`
|
|
184
|
+
Error related to flow target service issues.
|
|
185
|
+
|
|
186
|
+
#### `UnknownFlow`
|
|
187
|
+
Error when referenced flow doesn't exist.
|
|
188
|
+
|
|
189
|
+
#### `FlowStepError`
|
|
190
|
+
Base error for step-related issues.
|
|
191
|
+
|
|
192
|
+
#### `FlowStepMissconfigured`
|
|
193
|
+
Error for misconfigured flow steps.
|
|
194
|
+
|
|
195
|
+
#### `UnknownFlowStep`
|
|
196
|
+
Error when referenced step doesn't exist.
|
|
197
|
+
|
|
198
|
+
#### `UnknownTransition`
|
|
199
|
+
Error when referenced transition doesn't exist.
|
|
200
|
+
|
|
201
|
+
### Constants
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
// Standard flow types
|
|
205
|
+
const STD_AUTH_FLOW = '_oa' // Authentication flow
|
|
206
|
+
const STD_OIDC_FLOW = '_oidc' // OIDC flow
|
|
207
|
+
const STD_STAB_FLOW = '_stb' // Stabilization flow
|
|
208
|
+
const STD_OIDP_FLOW = '_oidp' // OIDC Provider flow
|
|
209
|
+
|
|
210
|
+
// Configuration
|
|
211
|
+
const CFG_FLOW_PREFIX = 'flow'
|
|
212
|
+
const FLOW_RECORD = 'flow'
|
|
213
|
+
const TARGET_SERVICE = '-'
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Usage Examples
|
|
217
|
+
|
|
218
|
+
### Basic Flow Definition
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import { flow, makeFlowModel } from '@owlmeans/flow'
|
|
222
|
+
|
|
223
|
+
const registrationFlow: ShallowFlow = {
|
|
224
|
+
flow: 'user-registration',
|
|
225
|
+
initialStep: 'start',
|
|
226
|
+
steps: {
|
|
227
|
+
start: {
|
|
228
|
+
index: 0,
|
|
229
|
+
step: 'start',
|
|
230
|
+
service: 'auth-service',
|
|
231
|
+
path: '/register/start',
|
|
232
|
+
initial: true,
|
|
233
|
+
transitions: {
|
|
234
|
+
next: { transition: 'next', step: 'credentials' },
|
|
235
|
+
cancel: { transition: 'cancel', step: 'cancelled' }
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
credentials: {
|
|
239
|
+
index: 1,
|
|
240
|
+
step: 'credentials',
|
|
241
|
+
service: 'auth-service',
|
|
242
|
+
path: '/register/credentials',
|
|
243
|
+
transitions: {
|
|
244
|
+
submit: { transition: 'submit', step: 'verify' },
|
|
245
|
+
back: { transition: 'back', step: 'start', reversible: true }
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
verify: {
|
|
249
|
+
index: 2,
|
|
250
|
+
step: 'verify',
|
|
251
|
+
service: 'auth-service',
|
|
252
|
+
path: '/register/verify',
|
|
253
|
+
transitions: {
|
|
254
|
+
confirmed: { transition: 'confirmed', step: 'complete' },
|
|
255
|
+
resend: { transition: 'resend', step: 'verify' }
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
complete: {
|
|
259
|
+
index: 3,
|
|
260
|
+
step: 'complete',
|
|
261
|
+
service: 'auth-service',
|
|
262
|
+
path: '/register/complete',
|
|
263
|
+
transitions: {}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Add to configuration
|
|
269
|
+
const config = flow(baseConfig, registrationFlow)
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Flow Execution
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// Create flow model
|
|
276
|
+
const flowModel = await makeFlowModel('user-registration', flowProvider)
|
|
277
|
+
|
|
278
|
+
// Target a specific service
|
|
279
|
+
flowModel.target('auth-service')
|
|
280
|
+
|
|
281
|
+
// Set entity context
|
|
282
|
+
flowModel.entity('user123')
|
|
283
|
+
|
|
284
|
+
// Get current step
|
|
285
|
+
const currentStep = flowModel.step()
|
|
286
|
+
console.log(`Current step: ${currentStep.step}`)
|
|
287
|
+
|
|
288
|
+
// Get available transitions
|
|
289
|
+
const transitions = flowModel.transitions(true) // Only explicit transitions
|
|
290
|
+
console.log('Available actions:', transitions.map(t => t.transition))
|
|
291
|
+
|
|
292
|
+
// Execute transition
|
|
293
|
+
const nextState = flowModel.transit('submit', true, 'Success', {
|
|
294
|
+
username: 'john_doe',
|
|
295
|
+
email: 'john@example.com'
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
// Serialize state for URL or storage
|
|
299
|
+
const serializedState = flowModel.serialize()
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Flow State Management
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
// Load from serialized state
|
|
306
|
+
const flowModel = await makeFlowModel(serializedState, flowProvider)
|
|
307
|
+
|
|
308
|
+
// Check if flow completed successfully
|
|
309
|
+
const state = flowModel.state()
|
|
310
|
+
if (state.ok && state.step === 'complete') {
|
|
311
|
+
console.log('Registration completed successfully')
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Handle errors
|
|
315
|
+
if (!state.ok && state.message) {
|
|
316
|
+
console.error('Flow error:', state.message)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Access payload data
|
|
320
|
+
const payload = flowModel.payload<{username: string, email: string}>()
|
|
321
|
+
console.log('User data:', payload)
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Cross-Service Flow
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
const crossServiceFlow: ShallowFlow = {
|
|
328
|
+
flow: 'order-fulfillment',
|
|
329
|
+
initialStep: 'create-order',
|
|
330
|
+
steps: {
|
|
331
|
+
'create-order': {
|
|
332
|
+
index: 0,
|
|
333
|
+
step: 'create-order',
|
|
334
|
+
service: 'order-service',
|
|
335
|
+
transitions: {
|
|
336
|
+
created: { transition: 'created', step: 'process-payment' }
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
'process-payment': {
|
|
340
|
+
index: 1,
|
|
341
|
+
step: 'process-payment',
|
|
342
|
+
service: 'payment-service',
|
|
343
|
+
transitions: {
|
|
344
|
+
paid: { transition: 'paid', step: 'ship-order' },
|
|
345
|
+
failed: { transition: 'failed', step: 'payment-failed' }
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
'ship-order': {
|
|
349
|
+
index: 2,
|
|
350
|
+
step: 'ship-order',
|
|
351
|
+
service: 'shipping-service',
|
|
352
|
+
transitions: {
|
|
353
|
+
shipped: { transition: 'shipped', step: 'complete' }
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Configuration Setup
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
import { configureFlows } from '@owlmeans/flow'
|
|
364
|
+
|
|
365
|
+
const config = configureFlows(baseConfig, {
|
|
366
|
+
queryParam: 'flow',
|
|
367
|
+
defaultFlow: 'user-registration',
|
|
368
|
+
services: {
|
|
369
|
+
auth: 'auth-service',
|
|
370
|
+
payment: 'payment-service'
|
|
371
|
+
},
|
|
372
|
+
modules: {
|
|
373
|
+
login: 'auth:login',
|
|
374
|
+
register: 'auth:register'
|
|
375
|
+
},
|
|
376
|
+
pathes: {
|
|
377
|
+
home: '/dashboard',
|
|
378
|
+
login: '/auth/login'
|
|
379
|
+
}
|
|
380
|
+
})
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Flow Provider Implementation
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
import { FlowProvider } from '@owlmeans/flow'
|
|
387
|
+
|
|
388
|
+
const flowProvider: FlowProvider = async (flowName: string) => {
|
|
389
|
+
// Load from database, configuration, or static definitions
|
|
390
|
+
const flowRecord = await flowRepository.get(flowName)
|
|
391
|
+
|
|
392
|
+
return {
|
|
393
|
+
...flowRecord,
|
|
394
|
+
config: {
|
|
395
|
+
queryParam: 'f',
|
|
396
|
+
services: { auth: 'auth-service' }
|
|
397
|
+
},
|
|
398
|
+
prefabs: {
|
|
399
|
+
start: 'welcome-template',
|
|
400
|
+
complete: 'success-template'
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Error Handling
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
import { UnknownFlow, FlowStepError } from '@owlmeans/flow'
|
|
410
|
+
|
|
411
|
+
try {
|
|
412
|
+
const flowModel = await makeFlowModel('unknown-flow', flowProvider)
|
|
413
|
+
} catch (error) {
|
|
414
|
+
if (error instanceof UnknownFlow) {
|
|
415
|
+
console.error('Flow not found')
|
|
416
|
+
} else if (error instanceof FlowStepError) {
|
|
417
|
+
console.error('Step configuration error:', error.message)
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Advanced Features
|
|
423
|
+
|
|
424
|
+
### Payload Mapping
|
|
425
|
+
|
|
426
|
+
Configure how data flows between steps:
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
const step: FlowStep = {
|
|
430
|
+
index: 1,
|
|
431
|
+
step: 'credentials',
|
|
432
|
+
service: 'auth-service',
|
|
433
|
+
payloadMap: {
|
|
434
|
+
username: 0, // Map to first payload parameter
|
|
435
|
+
email: 1, // Map to second payload parameter
|
|
436
|
+
phone: 2 // Map to third payload parameter
|
|
437
|
+
},
|
|
438
|
+
transitions: { /* ... */ }
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Conditional Transitions
|
|
443
|
+
|
|
444
|
+
Implement business logic in transitions:
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
const flowModel = await makeFlowModel(flow, flowProvider)
|
|
448
|
+
|
|
449
|
+
// Custom transition logic
|
|
450
|
+
const canProceed = await validateUserInput(payload)
|
|
451
|
+
const nextState = flowModel.transit(
|
|
452
|
+
canProceed ? 'submit' : 'error',
|
|
453
|
+
canProceed,
|
|
454
|
+
canProceed ? 'Validation passed' : 'Please fix errors'
|
|
455
|
+
)
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### URL-Based Flow State
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
// Extract flow state from URL
|
|
462
|
+
const urlParams = new URLSearchParams(window.location.search)
|
|
463
|
+
const flowState = urlParams.get('flow')
|
|
464
|
+
|
|
465
|
+
if (flowState) {
|
|
466
|
+
const flowModel = await makeFlowModel(flowState, flowProvider)
|
|
467
|
+
// Resume flow from URL state
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Update URL with current flow state
|
|
471
|
+
const currentState = flowModel.serialize()
|
|
472
|
+
const newUrl = `${window.location.pathname}?flow=${currentState}`
|
|
473
|
+
window.history.pushState({}, '', newUrl)
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
## Integration with OwlMeans Ecosystem
|
|
477
|
+
|
|
478
|
+
The `@owlmeans/flow` package integrates with:
|
|
479
|
+
|
|
480
|
+
- **@owlmeans/config**: Configuration management and record storage
|
|
481
|
+
- **@owlmeans/error**: Resilient error handling system
|
|
482
|
+
- **@owlmeans/resource**: Flow state persistence and retrieval
|
|
483
|
+
- **@owlmeans/i18n**: Internationalization for flow content
|
|
484
|
+
- **@owlmeans/client-flow**: Client-side flow execution
|
|
485
|
+
- **@owlmeans/server-flow**: Server-side flow management
|
|
486
|
+
- **@owlmeans/web-flow**: Web-specific flow features
|
|
487
|
+
|
|
488
|
+
## Best Practices
|
|
489
|
+
|
|
490
|
+
### Flow Design
|
|
491
|
+
- Keep steps focused on single responsibilities
|
|
492
|
+
- Design for both forward and backward navigation
|
|
493
|
+
- Use meaningful step and transition names
|
|
494
|
+
- Plan for error scenarios and recovery paths
|
|
495
|
+
|
|
496
|
+
### State Management
|
|
497
|
+
- Minimize payload size for URL serialization
|
|
498
|
+
- Use appropriate TTL for flow state persistence
|
|
499
|
+
- Handle state corruption gracefully
|
|
500
|
+
- Implement proper cleanup for abandoned flows
|
|
501
|
+
|
|
502
|
+
### Security
|
|
503
|
+
- Validate payload data at each step
|
|
504
|
+
- Implement proper authorization for step access
|
|
505
|
+
- Sanitize flow state when exposing in URLs
|
|
506
|
+
- Use secure channels for sensitive flow data
|
|
507
|
+
|
|
508
|
+
### Performance
|
|
509
|
+
- Cache flow definitions for better performance
|
|
510
|
+
- Implement efficient flow provider lookups
|
|
511
|
+
- Use pagination for large step lists
|
|
512
|
+
- Optimize serialization for large payloads
|
|
513
|
+
|
|
514
|
+
Fixes #32.
|
package/build/.gitkeep
ADDED
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const FLOW_SEP = ":";
|
|
2
|
+
export declare const PAYLOAD_SEP = ",";
|
|
3
|
+
export declare const STD_AUTH_FLOW = "_oa";
|
|
4
|
+
export declare const STD_OIDC_FLOW = "_oidc";
|
|
5
|
+
export declare const STD_STAB_FLOW = "_stb";
|
|
6
|
+
export declare const STD_OIDP_FLOW = "_oidp";
|
|
7
|
+
export declare const TARGET_SERVICE = "-";
|
|
8
|
+
export declare const CFG_FLOW_PREFIX = "flow";
|
|
9
|
+
export declare const FLOW_RECORD = "flow";
|
|
10
|
+
//# sourceMappingURL=consts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,QAAQ,MAAM,CAAA;AAE3B,eAAO,MAAM,WAAW,MAAM,CAAA;AAE9B,eAAO,MAAM,aAAa,QAAQ,CAAA;AAElC,eAAO,MAAM,aAAa,UAAU,CAAA;AAEpC,eAAO,MAAM,aAAa,SAAS,CAAA;AAEnC,eAAO,MAAM,aAAa,UAAU,CAAA;AAEpC,eAAO,MAAM,cAAc,MAAM,CAAA;AAEjC,eAAO,MAAM,eAAe,SAAS,CAAA;AAErC,eAAO,MAAM,WAAW,SAAS,CAAA"}
|
package/build/consts.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const FLOW_SEP = ':';
|
|
2
|
+
export const PAYLOAD_SEP = ',';
|
|
3
|
+
export const STD_AUTH_FLOW = '_oa';
|
|
4
|
+
export const STD_OIDC_FLOW = '_oidc';
|
|
5
|
+
export const STD_STAB_FLOW = '_stb';
|
|
6
|
+
export const STD_OIDP_FLOW = '_oidp';
|
|
7
|
+
export const TARGET_SERVICE = '-';
|
|
8
|
+
export const CFG_FLOW_PREFIX = 'flow';
|
|
9
|
+
export const FLOW_RECORD = 'flow';
|
|
10
|
+
//# sourceMappingURL=consts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,CAAA;AAE3B,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAA;AAE9B,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAA;AAElC,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAA;AAEpC,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAA;AAEnC,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAA;AAEpC,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAA;AAEjC,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAA;AAErC,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAA"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ResilientError } from '@owlmeans/error';
|
|
2
|
+
export declare class FlowError extends ResilientError {
|
|
3
|
+
static typeName: string;
|
|
4
|
+
constructor(message?: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class FlowUnsupported extends FlowError {
|
|
7
|
+
static typeName: string;
|
|
8
|
+
constructor(message?: string);
|
|
9
|
+
}
|
|
10
|
+
export declare class FlowTargetError extends FlowError {
|
|
11
|
+
static typeName: string;
|
|
12
|
+
constructor(message?: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class UnknownFlow extends FlowError {
|
|
15
|
+
static typeName: string;
|
|
16
|
+
constructor(message?: string);
|
|
17
|
+
}
|
|
18
|
+
export declare class FlowStepError extends FlowError {
|
|
19
|
+
static typeName: string;
|
|
20
|
+
constructor(message?: string);
|
|
21
|
+
}
|
|
22
|
+
export declare class FlowStepMissconfigured extends FlowStepError {
|
|
23
|
+
static typeName: string;
|
|
24
|
+
constructor(message?: string);
|
|
25
|
+
}
|
|
26
|
+
export declare class UnknownFlowStep extends FlowStepError {
|
|
27
|
+
static typeName: string;
|
|
28
|
+
constructor(message?: string);
|
|
29
|
+
}
|
|
30
|
+
export declare class UnknownTransition extends FlowStepError {
|
|
31
|
+
static typeName: string;
|
|
32
|
+
constructor(message?: string);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,qBAAa,SAAU,SAAQ,cAAc;IAC3C,OAAuB,QAAQ,EAAE,MAAM,CAAc;gBAEzC,OAAO,GAAE,MAAgB;CAGtC;AAED,qBAAa,eAAgB,SAAQ,SAAS;IAC5C,OAAuB,QAAQ,EAAE,MAAM,CAAqC;gBAEhE,OAAO,GAAE,MAAgB;CAItC;AAED,qBAAa,eAAgB,SAAQ,SAAS;IAC5C,OAAuB,QAAQ,EAAE,MAAM,CAAgC;gBAE3D,OAAO,GAAE,MAAgB;CAItC;AAED,qBAAa,WAAY,SAAQ,SAAS;IACxC,OAAuB,QAAQ,EAAE,MAAM,CAAiC;gBAE5D,OAAO,GAAE,MAAgB;CAItC;AAED,qBAAa,aAAc,SAAQ,SAAS;IAC1C,OAAuB,QAAQ,EAAE,MAAM,CAA8B;gBAEzD,OAAO,GAAE,MAAgB;CAItC;AAED,qBAAa,sBAAuB,SAAQ,aAAa;IACvD,OAAuB,QAAQ,EAAE,MAAM,CAA4C;gBAEvE,OAAO,GAAE,MAAgB;CAItC;AAED,qBAAa,eAAgB,SAAQ,aAAa;IAChD,OAAuB,QAAQ,EAAE,MAAM,CAAqC;gBAEhE,OAAO,GAAE,MAAgB;CAItC;AAED,qBAAa,iBAAkB,SAAQ,aAAa;IAClD,OAAuB,QAAQ,EAAE,MAAM,CAAwC;gBAEnE,OAAO,GAAE,MAAgB;CAItC"}
|
package/build/errors.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ResilientError } from '@owlmeans/error';
|
|
2
|
+
export class FlowError extends ResilientError {
|
|
3
|
+
static typeName = 'FlowError';
|
|
4
|
+
constructor(message = 'error') {
|
|
5
|
+
super(FlowError.typeName, `flow:${message}`);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class FlowUnsupported extends FlowError {
|
|
9
|
+
static typeName = `${FlowError.typeName}Unsupported`;
|
|
10
|
+
constructor(message = 'error') {
|
|
11
|
+
super(`unsupported:${message}`);
|
|
12
|
+
this.type = FlowUnsupported.typeName;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export class FlowTargetError extends FlowError {
|
|
16
|
+
static typeName = `${FlowError.typeName}Target`;
|
|
17
|
+
constructor(message = 'error') {
|
|
18
|
+
super(`target:${message}`);
|
|
19
|
+
this.type = FlowTargetError.typeName;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export class UnknownFlow extends FlowError {
|
|
23
|
+
static typeName = `${FlowError.typeName}Unknown`;
|
|
24
|
+
constructor(message = 'error') {
|
|
25
|
+
super(`unknown:${message}`);
|
|
26
|
+
this.type = UnknownFlow.typeName;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export class FlowStepError extends FlowError {
|
|
30
|
+
static typeName = `${FlowError.typeName}Step`;
|
|
31
|
+
constructor(message = 'error') {
|
|
32
|
+
super(`step:${message}`);
|
|
33
|
+
this.type = FlowStepError.typeName;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export class FlowStepMissconfigured extends FlowStepError {
|
|
37
|
+
static typeName = `${FlowStepError.typeName}Missconfigured`;
|
|
38
|
+
constructor(message = 'error') {
|
|
39
|
+
super(`missconfigured:${message}`);
|
|
40
|
+
this.type = FlowStepMissconfigured.typeName;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export class UnknownFlowStep extends FlowStepError {
|
|
44
|
+
static typeName = `${FlowStepError.typeName}Unknown`;
|
|
45
|
+
constructor(message = 'error') {
|
|
46
|
+
super(`unknown:${message}`);
|
|
47
|
+
this.type = UnknownFlowStep.typeName;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export class UnknownTransition extends FlowStepError {
|
|
51
|
+
static typeName = `${FlowStepError.typeName}Transition`;
|
|
52
|
+
constructor(message = 'error') {
|
|
53
|
+
super(`transition:${message}`);
|
|
54
|
+
this.type = UnknownTransition.typeName;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
ResilientError.registerErrorClass(FlowError);
|
|
58
|
+
ResilientError.registerErrorClass(FlowUnsupported);
|
|
59
|
+
ResilientError.registerErrorClass(FlowTargetError);
|
|
60
|
+
ResilientError.registerErrorClass(UnknownFlow);
|
|
61
|
+
ResilientError.registerErrorClass(FlowStepError);
|
|
62
|
+
ResilientError.registerErrorClass(FlowStepMissconfigured);
|
|
63
|
+
ResilientError.registerErrorClass(UnknownFlowStep);
|
|
64
|
+
ResilientError.registerErrorClass(UnknownTransition);
|
|
65
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ShallowFlow } from '../types.js';
|
|
2
|
+
export declare enum StdAuthStep {
|
|
3
|
+
Choice = "choice",
|
|
4
|
+
Signup = "signup",
|
|
5
|
+
Signin = "signin",
|
|
6
|
+
New = "new",
|
|
7
|
+
Existing = "existing",
|
|
8
|
+
Payment = "payment",
|
|
9
|
+
Success = "success",
|
|
10
|
+
Target = "target"
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* @TODO Actually this thing is kept here for the iteration when
|
|
14
|
+
* OwlMeans ID account may be used without owlmeans.net account.
|
|
15
|
+
*
|
|
16
|
+
* @deprecated
|
|
17
|
+
*/
|
|
18
|
+
export declare const stdAuthFlow: ShallowFlow;
|
|
19
|
+
//# sourceMappingURL=auth.d.ts.map
|