@pumped-fn/lite 1.11.2 → 1.11.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/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @pumped-fn/lite
2
2
 
3
+ ## 1.11.4
4
+
5
+ ### Patch Changes
6
+
7
+ - a3ae2b7: Replace text glossary with mermaid sequence diagrams in documentation
8
+
9
+ - README.md now uses visual diagrams for composition, atom lifecycle, tag resolution, type utilities, and API surface
10
+ - PATTERNS.md converted all usage patterns to sequence diagrams for clarity
11
+
12
+ ## 1.11.3
13
+
14
+ ### Patch Changes
15
+
16
+ - eda1154: Extend preset() to support Flow in addition to Atom
17
+
18
+ - `preset(flow, fn)` - replacement function bypasses deps resolution (mock scenario)
19
+ - `preset(flow, otherFlow)` - delegates parse/deps/factory entirely to replacement
20
+ - Self-preset throws at creation time
21
+ - Extensions wrap both preset variants
22
+
3
23
  ## 1.11.2
4
24
 
5
25
  ### Patch Changes
package/PATTERNS.md CHANGED
@@ -1,380 +1,263 @@
1
- # Architectural Patterns
1
+ # Patterns
2
2
 
3
- Design patterns implemented by `@pumped-fn/lite` and how to compose them for application architecture.
3
+ Usage patterns as sequences. For API details, see `packages/lite/dist/index.d.mts`.
4
4
 
5
- ## Composite Patterns
5
+ ## A. Fundamental Usage
6
6
 
7
7
  ### Request Lifecycle
8
8
 
9
- **Combines:** IoC Container + Command + Composite
10
-
11
- | GoF Pattern | Primitive |
12
- |-------------|-----------|
13
- | IoC Container | `Scope` (long-lived, caches atoms) |
14
- | Command | `Flow` (operations within request) |
15
- | Composite | `ExecutionContext` (parent-child with isolated data) |
16
-
17
- **Key Insight:**
18
- - `Scope` = application container (atoms cached here)
19
- - `ExecutionContext` = request boundary (data lives here, closed at request end)
20
- - `Flow` / `ctx.exec` = operations within the request (share context via `seekTag`)
9
+ Model a request boundary with cleanup and shared context.
21
10
 
22
11
  ```mermaid
23
12
  sequenceDiagram
24
13
  participant App
25
14
  participant Scope
26
- participant Context as ExecutionContext
27
- participant ServiceAtom as Service Atom
28
- participant Flow1 as Flow (validate)
29
- participant Flow2 as Flow (process)
30
-
31
- Note over Scope: Long-lived (app lifetime)
32
- App->>Scope: resolve(serviceAtom)
33
- Scope-->>App: service (cached)
15
+ participant Ctx as ExecutionContext
16
+ participant Flow
34
17
 
35
- Note over Context: Per-request boundary
36
- App->>Scope: createContext({ tags: [requestId, userId] })
18
+ App->>Scope: createScope()
19
+ App->>Scope: scope.createContext({ tags })
37
20
  Scope-->>App: ctx
38
21
 
39
- App->>Context: ctx.data.setTag(TX_TAG, beginTransaction())
40
- App->>Context: ctx.onClose(() => tx.rollback())
41
-
42
- App->>Context: ctx.exec({ flow: validateFlow, input })
43
- Context->>Scope: resolve flow deps (atoms)
44
- Scope-->>Context: deps (cached in scope)
45
- Context->>Flow1: factory(childCtx, deps)
46
- Note over Flow1: childCtx.parent = ctx
47
- Flow1->>Flow1: tags.required(userIdTag) from merged tags
48
- Flow1-->>Context: validated
49
-
50
- App->>Context: ctx.exec({ flow: processFlow, input: validated })
51
- Context->>Scope: resolve flow deps (atoms)
52
- Scope-->>Context: deps (from cache)
53
- Context->>Flow2: factory(childCtx, deps)
54
- Flow2->>Flow2: childCtx.exec({ fn: service.save, params: [data] })
55
- Note over Flow2: Creates grandchildCtx
56
- Flow2->>ServiceAtom: service.save(grandchildCtx, data)
57
- ServiceAtom->>ServiceAtom: grandchildCtx.data.seekTag(TX_TAG)
58
- Note over ServiceAtom: seekTag traverses parent chain
59
- ServiceAtom-->>Flow2: saved
60
- Flow2-->>Context: result
61
-
62
- App->>Context: ctx.data.getTag(TX_TAG).commit()
63
- App->>Context: ctx.close()
64
- Context->>Context: onClose cleanups run (rollback skipped)
65
- ```
22
+ App->>Ctx: ctx.exec({ flow, input, tags })
23
+ Ctx->>Flow: factory(childCtx, deps)
24
+ Flow-->>Ctx: output
25
+ Ctx->>Ctx: childCtx.close()
26
+ Ctx-->>App: output
66
27
 
67
- **Characteristics:**
68
- - Scope caches atoms across requests (resolve once, use many)
69
- - ExecutionContext bounds request lifecycle (`onClose` for cleanup)
70
- - Each `exec()` creates child context with isolated `data` Map
71
- - `seekTag()` traverses parent chain for shared data (e.g., transaction)
72
- - `ctx.close()` runs all `onClose` cleanups (LIFO order)
73
- - On error: child context auto-closes, cleanups still run
28
+ App->>Ctx: ctx.onClose(cleanup)
29
+ App->>Ctx: ctx.close()
30
+ Ctx->>Ctx: run cleanups (LIFO)
31
+ ```
74
32
 
75
- **Primitives:** `createScope()`, `scope.createContext()`, `ctx.exec()`, `ctx.data.setTag/seekTag()`, `ctx.onClose()`, `ctx.close()`
33
+ ### Extensions Pipeline
76
34
 
77
- ---
35
+ Observe and wrap timing for atoms/flows (logging, auth, tracing).
78
36
 
79
- ### Flow Deps & Execution
37
+ ```mermaid
38
+ sequenceDiagram
39
+ participant App
40
+ participant Scope
41
+ participant Ext as Extension
42
+ participant Atom
43
+ participant Flow
80
44
 
81
- **Combines:** Command + Composite + Resource Management
45
+ App->>Scope: createScope({ extensions: [ext] })
46
+ Scope->>Ext: ext.init(scope)
47
+ App->>Scope: await scope.ready
48
+
49
+ App->>Scope: resolve(atom)
50
+ Scope->>Ext: wrapResolve(next, atom, scope)
51
+ Ext->>Ext: before logic
52
+ Ext->>Atom: next()
53
+ Atom-->>Ext: value
54
+ Ext->>Ext: after logic
55
+ Ext-->>Scope: value
56
+
57
+ App->>Scope: ctx.exec({ flow })
58
+ Scope->>Ext: wrapExec(next, flow, ctx)
59
+ Ext->>Flow: next()
60
+ Flow-->>Ext: output
61
+ Ext-->>Scope: output
62
+
63
+ App->>Scope: dispose()
64
+ Scope->>Ext: ext.dispose(scope)
65
+ ```
82
66
 
83
- | Concern | Primitive |
84
- |---------|-----------|
85
- | Dependency injection | `deps` (atoms from Scope, tags from context hierarchy) |
86
- | Service invocation | `ctx.exec({ fn, params })` (observable by extensions) |
87
- | Resource cleanup | `ctx.onClose()` (LIFO, runs on success or failure) |
67
+ ### Scoped Isolation + Testing
88
68
 
89
- **Deps Resolution:**
69
+ Swap implementations and isolate tenants/tests.
90
70
 
91
71
  ```mermaid
92
- flowchart TB
93
- subgraph Flow["flow({ deps, factory })"]
94
- Deps["deps: { db: dbAtom, userId: tags.required(userIdTag) }"]
95
- end
72
+ sequenceDiagram
73
+ participant Test
74
+ participant Scope
75
+ participant Atom
96
76
 
97
- subgraph Resolution
98
- Deps --> AtomPath["Atom deps"]
99
- Deps --> TagPath["Tag deps (TagExecutor)"]
77
+ Test->>Scope: createScope({ presets: [preset(dbAtom, mockDb)], tags: [tenantTag(id)] })
78
+ Test->>Scope: resolve(dbAtom)
79
+ Scope-->>Test: mockDb (not real db)
100
80
 
101
- AtomPath --> Scope["Scope.resolve()"]
102
- Scope --> |"cached in scope"| ResolvedAtom["db instance"]
81
+ Test->>Scope: createContext()
82
+ Scope-->>Test: ctx with tenantTag
83
+ ```
103
84
 
104
- TagPath --> CtxHierarchy["ctx.data.seekTag()"]
105
- CtxHierarchy --> |"traverses parent chain"| ResolvedTag["userId value"]
106
- end
85
+ ## B. Advanced Client/State Usage
107
86
 
108
- subgraph Factory["factory(ctx, { db, userId })"]
109
- ResolvedAtom --> DepsObj["deps object"]
110
- ResolvedTag --> DepsObj
111
- end
112
- ```
87
+ ### Controller Reactivity
113
88
 
114
- **Service Invocation:**
89
+ Client-side state with lifecycle hooks and invalidation.
115
90
 
116
91
  ```mermaid
117
92
  sequenceDiagram
118
- participant Flow
119
- participant Ctx as ExecutionContext
120
- participant Ext as Extension.wrapExec
121
- participant Fn as service.method
122
-
123
- Note over Flow: ❌ Direct call
124
- Flow->>Fn: service.method(ctx, data)
125
- Note over Fn: Extensions cannot observe
126
-
127
- Note over Flow: ✅ Via ctx.exec
128
- Flow->>Ctx: ctx.exec({ fn: service.method, params: [data] })
129
- Ctx->>Ctx: create childCtx (parent = ctx)
130
- Ctx->>Ext: wrapExec(next, fn, childCtx)
131
- Ext->>Fn: next()
132
- Fn-->>Ext: result
133
- Ext-->>Ctx: result
134
- Ctx->>Ctx: childCtx.close()
135
- Ctx-->>Flow: result
136
- ```
93
+ participant App
94
+ participant Scope
95
+ participant Ctrl as Controller
96
+ participant Atom
137
97
 
138
- **Cleanup Pattern:**
98
+ App->>Scope: controller(atom)
99
+ Scope-->>App: ctrl
139
100
 
140
- ```mermaid
141
- sequenceDiagram
142
- participant Flow
143
- participant Ctx as ExecutionContext
144
- participant Tx as Transaction
145
-
146
- Flow->>Tx: beginTransaction()
147
- Tx-->>Flow: tx
148
- Flow->>Ctx: ctx.onClose(() => tx.rollback())
149
-
150
- alt Success
151
- Flow->>Flow: do work
152
- Flow->>Tx: tx.commit()
153
- Flow->>Ctx: return result
154
- Ctx->>Ctx: close() → rollback() is no-op
155
- else Error
156
- Flow->>Flow: do work (throws)
157
- Ctx->>Ctx: close() → rollback() executes
158
- Ctx->>Tx: tx.rollback()
159
- end
160
- ```
101
+ App->>Ctrl: ctrl.on('resolving' | 'resolved' | '*', listener)
102
+ Ctrl-->>App: unsubscribe
161
103
 
162
- **Characteristics:**
163
- - Atoms resolve via `Scope.resolve()` (cached, long-lived)
164
- - Tag deps resolve via `ctx.data.seekTag()` (traverses parent → grandparent → scope tags)
165
- - `ctx.exec({ fn, params })` creates child context with isolated `data` Map
166
- - Extensions intercept via `wrapExec(next, target, ctx)`
167
- - Register pessimistic cleanup via `ctx.onClose(fn)`, neutralize on success
104
+ App->>Ctrl: ctrl.get()
105
+ Ctrl-->>App: current value
168
106
 
169
- **Primitives:** `flow({ deps })`, `tags.required()`, `tags.optional()`, `tags.all()`, `ctx.exec()`, `ctx.onClose()`
107
+ App->>Ctrl: ctrl.set(newValue)
108
+ Ctrl->>Ctrl: notify listeners
170
109
 
171
- ---
110
+ App->>Ctrl: ctrl.update(v => v + 1)
111
+ Ctrl->>Ctrl: notify listeners
172
112
 
173
- ### Request Pipeline
113
+ App->>Ctrl: ctrl.invalidate()
114
+ Ctrl->>Atom: re-run factory
115
+ Ctrl->>Ctrl: notify listeners
116
+ ```
174
117
 
175
- **Combines:** Command + Interceptor + Context Object
118
+ ### Ambient Context (Tags)
176
119
 
177
- | GoF Pattern | Primitive |
178
- |-------------|-----------|
179
- | Command | `Flow` (encapsulates request with input/output) |
180
- | Interceptor | `Extension.wrapExec()` (wraps execution) |
181
- | Context Object | `Tag` (propagates metadata without explicit passing) |
120
+ Propagate state without wiring parameters (app shell, user, locale).
182
121
 
183
122
  ```mermaid
184
123
  sequenceDiagram
185
- participant Client
186
- participant Scope
187
- participant Extension1 as Extension (Auth)
188
- participant Extension2 as Extension (Tracing)
189
- participant Flow
190
- participant Context as ExecutionContext
191
-
192
- Client->>Scope: createContext({ tags: [requestId] })
193
- Scope-->>Client: ctx
194
- Client->>Context: exec({ flow, input, tags: [userId] })
195
-
196
- Context->>Context: merge tags (flow → scope → context → exec)
197
- Context->>Context: create child context
198
-
199
- Context->>Extension1: wrapExec(next, flow, childCtx)
200
- Extension1->>Extension1: extract userId tag, validate
201
- Extension1->>Extension2: next()
202
- Extension2->>Extension2: read parent span from ctx.parent?.data
203
- Extension2->>Extension2: create child span, store in ctx.data
204
- Extension2->>Flow: next()
205
- Flow->>Flow: factory(ctx, deps) with tags.required(userId)
206
- Flow-->>Extension2: result
207
- Extension2->>Extension2: end span
208
- Extension2-->>Extension1: result
209
- Extension1-->>Context: result
210
- Context->>Context: auto-close child (run onClose cleanups)
211
- Context-->>Client: result
212
- ```
213
-
214
- **Characteristics:**
215
- - Extensions wrap in registration order (outer → inner)
216
- - Each `exec()` creates isolated child context with own `data` Map
217
- - Tags merge with later sources winning (exec tags override flow tags)
218
- - Parent chain enables span correlation without AsyncLocalStorage
124
+ participant App
125
+ participant Ctx as ExecutionContext
126
+ participant ChildCtx
127
+ participant Data as ctx.data
219
128
 
220
- **Primitives:** `flow()`, `Extension.wrapExec`, `tag()`, `ctx.exec()`, `ctx.parent`, `ctx.data`
129
+ App->>Data: ctx.data.setTag(userTag, user)
130
+ App->>Ctx: ctx.exec({ flow, tags: [localeTag('en')] })
131
+ Ctx->>ChildCtx: create with merged tags
221
132
 
222
- ---
133
+ ChildCtx->>Data: ctx.data.seekTag(userTag)
134
+ Data-->>ChildCtx: user (from parent)
223
135
 
224
- ### Scoped Isolation
136
+ ChildCtx->>Data: ctx.data.getTag(localeTag)
137
+ Data-->>ChildCtx: 'en'
138
+ ```
225
139
 
226
- **Combines:** IoC Container + Strategy + Composite
140
+ ### Derived State (Select)
227
141
 
228
- | GoF Pattern | Primitive |
229
- |-------------|-----------|
230
- | IoC Container | `Scope` (manages atom lifecycles and resolution) |
231
- | Strategy | `Preset` (swap implementations at scope creation) |
232
- | Composite | `ExecutionContext` (parent-child isolation) |
142
+ Subscribe to a slice of atom state with custom equality.
233
143
 
234
144
  ```mermaid
235
145
  sequenceDiagram
236
146
  participant App
237
- participant TenantScope as Scope (Tenant A)
238
- participant TestScope as Scope (Test)
239
- participant DbAtom as dbAtom
240
- participant MockDb as mockDbAtom
241
-
242
- Note over App: Production - Tenant A
243
- App->>TenantScope: createScope({ tags: [tenantId('A')] })
244
- App->>TenantScope: resolve(dbAtom)
245
- TenantScope->>DbAtom: factory(ctx, deps)
246
- DbAtom->>DbAtom: tags.required(tenantId) → 'A'
247
- DbAtom-->>TenantScope: TenantA DB connection
248
-
249
- Note over App: Test - Mocked DB
250
- App->>TestScope: createScope({ presets: [preset(dbAtom, mockDbAtom)] })
251
- App->>TestScope: resolve(dbAtom)
252
- TestScope->>TestScope: check presets → found
253
- TestScope->>MockDb: resolve mockDbAtom instead
254
- MockDb-->>TestScope: Mock DB instance
255
-
256
- Note over App: Parallel tenant contexts
257
- par Tenant A request
258
- TenantScope->>TenantScope: createContext({ tags: [requestId('r1')] })
259
- and Tenant B request
260
- TenantScope->>TenantScope: createContext({ tags: [requestId('r2')] })
261
- end
262
- Note over TenantScope: Each context isolated, same scope
263
- ```
264
-
265
- **Characteristics:**
266
- - Each Scope is an isolated DI container with own cache
267
- - Presets swap atom implementations without changing definitions
268
- - Tags at scope level apply to all resolutions
269
- - Multiple ExecutionContexts share scope but isolate request data
270
- - Child contexts inherit parent tags, can override
147
+ participant Scope
148
+ participant Handle as SelectHandle
149
+ participant Atom
271
150
 
272
- **Use Cases:**
273
- - Multi-tenancy: scope-level tenant tag, context-level request isolation
274
- - Testing: preset mocks without touching production atom definitions
275
- - Feature flags: preset alternative implementations per environment
151
+ App->>Scope: select(atom, v => v.count, { eq: shallowEqual })
152
+ Scope-->>App: handle
276
153
 
277
- **Primitives:** `createScope()`, `preset()`, `tag()`, `createContext()`, scope `tags` option
154
+ App->>Handle: handle.get()
155
+ Handle-->>App: selected value
278
156
 
279
- ---
157
+ App->>Handle: handle.subscribe(listener)
158
+ Handle-->>App: unsubscribe
280
159
 
281
- ## Foundational Patterns
160
+ Note over Atom,Handle: atom changes
161
+ Handle->>Handle: eq(prev, next)?
162
+ Handle->>App: notify if changed
163
+ ```
282
164
 
283
- ### IoC Container
165
+ ### Service Pattern
284
166
 
285
- **GoF:** Inversion of Control / Dependency Injection Container
167
+ Constrain atom methods to ExecutionContext-first signature for tracing/auth.
286
168
 
287
169
  ```mermaid
288
- graph TB
289
- Scope["Scope (Container)"]
290
- AtomA["Atom A"]
291
- AtomB["Atom B"]
292
- AtomC["Atom C"]
293
- Cache["Resolution Cache"]
294
-
295
- Scope -->|resolve| AtomA
296
- Scope -->|resolve| AtomB
297
- AtomB -->|deps| AtomA
298
- AtomC -->|deps| AtomA
299
- AtomC -->|deps| AtomB
300
- Scope --- Cache
301
- ```
302
-
303
- **Primitives:** `createScope()`, `atom()`, `deps`, `scope.resolve()`
170
+ sequenceDiagram
171
+ participant App
172
+ participant Scope
173
+ participant Ctx as ExecutionContext
174
+ participant Svc as Service Atom
304
175
 
305
- **Characteristics:** Lazy resolution, automatic caching, dependency graph traversal, circular dependency detection.
176
+ App->>Scope: resolve(userService)
177
+ Scope-->>App: { getUser, updateUser }
306
178
 
307
- ---
179
+ App->>Ctx: svc.getUser(ctx, userId)
180
+ Ctx->>Svc: traced execution
181
+ Svc-->>Ctx: user
182
+ Ctx-->>App: user
183
+ ```
308
184
 
309
- ### Observer
185
+ ### Typed Flow Input
310
186
 
311
- **GoF:** Observer Pattern with State Machine
187
+ Type flow input without runtime parsing overhead.
312
188
 
313
189
  ```mermaid
314
- stateDiagram-v2
315
- [*] --> idle
316
- idle --> resolving: resolve()
317
- resolving --> resolved: success
318
- resolving --> failed: error
319
- resolved --> resolving: invalidate()
320
- failed --> resolving: invalidate()
321
-
322
- note right of resolving: listeners notified
323
- note right of resolved: listeners notified
190
+ sequenceDiagram
191
+ participant App
192
+ participant Flow
193
+ participant Ctx
194
+
195
+ Note over Flow: flow({ parse: typed<T>(), factory })
196
+ App->>Flow: ctx.exec({ flow, input: typedInput })
197
+ Flow->>Flow: skip parse (type-only)
198
+ Flow->>Ctx: factory(ctx) with ctx.input: T
199
+ Ctx-->>App: output
324
200
  ```
325
201
 
326
- **Primitives:** `controller()`, `ctrl.on('resolved' | 'resolving' | '*')`, `ctrl.invalidate()`
202
+ ### Controller as Dependency
327
203
 
328
- **Characteristics:** State-filtered subscriptions, LIFO cleanup before re-resolution, sequential invalidation chains with loop detection.
204
+ Receive reactive handle instead of resolved value in atom/flow deps.
329
205
 
330
- ---
206
+ ```mermaid
207
+ sequenceDiagram
208
+ participant Scope
209
+ participant AtomA as serverAtom
210
+ participant Ctrl as Controller
211
+ participant AtomB as configAtom
212
+
213
+ Scope->>AtomA: resolve(serverAtom)
214
+ Note over AtomA: deps: { cfg: controller(configAtom, { resolve: true }) }
215
+ AtomA->>Scope: resolve configAtom first
216
+ Scope-->>Ctrl: ctrl (already resolved)
217
+ AtomA->>AtomA: factory(ctx, { cfg: ctrl })
218
+ AtomA->>Ctrl: ctrl.on('resolved', () => ctx.invalidate())
219
+ Note over AtomA: react to config changes
220
+ ```
331
221
 
332
- ### Context Object
222
+ ### Inline Function Execution
333
223
 
334
- **GoF:** Context Object / Ambient Context
224
+ Execute ad-hoc logic within context without defining a flow.
335
225
 
336
226
  ```mermaid
337
- graph TB
338
- subgraph Sources
339
- FlowTags[Flow tags]
340
- ScopeTags[Scope tags]
341
- CtxTags[Context tags]
342
- ExecTags[Exec tags]
343
- end
344
-
345
- subgraph Merge[Tag Merge - later wins]
346
- FlowTags --> Merged
347
- ScopeTags --> Merged
348
- CtxTags --> Merged
349
- ExecTags --> Merged
350
- end
227
+ sequenceDiagram
228
+ participant App
229
+ participant Ctx as ExecutionContext
351
230
 
352
- subgraph Extract
353
- Merged --> Required[tags.required]
354
- Merged --> Optional[tags.optional]
355
- Merged --> All[tags.all]
356
- end
231
+ App->>Ctx: ctx.exec({ fn: (ctx, a, b) => a + b, params: [1, 2], tags })
232
+ Ctx->>Ctx: create childCtx with tags
233
+ Ctx->>Ctx: fn(childCtx, 1, 2)
234
+ Ctx->>Ctx: childCtx.close()
235
+ Ctx-->>App: 3
357
236
  ```
358
237
 
359
- **Primitives:** `tag()`, `tags.required()`, `tags.optional()`, `tags.all()`, `Tagged`
238
+ ### Atom Retention (GC)
360
239
 
361
- **Characteristics:** Implicit propagation through execution layers, type-safe extraction, merge precedence (exec > context > scope > flow).
240
+ Control when atoms are garbage collected or kept alive indefinitely.
362
241
 
363
- ---
242
+ ```mermaid
243
+ sequenceDiagram
244
+ participant App
245
+ participant Scope
246
+ participant Atom
364
247
 
365
- ### Strategy
248
+ App->>Scope: createScope({ gc: { enabled: true, graceMs: 3000 } })
366
249
 
367
- **GoF:** Strategy Pattern
250
+ App->>Scope: resolve(atom)
251
+ Scope-->>App: value
252
+ Note over Scope: no refs → start grace timer
368
253
 
369
- ```mermaid
370
- graph TB
371
- Scope -->|resolve| Atom
372
- Atom -->|check| Presets{Preset?}
373
- Presets -->|value| Direct[Return value]
374
- Presets -->|atom| Redirect[Resolve other atom]
375
- Presets -->|none| Factory[Run factory]
376
- ```
377
-
378
- **Primitives:** `preset()`, `createScope({ presets })`, `isPreset()`
254
+ alt keepAlive: true
255
+ Note over Atom: never GC'd
256
+ else graceMs expires
257
+ Scope->>Atom: release()
258
+ Atom->>Atom: run cleanups
259
+ end
379
260
 
380
- **Characteristics:** Swap implementations at scope creation, value injection bypasses factory, atom redirection for mock substitution.
261
+ App->>Scope: flush()
262
+ Note over Scope: wait all pending
263
+ ```