tas-uell-sdk 0.0.5 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +161 -51
  2. package/esm2020/lib/components/tas-avatar/tas-avatar.component.mjs +75 -0
  3. package/esm2020/lib/components/tas-btn/tas-btn.component.mjs +146 -61
  4. package/esm2020/lib/components/tas-floating-call/tas-floating-call.component.mjs +48 -23
  5. package/esm2020/lib/components/tas-incoming-appointment/tas-incoming-appointment.component.mjs +109 -0
  6. package/esm2020/lib/components/tas-videocall/tas-videocall.component.mjs +217 -20
  7. package/esm2020/lib/components/tas-waiting-room/tas-waiting-room.component.mjs +226 -160
  8. package/esm2020/lib/config/tas.config.mjs +1 -1
  9. package/esm2020/lib/interfaces/tas.interfaces.mjs +45 -2
  10. package/esm2020/lib/services/geolocation.service.mjs +56 -0
  11. package/esm2020/lib/services/tas.service.mjs +400 -34
  12. package/esm2020/lib/tas-uell-sdk.module.mjs +25 -21
  13. package/esm2020/public-api.mjs +4 -1
  14. package/fesm2015/tas-uell-sdk.mjs +1323 -302
  15. package/fesm2015/tas-uell-sdk.mjs.map +1 -1
  16. package/fesm2020/tas-uell-sdk.mjs +1307 -300
  17. package/fesm2020/tas-uell-sdk.mjs.map +1 -1
  18. package/lib/components/tas-avatar/tas-avatar.component.d.ts +9 -0
  19. package/lib/components/tas-btn/tas-btn.component.d.ts +35 -15
  20. package/lib/components/tas-floating-call/tas-floating-call.component.d.ts +5 -1
  21. package/lib/components/tas-incoming-appointment/tas-incoming-appointment.component.d.ts +33 -0
  22. package/lib/components/tas-videocall/tas-videocall.component.d.ts +49 -3
  23. package/lib/components/tas-waiting-room/tas-waiting-room.component.d.ts +50 -35
  24. package/lib/config/tas.config.d.ts +7 -0
  25. package/lib/interfaces/tas.interfaces.d.ts +127 -35
  26. package/lib/services/geolocation.service.d.ts +24 -0
  27. package/lib/services/tas.service.d.ts +98 -9
  28. package/lib/tas-uell-sdk.module.d.ts +6 -3
  29. package/package.json +1 -1
  30. package/public-api.d.ts +3 -0
  31. package/src/lib/styles/tas-global.scss +27 -28
  32. package/INSTALL_AND_TEST.md +0 -427
package/README.md CHANGED
@@ -29,7 +29,7 @@ npm install @ng-bootstrap/ng-bootstrap @opentok/client interactjs
29
29
 
30
30
  ### 1. Create an HTTP Adapter
31
31
 
32
- The library requires an HTTP adapter that implements `TasHttpClient` interface:
32
+ The library requires an HTTP adapter that implements the `TasHttpClient` interface:
33
33
 
34
34
  ```typescript
35
35
  import { Injectable } from '@angular/core';
@@ -41,9 +41,21 @@ import { TasHttpClient } from 'tas-uell-sdk';
41
41
  export class TasHttpAdapterService implements TasHttpClient {
42
42
  constructor(private http: HttpClient) {}
43
43
 
44
+ get<T>(url: string, options: { headers?: Record<string, string> }): Observable<T> {
45
+ return this.http.get<T>(`https://your-api.com/${url}`, {
46
+ headers: options.headers,
47
+ });
48
+ }
49
+
44
50
  post<T>(url: string, options: { body: any; headers?: Record<string, string> }): Observable<T> {
45
- return this.http.post<T>(`https://your-api.com/${url}`, options.body, {
46
- headers: options.headers
51
+ return this.http.post<T>(`https://your-api.com/${url}`, options.body, {
52
+ headers: options.headers,
53
+ });
54
+ }
55
+
56
+ patch<T>(url: string, options: { body: any; headers?: Record<string, string> }): Observable<T> {
57
+ return this.http.patch<T>(`https://your-api.com/${url}`, options.body, {
58
+ headers: options.headers,
47
59
  });
48
60
  }
49
61
  }
@@ -60,22 +72,26 @@ import { TasHttpAdapterService } from './adapters/tas-http-adapter.service';
60
72
  @NgModule({
61
73
  imports: [
62
74
  TasUellSdkModule.forRoot({
63
- config: {
64
- tokBoxApiKey: 'YOUR_TOKBOX_API_KEY'
75
+ config: {
76
+ tokBoxApiKey: 'YOUR_TOKBOX_API_KEY',
77
+ // apiBaseUrl: 'https://your-api.com/v2', // Optional
65
78
  },
66
- httpClient: TasHttpAdapterService
67
- })
68
- ]
79
+ httpClient: TasHttpAdapterService,
80
+ }),
81
+ ],
69
82
  })
70
- export class AppModule { }
83
+ export class AppModule {}
71
84
  ```
72
85
 
73
86
  ### 3. Add Global Styles
74
87
 
75
- Add the TAS modal styles to your global `styles.scss`:
88
+ Add the TAS modal styles to your global `styles.scss`. You can either import the library's global styles or copy the CSS below:
76
89
 
77
90
  ```scss
78
- /* TAS Video Modal */
91
+ /* Option 1: Import from library */
92
+ @import 'tas-uell-sdk/src/lib/styles/tas-global.scss';
93
+
94
+ /* Option 2: Manual styles */
79
95
  .tas-video-modal {
80
96
  .modal-dialog {
81
97
  max-width: 100vw;
@@ -93,7 +109,6 @@ Add the TAS modal styles to your global `styles.scss`:
93
109
  }
94
110
  }
95
111
 
96
- /* TAS Waiting Room Modal */
97
112
  .tas-waiting-room-modal {
98
113
  .modal-dialog {
99
114
  max-width: 480px;
@@ -122,76 +137,171 @@ Add `<tas-floating-call>` to your root component template (e.g., `app.component.
122
137
 
123
138
  ## Usage
124
139
 
140
+ > **Note:** The appointment/room must be pre-created in your backend before using this library.
141
+
125
142
  ### TAS Button Component
126
143
 
127
144
  Use the `<tas-btn>` component to initiate a video call:
128
145
 
129
146
  ```html
130
147
  <tas-btn
131
- [appointmentId]="appointment.id"
132
- [product]="'your-product'"
133
- [tenantId]="tenantId"
148
+ [roomType]="'TAS'"
149
+ [entityId]="appointment.entityId"
150
+ [tenant]="tenantId"
151
+ [businessRole]="'BACKOFFICE'"
134
152
  [currentUser]="currentUser"
135
- [ownerUserIds]="[ownerId]"
136
- [regularUserIds]="regularUserIds"
137
- [moderatorUserIds]="moderatorUserIds"
138
153
  ></tas-btn>
139
154
  ```
140
155
 
141
156
  ### Input Properties
142
157
 
143
- | Property | Type | Required | Description |
144
- |----------|------|----------|-------------|
145
- | `appointmentId` | `number` | Yes | The appointment/session ID |
146
- | `product` | `string` | Yes | Product identifier |
147
- | `tenantId` | `string` | Yes | Tenant identifier |
148
- | `currentUser` | `TasCurrentUser` | Yes | Current user info (name, lastname, role) |
149
- | `ownerUserIds` | `number[]` | Yes | Array with exactly one owner user ID |
150
- | `regularUserIds` | `number[]` | No | Array of regular participant user IDs |
151
- | `moderatorUserIds` | `number[]` | No | Array of moderator user IDs |
158
+ | Property | Type | Required | Default | Description |
159
+ | :--- | :--- | :--- | :--- | :--- |
160
+ | `entityId` | `number` | Yes | - | The entity/ausencia ID |
161
+ | `tenant` | `string` | Yes | - | Tenant identifier |
162
+ | `roomType` | `TasRoomType` | No | `'TAS'` | Room type (TAS, JM, WEBINAR, WELLNESS_MANAGER) |
163
+ | `businessRole` | `TasBusinessRole` | No | `'USER'` | Role (ADMIN_MANAGER, MANAGER, BACKOFFICE, USER) |
164
+ | `currentUser` | `TasCurrentUser` | Yes | - | Current user info (name, lastname, role) |
165
+
166
+ ### Waiting Room Behavior
167
+
168
+ When clicking the TAS button, a waiting room modal opens:
169
+
170
+ 1. **Status Check**: The library calls `/v2/proxy/video/status` to get session info
171
+ 2. **Token Acquisition**: Calls `/v2/proxy/video/start` to get the video call token
152
172
 
153
- ### TasCurrentUser Interface
173
+ **Owner/Backoffice Users** (`businessRole: 'BACKOFFICE'` or user with `role: 'OWNER'`):
174
+ - Token is obtained immediately after status check
175
+ - Once token is received, the "Join" button appears
176
+ - If `/start` fails, an error screen is shown with a "Retry" button
154
177
 
178
+ **Non-Owner Users** (regular attendees):
179
+ - See "Medicina laboral te va a admitir en unos instantes..."
180
+ - Wait until the backend marks the session as `joinable: true`
181
+ - Once joinable, the token is obtained and the "Join" button appears
182
+
183
+ ### Error Handling
184
+
185
+ The library handles errors gracefully:
186
+ - If `/status` fails: Shows error with retry option
187
+ - If `/start` fails: Shows error message from backend, stops polling, shows retry option
188
+ - Network errors: Displays user-friendly error message
189
+
190
+ ## Interfaces & Enums
191
+
192
+ ### TasCurrentUser
155
193
  ```typescript
156
194
  interface TasCurrentUser {
195
+ id: number;
157
196
  name: string;
158
197
  lastname: string;
159
- role: TasUserRole; // 'OWNER' | 'USER' | 'MODERATOR'
198
+ role: TasUserRole;
160
199
  }
161
200
  ```
162
201
 
163
- ## Exported Components
202
+ ### TasCallConfig
203
+ ```typescript
204
+ interface TasCallConfig {
205
+ roomType: TasRoomType;
206
+ entityId: number;
207
+ tenant: string;
208
+ businessRole: TasBusinessRole;
209
+ currentUser: TasCurrentUser;
210
+ }
211
+ ```
164
212
 
165
- - `TasButtonComponent` - Button to initiate video calls
166
- - `TasVideocallComponent` - Full-screen video call interface
167
- - `TasWaitingRoomComponent` - Pre-call waiting room
168
- - `TasFloatingCallComponent` - Picture-in-Picture floating window
213
+ ### TasAppointment
214
+ ```typescript
215
+ interface TasAppointment {
216
+ id: number;
217
+ agendaId: number;
218
+ date: string; // "YYYY-MM-DD"
219
+ startTime: string; // "HH:mm"
220
+ endTime: string; // "HH:mm"
221
+ bookingType: string;
222
+ status: AppointmentStatus;
223
+ title: string;
224
+ notes: string;
225
+ }
226
+ ```
227
+
228
+ ### Enums
229
+ - **TasUserRole**: `OWNER`, `USER`, `MODERATOR`
230
+ - **TasBusinessRole**: `ADMIN_MANAGER`, `MANAGER`, `BACKOFFICE`, `USER`
231
+ - **TasRoomType**: `TAS`, `JM`, `WEBINAR`, `WELLNESS_MANAGER`
232
+ - **CallState**: `IDLE`, `CONNECTING`, `CONNECTED`, `DISCONNECTED`, `ERROR`
233
+ - **ViewMode**: `FULLSCREEN`, `PIP`
234
+ - **AppointmentStatus**: `CONFIRMED`, `CANCELLED`
169
235
 
170
236
  ## Exported Services
171
237
 
172
- - `TasService` - Core service for managing video sessions
238
+ ### TasService
239
+ Core service for managing video sessions and state.
173
240
 
174
- ## Exported Interfaces & Types
241
+ **Public API:**
242
+ - `callState$: Observable<CallState>` - Current connection status
243
+ - `viewMode$: Observable<ViewMode>` - Current view mode
244
+ - `isMuted$: Observable<boolean>` - Local audio mute state
245
+ - `joinable$: Observable<boolean>` - Whether the call is joinable
246
+ - `ownerHasJoined$: Observable<boolean>` - Whether an owner has joined
247
+ - `toggleMute()` - Toggles local audio
248
+ - `disconnectSession()` - Ends the current session
249
+ - `getProxyVideoStatus(params)` - Gets session status
250
+ - `getAppointments(params)` - Gets user appointments within date range
175
251
 
176
- - `TasConfig`
177
- - `TasHttpClient`
178
- - `TasCurrentUser`
179
- - `CreateRoomRequest`
180
- - `CreateRoomResponse`
181
- - `GenerateTokenRequest`
182
- - `GenerateTokenResponse`
183
- - `TasRoomType`
184
- - `TasSessionType`
185
- - `TasUserRole`
186
- - `CallState`
187
- - `ViewMode`
252
+ ## Exported Components
188
253
 
189
- ## Building the Library
254
+ ### TasButtonComponent - `<tas-btn>`
190
255
 
191
- ```bash
192
- ng build tas-uell-sdk --configuration=production
256
+ Initiates video calls. Supports style variants:
257
+
258
+ ```html
259
+ <!-- Default (pink) -->
260
+ <tas-btn [entityId]="123" [tenant]="'tenant'" [currentUser]="user"></tas-btn>
261
+
262
+ <!-- Teal variant with custom label -->
263
+ <tas-btn variant="teal" buttonLabel="Ingresar" [entityId]="123" [tenant]="'tenant'" [currentUser]="user"></tas-btn>
193
264
  ```
194
265
 
266
+ | Property | Type | Default | Description |
267
+ | :--- | :--- | :--- | :--- |
268
+ | `variant` | `'default' \| 'teal'` | `'default'` | Button style variant |
269
+ | `buttonLabel` | `string` | `'Iniciar TAS'` | Button text |
270
+
271
+ ### TasIncomingAppointmentComponent - `<tas-incoming-appointment>`
272
+
273
+ Displays the user's next scheduled appointment or an empty state. Uses `<tas-btn>` internally.
274
+
275
+ ```html
276
+ <tas-incoming-appointment
277
+ [entityId]="123"
278
+ [tenant]="'tenant'"
279
+ [currentUser]="user"
280
+ [businessRole]="'USER'"
281
+ (enterCall)="onEnterCall($event)"
282
+ ></tas-incoming-appointment>
283
+ ```
284
+
285
+ | Property | Type | Required | Default | Description |
286
+ | :--- | :--- | :--- | :--- | :--- |
287
+ | `entityId` | `number` | Yes | - | Entity/ausencia ID |
288
+ | `tenant` | `string` | Yes | - | Tenant identifier |
289
+ | `currentUser` | `TasCurrentUser` | Yes | - | Current user info |
290
+ | `roomType` | `TasRoomType` | No | `'TAS'` | Room type |
291
+ | `businessRole` | `TasBusinessRole` | No | `'USER'` | User's business role |
292
+
293
+ | Output | Type | Description |
294
+ | :--- | :--- | :--- |
295
+ | `enterCall` | `EventEmitter<TasAppointment>` | Emits appointment when clicking "Ingresar" |
296
+
297
+ **Note:** Requires the `TasHttpClient` adapter to implement the `get()` method.
298
+
299
+ ### Other Components
300
+ - `TasVideocallComponent` - Full-screen video call interface
301
+ - `TasWaitingRoomComponent` - Pre-call waiting room
302
+ - `TasFloatingCallComponent` - `<tas-floating-call>`
303
+ - `TasAvatarComponent` - `<tas-avatar>`
304
+
195
305
  ## License
196
306
 
197
307
  MIT
@@ -0,0 +1,75 @@
1
+ import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ export class TasAvatarComponent {
4
+ constructor() {
5
+ this.name = '';
6
+ this.size = 80;
7
+ }
8
+ get initials() {
9
+ if (!this.name)
10
+ return '';
11
+ return this.name
12
+ .split(' ')
13
+ .filter((n) => n.length > 0)
14
+ .map((n) => n[0])
15
+ .join('')
16
+ .toUpperCase()
17
+ .substring(0, 2);
18
+ }
19
+ get fontSize() {
20
+ return Math.round(this.size * 0.4);
21
+ }
22
+ }
23
+ TasAvatarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasAvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
24
+ TasAvatarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.12", type: TasAvatarComponent, selector: "tas-avatar", inputs: { name: "name", size: "size" }, ngImport: i0, template: `
25
+ <div
26
+ class="avatar"
27
+ [style.width.px]="size"
28
+ [style.height.px]="size"
29
+ [style.fontSize.px]="fontSize"
30
+ >
31
+ <span class="initials">{{ initials }}</span>
32
+ </div>
33
+ `, isInline: true, styles: [".avatar{display:flex;align-items:center;justify-content:center;border-radius:50%;background-color:#fff;color:#0072ac;font-weight:600;font-family:inherit;box-shadow:0 4px 12px #00000026}.initials{text-transform:uppercase;-webkit-user-select:none;user-select:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
34
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasAvatarComponent, decorators: [{
35
+ type: Component,
36
+ args: [{
37
+ selector: 'tas-avatar',
38
+ template: `
39
+ <div
40
+ class="avatar"
41
+ [style.width.px]="size"
42
+ [style.height.px]="size"
43
+ [style.fontSize.px]="fontSize"
44
+ >
45
+ <span class="initials">{{ initials }}</span>
46
+ </div>
47
+ `,
48
+ styles: [
49
+ `
50
+ .avatar {
51
+ display: flex;
52
+ align-items: center;
53
+ justify-content: center;
54
+ border-radius: 50%;
55
+ background-color: #ffffff;
56
+ color: #0072ac;
57
+ font-weight: 600;
58
+ font-family: inherit;
59
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
60
+ }
61
+
62
+ .initials {
63
+ text-transform: uppercase;
64
+ user-select: none;
65
+ }
66
+ `,
67
+ ],
68
+ changeDetection: ChangeDetectionStrategy.OnPush,
69
+ }]
70
+ }], propDecorators: { name: [{
71
+ type: Input
72
+ }], size: [{
73
+ type: Input
74
+ }] } });
75
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFzLWF2YXRhci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy90YXMtdWVsbC1zZGsvc3JjL2xpYi9jb21wb25lbnRzL3Rhcy1hdmF0YXIvdGFzLWF2YXRhci5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxlQUFlLENBQUM7O0FBb0MxRSxNQUFNLE9BQU8sa0JBQWtCO0lBbEMvQjtRQW1DVyxTQUFJLEdBQVcsRUFBRSxDQUFDO1FBQ2xCLFNBQUksR0FBVyxFQUFFLENBQUM7S0FnQjVCO0lBZEMsSUFBSSxRQUFRO1FBQ1YsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJO1lBQUUsT0FBTyxFQUFFLENBQUM7UUFDMUIsT0FBTyxJQUFJLENBQUMsSUFBSTthQUNiLEtBQUssQ0FBQyxHQUFHLENBQUM7YUFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO2FBQzNCLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO2FBQ2hCLElBQUksQ0FBQyxFQUFFLENBQUM7YUFDUixXQUFXLEVBQUU7YUFDYixTQUFTLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3JCLENBQUM7SUFFRCxJQUFJLFFBQVE7UUFDVixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksR0FBRyxHQUFHLENBQUMsQ0FBQztJQUNyQyxDQUFDOztnSEFqQlUsa0JBQWtCO29HQUFsQixrQkFBa0IsMEZBaENuQjs7Ozs7Ozs7O0dBU1Q7NEZBdUJVLGtCQUFrQjtrQkFsQzlCLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLFlBQVk7b0JBQ3RCLFFBQVEsRUFBRTs7Ozs7Ozs7O0dBU1Q7b0JBQ0QsTUFBTSxFQUFFO3dCQUNOOzs7Ozs7Ozs7Ozs7Ozs7OztLQWlCQztxQkFDRjtvQkFDRCxlQUFlLEVBQUUsdUJBQXVCLENBQUMsTUFBTTtpQkFDaEQ7OEJBRVUsSUFBSTtzQkFBWixLQUFLO2dCQUNHLElBQUk7c0JBQVosS0FBSyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgSW5wdXQsIENoYW5nZURldGVjdGlvblN0cmF0ZWd5IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ3Rhcy1hdmF0YXInLFxuICB0ZW1wbGF0ZTogYFxuICAgIDxkaXZcbiAgICAgIGNsYXNzPVwiYXZhdGFyXCJcbiAgICAgIFtzdHlsZS53aWR0aC5weF09XCJzaXplXCJcbiAgICAgIFtzdHlsZS5oZWlnaHQucHhdPVwic2l6ZVwiXG4gICAgICBbc3R5bGUuZm9udFNpemUucHhdPVwiZm9udFNpemVcIlxuICAgID5cbiAgICAgIDxzcGFuIGNsYXNzPVwiaW5pdGlhbHNcIj57eyBpbml0aWFscyB9fTwvc3Bhbj5cbiAgICA8L2Rpdj5cbiAgYCxcbiAgc3R5bGVzOiBbXG4gICAgYFxuICAgICAgLmF2YXRhciB7XG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gICAgICAgIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICAgICAgICBib3JkZXItcmFkaXVzOiA1MCU7XG4gICAgICAgIGJhY2tncm91bmQtY29sb3I6ICNmZmZmZmY7XG4gICAgICAgIGNvbG9yOiAjMDA3MmFjO1xuICAgICAgICBmb250LXdlaWdodDogNjAwO1xuICAgICAgICBmb250LWZhbWlseTogaW5oZXJpdDtcbiAgICAgICAgYm94LXNoYWRvdzogMCA0cHggMTJweCByZ2JhKDAsIDAsIDAsIDAuMTUpO1xuICAgICAgfVxuXG4gICAgICAuaW5pdGlhbHMge1xuICAgICAgICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlO1xuICAgICAgICB1c2VyLXNlbGVjdDogbm9uZTtcbiAgICAgIH1cbiAgICBgLFxuICBdLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbn0pXG5leHBvcnQgY2xhc3MgVGFzQXZhdGFyQ29tcG9uZW50IHtcbiAgQElucHV0KCkgbmFtZTogc3RyaW5nID0gJyc7XG4gIEBJbnB1dCgpIHNpemU6IG51bWJlciA9IDgwO1xuXG4gIGdldCBpbml0aWFscygpOiBzdHJpbmcge1xuICAgIGlmICghdGhpcy5uYW1lKSByZXR1cm4gJyc7XG4gICAgcmV0dXJuIHRoaXMubmFtZVxuICAgICAgLnNwbGl0KCcgJylcbiAgICAgIC5maWx0ZXIoKG4pID0+IG4ubGVuZ3RoID4gMClcbiAgICAgIC5tYXAoKG4pID0+IG5bMF0pXG4gICAgICAuam9pbignJylcbiAgICAgIC50b1VwcGVyQ2FzZSgpXG4gICAgICAuc3Vic3RyaW5nKDAsIDIpO1xuICB9XG5cbiAgZ2V0IGZvbnRTaXplKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIE1hdGgucm91bmQodGhpcy5zaXplICogMC40KTtcbiAgfVxufVxuIl19