@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 +6 -109
- package/README.md +45 -44
- package/docs/assets/metropolisjs-architecture.png +0 -0
- package/docs/assets/metropolisjs-architecture.svg +58 -0
- package/docs/assets/metropolisjs-request-lifecycle.svg +42 -0
- package/examples/content-actions-usage.ts +1 -2
- package/examples/factory-pattern-usage.ts +0 -18
- package/lib/actions/awsRumActions/awsRumActions.d.ts +1 -1
- package/lib/actions/awsRumActions/awsRumActions.js +12 -20
- package/lib/actions/userActions/userActions.d.ts +0 -2
- package/lib/actions/userActions/userActions.js +2 -7
- package/lib/adapters/tagAdapter/tagAdapter.d.ts +0 -1
- package/lib/adapters/tagAdapter/tagAdapter.js +1 -2
- package/lib/adapters/userAdapter/userAdapter.d.ts +0 -1
- package/lib/adapters/userAdapter/userAdapter.js +1 -2
- package/lib/config/index.d.ts +2 -2
- package/lib/config/index.js +1 -1
- package/lib/index.d.ts +4 -4
- package/lib/index.js +5 -5
- package/lib/utils/{MetropolisProvider.d.ts → MetropolisContext.d.ts} +3 -14
- package/lib/utils/MetropolisContext.js +7 -0
- package/lib/utils/actionFactory.js +2 -2
- package/lib/utils/api.d.ts +1 -0
- package/lib/utils/api.js +10 -2
- package/lib/utils/index.d.ts +2 -2
- package/lib/utils/index.js +3 -3
- package/lib/utils/useMetropolis.d.ts +2 -6
- package/lib/utils/useMetropolis.js +16 -30
- package/lib/utils/useTranslations.js +4 -2
- package/package.json +5 -2
- package/lib/utils/MetropolisProvider.js +0 -31
package/CHANGELOG.md
CHANGED
|
@@ -1,112 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 1.0.3
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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:
|
|
105
|
-
const {
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
520
|
-
const {
|
|
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
|
|
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
|
-
###
|
|
1081
|
+
### Permission Levels
|
|
1081
1082
|
|
|
1082
|
-
The
|
|
1083
|
+
The `userAccess` field uses the numeric `PermissionLevel` values from 0 through 4.
|
|
1083
1084
|
|
|
1084
1085
|
### Available Permission Actions
|
|
1085
1086
|
|
|
Binary file
|
|
@@ -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
|
|
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 {
|
|
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
|
|
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 || !
|
|
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
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 || !
|
|
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
|
-
|
|
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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
|