@nlabs/metropolisjs 1.0.2 → 1.0.7

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,112 +1,9 @@
1
1
  # Changelog
2
2
 
3
- ## Architecture Modernization (2025)
3
+ ## 1.0.3
4
4
 
5
- MetropolisJS has undergone a comprehensive three-phase refactoring to align with React best practices, improve type safety, and enhance developer experience.
6
-
7
- ### Phase 1: Context-Based Configuration
8
-
9
- **Status:** Complete (Backward Compatible)
10
-
11
- - Added `resolveEnvironmentConfig()` utility for environment-specific config resolution
12
- - Enhanced `MetropolisContext` with `config` and `flux` properties
13
- - Created `useMetropolisConfig()` and `useMetropolisFlux()` hooks
14
- - Configuration now available through React Context
15
- - Global `Config` class still works for backward compatibility
16
-
17
- **New Features:**
18
- - `useMetropolisConfig()` - Access config from React context
19
- - `useMetropolisFlux()` - Access flux instance from context
20
- - `resolveEnvironmentConfig()` - Resolve environment-specific configuration
21
-
22
- ### Phase 2: Selective Action Creation & Specialized Hooks ✅
23
-
24
- **Status:** Complete (Backward Compatible)
25
-
26
- - Added selective action creation: `useMetropolis(['user', 'post'])`
27
- - Created 12 specialized hooks for individual action types
28
- - Improved type safety by removing `as any` casts
29
- - Better performance through selective action creation
30
-
31
- **New Features:**
32
- - `useUserActions()` - User management actions only
33
- - `usePostActions()` - Post management actions only
34
- - `useMessageActions()` - Messaging actions only
35
- - `useEventActions()` - Event management actions only
36
- - `useImageActions()` - Image handling actions only
37
- - `useLocationActions()` - Location services actions only
38
- - `useReactionActions()` - Reaction actions only
39
- - `useTagActions()` - Tag management actions only
40
- - `useContentActions()` - Content management actions only
41
- - `usePersonaActions()` - Persona management actions only
42
- - `useTranslationActions()` - Translation actions only
43
- - `useWebsocketActions()` - WebSocket actions only
44
- - Selective creation: `useMetropolis(['user', 'post'])`
45
-
46
- **Performance Improvements:**
47
- - Only creates actions you need
48
- - Better tree-shaking opportunities
49
- - More efficient memoization
50
-
51
- ### Phase 3: Remove Global Config Singleton ✅
52
-
53
- **Status:** Complete (Breaking Changes)
54
-
55
- - Removed global `Config.set()` from Metropolis component
56
- - Config now stored in flux state (`app.config`)
57
- - Created `getConfigFromFlux()` utility for non-React code
58
- - Deprecated `Config.get()` and `Config.set()` with warnings
59
- - `useMetropolisConfig()` now requires Metropolis context
60
-
61
- **Breaking Changes:**
62
- - `useMetropolisConfig()` throws error if used outside Metropolis component
63
- - `Config.get()` and `Config.set()` are deprecated (show warnings)
64
- - Configuration must be passed to `<Metropolis>` component
65
-
66
- **Migration Required:**
67
- - React components: Use `useMetropolisConfig()` instead of `Config.get()`
68
- - Non-React code: Use `getConfigFromFlux(flux)` instead of `Config.get()`
69
- - Ensure components are wrapped with `<Metropolis>` provider
70
-
71
- ## Summary of Changes
72
-
73
- ### New Hooks
74
-
75
- 1. **Configuration Hooks:**
76
- - `useMetropolisConfig()` - Get configuration from context
77
- - `useMetropolisFlux()` - Get flux instance from context
78
-
79
- 2. **Specialized Action Hooks:**
80
- - `useUserActions()`
81
- - `usePostActions()`
82
- - `useMessageActions()`
83
- - `useEventActions()`
84
- - `useImageActions()`
85
- - `useLocationActions()`
86
- - `useReactionActions()`
87
- - `useTagActions()`
88
- - `useContentActions()`
89
- - `usePersonaActions()`
90
- - `useTranslationActions()`
91
- - `useWebsocketActions()`
92
-
93
- ### Enhanced Features
94
-
95
- - **Selective Action Creation:** `useMetropolis(['user', 'post'])`
96
- - **Context-Based Configuration:** No global state
97
- - **Type Safety:** Removed `as any` casts
98
- - **Better Performance:** Only create needed actions
99
-
100
- ### Deprecated APIs
101
-
102
- - `Config.get()` - Use `useMetropolisConfig()` or `getConfigFromFlux(flux)`
103
- - `Config.set()` - Pass config to `<Metropolis>` component
104
-
105
- ### New Utilities
106
-
107
- - `getConfigFromFlux(flux)` - Get config from flux state (for non-React code)
108
- - `resolveEnvironmentConfig(config)` - Resolve environment-specific config
109
-
110
- ## Migration Guide
111
-
112
- See the [README.md](./README.md) for detailed migration examples and usage patterns.
5
+ - Send the public RUM `analyticsId` to Reaktor in the JSON mutation payload.
6
+ - Use `app.rum.analyticsId` as the RUM configuration field.
7
+ - Require explicit action selection when using `useMetropolis(actionTypes)`.
8
+ - Require hooks to run within the `<Metropolis>` context.
9
+ - Remove obsolete parser aliases, action aliases, and response handling.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # MetropolisJS: Seamless Frontend-Backend Integration Framework
2
2
 
3
3
  <p align="center">
4
- <img src="docs/assets/metropolisjs-logo.png" alt="MetropolisJS logo" width="432">
4
+ <img src="https://raw.githubusercontent.com/nitrogenlabs/metropolisjs/main/docs/assets/metropolisjs-logo.png" alt="MetropolisJS logo" width="432">
5
5
  </p>
6
6
 
7
7
  > **The Ultimate Frontend Integration Library for Modern Web Applications**
@@ -101,8 +101,8 @@ const restActions = useRestActions();
101
101
  // Option 2: Selective creation
102
102
  const {userActions, postActions, restActions} = useMetropolis(['user', 'post', 'rest']);
103
103
 
104
- // Option 3: All actions (default behavior)
105
- const {userActions, postActions, messageActions} = useMetropolis();
104
+ // Option 3: Create an explicit action group
105
+ const {messageActions} = useMetropolis(['message']);
106
106
  ```
107
107
 
108
108
  ### External REST Endpoints
@@ -153,6 +153,29 @@ const profile = await restActions.request(
153
153
 
154
154
  The `Metropolis` component accepts three main props: `config`, `adapters`, and `translations`. Here's how to configure each:
155
155
 
156
+ ### RUM analytics
157
+
158
+ Configure the public analytics identifier returned by Reaktor under `app.rum.analyticsId`. MetropolisJS sends it in the JSON `RumBatchInput.analyticsId` field; it is not placed in a URL or request header.
159
+
160
+ ```tsx
161
+ <Metropolis
162
+ config={{
163
+ production: {
164
+ app: {
165
+ name: 'My App',
166
+ rum: {
167
+ analyticsId: '00000000-0000-4000-8000-000000000000',
168
+ enabled: true
169
+ }
170
+ }
171
+ }
172
+ }}>
173
+ <YourApp />
174
+ </Metropolis>
175
+ ```
176
+
177
+ `app.rum.analyticsId` is the identifier sent with each analytics batch.
178
+
156
179
  ### Configuration Object
157
180
 
158
181
  The `config` prop accepts a `MetropolisConfiguration` object that supports environment-specific settings:
@@ -442,28 +465,11 @@ const ChatComponent = () => {
442
465
 
443
466
  ## Architecture
444
467
 
445
- MetropolisJS is built on a powerful three-layer architecture with modern React patterns:
468
+ MetropolisJS sits between React and your services. The provider supplies configuration and action access, adapters keep data typed, Rip-Hunter handles transport, and ArkhamJS keeps application state reactive.
446
469
 
447
- ```
448
- ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
449
- │ React App │ │ MetropolisJS │ │ Reaktor │
450
- │ │◄──►│ │◄──►│ Backend │
451
- │ UI Layer │ │ Integration │ │ Services │
452
- └─────────────────┘ └─────────────────┘ └─────────────────┘
453
- │ │
454
- │ ▼
455
- │ ┌─────────────────┐
456
- │ │ ArkhamJS │
457
- │ │ Data Store │
458
- │ │ │
459
- │ └─────────────────┘
460
-
461
-
462
- ┌─────────────────┐
463
- │ React Context │
464
- │ (Config/Flux) │
465
- └─────────────────┘
466
- ```
470
+ <p align="center">
471
+ <img src="https://raw.githubusercontent.com/nitrogenlabs/metropolisjs/main/docs/assets/metropolisjs-architecture.png" alt="MetropolisJS architecture showing React components, the provider and hooks, actions, adapters, ArkhamJS state, Rip-Hunter transport, Reaktor services, and real-time updates" width="960">
472
+ </p>
467
473
 
468
474
  ### Core Components
469
475
 
@@ -474,6 +480,14 @@ MetropolisJS is built on a powerful three-layer architecture with modern React p
474
480
  - **Configuration**: Context-based configuration (React best practices)
475
481
  - **Hooks**: Specialized hooks for accessing actions and configuration
476
482
 
483
+ ### Request and State Lifecycle
484
+
485
+ Actions hide the full request lifecycle behind a typed method. They validate inputs, use the request cache when configured, communicate through Rip-Hunter, update the ArkhamJS store, and then dispatch Flux events to subscribers. WebSocket and SSE messages enter the same reactive state flow.
486
+
487
+ <p align="center">
488
+ <img src="https://raw.githubusercontent.com/nitrogenlabs/metropolisjs/main/docs/assets/metropolisjs-request-lifecycle.svg" alt="Sequence diagram showing a React component calling a MetropolisJS action, input validation and caching, Rip-Hunter transport, a Reaktor response, and an ArkhamJS state update followed by a Flux event" width="960">
489
+ </p>
490
+
477
491
  ### Modern Architecture Features
478
492
 
479
493
  MetropolisJS follows React best practices:
@@ -487,7 +501,7 @@ MetropolisJS follows React best practices:
487
501
 
488
502
  ## Available Actions
489
503
 
490
- MetropolisJS provides comprehensive actions for all your needs. Access them using specialized hooks or the main `useMetropolis()` hook:
504
+ MetropolisJS provides comprehensive actions for all your needs. Access them using specialized hooks or `useMetropolis(actionTypes)`:
491
505
 
492
506
  ### Specialized Hooks (Recommended)
493
507
 
@@ -516,13 +530,13 @@ const permissionActions = usePermissionActions();
516
530
  // Alternative: Selective creation
517
531
  const {userActions, postActions, permissionActions} = useMetropolis(['user', 'post', 'permission']);
518
532
 
519
- // Alternative: All actions (creates all action types)
520
- const {userActions, postActions, messageActions, permissionActions} = useMetropolis();
533
+ // Create every action you use explicitly
534
+ const {messageActions, permissionActions} = useMetropolis(['message', 'permission']);
521
535
  ```
522
536
 
523
537
  ## Factory Pattern Guide
524
538
 
525
- MetropolisJS has been refactored to use a **factory function pattern** for actions instead of class-based approaches. This provides better functional programming practices, improved testability, and enhanced flexibility through dependency injection.
539
+ MetropolisJS uses a **factory function pattern** for actions. This provides functional composition, straightforward testing, and dependency injection.
526
540
 
527
541
  ### Key Benefits
528
542
 
@@ -530,21 +544,8 @@ MetropolisJS has been refactored to use a **factory function pattern** for actio
530
544
  2. **Better Testability**: Easier to mock and test individual functions
531
545
  3. **Composability**: Actions can be easily combined and extended
532
546
  4. **Dependency Injection**: Custom adapters can be injected and merged with defaults
533
- 5. **Backward Compatibility**: Legacy class wrappers maintain existing API compatibility
534
-
535
547
  ### Basic Usage
536
548
 
537
- #### Before (Class-based)
538
-
539
- ```typescript
540
- import {userActions} from '../actions/userActions';
541
-
542
- const userActions = new userActions(flux);
543
- const user = await userActions.add(userData);
544
- ```
545
-
546
- #### After (Factory Pattern)
547
-
548
549
  ```typescript
549
550
  import {createUserActions} from '../actions/userActions';
550
551
 
@@ -775,7 +776,7 @@ describe('useUserActions', () => {
775
776
 
776
777
  ### Best Practices
777
778
 
778
- 1. **Use Specialized Hooks**: Prefer `useUserActions()` over `useMetropolis()` when you only need one action type
779
+ 1. **Use Specialized Hooks**: Prefer `useUserActions()` over `useMetropolis(['user'])` when you only need one action type
779
780
  2. **Selective Creation**: Use `useMetropolis(['user', 'post'])` when you need multiple specific actions
780
781
  3. **Context-Based Config**: Use `useMetropolisConfig()` for accessing configuration
781
782
  4. **Leverage Adapter Injection**: Pass custom adapters through the `Metropolis` component
@@ -1077,9 +1078,9 @@ const ResourceProtectedComponent = () => {
1077
1078
  };
1078
1079
  ```
1079
1080
 
1080
- ### Backward Compatibility
1081
+ ### Permission Levels
1081
1082
 
1082
- The permission system is fully backward compatible with the existing `userAccess` field in the User model. The `userAccess` number (0-4) directly maps to the `PermissionLevel` enum values, ensuring seamless integration with existing code.
1083
+ The `userAccess` field uses the numeric `PermissionLevel` values from 0 through 4.
1083
1084
 
1084
1085
  ### Available Permission Actions
1085
1086
 
@@ -0,0 +1,58 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1440" height="760" viewBox="0 0 1440 760" role="img" aria-labelledby="title desc">
2
+ <title id="title">How MetropolisJS connects a React application to state and backend services</title>
3
+ <desc id="desc">React components, hooks, configuration, and live experiences connect to MetropolisJS. MetropolisJS provides configuration, typed actions, adapters, caching, and real-time coordination, then integrates with ArkhamJS, Rip-Hunter, Reaktor, WebSocket, and server-sent event services.</desc>
4
+ <defs>
5
+ <style>
6
+ .canvas{fill:#fff}.heading{fill:#111827;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:21px;font-weight:700}.brand{fill:#2563eb;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:32px;font-weight:700}.label{fill:#111827;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:20px;font-weight:600}.detail{fill:#64748b;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:16px;font-weight:400}.feature{fill:#1f2937;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:18px;font-weight:500}.ink{fill:none;stroke:#374151;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}.blue{fill:none;stroke:#2563eb;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round}.teal{fill:none;stroke:#14b8a6;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round}.teal-fill{fill:#14b8a6}
7
+ </style>
8
+ <symbol id="window" viewBox="0 0 64 64"><rect class="ink" x="7" y="9" width="50" height="46" rx="5"/><path class="ink" d="M7 20h50M14 15h2m6 0h2M15 28h15v15H15zm22 0h12m-12 8h12m-12 8h8"/></symbol>
9
+ <symbol id="hook" viewBox="0 0 64 64"><path class="ink" d="M19 12v18c0 9 5 15 13 15s13-6 13-15V12M14 12h10m16 0h10M32 45v8"/><circle class="ink" cx="32" cy="55" r="3"/></symbol>
10
+ <symbol id="sliders" viewBox="0 0 64 64"><path class="ink" d="M11 17h42M11 32h42M11 47h42"/><circle class="canvas" cx="25" cy="17" r="6"/><circle class="ink" cx="25" cy="17" r="6"/><circle class="canvas" cx="42" cy="32" r="6"/><circle class="ink" cx="42" cy="32" r="6"/><circle class="canvas" cx="20" cy="47" r="6"/><circle class="ink" cx="20" cy="47" r="6"/></symbol>
11
+ <symbol id="pulse" viewBox="0 0 64 64"><path class="ink" d="M7 35h11l6-15 11 29 7-18 5 4h10"/></symbol>
12
+ <symbol id="database" viewBox="0 0 64 64"><ellipse class="ink" cx="32" cy="14" rx="20" ry="8"/><path class="ink" d="M12 14v34c0 4 9 8 20 8s20-4 20-8V14M12 31c0 4 9 8 20 8s20-4 20-8M12 47c0 4 9 8 20 8s20-4 20-8"/></symbol>
13
+ <symbol id="network" viewBox="0 0 64 64"><circle class="ink" cx="32" cy="13" r="6"/><circle class="ink" cx="13" cy="48" r="6"/><circle class="ink" cx="51" cy="48" r="6"/><path class="ink" d="M29 18 16 43m19-25 13 25M19 48h26"/></symbol>
14
+ <symbol id="server" viewBox="0 0 64 64"><rect class="ink" x="10" y="10" width="44" height="18" rx="4"/><rect class="ink" x="10" y="36" width="44" height="18" rx="4"/><circle class="ink" cx="18" cy="19" r="2"/><circle class="ink" cx="18" cy="45" r="2"/><path class="ink" d="M26 19h20M26 45h20"/></symbol>
15
+ <symbol id="broadcast" viewBox="0 0 64 64"><circle class="ink" cx="32" cy="32" r="4"/><path class="ink" d="M24 24a11 11 0 0 0 0 16m16-16a11 11 0 0 1 0 16M17 17a21 21 0 0 0 0 30m30-30a21 21 0 0 1 0 30"/></symbol>
16
+ </defs>
17
+
18
+ <rect class="canvas" width="1440" height="760"/>
19
+ <text class="heading" x="72" y="68">Your React application</text>
20
+ <text class="heading" x="525" y="68">MetropolisJS integration layer</text>
21
+ <text class="heading" x="1045" y="68">State + services</text>
22
+
23
+ <use href="#window" x="70" y="125" width="64" height="64"/>
24
+ <text class="label" x="160" y="153">React components</text><text class="detail" x="160" y="178">UI, routes, and views</text>
25
+ <use href="#hook" x="70" y="260" width="64" height="64"/>
26
+ <text class="label" x="160" y="288">Specialized hooks</text><text class="detail" x="160" y="313">Typed action access</text>
27
+ <use href="#sliders" x="70" y="395" width="64" height="64"/>
28
+ <text class="label" x="160" y="423">App configuration</text><text class="detail" x="160" y="448">Endpoints, auth, adapters</text>
29
+ <use href="#pulse" x="70" y="530" width="64" height="64"/>
30
+ <text class="label" x="160" y="558">Live experiences</text><text class="detail" x="160" y="583">Messages and notifications</text>
31
+
32
+ <path class="blue" d="M375 157h20q20 0 20 20v365q0 20-20 20h-20M375 292h40M375 427h40"/>
33
+ <path class="blue" d="M415 480h85m-13-13 13 13-13 13"/>
34
+
35
+ <circle cx="660" cy="174" r="82" fill="none" stroke="#2563eb" stroke-width="3"/>
36
+ <path fill="#2563eb" d="m630 169 30 18 30-18v38l-30 19-30-19z"/>
37
+ <path fill="#0f172a" d="m610 128 50 30 50-30v74l-20 12v-44l-30 18-30-18v44l-20-12z"/>
38
+ <text class="brand" x="660" y="300" text-anchor="middle">MetropolisJS</text>
39
+ <text class="detail" x="660" y="330" text-anchor="middle">Frontend API integration and reactive data</text>
40
+
41
+ <circle class="teal" cx="533" cy="360" r="12"/><path class="teal" d="m527 360 4 4 8-9"/><text class="feature" x="563" y="367">Provider + configuration context</text>
42
+ <circle class="teal" cx="533" cy="420" r="12"/><path class="teal" d="M527 420h12m-6-6v12"/><text class="feature" x="563" y="427">Typed actions + domain hooks</text>
43
+ <circle class="teal" cx="533" cy="480" r="12"/><path class="teal" d="m527 480 4 4 8-9"/><text class="feature" x="563" y="487">Adapters + validation</text>
44
+ <circle class="teal" cx="533" cy="540" r="12"/><path class="teal" d="M527 540h12"/><text class="feature" x="563" y="547">Request cache + Flux events</text>
45
+ <circle class="teal" cx="533" cy="600" r="12"/><path class="teal" d="M527 600h12m-6-6v12"/><text class="feature" x="563" y="607">Real-time coordination</text>
46
+
47
+ <path class="blue" d="M805 480h140m-13-13 13 13-13 13"/>
48
+ <path class="teal" d="M1011 157h-22q-20 0-20 20v365q0 20 20 20h22M969 292h42M969 427h42"/>
49
+
50
+ <circle class="teal-fill" cx="1011" cy="157" r="6"/><use href="#database" x="1040" y="125" width="64" height="64"/>
51
+ <text class="label" x="1130" y="153">ArkhamJS</text><text class="detail" x="1130" y="178">Reactive Flux state</text>
52
+ <circle class="teal-fill" cx="1011" cy="292" r="6"/><use href="#network" x="1040" y="260" width="64" height="64"/>
53
+ <text class="label" x="1130" y="288">Rip-Hunter</text><text class="detail" x="1130" y="313">GraphQL + REST transport</text>
54
+ <circle class="teal-fill" cx="1011" cy="427" r="6"/><use href="#server" x="1040" y="395" width="64" height="64"/>
55
+ <text class="label" x="1130" y="423">Reaktor</text><text class="detail" x="1130" y="448">Backend actions + APIs</text>
56
+ <circle class="teal-fill" cx="1011" cy="562" r="6"/><use href="#broadcast" x="1040" y="530" width="64" height="64"/>
57
+ <text class="label" x="1130" y="558">WebSocket + SSE</text><text class="detail" x="1130" y="583">Real-time event streams</text>
58
+ </svg>
@@ -0,0 +1,42 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1440" height="520" viewBox="0 0 1440 520" role="img" aria-labelledby="title desc">
2
+ <title id="title">MetropolisJS request and state lifecycle</title>
3
+ <desc id="desc">A React component invokes a typed action, an adapter validates the input, the request cache or Rip-Hunter resolves the data, Reaktor responds, ArkhamJS updates state, and a Flux event renders the latest state.</desc>
4
+ <defs>
5
+ <style>
6
+ .canvas{fill:#fff}.heading{fill:#111827;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:22px;font-weight:700}.step{fill:#111827;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:17px;font-weight:650}.detail{fill:#64748b;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:14px;font-weight:400}.number{fill:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:13px;font-weight:700}.ink{fill:none;stroke:#374151;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}.blue{fill:none;stroke:#2563eb;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round}.teal{fill:none;stroke:#14b8a6;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round}.blue-fill{fill:#2563eb}.teal-fill{fill:#14b8a6}
7
+ </style>
8
+ <symbol id="cursor" viewBox="0 0 64 64"><path class="ink" d="m16 9 33 31-17 1-9 14z"/><path class="ink" d="m34 41 11 13"/></symbol>
9
+ <symbol id="check" viewBox="0 0 64 64"><path class="ink" d="M18 8h28v48H18zM25 8V4h14v4M25 25l5 5 10-12M25 40h14"/></symbol>
10
+ <symbol id="route" viewBox="0 0 64 64"><circle class="ink" cx="14" cy="17" r="6"/><circle class="ink" cx="50" cy="47" r="6"/><path class="ink" d="M20 17h12q10 0 10 10v10q0 10 8 10M32 17l-6-6m6 6-6 6"/></symbol>
11
+ <symbol id="server" viewBox="0 0 64 64"><rect class="ink" x="10" y="10" width="44" height="18" rx="4"/><rect class="ink" x="10" y="36" width="44" height="18" rx="4"/><circle class="ink" cx="18" cy="19" r="2"/><circle class="ink" cx="18" cy="45" r="2"/><path class="ink" d="M26 19h20M26 45h20"/></symbol>
12
+ <symbol id="database" viewBox="0 0 64 64"><ellipse class="ink" cx="32" cy="14" rx="20" ry="8"/><path class="ink" d="M12 14v34c0 4 9 8 20 8s20-4 20-8V14M12 31c0 4 9 8 20 8s20-4 20-8M12 47c0 4 9 8 20 8s20-4 20-8"/></symbol>
13
+ <symbol id="refresh" viewBox="0 0 64 64"><path class="ink" d="M49 24A19 19 0 0 0 17 17l-5 6m3-13 2 7 7-2M15 40a19 19 0 0 0 32 7l5-6m-3 13-2-7-7 2"/></symbol>
14
+ </defs>
15
+
16
+ <rect class="canvas" width="1440" height="520"/>
17
+ <text class="heading" x="70" y="62">From UI intent to reactive state</text>
18
+ <path class="blue" d="M150 229h505m-14-12 14 12-14 12"/>
19
+ <path class="teal" d="M655 229h635m-14-12 14 12-14 12"/>
20
+
21
+ <g transform="translate(105 160)"><circle class="blue-fill" cx="45" cy="69" r="8"/><use href="#cursor" x="13" y="-3" width="64" height="64"/><circle class="blue-fill" cx="16" cy="-3" r="13"/><text class="number" x="16" y="2" text-anchor="middle">1</text></g>
22
+ <text class="step" x="150" y="304" text-anchor="middle">Invoke an action</text><text class="detail" x="150" y="329" text-anchor="middle">Component + typed hook</text>
23
+
24
+ <g transform="translate(330 160)"><circle class="blue-fill" cx="45" cy="69" r="8"/><use href="#check" x="13" y="-3" width="64" height="64"/><circle class="blue-fill" cx="16" cy="-3" r="13"/><text class="number" x="16" y="2" text-anchor="middle">2</text></g>
25
+ <text class="step" x="375" y="304" text-anchor="middle">Validate input</text><text class="detail" x="375" y="329" text-anchor="middle">Adapter produces typed data</text>
26
+
27
+ <g transform="translate(555 160)"><circle class="blue-fill" cx="45" cy="69" r="8"/><use href="#route" x="13" y="-3" width="64" height="64"/><circle class="blue-fill" cx="16" cy="-3" r="13"/><text class="number" x="16" y="2" text-anchor="middle">3</text></g>
28
+ <text class="step" x="600" y="304" text-anchor="middle">Resolve request</text><text class="detail" x="600" y="329" text-anchor="middle">Cache hit or Rip-Hunter</text>
29
+
30
+ <g transform="translate(780 160)"><circle class="teal-fill" cx="45" cy="69" r="8"/><use href="#server" x="13" y="-3" width="64" height="64"/><circle class="teal-fill" cx="16" cy="-3" r="13"/><text class="number" x="16" y="2" text-anchor="middle">4</text></g>
31
+ <text class="step" x="825" y="304" text-anchor="middle">Receive response</text><text class="detail" x="825" y="329" text-anchor="middle">Reaktor returns result</text>
32
+
33
+ <g transform="translate(1005 160)"><circle class="teal-fill" cx="45" cy="69" r="8"/><use href="#database" x="13" y="-3" width="64" height="64"/><circle class="teal-fill" cx="16" cy="-3" r="13"/><text class="number" x="16" y="2" text-anchor="middle">5</text></g>
34
+ <text class="step" x="1050" y="304" text-anchor="middle">Update state</text><text class="detail" x="1050" y="329" text-anchor="middle">ArkhamJS stores the result</text>
35
+
36
+ <g transform="translate(1230 160)"><circle class="teal-fill" cx="45" cy="69" r="8"/><use href="#refresh" x="13" y="-3" width="64" height="64"/><circle class="teal-fill" cx="16" cy="-3" r="13"/><text class="number" x="16" y="2" text-anchor="middle">6</text></g>
37
+ <text class="step" x="1275" y="304" text-anchor="middle">Render latest data</text><text class="detail" x="1275" y="329" text-anchor="middle">Flux event notifies listeners</text>
38
+
39
+ <path class="teal" d="M1275 365v35q0 24-24 24H174q-24 0-24-24v-35"/>
40
+ <path class="teal" d="m138 379 12-14 12 14"/>
41
+ <text class="detail" x="712" y="456" text-anchor="middle">WebSocket and SSE events join the same state-update loop</text>
42
+ </svg>
@@ -102,7 +102,7 @@ import React, { useEffect, useState } from 'react';
102
102
  import { useMetropolis } from '@nlabs/metropolisjs';
103
103
 
104
104
  const WelcomeComponent = () => {
105
- const { contentActions } = useMetropolis();
105
+ const { contentActions } = useMetropolis(['content']);
106
106
  const [welcomeMessage, setWelcomeMessage] = useState('Loading...');
107
107
  const [isLoading, setIsLoading] = useState(true);
108
108
  const [error, setError] = useState(null);
@@ -141,4 +141,3 @@ export {
141
141
  addContentExample, deleteContentExample, getContentByCategoryExample, getContentByKeyExample,
142
142
  updateContentExample
143
143
  };
144
-
@@ -268,21 +268,3 @@ export const allActionsExample = (flux: FluxFramework) => {
268
268
 
269
269
  return { allActions, comprehensiveWorkflow };
270
270
  };
271
-
272
- // Example 9: Migration from Old Pattern
273
- export const migrationExample = (flux: FluxFramework) => {
274
- // New way (recommended)
275
- const userActions = createAction('user', flux) as any;
276
- const postActions = createAction('post', flux) as any;
277
- const eventActions = createAction('event', flux) as any;
278
-
279
- const createUser = async (userData: any) => {
280
- return await userActions.add(userData);
281
- };
282
-
283
- const createPost = async (postData: any) => {
284
- return await postActions.add(postData);
285
- };
286
-
287
- return { createUser, createPost };
288
- };
@@ -19,7 +19,7 @@ export interface AwsRumEvent extends AwsRumTrackInput {
19
19
  readonly timestamp: number;
20
20
  }
21
21
  export interface AwsRumActionsOptions {
22
- readonly appId?: string;
22
+ readonly analyticsId?: string;
23
23
  readonly debounceMs?: number;
24
24
  readonly dedupeMs?: number;
25
25
  readonly enabled?: boolean;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Copyright (c) 2026-Present, Nitrogen Labs, Inc.
3
3
  * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
4
- */ import { rumMutation } from '../../utils/api.js';
4
+ */ import { rumRequest } from '../../utils/api.js';
5
5
  export const AWS_RUM_CONSTANTS = {
6
6
  TRACK_ERROR: 'AWS_RUM_TRACK_ERROR',
7
7
  TRACK_QUEUED: 'AWS_RUM_TRACK_QUEUED',
@@ -87,7 +87,7 @@ const eventFingerprint = (event)=>{
87
87
  ]);
88
88
  };
89
89
  export const createAwsRumActions = (flux, options = {})=>{
90
- const appId = normalizeText(options.appId, 128);
90
+ const analyticsId = normalizeText(options.analyticsId, 128);
91
91
  const debounceMs = Math.max(0, options.debounceMs ?? DEFAULT_DEBOUNCE_MS);
92
92
  const dedupeMs = Math.max(0, options.dedupeMs ?? DEFAULT_DEDUPE_MS);
93
93
  const enabled = options.enabled !== false;
@@ -129,7 +129,7 @@ export const createAwsRumActions = (flux, options = {})=>{
129
129
  if (flushPromise) {
130
130
  return flushPromise;
131
131
  }
132
- if (!enabled || !appId || hasPrivacySignal() || pendingEvents.size === 0) {
132
+ if (!enabled || !analyticsId || hasPrivacySignal() || pendingEvents.size === 0) {
133
133
  return;
134
134
  }
135
135
  if (timer) {
@@ -145,27 +145,19 @@ export const createAwsRumActions = (flux, options = {})=>{
145
145
  for(let index = 0; index < events.length; index += MAX_BATCH_SIZE){
146
146
  const batchEvents = events.slice(index, index + MAX_BATCH_SIZE);
147
147
  try {
148
- await rumMutation(flux, 'track', 'rum', {
149
- batch: {
150
- type: 'RumBatchInput!',
151
- value: {
152
- appId,
153
- events: batchEvents
154
- }
155
- }
156
- }, [
157
- 'accepted',
158
- 'appId'
159
- ]);
148
+ await rumRequest(flux, {
149
+ analyticsId,
150
+ events: batchEvents
151
+ });
160
152
  await flux.dispatch({
161
- appId,
153
+ analyticsId,
162
154
  events: batchEvents,
163
155
  type: AWS_RUM_CONSTANTS.TRACK_SUCCESS
164
156
  });
165
157
  } catch (error) {
166
158
  events.slice(index).forEach((event)=>pendingEvents.set(eventFingerprint(event), event));
167
159
  await flux.dispatch({
168
- appId,
160
+ analyticsId,
169
161
  error,
170
162
  events: batchEvents,
171
163
  type: AWS_RUM_CONSTANTS.TRACK_ERROR
@@ -186,7 +178,7 @@ export const createAwsRumActions = (flux, options = {})=>{
186
178
  timer = setTimeout(()=>void flush(), Math.max(debounceMs, throttleRemaining));
187
179
  };
188
180
  const track = (input)=>{
189
- if (!enabled || !appId || hasPrivacySignal()) {
181
+ if (!enabled || !analyticsId || hasPrivacySignal()) {
190
182
  return;
191
183
  }
192
184
  const event = sanitizeEvent(input);
@@ -211,7 +203,7 @@ export const createAwsRumActions = (flux, options = {})=>{
211
203
  };
212
204
  pendingEvents.set(fingerprint, queuedEvent);
213
205
  void flux.dispatch({
214
- appId,
206
+ analyticsId,
215
207
  event: queuedEvent,
216
208
  type: AWS_RUM_CONSTANTS.TRACK_QUEUED
217
209
  });
@@ -232,4 +224,4 @@ export const createAwsRumActions = (flux, options = {})=>{
232
224
  };
233
225
  };
234
226
 
235
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9Vc2Vycy9uaXRyb2c3L0RldmVsb3BtZW50L21ldHJvcG9saXNqcy9zcmMvYWN0aW9ucy9hd3NSdW1BY3Rpb25zL2F3c1J1bUFjdGlvbnMudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDb3B5cmlnaHQgKGMpIDIwMjYtUHJlc2VudCwgTml0cm9nZW4gTGFicywgSW5jLlxuICogQ29weXJpZ2h0cyBsaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSB0aGUgYWNjb21wYW55aW5nIExJQ0VOU0UgZmlsZSBmb3IgdGVybXMuXG4gKi9cbmltcG9ydCB7cnVtTXV0YXRpb259IGZyb20gJy4uLy4uL3V0aWxzL2FwaS5qcyc7XG5cbmltcG9ydCB0eXBlIHtGbHV4RnJhbWV3b3JrfSBmcm9tICdAbmxhYnMvYXJraGFtanMnO1xuXG5leHBvcnQgY29uc3QgQVdTX1JVTV9DT05TVEFOVFMgPSB7XG4gIFRSQUNLX0VSUk9SOiAnQVdTX1JVTV9UUkFDS19FUlJPUicsXG4gIFRSQUNLX1FVRVVFRDogJ0FXU19SVU1fVFJBQ0tfUVVFVUVEJyxcbiAgVFJBQ0tfU1VDQ0VTUzogJ0FXU19SVU1fVFJBQ0tfU1VDQ0VTUydcbn0gYXMgY29uc3Q7XG5cbmV4cG9ydCB0eXBlIEF3c1J1bUV2ZW50VHlwZSA9ICdjbGljaycgfCAncGFnZV92aWV3JyB8IChzdHJpbmcgJiB7fSk7XG5leHBvcnQgdHlwZSBBd3NSdW1Qcm9wZXJ0eSA9IGJvb2xlYW4gfCBudW1iZXIgfCBzdHJpbmc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgQXdzUnVtVHJhY2tJbnB1dCB7XG4gIHJlYWRvbmx5IG5hbWU6IHN0cmluZztcbiAgcmVhZG9ubHkgcGF0aD86IHN0cmluZztcbiAgcmVhZG9ubHkgcHJvcGVydGllcz86IFJlY29yZDxzdHJpbmcsIEF3c1J1bVByb3BlcnR5PjtcbiAgcmVhZG9ubHkgdHlwZTogQXdzUnVtRXZlbnRUeXBlO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEF3c1J1bUV2ZW50IGV4dGVuZHMgQXdzUnVtVHJhY2tJbnB1dCB7XG4gIHJlYWRvbmx5IGV2ZW50SWQ6IHN0cmluZztcbiAgcmVhZG9ubHkgam91cm5leUlkOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHNlcXVlbmNlOiBudW1iZXI7XG4gIHJlYWRvbmx5IHRpbWVzdGFtcDogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEF3c1J1bUFjdGlvbnNPcHRpb25zIHtcbiAgcmVhZG9ubHkgYXBwSWQ/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IGRlYm91bmNlTXM/OiBudW1iZXI7XG4gIHJlYWRvbmx5IGRlZHVwZU1zPzogbnVtYmVyO1xuICByZWFkb25seSBlbmFibGVkPzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgcmVzcGVjdFByaXZhY3lTaWduYWxzPzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgdGhyb3R0bGVNcz86IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBBd3NSdW1BY3Rpb25zIHtcbiAgcmVhZG9ubHkgZGVzdHJveTogKCkgPT4gUHJvbWlzZTx2b2lkPjtcbiAgcmVhZG9ubHkgZmx1c2g6ICgpID0+IFByb21pc2U8dm9pZD47XG4gIHJlYWRvbmx5IHRyYWNrOiAoZXZlbnQ6IEF3c1J1bVRyYWNrSW5wdXQpID0+IHZvaWQ7XG59XG5cbmNvbnN0IERFRkFVTFRfREVCT1VOQ0VfTVMgPSAyNTA7XG5jb25zdCBERUZBVUxUX0RFRFVQRV9NUyA9IDEwMDA7XG5jb25zdCBERUZBVUxUX1RIUk9UVExFX01TID0gMTAwMDtcbmNvbnN0IE1BWF9CQVRDSF9TSVpFID0gNTA7XG5jb25zdCBNQVhfUFJPUEVSVElFUyA9IDIwO1xuY29uc3QgTUFYX1NFRU5fRVZFTlRTID0gNTAwO1xuY29uc3QgQkxPQ0tFRF9QUk9QRVJUWV9LRVlTID0gbmV3IFNldChbXG4gICdhY2NvdW50aWQnLFxuICAnYWRkcmVzcycsXG4gICdlbWFpbCcsXG4gICdmaXJzdG5hbWUnLFxuICAnZnVsbG5hbWUnLFxuICAnZ2VvbG9jYXRpb24nLFxuICAnaXAnLFxuICAnaXBhZGRyZXNzJyxcbiAgJ2xhc3RuYW1lJyxcbiAgJ2xhdGl0dWRlJyxcbiAgJ2xvbmdpdHVkZScsXG4gICduYW1lJyxcbiAgJ3Bob25lJyxcbiAgJ3JlZmVycmVyJyxcbiAgJ3Rva2VuJyxcbiAgJ3VzZXJhZ2VudCcsXG4gICd1c2VyaWQnXG5dKTtcbmNvbnN0IEVNQUlMX1BBVFRFUk4gPSAvXFxiW15cXHNAXStAW15cXHNAXStcXC5bXlxcc0BdK1xcYi9pO1xuXG5pbnRlcmZhY2UgUHJpdmFjeU5hdmlnYXRvciBleHRlbmRzIE5hdmlnYXRvciB7XG4gIHJlYWRvbmx5IGdsb2JhbFByaXZhY3lDb250cm9sPzogYm9vbGVhbjtcbn1cblxuY29uc3QgY3JlYXRlSWQgPSAoKTogc3RyaW5nID0+IHtcbiAgaWYodHlwZW9mIGdsb2JhbFRoaXMuY3J5cHRvPy5yYW5kb21VVUlEID09PSAnZnVuY3Rpb24nKSB7XG4gICAgcmV0dXJuIGdsb2JhbFRoaXMuY3J5cHRvLnJhbmRvbVVVSUQoKTtcbiAgfVxuXG4gIHJldHVybiBgJHtEYXRlLm5vdygpLnRvU3RyaW5nKDM2KX0tJHtNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zbGljZSgyKX1gO1xufTtcblxuY29uc3Qgbm9ybWFsaXplVGV4dCA9ICh2YWx1ZTogdW5rbm93biwgbWF4TGVuZ3RoOiBudW1iZXIpOiBzdHJpbmcgPT5cbiAgU3RyaW5nKHZhbHVlIHx8ICcnKS50cmltKCkuc2xpY2UoMCwgbWF4TGVuZ3RoKTtcblxuY29uc3Qgbm9ybWFsaXplUHJvcGVydHlLZXkgPSAodmFsdWU6IHN0cmluZyk6IHN0cmluZyA9PlxuICB2YWx1ZS50b0xvd2VyQ2FzZSgpLnJlcGxhY2UoL1teYS16MC05XS9nLCAnJyk7XG5cbmNvbnN0IHNhbml0aXplUHJvcGVydGllcyA9IChcbiAgcHJvcGVydGllczogUmVjb3JkPHN0cmluZywgQXdzUnVtUHJvcGVydHk+ID0ge31cbik6IFJlY29yZDxzdHJpbmcsIEF3c1J1bVByb3BlcnR5PiA9PiBPYmplY3QuZW50cmllcyhwcm9wZXJ0aWVzKVxuICAuc2xpY2UoMCwgTUFYX1BST1BFUlRJRVMpXG4gIC5yZWR1Y2U8UmVjb3JkPHN0cmluZywgQXdzUnVtUHJvcGVydHk+PigocmVzdWx0LCBba2V5LCB2YWx1ZV0pID0+IHtcbiAgICBjb25zdCBwcm9wZXJ0eUtleSA9IG5vcm1hbGl6ZVRleHQoa2V5LCA2NCk7XG4gICAgY29uc3Qgbm9ybWFsaXplZFByb3BlcnR5S2V5ID0gbm9ybWFsaXplUHJvcGVydHlLZXkocHJvcGVydHlLZXkpO1xuXG4gICAgaWYoXG4gICAgICAhcHJvcGVydHlLZXkgfHxcbiAgICAgIEJMT0NLRURfUFJPUEVSVFlfS0VZUy5oYXMobm9ybWFsaXplZFByb3BlcnR5S2V5KSB8fFxuICAgICAgIVsnYm9vbGVhbicsICdudW1iZXInLCAnc3RyaW5nJ10uaW5jbHVkZXModHlwZW9mIHZhbHVlKVxuICAgICkge1xuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG5cbiAgICBpZih0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XG4gICAgICBjb25zdCB0ZXh0VmFsdWUgPSBub3JtYWxpemVUZXh0KHZhbHVlLCAyNTYpO1xuXG4gICAgICBpZih0ZXh0VmFsdWUgJiYgIUVNQUlMX1BBVFRFUk4udGVzdCh0ZXh0VmFsdWUpKSB7XG4gICAgICAgIHJlc3VsdFtwcm9wZXJ0eUtleV0gPSB0ZXh0VmFsdWU7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmKHR5cGVvZiB2YWx1ZSA9PT0gJ2Jvb2xlYW4nIHx8IE51bWJlci5pc0Zpbml0ZSh2YWx1ZSkpIHtcbiAgICAgIHJlc3VsdFtwcm9wZXJ0eUtleV0gPSB2YWx1ZTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9LCB7fSk7XG5cbmNvbnN0IHNhbml0aXplRXZlbnQgPSAoZXZlbnQ6IEF3c1J1bVRyYWNrSW5wdXQpOiBBd3NSdW1UcmFja0lucHV0IHwgbnVsbCA9PiB7XG4gIGNvbnN0IG5hbWUgPSBub3JtYWxpemVUZXh0KGV2ZW50Lm5hbWUsIDEyOCk7XG4gIGNvbnN0IHR5cGUgPSBub3JtYWxpemVUZXh0KGV2ZW50LnR5cGUsIDY0KTtcblxuICBpZighbmFtZSB8fCAhdHlwZSkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgY29uc3QgcGF0aCA9IG5vcm1hbGl6ZVRleHQoZXZlbnQucGF0aCwgMjU2KS5zcGxpdCgnPycpWzBdLnNwbGl0KCcjJylbMF07XG5cbiAgcmV0dXJuIHtcbiAgICBuYW1lLFxuICAgIC4uLihwYXRoID8ge3BhdGh9IDoge30pLFxuICAgIHByb3BlcnRpZXM6IHNhbml0aXplUHJvcGVydGllcyhldmVudC5wcm9wZXJ0aWVzKSxcbiAgICB0eXBlXG4gIH07XG59O1xuXG5jb25zdCBldmVudEZpbmdlcnByaW50ID0gKGV2ZW50OiBBd3NSdW1UcmFja0lucHV0KTogc3RyaW5nID0+IHtcbiAgY29uc3QgcHJvcGVydGllcyA9IE9iamVjdC5lbnRyaWVzKGV2ZW50LnByb3BlcnRpZXMgfHwge30pLnNvcnQoKFtsZWZ0XSwgW3JpZ2h0XSkgPT4gbGVmdC5sb2NhbGVDb21wYXJlKHJpZ2h0KSk7XG4gIHJldHVybiBKU09OLnN0cmluZ2lmeShbZXZlbnQudHlwZSwgZXZlbnQubmFtZSwgZXZlbnQucGF0aCB8fCAnJywgcHJvcGVydGllc10pO1xufTtcblxuZXhwb3J0IGNvbnN0IGNyZWF0ZUF3c1J1bUFjdGlvbnMgPSAoXG4gIGZsdXg6IEZsdXhGcmFtZXdvcmssXG4gIG9wdGlvbnM6IEF3c1J1bUFjdGlvbnNPcHRpb25zID0ge31cbik6IEF3c1J1bUFjdGlvbnMgPT4ge1xuICBjb25zdCBhcHBJZCA9IG5vcm1hbGl6ZVRleHQob3B0aW9ucy5hcHBJZCwgMTI4KTtcbiAgY29uc3QgZGVib3VuY2VNcyA9IE1hdGgubWF4KDAsIG9wdGlvbnMuZGVib3VuY2VNcyA/PyBERUZBVUxUX0RFQk9VTkNFX01TKTtcbiAgY29uc3QgZGVkdXBlTXMgPSBNYXRoLm1heCgwLCBvcHRpb25zLmRlZHVwZU1zID8/IERFRkFVTFRfREVEVVBFX01TKTtcbiAgY29uc3QgZW5hYmxlZCA9IG9wdGlvbnMuZW5hYmxlZCAhPT0gZmFsc2U7XG4gIGNvbnN0IHJlc3BlY3RQcml2YWN5U2lnbmFscyA9IG9wdGlvbnMucmVzcGVjdFByaXZhY3lTaWduYWxzICE9PSBmYWxzZTtcbiAgY29uc3QgdGhyb3R0bGVNcyA9IE1hdGgubWF4KDAsIG9wdGlvbnMudGhyb3R0bGVNcyA/PyBERUZBVUxUX1RIUk9UVExFX01TKTtcbiAgY29uc3Qgam91cm5leUlkID0gY3JlYXRlSWQoKTtcbiAgY29uc3QgcGVuZGluZ0V2ZW50cyA9IG5ldyBNYXA8c3RyaW5nLCBBd3NSdW1FdmVudD4oKTtcbiAgY29uc3Qgc2VlbkV2ZW50cyA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7XG4gIGxldCBmbHVzaFByb21pc2U6IFByb21pc2U8dm9pZD4gfCB1bmRlZmluZWQ7XG4gIGxldCBsYXN0Rmx1c2hBdCA9IDA7XG4gIGxldCBzZXF1ZW5jZSA9IDA7XG4gIGxldCB0aW1lcjogUmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD4gfCB1bmRlZmluZWQ7XG5cbiAgY29uc3QgaGFzUHJpdmFjeVNpZ25hbCA9ICgpOiBib29sZWFuID0+IHtcbiAgICBpZighcmVzcGVjdFByaXZhY3lTaWduYWxzIHx8IHR5cGVvZiBnbG9iYWxUaGlzLm5hdmlnYXRvciA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBjb25zdCBuYXZpZ2F0b3JTdGF0ZSA9IGdsb2JhbFRoaXMubmF2aWdhdG9yIGFzIFByaXZhY3lOYXZpZ2F0b3I7XG5cbiAgICByZXR1cm4gbmF2aWdhdG9yU3RhdGUuZG9Ob3RUcmFjayA9PT0gJzEnIHx8IG5hdmlnYXRvclN0YXRlLmdsb2JhbFByaXZhY3lDb250cm9sID09PSB0cnVlO1xuICB9O1xuXG4gIGNvbnN0IHBydW5lU2VlbkV2ZW50cyA9ICgpOiB2b2lkID0+IHtcbiAgICBpZihzZWVuRXZlbnRzLnNpemUgPD0gTUFYX1NFRU5fRVZFTlRTKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgY3V0b2ZmID0gRGF0ZS5ub3coKSAtIGRlZHVwZU1zO1xuICAgIHNlZW5FdmVudHMuZm9yRWFjaCgodGltZXN0YW1wLCBmaW5nZXJwcmludCkgPT4ge1xuICAgICAgaWYodGltZXN0YW1wIDwgY3V0b2ZmKSB7XG4gICAgICAgIHNlZW5FdmVudHMuZGVsZXRlKGZpbmdlcnByaW50KTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHdoaWxlKHNlZW5FdmVudHMuc2l6ZSA+IE1BWF9TRUVOX0VWRU5UUykge1xuICAgICAgY29uc3Qgb2xkZXN0RmluZ2VycHJpbnQgPSBzZWVuRXZlbnRzLmtleXMoKS5uZXh0KCkudmFsdWU7XG5cbiAgICAgIGlmKCFvbGRlc3RGaW5nZXJwcmludCkge1xuICAgICAgICBicmVhaztcbiAgICAgIH1cblxuICAgICAgc2VlbkV2ZW50cy5kZWxldGUob2xkZXN0RmluZ2VycHJpbnQpO1xuICAgIH1cbiAgfTtcblxuICBjb25zdCBmbHVzaCA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICBpZihmbHVzaFByb21pc2UpIHtcbiAgICAgIHJldHVybiBmbHVzaFByb21pc2U7XG4gICAgfVxuXG4gICAgaWYoIWVuYWJsZWQgfHwgIWFwcElkIHx8IGhhc1ByaXZhY3lTaWduYWwoKSB8fCBwZW5kaW5nRXZlbnRzLnNpemUgPT09IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZih0aW1lcikge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVyKTtcbiAgICAgIHRpbWVyID0gdW5kZWZpbmVkO1xuICAgIH1cblxuICAgIGNvbnN0IGV2ZW50cyA9IFsuLi5wZW5kaW5nRXZlbnRzLnZhbHVlcygpXTtcbiAgICBwZW5kaW5nRXZlbnRzLmNsZWFyKCk7XG4gICAgbGFzdEZsdXNoQXQgPSBEYXRlLm5vdygpO1xuXG4gICAgZmx1c2hQcm9taXNlID0gKGFzeW5jICgpID0+IHtcbiAgICAgIGZvcihsZXQgaW5kZXggPSAwOyBpbmRleCA8IGV2ZW50cy5sZW5ndGg7IGluZGV4ICs9IE1BWF9CQVRDSF9TSVpFKSB7XG4gICAgICAgIGNvbnN0IGJhdGNoRXZlbnRzID0gZXZlbnRzLnNsaWNlKGluZGV4LCBpbmRleCArIE1BWF9CQVRDSF9TSVpFKTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGF3YWl0IHJ1bU11dGF0aW9uKGZsdXgsICd0cmFjaycsICdydW0nLCB7XG4gICAgICAgICAgICBiYXRjaDoge1xuICAgICAgICAgICAgICB0eXBlOiAnUnVtQmF0Y2hJbnB1dCEnLFxuICAgICAgICAgICAgICB2YWx1ZToge2FwcElkLCBldmVudHM6IGJhdGNoRXZlbnRzfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0sIFsnYWNjZXB0ZWQnLCAnYXBwSWQnXSk7XG4gICAgICAgICAgYXdhaXQgZmx1eC5kaXNwYXRjaCh7YXBwSWQsIGV2ZW50czogYmF0Y2hFdmVudHMsIHR5cGU6IEFXU19SVU1fQ09OU1RBTlRTLlRSQUNLX1NVQ0NFU1N9KTtcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xuICAgICAgICAgIGV2ZW50cy5zbGljZShpbmRleCkuZm9yRWFjaCgoZXZlbnQpID0+IHBlbmRpbmdFdmVudHMuc2V0KGV2ZW50RmluZ2VycHJpbnQoZXZlbnQpLCBldmVudCkpO1xuICAgICAgICAgIGF3YWl0IGZsdXguZGlzcGF0Y2goe2FwcElkLCBlcnJvciwgZXZlbnRzOiBiYXRjaEV2ZW50cywgdHlwZTogQVdTX1JVTV9DT05TVEFOVFMuVFJBQ0tfRVJST1J9KTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KSgpXG4gICAgICAuZmluYWxseSgoKSA9PiB7XG4gICAgICAgIGZsdXNoUHJvbWlzZSA9IHVuZGVmaW5lZDtcbiAgICAgIH0pO1xuXG4gICAgcmV0dXJuIGZsdXNoUHJvbWlzZTtcbiAgfTtcblxuICBjb25zdCBzY2hlZHVsZUZsdXNoID0gKCk6IHZvaWQgPT4ge1xuICAgIGlmKHRpbWVyKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXIpO1xuICAgIH1cblxuICAgIGNvbnN0IHRocm90dGxlUmVtYWluaW5nID0gTWF0aC5tYXgoMCwgdGhyb3R0bGVNcyAtIChEYXRlLm5vdygpIC0gbGFzdEZsdXNoQXQpKTtcbiAgICB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4gdm9pZCBmbHVzaCgpLCBNYXRoLm1heChkZWJvdW5jZU1zLCB0aHJvdHRsZVJlbWFpbmluZykpO1xuICB9O1xuXG4gIGNvbnN0IHRyYWNrID0gKGlucHV0OiBBd3NSdW1UcmFja0lucHV0KTogdm9pZCA9PiB7XG4gICAgaWYoIWVuYWJsZWQgfHwgIWFwcElkIHx8IGhhc1ByaXZhY3lTaWduYWwoKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IGV2ZW50ID0gc2FuaXRpemVFdmVudChpbnB1dCk7XG5cbiAgICBpZighZXZlbnQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBmaW5nZXJwcmludCA9IGV2ZW50RmluZ2VycHJpbnQoZXZlbnQpO1xuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgY29uc3QgbGFzdFNlZW5BdCA9IHNlZW5FdmVudHMuZ2V0KGZpbmdlcnByaW50KSB8fCAwO1xuXG4gICAgaWYoc2VlbkV2ZW50cy5oYXMoZmluZ2VycHJpbnQpICYmIG5vdyAtIGxhc3RTZWVuQXQgPCBkZWR1cGVNcykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHNlZW5FdmVudHMuc2V0KGZpbmdlcnByaW50LCBub3cpO1xuICAgIHBydW5lU2VlbkV2ZW50cygpO1xuICAgIHNlcXVlbmNlICs9IDE7XG5cbiAgICBjb25zdCBxdWV1ZWRFdmVudDogQXdzUnVtRXZlbnQgPSB7XG4gICAgICAuLi5ldmVudCxcbiAgICAgIGV2ZW50SWQ6IGNyZWF0ZUlkKCksXG4gICAgICBqb3VybmV5SWQsXG4gICAgICBzZXF1ZW5jZSxcbiAgICAgIHRpbWVzdGFtcDogbm93XG4gICAgfTtcblxuICAgIHBlbmRpbmdFdmVudHMuc2V0KGZpbmdlcnByaW50LCBxdWV1ZWRFdmVudCk7XG4gICAgdm9pZCBmbHV4LmRpc3BhdGNoKHthcHBJZCwgZXZlbnQ6IHF1ZXVlZEV2ZW50LCB0eXBlOiBBV1NfUlVNX0NPTlNUQU5UUy5UUkFDS19RVUVVRUR9KTtcbiAgICBzY2hlZHVsZUZsdXNoKCk7XG4gIH07XG5cbiAgY29uc3QgZGVzdHJveSA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICBpZih0aW1lcikge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVyKTtcbiAgICAgIHRpbWVyID0gdW5kZWZpbmVkO1xuICAgIH1cblxuICAgIGF3YWl0IGZsdXNoKCk7XG4gICAgc2VlbkV2ZW50cy5jbGVhcigpO1xuICB9O1xuXG4gIHJldHVybiB7ZGVzdHJveSwgZmx1c2gsIHRyYWNrfTtcbn07XG4iXSwibmFtZXMiOlsicnVtTXV0YXRpb24iLCJBV1NfUlVNX0NPTlNUQU5UUyIsIlRSQUNLX0VSUk9SIiwiVFJBQ0tfUVVFVUVEIiwiVFJBQ0tfU1VDQ0VTUyIsIkRFRkFVTFRfREVCT1VOQ0VfTVMiLCJERUZBVUxUX0RFRFVQRV9NUyIsIkRFRkFVTFRfVEhST1RUTEVfTVMiLCJNQVhfQkFUQ0hfU0laRSIsIk1BWF9QUk9QRVJUSUVTIiwiTUFYX1NFRU5fRVZFTlRTIiwiQkxPQ0tFRF9QUk9QRVJUWV9LRVlTIiwiU2V0IiwiRU1BSUxfUEFUVEVSTiIsImNyZWF0ZUlkIiwiZ2xvYmFsVGhpcyIsImNyeXB0byIsInJhbmRvbVVVSUQiLCJEYXRlIiwibm93IiwidG9TdHJpbmciLCJNYXRoIiwicmFuZG9tIiwic2xpY2UiLCJub3JtYWxpemVUZXh0IiwidmFsdWUiLCJtYXhMZW5ndGgiLCJTdHJpbmciLCJ0cmltIiwibm9ybWFsaXplUHJvcGVydHlLZXkiLCJ0b0xvd2VyQ2FzZSIsInJlcGxhY2UiLCJzYW5pdGl6ZVByb3BlcnRpZXMiLCJwcm9wZXJ0aWVzIiwiT2JqZWN0IiwiZW50cmllcyIsInJlZHVjZSIsInJlc3VsdCIsImtleSIsInByb3BlcnR5S2V5Iiwibm9ybWFsaXplZFByb3BlcnR5S2V5IiwiaGFzIiwiaW5jbHVkZXMiLCJ0ZXh0VmFsdWUiLCJ0ZXN0IiwiTnVtYmVyIiwiaXNGaW5pdGUiLCJzYW5pdGl6ZUV2ZW50IiwiZXZlbnQiLCJuYW1lIiwidHlwZSIsInBhdGgiLCJzcGxpdCIsImV2ZW50RmluZ2VycHJpbnQiLCJzb3J0IiwibGVmdCIsInJpZ2h0IiwibG9jYWxlQ29tcGFyZSIsIkpTT04iLCJzdHJpbmdpZnkiLCJjcmVhdGVBd3NSdW1BY3Rpb25zIiwiZmx1eCIsIm9wdGlvbnMiLCJhcHBJZCIsImRlYm91bmNlTXMiLCJtYXgiLCJkZWR1cGVNcyIsImVuYWJsZWQiLCJyZXNwZWN0UHJpdmFjeVNpZ25hbHMiLCJ0aHJvdHRsZU1zIiwiam91cm5leUlkIiwicGVuZGluZ0V2ZW50cyIsIk1hcCIsInNlZW5FdmVudHMiLCJmbHVzaFByb21pc2UiLCJsYXN0Rmx1c2hBdCIsInNlcXVlbmNlIiwidGltZXIiLCJoYXNQcml2YWN5U2lnbmFsIiwibmF2aWdhdG9yIiwibmF2aWdhdG9yU3RhdGUiLCJkb05vdFRyYWNrIiwiZ2xvYmFsUHJpdmFjeUNvbnRyb2wiLCJwcnVuZVNlZW5FdmVudHMiLCJzaXplIiwiY3V0b2ZmIiwiZm9yRWFjaCIsInRpbWVzdGFtcCIsImZpbmdlcnByaW50IiwiZGVsZXRlIiwib2xkZXN0RmluZ2VycHJpbnQiLCJrZXlzIiwibmV4dCIsImZsdXNoIiwiY2xlYXJUaW1lb3V0IiwidW5kZWZpbmVkIiwiZXZlbnRzIiwidmFsdWVzIiwiY2xlYXIiLCJpbmRleCIsImxlbmd0aCIsImJhdGNoRXZlbnRzIiwiYmF0Y2giLCJkaXNwYXRjaCIsImVycm9yIiwic2V0IiwiZmluYWxseSIsInNjaGVkdWxlRmx1c2giLCJ0aHJvdHRsZVJlbWFpbmluZyIsInNldFRpbWVvdXQiLCJ0cmFjayIsImlucHV0IiwibGFzdFNl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
227
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9Vc2Vycy9uaXRyb2c3L0RldmVsb3BtZW50L21ldHJvcG9saXNqcy9zcmMvYWN0aW9ucy9hd3NSdW1BY3Rpb25zL2F3c1J1bUFjdGlvbnMudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDb3B5cmlnaHQgKGMpIDIwMjYtUHJlc2VudCwgTml0cm9nZW4gTGFicywgSW5jLlxuICogQ29weXJpZ2h0cyBsaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSB0aGUgYWNjb21wYW55aW5nIExJQ0VOU0UgZmlsZSBmb3IgdGVybXMuXG4gKi9cbmltcG9ydCB7cnVtUmVxdWVzdH0gZnJvbSAnLi4vLi4vdXRpbHMvYXBpLmpzJztcblxuaW1wb3J0IHR5cGUge0ZsdXhGcmFtZXdvcmt9IGZyb20gJ0BubGFicy9hcmtoYW1qcyc7XG5cbmV4cG9ydCBjb25zdCBBV1NfUlVNX0NPTlNUQU5UUyA9IHtcbiAgVFJBQ0tfRVJST1I6ICdBV1NfUlVNX1RSQUNLX0VSUk9SJyxcbiAgVFJBQ0tfUVVFVUVEOiAnQVdTX1JVTV9UUkFDS19RVUVVRUQnLFxuICBUUkFDS19TVUNDRVNTOiAnQVdTX1JVTV9UUkFDS19TVUNDRVNTJ1xufSBhcyBjb25zdDtcblxuZXhwb3J0IHR5cGUgQXdzUnVtRXZlbnRUeXBlID0gJ2NsaWNrJyB8ICdwYWdlX3ZpZXcnIHwgKHN0cmluZyAmIHt9KTtcbmV4cG9ydCB0eXBlIEF3c1J1bVByb3BlcnR5ID0gYm9vbGVhbiB8IG51bWJlciB8IHN0cmluZztcblxuZXhwb3J0IGludGVyZmFjZSBBd3NSdW1UcmFja0lucHV0IHtcbiAgcmVhZG9ubHkgbmFtZTogc3RyaW5nO1xuICByZWFkb25seSBwYXRoPzogc3RyaW5nO1xuICByZWFkb25seSBwcm9wZXJ0aWVzPzogUmVjb3JkPHN0cmluZywgQXdzUnVtUHJvcGVydHk+O1xuICByZWFkb25seSB0eXBlOiBBd3NSdW1FdmVudFR5cGU7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQXdzUnVtRXZlbnQgZXh0ZW5kcyBBd3NSdW1UcmFja0lucHV0IHtcbiAgcmVhZG9ubHkgZXZlbnRJZDogc3RyaW5nO1xuICByZWFkb25seSBqb3VybmV5SWQ6IHN0cmluZztcbiAgcmVhZG9ubHkgc2VxdWVuY2U6IG51bWJlcjtcbiAgcmVhZG9ubHkgdGltZXN0YW1wOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQXdzUnVtQWN0aW9uc09wdGlvbnMge1xuICByZWFkb25seSBhbmFseXRpY3NJZD86IHN0cmluZztcbiAgcmVhZG9ubHkgZGVib3VuY2VNcz86IG51bWJlcjtcbiAgcmVhZG9ubHkgZGVkdXBlTXM/OiBudW1iZXI7XG4gIHJlYWRvbmx5IGVuYWJsZWQ/OiBib29sZWFuO1xuICByZWFkb25seSByZXNwZWN0UHJpdmFjeVNpZ25hbHM/OiBib29sZWFuO1xuICByZWFkb25seSB0aHJvdHRsZU1zPzogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEF3c1J1bUFjdGlvbnMge1xuICByZWFkb25seSBkZXN0cm95OiAoKSA9PiBQcm9taXNlPHZvaWQ+O1xuICByZWFkb25seSBmbHVzaDogKCkgPT4gUHJvbWlzZTx2b2lkPjtcbiAgcmVhZG9ubHkgdHJhY2s6IChldmVudDogQXdzUnVtVHJhY2tJbnB1dCkgPT4gdm9pZDtcbn1cblxuY29uc3QgREVGQVVMVF9ERUJPVU5DRV9NUyA9IDI1MDtcbmNvbnN0IERFRkFVTFRfREVEVVBFX01TID0gMTAwMDtcbmNvbnN0IERFRkFVTFRfVEhST1RUTEVfTVMgPSAxMDAwO1xuY29uc3QgTUFYX0JBVENIX1NJWkUgPSA1MDtcbmNvbnN0IE1BWF9QUk9QRVJUSUVTID0gMjA7XG5jb25zdCBNQVhfU0VFTl9FVkVOVFMgPSA1MDA7XG5jb25zdCBCTE9DS0VEX1BST1BFUlRZX0tFWVMgPSBuZXcgU2V0KFtcbiAgJ2FjY291bnRpZCcsXG4gICdhZGRyZXNzJyxcbiAgJ2VtYWlsJyxcbiAgJ2ZpcnN0bmFtZScsXG4gICdmdWxsbmFtZScsXG4gICdnZW9sb2NhdGlvbicsXG4gICdpcCcsXG4gICdpcGFkZHJlc3MnLFxuICAnbGFzdG5hbWUnLFxuICAnbGF0aXR1ZGUnLFxuICAnbG9uZ2l0dWRlJyxcbiAgJ25hbWUnLFxuICAncGhvbmUnLFxuICAncmVmZXJyZXInLFxuICAndG9rZW4nLFxuICAndXNlcmFnZW50JyxcbiAgJ3VzZXJpZCdcbl0pO1xuY29uc3QgRU1BSUxfUEFUVEVSTiA9IC9cXGJbXlxcc0BdK0BbXlxcc0BdK1xcLlteXFxzQF0rXFxiL2k7XG5cbmludGVyZmFjZSBQcml2YWN5TmF2aWdhdG9yIGV4dGVuZHMgTmF2aWdhdG9yIHtcbiAgcmVhZG9ubHkgZ2xvYmFsUHJpdmFjeUNvbnRyb2w/OiBib29sZWFuO1xufVxuXG5jb25zdCBjcmVhdGVJZCA9ICgpOiBzdHJpbmcgPT4ge1xuICBpZih0eXBlb2YgZ2xvYmFsVGhpcy5jcnlwdG8/LnJhbmRvbVVVSUQgPT09ICdmdW5jdGlvbicpIHtcbiAgICByZXR1cm4gZ2xvYmFsVGhpcy5jcnlwdG8ucmFuZG9tVVVJRCgpO1xuICB9XG5cbiAgcmV0dXJuIGAke0RhdGUubm93KCkudG9TdHJpbmcoMzYpfS0ke01hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnNsaWNlKDIpfWA7XG59O1xuXG5jb25zdCBub3JtYWxpemVUZXh0ID0gKHZhbHVlOiB1bmtub3duLCBtYXhMZW5ndGg6IG51bWJlcik6IHN0cmluZyA9PlxuICBTdHJpbmcodmFsdWUgfHwgJycpLnRyaW0oKS5zbGljZSgwLCBtYXhMZW5ndGgpO1xuXG5jb25zdCBub3JtYWxpemVQcm9wZXJ0eUtleSA9ICh2YWx1ZTogc3RyaW5nKTogc3RyaW5nID0+XG4gIHZhbHVlLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgvW15hLXowLTldL2csICcnKTtcblxuY29uc3Qgc2FuaXRpemVQcm9wZXJ0aWVzID0gKFxuICBwcm9wZXJ0aWVzOiBSZWNvcmQ8c3RyaW5nLCBBd3NSdW1Qcm9wZXJ0eT4gPSB7fVxuKTogUmVjb3JkPHN0cmluZywgQXdzUnVtUHJvcGVydHk+ID0+IE9iamVjdC5lbnRyaWVzKHByb3BlcnRpZXMpXG4gIC5zbGljZSgwLCBNQVhfUFJPUEVSVElFUylcbiAgLnJlZHVjZTxSZWNvcmQ8c3RyaW5nLCBBd3NSdW1Qcm9wZXJ0eT4+KChyZXN1bHQsIFtrZXksIHZhbHVlXSkgPT4ge1xuICAgIGNvbnN0IHByb3BlcnR5S2V5ID0gbm9ybWFsaXplVGV4dChrZXksIDY0KTtcbiAgICBjb25zdCBub3JtYWxpemVkUHJvcGVydHlLZXkgPSBub3JtYWxpemVQcm9wZXJ0eUtleShwcm9wZXJ0eUtleSk7XG5cbiAgICBpZihcbiAgICAgICFwcm9wZXJ0eUtleSB8fFxuICAgICAgQkxPQ0tFRF9QUk9QRVJUWV9LRVlTLmhhcyhub3JtYWxpemVkUHJvcGVydHlLZXkpIHx8XG4gICAgICAhWydib29sZWFuJywgJ251bWJlcicsICdzdHJpbmcnXS5pbmNsdWRlcyh0eXBlb2YgdmFsdWUpXG4gICAgKSB7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cblxuICAgIGlmKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgIGNvbnN0IHRleHRWYWx1ZSA9IG5vcm1hbGl6ZVRleHQodmFsdWUsIDI1Nik7XG5cbiAgICAgIGlmKHRleHRWYWx1ZSAmJiAhRU1BSUxfUEFUVEVSTi50ZXN0KHRleHRWYWx1ZSkpIHtcbiAgICAgICAgcmVzdWx0W3Byb3BlcnR5S2V5XSA9IHRleHRWYWx1ZTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYodHlwZW9mIHZhbHVlID09PSAnYm9vbGVhbicgfHwgTnVtYmVyLmlzRmluaXRlKHZhbHVlKSkge1xuICAgICAgcmVzdWx0W3Byb3BlcnR5S2V5XSA9IHZhbHVlO1xuICAgIH1cblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH0sIHt9KTtcblxuY29uc3Qgc2FuaXRpemVFdmVudCA9IChldmVudDogQXdzUnVtVHJhY2tJbnB1dCk6IEF3c1J1bVRyYWNrSW5wdXQgfCBudWxsID0+IHtcbiAgY29uc3QgbmFtZSA9IG5vcm1hbGl6ZVRleHQoZXZlbnQubmFtZSwgMTI4KTtcbiAgY29uc3QgdHlwZSA9IG5vcm1hbGl6ZVRleHQoZXZlbnQudHlwZSwgNjQpO1xuXG4gIGlmKCFuYW1lIHx8ICF0eXBlKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICBjb25zdCBwYXRoID0gbm9ybWFsaXplVGV4dChldmVudC5wYXRoLCAyNTYpLnNwbGl0KCc/JylbMF0uc3BsaXQoJyMnKVswXTtcblxuICByZXR1cm4ge1xuICAgIG5hbWUsXG4gICAgLi4uKHBhdGggPyB7cGF0aH0gOiB7fSksXG4gICAgcHJvcGVydGllczogc2FuaXRpemVQcm9wZXJ0aWVzKGV2ZW50LnByb3BlcnRpZXMpLFxuICAgIHR5cGVcbiAgfTtcbn07XG5cbmNvbnN0IGV2ZW50RmluZ2VycHJpbnQgPSAoZXZlbnQ6IEF3c1J1bVRyYWNrSW5wdXQpOiBzdHJpbmcgPT4ge1xuICBjb25zdCBwcm9wZXJ0aWVzID0gT2JqZWN0LmVudHJpZXMoZXZlbnQucHJvcGVydGllcyB8fCB7fSkuc29ydCgoW2xlZnRdLCBbcmlnaHRdKSA9PiBsZWZ0LmxvY2FsZUNvbXBhcmUocmlnaHQpKTtcbiAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KFtldmVudC50eXBlLCBldmVudC5uYW1lLCBldmVudC5wYXRoIHx8ICcnLCBwcm9wZXJ0aWVzXSk7XG59O1xuXG5leHBvcnQgY29uc3QgY3JlYXRlQXdzUnVtQWN0aW9ucyA9IChcbiAgZmx1eDogRmx1eEZyYW1ld29yayxcbiAgb3B0aW9uczogQXdzUnVtQWN0aW9uc09wdGlvbnMgPSB7fVxuKTogQXdzUnVtQWN0aW9ucyA9PiB7XG4gIGNvbnN0IGFuYWx5dGljc0lkID0gbm9ybWFsaXplVGV4dChvcHRpb25zLmFuYWx5dGljc0lkLCAxMjgpO1xuICBjb25zdCBkZWJvdW5jZU1zID0gTWF0aC5tYXgoMCwgb3B0aW9ucy5kZWJvdW5jZU1zID8/IERFRkFVTFRfREVCT1VOQ0VfTVMpO1xuICBjb25zdCBkZWR1cGVNcyA9IE1hdGgubWF4KDAsIG9wdGlvbnMuZGVkdXBlTXMgPz8gREVGQVVMVF9ERURVUEVfTVMpO1xuICBjb25zdCBlbmFibGVkID0gb3B0aW9ucy5lbmFibGVkICE9PSBmYWxzZTtcbiAgY29uc3QgcmVzcGVjdFByaXZhY3lTaWduYWxzID0gb3B0aW9ucy5yZXNwZWN0UHJpdmFjeVNpZ25hbHMgIT09IGZhbHNlO1xuICBjb25zdCB0aHJvdHRsZU1zID0gTWF0aC5tYXgoMCwgb3B0aW9ucy50aHJvdHRsZU1zID8/IERFRkFVTFRfVEhST1RUTEVfTVMpO1xuICBjb25zdCBqb3VybmV5SWQgPSBjcmVhdGVJZCgpO1xuICBjb25zdCBwZW5kaW5nRXZlbnRzID0gbmV3IE1hcDxzdHJpbmcsIEF3c1J1bUV2ZW50PigpO1xuICBjb25zdCBzZWVuRXZlbnRzID0gbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTtcbiAgbGV0IGZsdXNoUHJvbWlzZTogUHJvbWlzZTx2b2lkPiB8IHVuZGVmaW5lZDtcbiAgbGV0IGxhc3RGbHVzaEF0ID0gMDtcbiAgbGV0IHNlcXVlbmNlID0gMDtcbiAgbGV0IHRpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PiB8IHVuZGVmaW5lZDtcblxuICBjb25zdCBoYXNQcml2YWN5U2lnbmFsID0gKCk6IGJvb2xlYW4gPT4ge1xuICAgIGlmKCFyZXNwZWN0UHJpdmFjeVNpZ25hbHMgfHwgdHlwZW9mIGdsb2JhbFRoaXMubmF2aWdhdG9yID09PSAndW5kZWZpbmVkJykge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIGNvbnN0IG5hdmlnYXRvclN0YXRlID0gZ2xvYmFsVGhpcy5uYXZpZ2F0b3IgYXMgUHJpdmFjeU5hdmlnYXRvcjtcblxuICAgIHJldHVybiBuYXZpZ2F0b3JTdGF0ZS5kb05vdFRyYWNrID09PSAnMScgfHwgbmF2aWdhdG9yU3RhdGUuZ2xvYmFsUHJpdmFjeUNvbnRyb2wgPT09IHRydWU7XG4gIH07XG5cbiAgY29uc3QgcHJ1bmVTZWVuRXZlbnRzID0gKCk6IHZvaWQgPT4ge1xuICAgIGlmKHNlZW5FdmVudHMuc2l6ZSA8PSBNQVhfU0VFTl9FVkVOVFMpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBjdXRvZmYgPSBEYXRlLm5vdygpIC0gZGVkdXBlTXM7XG4gICAgc2VlbkV2ZW50cy5mb3JFYWNoKCh0aW1lc3RhbXAsIGZpbmdlcnByaW50KSA9PiB7XG4gICAgICBpZih0aW1lc3RhbXAgPCBjdXRvZmYpIHtcbiAgICAgICAgc2VlbkV2ZW50cy5kZWxldGUoZmluZ2VycHJpbnQpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgd2hpbGUoc2VlbkV2ZW50cy5zaXplID4gTUFYX1NFRU5fRVZFTlRTKSB7XG4gICAgICBjb25zdCBvbGRlc3RGaW5nZXJwcmludCA9IHNlZW5FdmVudHMua2V5cygpLm5leHQoKS52YWx1ZTtcblxuICAgICAgaWYoIW9sZGVzdEZpbmdlcnByaW50KSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuXG4gICAgICBzZWVuRXZlbnRzLmRlbGV0ZShvbGRlc3RGaW5nZXJwcmludCk7XG4gICAgfVxuICB9O1xuXG4gIGNvbnN0IGZsdXNoID0gYXN5bmMgKCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgIGlmKGZsdXNoUHJvbWlzZSkge1xuICAgICAgcmV0dXJuIGZsdXNoUHJvbWlzZTtcbiAgICB9XG5cbiAgICBpZighZW5hYmxlZCB8fCAhYW5hbHl0aWNzSWQgfHwgaGFzUHJpdmFjeVNpZ25hbCgpIHx8IHBlbmRpbmdFdmVudHMuc2l6ZSA9PT0gMCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmKHRpbWVyKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXIpO1xuICAgICAgdGltZXIgPSB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgY29uc3QgZXZlbnRzID0gWy4uLnBlbmRpbmdFdmVudHMudmFsdWVzKCldO1xuICAgIHBlbmRpbmdFdmVudHMuY2xlYXIoKTtcbiAgICBsYXN0Rmx1c2hBdCA9IERhdGUubm93KCk7XG5cbiAgICBmbHVzaFByb21pc2UgPSAoYXN5bmMgKCkgPT4ge1xuICAgICAgZm9yKGxldCBpbmRleCA9IDA7IGluZGV4IDwgZXZlbnRzLmxlbmd0aDsgaW5kZXggKz0gTUFYX0JBVENIX1NJWkUpIHtcbiAgICAgICAgY29uc3QgYmF0Y2hFdmVudHMgPSBldmVudHMuc2xpY2UoaW5kZXgsIGluZGV4ICsgTUFYX0JBVENIX1NJWkUpO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgYXdhaXQgcnVtUmVxdWVzdChmbHV4LCB7YW5hbHl0aWNzSWQsIGV2ZW50czogYmF0Y2hFdmVudHN9KTtcbiAgICAgICAgICBhd2FpdCBmbHV4LmRpc3BhdGNoKHthbmFseXRpY3NJZCwgZXZlbnRzOiBiYXRjaEV2ZW50cywgdHlwZTogQVdTX1JVTV9DT05TVEFOVFMuVFJBQ0tfU1VDQ0VTU30pO1xuICAgICAgICB9IGNhdGNoKGVycm9yKSB7XG4gICAgICAgICAgZXZlbnRzLnNsaWNlKGluZGV4KS5mb3JFYWNoKChldmVudCkgPT4gcGVuZGluZ0V2ZW50cy5zZXQoZXZlbnRGaW5nZXJwcmludChldmVudCksIGV2ZW50KSk7XG4gICAgICAgICAgYXdhaXQgZmx1eC5kaXNwYXRjaCh7YW5hbHl0aWNzSWQsIGVycm9yLCBldmVudHM6IGJhdGNoRXZlbnRzLCB0eXBlOiBBV1NfUlVNX0NPTlNUQU5UUy5UUkFDS19FUlJPUn0pO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pKClcbiAgICAgIC5maW5hbGx5KCgpID0+IHtcbiAgICAgICAgZmx1c2hQcm9taXNlID0gdW5kZWZpbmVkO1xuICAgICAgfSk7XG5cbiAgICByZXR1cm4gZmx1c2hQcm9taXNlO1xuICB9O1xuXG4gIGNvbnN0IHNjaGVkdWxlRmx1c2ggPSAoKTogdm9pZCA9PiB7XG4gICAgaWYodGltZXIpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG4gICAgfVxuXG4gICAgY29uc3QgdGhyb3R0bGVSZW1haW5pbmcgPSBNYXRoLm1heCgwLCB0aHJvdHRsZU1zIC0gKERhdGUubm93KCkgLSBsYXN0Rmx1c2hBdCkpO1xuICAgIHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB2b2lkIGZsdXNoKCksIE1hdGgubWF4KGRlYm91bmNlTXMsIHRocm90dGxlUmVtYWluaW5nKSk7XG4gIH07XG5cbiAgY29uc3QgdHJhY2sgPSAoaW5wdXQ6IEF3c1J1bVRyYWNrSW5wdXQpOiB2b2lkID0+IHtcbiAgICBpZighZW5hYmxlZCB8fCAhYW5hbHl0aWNzSWQgfHwgaGFzUHJpdmFjeVNpZ25hbCgpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgZXZlbnQgPSBzYW5pdGl6ZUV2ZW50KGlucHV0KTtcblxuICAgIGlmKCFldmVudCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IGZpbmdlcnByaW50ID0gZXZlbnRGaW5nZXJwcmludChldmVudCk7XG4gICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbiAgICBjb25zdCBsYXN0U2VlbkF0ID0gc2VlbkV2ZW50cy5nZXQoZmluZ2VycHJpbnQpIHx8IDA7XG5cbiAgICBpZihzZWVuRXZlbnRzLmhhcyhmaW5nZXJwcmludCkgJiYgbm93IC0gbGFzdFNlZW5BdCA8IGRlZHVwZU1zKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgc2VlbkV2ZW50cy5zZXQoZmluZ2VycHJpbnQsIG5vdyk7XG4gICAgcHJ1bmVTZWVuRXZlbnRzKCk7XG4gICAgc2VxdWVuY2UgKz0gMTtcblxuICAgIGNvbnN0IHF1ZXVlZEV2ZW50OiBBd3NSdW1FdmVudCA9IHtcbiAgICAgIC4uLmV2ZW50LFxuICAgICAgZXZlbnRJZDogY3JlYXRlSWQoKSxcbiAgICAgIGpvdXJuZXlJZCxcbiAgICAgIHNlcXVlbmNlLFxuICAgICAgdGltZXN0YW1wOiBub3dcbiAgICB9O1xuXG4gICAgcGVuZGluZ0V2ZW50cy5zZXQoZmluZ2VycHJpbnQsIHF1ZXVlZEV2ZW50KTtcbiAgICB2b2lkIGZsdXguZGlzcGF0Y2goe2FuYWx5dGljc0lkLCBldmVudDogcXVldWVkRXZlbnQsIHR5cGU6IEFXU19SVU1fQ09OU1RBTlRTLlRSQUNLX1FVRVVFRH0pO1xuICAgIHNjaGVkdWxlRmx1c2goKTtcbiAgfTtcblxuICBjb25zdCBkZXN0cm95ID0gYXN5bmMgKCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgIGlmKHRpbWVyKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXIpO1xuICAgICAgdGltZXIgPSB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgYXdhaXQgZmx1c2goKTtcbiAgICBzZWVuRXZlbnRzLmNsZWFyKCk7XG4gIH07XG5cbiAgcmV0dXJuIHtkZXN0cm95LCBmbHVzaCwgdHJhY2t9O1xufTtcbiJdLCJuYW1lcyI6WyJydW1SZXF1ZXN0IiwiQVdTX1JVTV9DT05TVEFOVFMiLCJUUkFDS19FUlJPUiIsIlRSQUNLX1FVRVVFRCIsIlRSQUNLX1NVQ0NFU1MiLCJERUZBVUxUX0RFQk9VTkNFX01TIiwiREVGQVVMVF9ERURVUEVfTVMiLCJERUZBVUxUX1RIUk9UVExFX01TIiwiTUFYX0JBVENIX1NJWkUiLCJNQVhfUFJPUEVSVElFUyIsIk1BWF9TRUVOX0VWRU5UUyIsIkJMT0NLRURfUFJPUEVSVFlfS0VZUyIsIlNldCIsIkVNQUlMX1BBVFRFUk4iLCJjcmVhdGVJZCIsImdsb2JhbFRoaXMiLCJjcnlwdG8iLCJyYW5kb21VVUlEIiwiRGF0ZSIsIm5vdyIsInRvU3RyaW5nIiwiTWF0aCIsInJhbmRvbSIsInNsaWNlIiwibm9ybWFsaXplVGV4dCIsInZhbHVlIiwibWF4TGVuZ3RoIiwiU3RyaW5nIiwidHJpbSIsIm5vcm1hbGl6ZVByb3BlcnR5S2V5IiwidG9Mb3dlckNhc2UiLCJyZXBsYWNlIiwic2FuaXRpemVQcm9wZXJ0aWVzIiwicHJvcGVydGllcyIsIk9iamVjdCIsImVudHJpZXMiLCJyZWR1Y2UiLCJyZXN1bHQiLCJrZXkiLCJwcm9wZXJ0eUtleSIsIm5vcm1hbGl6ZWRQcm9wZXJ0eUtleSIsImhhcyIsImluY2x1ZGVzIiwidGV4dFZhbHVlIiwidGVzdCIsIk51bWJlciIsImlzRmluaXRlIiwic2FuaXRpemVFdmVudCIsImV2ZW50IiwibmFtZSIsInR5cGUiLCJwYXRoIiwic3BsaXQiLCJldmVudEZpbmdlcnByaW50Iiwic29ydCIsImxlZnQiLCJyaWdodCIsImxvY2FsZUNvbXBhcmUiLCJKU09OIiwic3RyaW5naWZ5IiwiY3JlYXRlQXdzUnVtQWN0aW9ucyIsImZsdXgiLCJvcHRpb25zIiwiYW5hbHl0aWNzSWQiLCJkZWJvdW5jZU1zIiwibWF4IiwiZGVkdXBlTXMiLCJlbmFibGVkIiwicmVzcGVjdFByaXZhY3lTaWduYWxzIiwidGhyb3R0bGVNcyIsImpvdXJuZXlJZCIsInBlbmRpbmdFdmVudHMiLCJNYXAiLCJzZWVuRXZlbnRzIiwiZmx1c2hQcm9taXNlIiwibGFzdEZsdXNoQXQiLCJzZXF1ZW5jZSIsInRpbWVyIiwiaGFzUHJpdmFjeVNpZ25hbCIsIm5hdmlnYXRvciIsIm5hdmlnYXRvclN0YXRlIiwiZG9Ob3RUcmFjayIsImdsb2JhbFByaXZhY3lDb250cm9sIiwicHJ1bmVTZWVuRXZlbnRzIiwic2l6ZSIsImN1dG9mZiIsImZvckVhY2giLCJ0aW1lc3RhbXAiLCJmaW5nZXJwcmludCIsImRlbGV0ZSIsIm9sZGVzdEZpbmdlcnByaW50Iiwia2V5cyIsIm5leHQiLCJmbHVzaCIsImNsZWFyVGltZW91dCIsInVuZGVmaW5lZCIsImV2ZW50cyIsInZhbHVlcyIsImNsZWFyIiwiaW5kZXgiLCJsZW5ndGgiLCJiYXRjaEV2ZW50cyIsImRpc3BhdGNoIiwiZXJyb3IiLCJzZXQiLCJmaW5hbGx5Iiwic2NoZWR1bGVGbHVzaCIsInRocm90dGxlUmVtYWluaW5nIiwic2V0VGltZW91dCIsInRyYWNrIiwiaW5wdXQiLCJsYXN0U2VlbkF0IiwiZ2V0IiwicXVldWVkRXZlbnQiLCJldmVudElkIiwiZGVzdHJveSJdLCJtYXBwaW5ncyI6IkFBQUE7OztDQUdDLEdBQ0QsU0FBUUEsVUFBVSxRQUFPLHFCQUFxQjtBQUk5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