UNPKG

@jigx/mdk

Version:

Jigx Mobile Development Kit - SDK for building Jigx applications

363 lines 45.8 kB
import { application, INP } from '../src'; /** * Template expense tracking app demonstrating Jigx mobile patterns. * ADAPT THIS: Replace 'expense' with your domain (tasks, inventory, etc.) * Add/edit/remove entities, datasources, screens, tabs, etc * Explore Jigx SDK types and docs for more info */ // ======================================== // CONSTANTS - IDs and references // ======================================== // Screen Ids const SCREEN = { HOME: 'home', EXPENSE_LIST: 'expense-list', ADD_EXPENSE: 'add-expense', EDIT_EXPENSE: 'edit-expense' }; // Datasource Ids const DATASOURCE = { EXPENSES: 'data-expenses', EXPENSE_CATEGORIES: 'expense-categories', EXPENSE_STATS: 'expense-stats', RECENT_EXPENSES: 'recent-expenses', EXPENSES_SUMMARY: 'expenses-summary', CURRENT_EXPENSE: 'current-expense' }; // Database entities const ENTITY = { EXPENSES: 'default/expenses' }; // Tab Ids const TABS = { HOME: 'home' }; // ======================================== // APPLICATION - Core app configuration // ======================================== export const app = application('expense-tracker') // Unique programmatic Id .title('Expense Tracker') // Display name .category('personal'); // App store category (choices in doc-comments) // Global expressions - available everywhere app.expression('formatCurrency', `=function($amount) { '$' & $formatNumber($amount, '#,##0.00') }`); app.expression('formatDate', `=function($date) { $fromMillis($toMillis($date), '[D1o] [MNn] [Y]') }`); // ======================================== // DATABASE - SQLite table definitions // ======================================== /* Database tables - JSON docs in sqlite */ app.addDatabase .default // Only ONE db, always named 'default' .table('expenses'); // Declares [default/expenses] table in DB // ======================================== // SHARED DATASOURCES - Available to all screens // ======================================== /* Main data provider - queries all expenses */ app.addDatasource.sqlite(DATASOURCE.EXPENSES, 'dynamic') // 'dynamic' = offline sync .entity(ENTITY.EXPENSES) // Table(s) to query .query(/* sql */ ` SELECT exp.id, -- Always include row id -- Option 1: Return entire JSON doc (preferred) exp.data -- Option 2: Extract specific fields for expressions -- json_extract(exp.data, '$.title') AS title FROM [${ENTITY.EXPENSES}] AS exp ORDER BY -- Always extract for joins, sorting, etc json_extract(exp.data, '$.date') DESC `) // Decorate data as 'this is a json document' .jsonProperties('data'); /* Static data - no database needed, ideal for fixed lists */ app.addDatasource.static(DATASOURCE.EXPENSE_CATEGORIES, [ { id: 'food', name: 'Food & Dining' }, { id: 'transport', name: 'Transportation' }, { id: 'accommodation', name: 'Accommodation' }, { id: 'office', name: 'Office Supplies' }, { id: 'technology', name: 'Technology' }, { id: 'entertainment', name: 'Entertainment' }, { id: 'health', name: 'Health & Medical' }, { id: 'education', name: 'Education & Training' }, { id: 'other', name: 'Other' } ]); // ======================================== // HOME SCREEN - Main dashboard // ======================================== const homeScreen = app.addScreen .default(SCREEN.HOME, 'Expense Tracker'); { // Screen-specific datasource for aggregate stats homeScreen.addDatasource.sqlite(DATASOURCE.EXPENSE_STATS, 'dynamic') .entity(ENTITY.EXPENSES) .query(/* sql */ ` SELECT COUNT(1) AS count, SUM(json_extract(exp.data, '$.amount')) AS total_amount, AVG(json_extract(exp.data, '$.amount')) AS avg_amount, MAX(json_extract(exp.data, '$.amount')) AS max_amount FROM [${ENTITY.EXPENSES}] AS exp `); //.isDocument() // Return single document instead of array // Card component - container for related fields const card = homeScreen.addControl.card(); { card.addControl.list() .data(DATASOURCE.EXPENSE_STATS) .addControl.listItem() // Call global function to format currency .title('=$formatCurrency(@ctx.current.item.total_amount) & " (" & @ctx.current.item.count & " items)"'); } // Recent items (LOCAL DATASOURCE) homeScreen.addDatasource.sqlite(DATASOURCE.RECENT_EXPENSES, 'dynamic') .entity(ENTITY.EXPENSES) .query(/* sql */ ` SELECT exp.id, exp.data FROM [${ENTITY.EXPENSES}] AS exp ORDER BY json_extract(exp.data, '$.date') DESC LIMIT 5 `) .jsonProperties('data'); // List component - displays collection const list = homeScreen.addControl .list('recent-list') .data(DATASOURCE.RECENT_EXPENSES); // Bind to datasource list.addControl.listItem() .title('=@ctx.current.item.data.title') .subtitle('=@ctx.current.item.data.categoryId & " • " & $formatDate(@ctx.current.item.data.date)') .description('=$formatCurrency(@ctx.current.item.data.amount)') .onPress .goto(SCREEN.EDIT_EXPENSE) // Navigate to screen .parameter('id', '=@ctx.current.item.id') .parameter('expenseItem', '=@ctx.current.item.data'); // Buttons at bottom of screen const homeButtons = homeScreen.bottomPanel.buttons(2); // Show 2 of 3 max homeButtons.add.goto('Add Expense', SCREEN.ADD_EXPENSE); homeButtons.add.goto('View All', SCREEN.EXPENSE_LIST); // Show all expenses } // ======================================== // EXPENSE LIST SCREEN // ======================================== /* Expense list - all transactions */ const expenseListScreen = app.addScreen .default(SCREEN.EXPENSE_LIST, 'All Expenses'); { // LOCAL datasource (only available in this screen) expenseListScreen.addDatasource.sqlite(DATASOURCE.EXPENSES_SUMMARY, 'dynamic') .entity(ENTITY.EXPENSES) .query(/* sql */ ` SELECT json_extract(exp.data, '$.categoryId') AS categoryId, SUM(json_extract(exp.data, '$.amount')) AS total_amount, COUNT(1) AS count FROM [${ENTITY.EXPENSES}] AS exp GROUP BY categoryId ORDER BY total_amount DESC `) .isDocument(false); // In this case we want the array, since it is multiple categories // Summary card const summaryCard = expenseListScreen.addControl.card(); { summaryCard.addControl.textField() .label('Summary by Category') .value('Category Breakdown') .isDisabled(); summaryCard.addControl.list() .data(DATASOURCE.EXPENSES_SUMMARY) .addControl.listItem() .title('=@ctx.current.item.categoryId') .subtitle('=$formatCurrency(@ctx.current.item.total_amount) & " • " & @ctx.current.item.count & " expenses"'); } // Expense list const expenseList = expenseListScreen.addControl.list('expense-list') .data(DATASOURCE.EXPENSES); // Bind to global datasource expenseList.addControl.listItem() .title('=@ctx.current.item.data.title') .subtitle('=@ctx.current.item.data.categoryId & " • " & @ctx.current.item.data.date') .description('=$formatCurrency(@ctx.current.item.data.amount)') .onPress .goto(SCREEN.EDIT_EXPENSE) // Actual parameters .parameter('id', '=@ctx.current.item.id') .parameter('expenseItem', '=@ctx.current.item.data'); // Actions const expenseListButtons = expenseListScreen.bottomPanel.buttons(); expenseListButtons.add.goto('Add New', SCREEN.ADD_EXPENSE); } // ======================================== // ADD EXPENSE SCREEN // ======================================== /* Add expense form */ const addExpenseScreen = app.addScreen .default(SCREEN.ADD_EXPENSE, 'Add Expense'); { // Clear previous form state on screen focus (else will pre-populate using old values) const formInstanceId = 'add-expense-form'; addExpenseScreen.onFocus .clearState(`=@ctx.components.${formInstanceId}.state.value`); // Form container const form = addExpenseScreen.addControl .form({ instanceId: formInstanceId }); { // If set to true, will warn user on back navigation form.discardChangesAlert(false); // Form fields - Title form.addControl // Use instanceId for state tracking later, eg `=@ctx.components.title.state.value` .textField({ instanceId: 'title' }) .label('Title') .required(true) .isAutoFocused() .isAutoCorrected() .autoCapitalize('sentences'); // Amount form.addControl .numberField({ instanceId: 'amount' }) .label('Amount') .required(true) .format({ numberStyle: 'currency' }); // Format as currency // Category const category = form.addControl .dropdown({ isRequired: true, instanceId: 'categoryId' }) .label('Category') // Dropdown bound to static datasource .data(DATASOURCE.EXPENSE_CATEGORIES); { // Template for dropdown members (property names from datasource) category.item() .value('=@ctx.current.item.id') // Stored key .title('=@ctx.current.item.name'); // Display text } // Description form.addControl .textField({ instanceId: 'description' }) .label('Description') .required(false) .isMultiline(true) .autoCapitalize('sentences') .isOptionalLabelHidden(); // Hide "optional" label // Date form.addControl.datePicker({ isRequired: true, mode: 'date', instanceId: 'date' }) .label('Date') .initialValue('=$now()'); // Jsonata expression } // Save to database const addExpenseButtons = addExpenseScreen.bottomPanel.buttons(); addExpenseButtons.add.executeEntity('Save Expense') .dynamicData(ENTITY.EXPENSES, 'create') .data({ // Component state mapped via instanceId title: '=@ctx.components.title.state.value', amount: '=@ctx.components.amount.state.value', categoryId: '=@ctx.components.categoryId.state.value', date: '=@ctx.components.date.state.value', description: '=@ctx.components.description.state.value', timestamp: '=$now()' }) .goBack('previous'); // Navigate back after save (see discardChangesAlert above) } // ======================================== // EDIT EXPENSE SCREEN // ======================================== /* Edit expense form */ const editExpenseScreen = app.addScreen .default(SCREEN.EDIT_EXPENSE, 'Edit Expense'); { // Formal parameters editExpenseScreen .input(INP.string('id').required()) .input(INP.object('expenseItem').required()); const formInstanceId = 'edit-expense-form'; // Edit form - pre-populated const form = editExpenseScreen.addControl.form({ instanceId: formInstanceId }); { form.discardChangesAlert(false); // Form fields - Title form.addControl .textField({ instanceId: 'title' }) .label('Title') .required(true) .isAutoFocused() .isAutoCorrected() .autoCapitalize('sentences') // Pre-populate from inputs. By convention, instanceId matches data-field/state .initialValue(`=@ctx.inputs.expenseItem.title`); // Amount form.addControl .numberField({ instanceId: 'amount' }) .label('Amount') .required(true) .format({ numberStyle: 'currency' }) .initialValue(`=@ctx.inputs.expenseItem.amount`); // Category const category = form.addControl .dropdown({ isRequired: true, instanceId: 'categoryId' }) .label('Category') .data(DATASOURCE.EXPENSE_CATEGORIES) .initialValue(`=@ctx.inputs.expenseItem.categoryId`); { category.item() .value('=@ctx.current.item.id') .title('=@ctx.current.item.name'); } // Description form.addControl .textField({ instanceId: 'description' }) .label('Description') .required(false) .isMultiline(true) .autoCapitalize('sentences') .initialValue('=@ctx.inputs.expenseItem.description'); // Date form.addControl .datePicker({ isRequired: true, mode: 'date', instanceId: 'date' }) .label('Date') .initialValue(`=@ctx.inputs.expenseItem.date`); } // Buttons/actions const editButtons = editExpenseScreen.bottomPanel.buttons(2); // Button/action to UPDATE selected expense editButtons.add .executeEntity('Update') .dynamicData(ENTITY.EXPENSES, 'update') // 'update' = modify existing .data({ id: `=@ctx.inputs.id`, // Locator // Include all fields for update title: '=@ctx.components.title.state.value', amount: '=@ctx.components.amount.state.value', categoryId: '=@ctx.components.categoryId.state.value', date: '=@ctx.components.date.state.value', description: '=@ctx.components.description.state.value', timestamp: '=$now()' }) .goBack('previous'); // Navigate back after update (see discardChangesAlert above) // Add button to DELETE with confirmation modal const deleteButton = editButtons.add.confirm('Delete'); deleteButton.modal({ text: 'Delete Expense?' }) // User must confirm .confirmText('Delete') .cancelText('Cancel'); deleteButton.onConfirmed.executeEntity() .dynamicData(ENTITY.EXPENSES, 'delete') // 'delete' = remove record .data({ id: `=@ctx.inputs.id` // Locator }) .goBack('previous'); } // ======================================== // NAVIGATION - Tab bar configuration // ======================================== // Bottom navigation - entry point app.addTab(TABS.HOME, 'credit-card') // Icon name .label('Expenses'); // Tab text // ======================================== // BUILD - Generate output // ======================================== // CRITICAL: MUST call build() app.build(); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdGVtcGxhdGUtYXBwLTEvYXBwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxXQUFXLEVBQUUsR0FBRyxFQUFFLE1BQU0sUUFBUSxDQUFBO0FBRXpDOzs7OztHQUtHO0FBRUgsMkNBQTJDO0FBQzNDLGlDQUFpQztBQUNqQywyQ0FBMkM7QUFFM0MsYUFBYTtBQUNiLE1BQU0sTUFBTSxHQUFHO0lBQ2IsSUFBSSxFQUFFLE1BQU07SUFDWixZQUFZLEVBQUUsY0FBYztJQUM1QixXQUFXLEVBQUUsYUFBYTtJQUMxQixZQUFZLEVBQUUsY0FBYztDQUNwQixDQUFBO0FBRVYsaUJBQWlCO0FBQ2pCLE1BQU0sVUFBVSxHQUFHO0lBQ2pCLFFBQVEsRUFBRSxlQUFlO0lBQ3pCLGtCQUFrQixFQUFFLG9CQUFvQjtJQUN4QyxhQUFhLEVBQUUsZUFBZTtJQUM5QixlQUFlLEVBQUUsaUJBQWlCO0lBQ2xDLGdCQUFnQixFQUFFLGtCQUFrQjtJQUNwQyxlQUFlLEVBQUUsaUJBQWlCO0NBQzFCLENBQUE7QUFFVixvQkFBb0I7QUFDcEIsTUFBTSxNQUFNLEdBQUc7SUFDYixRQUFRLEVBQUUsa0JBQWtCO0NBQ3BCLENBQUE7QUFFVixVQUFVO0FBQ1YsTUFBTSxJQUFJLEdBQUc7SUFDWCxJQUFJLEVBQUUsTUFBTTtDQUNKLENBQUE7QUFFViwyQ0FBMkM7QUFDM0MsdUNBQXVDO0FBQ3ZDLDJDQUEyQztBQUUzQyxNQUFNLENBQUMsTUFBTSxHQUFHLEdBQUcsV0FBVyxDQUFDLGlCQUFpQixDQUFDLENBQUMseUJBQXlCO0tBQ3hFLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLGVBQWU7S0FDeEMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxDQUFBLENBQUMsK0NBQStDO0FBRXZFLDRDQUE0QztBQUM1QyxHQUFHLENBQUMsVUFBVSxDQUFDLGdCQUFnQixFQUFFOztFQUUvQixDQUFDLENBQUE7QUFD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LEVBQUUsaUJBQWlCLENBQUMsVUFBVTtLQUNqQyxDQUFDO1NBQ0QsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFBO0FBQ3ZCLENBQUM7QUFFRCwyQ0FBMkM7QUFDM0MscUNBQXFDO0FBQ3JDLDJDQUEyQztBQUUzQyxrQ0FBa0M7QUFDbEMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLGFBQWEsQ0FBQyxDQUFDLFlBQVk7S0FDOUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFBLENBQUMsV0FBVztBQUVoQywyQ0FBMkM7QUFDM0MsMEJBQTBCO0FBQzFCLDJDQUEyQztBQUUzQyw4QkFBOEI7QUFDOUIsR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgYXBwbGljYXRpb24sIElOUCB9IGZyb20gJy4uL3NyYydcblxuLyoqXG4gKiBUZW1wbGF0ZSBleHBlbnNlIHRyYWNraW5nIGFwcCBkZW1vbnN0cmF0aW5nIEppZ3ggbW9iaWxlIHBhdHRlcm5zLlxuICogQURBUFQgVEhJUzogUmVwbGFjZSAnZXhwZW5zZScgd2l0aCB5b3VyIGRvbWFpbiAodGFza3MsIGludmVudG9yeSwgZXRjLilcbiAqIEFkZC9lZGl0L3JlbW92ZSBlbnRpdGllcywgZGF0YXNvdXJjZXMsIHNjcmVlbnMsIHRhYnMsIGV0Y1xuICogRXhwbG9yZSBKaWd4IFNESyB0eXBlcyBhbmQgZG9jcyBmb3IgbW9yZSBpbmZvXG4gKi9cblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gQ09OU1RBTlRTIC0gSURzIGFuZCByZWZlcmVuY2VzXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbi8vIFNjcmVlbiBJZHNcbmNvbnN0IFNDUkVFTiA9IHtcbiAgSE9NRTogJ2hvbWUnLFxuICBFWFBFTlNFX0xJU1Q6ICdleHBlbnNlLWxpc3QnLFxuICBBRERfRVhQRU5TRTogJ2FkZC1leHBlbnNlJyxcbiAgRURJVF9FWFBFTlNFOiAnZWRpdC1leHBlbnNlJ1xufSBhcyBjb25zdFxuXG4vLyBEYXRhc291cmNlIElkc1xuY29uc3QgREFUQVNPVVJDRSA9IHtcbiAgRVhQRU5TRVM6ICdkYXRhLWV4cGVuc2VzJyxcbiAgRVhQRU5TRV9DQVRFR09SSUVTOiAnZXhwZW5zZS1jYXRlZ29yaWVzJyxcbiAgRVhQRU5TRV9TVEFUUzogJ2V4cGVuc2Utc3RhdHMnLFxuICBSRUNFTlRfRVhQRU5TRVM6ICdyZWNlbnQtZXhwZW5zZXMnLFxuICBFWFBFTlNFU19TVU1NQVJZOiAnZXhwZW5zZXMtc3VtbWFyeScsXG4gIENVUlJFTlRfRVhQRU5TRTogJ2N1cnJlbnQtZXhwZW5zZSdcbn0gYXMgY29uc3RcblxuLy8gRGF0YWJhc2UgZW50aXRpZXNcbmNvbnN0IEVOVElUWSA9IHtcbiAgRVhQRU5TRVM6ICdkZWZhdWx0L2V4cGVuc2VzJ1xufSBhcyBjb25zdFxuXG4vLyBUYWIgSWRzXG5jb25zdCBUQUJTID0ge1xuICBIT01FOiAnaG9tZSdcbn0gYXMgY29uc3RcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gQVBQTElDQVRJT04gLSBDb3JlIGFwcCBjb25maWd1cmF0aW9uXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmV4cG9ydCBjb25zdCBhcHAgPSBhcHBsaWNhdGlvbignZXhwZW5zZS10cmFja2VyJykgLy8gVW5pcXVlIHByb2dyYW1tYXRpYyBJZFxuICAudGl0bGUoJ0V4cGVuc2UgVHJhY2tlcicpIC8vIERpc3BsYXkgbmFtZVxuICAuY2F0ZWdvcnkoJ3BlcnNvbmFsJykgLy8gQXBwIHN0b3JlIGNhdGVnb3J5IChjaG9pY2VzIGluIGRvYy1jb21tZW50cylcblxuLy8gR2xvYmFsIGV4cHJlc3Npb25zIC0gYXZhaWxhYmxlIGV2ZXJ5d2hlcmVcbmFwcC5leHByZXNzaW9uKCdmb3JtYXRDdXJyZW5jeScsIGA9ZnVuY3Rpb24oJGFtb3VudCkge1xuICAnJCcgJiAkZm9ybWF0TnVtYmVyKCRhbW91bnQsICcjLCMjMC4wMCcpXG59YClcbmFwcC5leHByZXNzaW9uKCdmb3JtYXREYXRlJywgYD1mdW5jdGlvbigkZGF0ZSkge1xuICAkZnJvbU1pbGxpcygkdG9NaWxsaXMoJGRhdGUpLCAnW0Qxb10gW01Obl0gW1ldJylcbn1gKVxuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBEQVRBQkFTRSAtIFNRTGl0ZSB0YWJsZSBkZWZpbml0aW9uc1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKiBEYXRhYmFzZSB0YWJsZXMgLSBKU09OIGRvY3MgaW4gc3FsaXRlICovXG5hcHAuYWRkRGF0YWJhc2VcbiAgLmRlZmF1bHQgLy8gT25seSBPTkUgZGIsIGFsd2F5cyBuYW1lZCAnZGVmYXVsdCdcbiAgLnRhYmxlKCdleHBlbnNlcycpIC8vIERlY2xhcmVzIFtkZWZhdWx0L2V4cGVuc2VzXSB0YWJsZSBpbiBEQiBcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gU0hBUkVEIERBVEFTT1VSQ0VTIC0gQXZhaWxhYmxlIHRvIGFsbCBzY3JlZW5zXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbi8qIE1haW4gZGF0YSBwcm92aWRlciAtIHF1ZXJpZXMgYWxsIGV4cGVuc2VzICovXG5hcHAuYWRkRGF0YXNvdXJjZS5zcWxpdGUoREFUQVNPVVJDRS5FWFBFTlNFUywgJ2R5bmFtaWMnKSAvLyAnZHluYW1pYycgPSBvZmZsaW5lIHN5bmNcbiAgLmVudGl0eShFTlRJVFkuRVhQRU5TRVMpIC8vIFRhYmxlKHMpIHRvIHF1ZXJ5XG4gIC5xdWVyeSgvKiBzcWwgKi9gXG4gICAgICBTRUxFQ1QgXG4gICAgICAgIGV4cC5pZCwgLS0gQWx3YXlzIGluY2x1ZGUgcm93IGlkXG4gICAgICAgIC0tIE9wdGlvbiAxOiBSZXR1cm4gZW50aXJlIEpTT04gZG9jIChwcmVmZXJyZWQpXG4gICAgICAgIGV4cC5kYXRhXG4gICAgICAgIC0tIE9wdGlvbiAyOiBFeHRyYWN0IHNwZWNpZmljIGZpZWxkcyBmb3IgZXhwcmVzc2lvbnNcbiAgICAgICAgLS0ganNvbl9leHRyYWN0KGV4cC5kYXRhLCAnJC50aXRsZScpIEFTIHRpdGxlXG4gICAgICBGUk9NIFske0VOVElUWS5FWFBFTlNFU31dIEFTIGV4cFxuICAgICAgT1JERVIgQllcbiAgICAgICAgLS0gQWx3YXlzIGV4dHJhY3QgZm9yIGpvaW5zLCBzb3J0aW5nLCBldGNcbiAgICAgICAganNvbl9leHRyYWN0KGV4cC5kYXRhLCAnJC5kYXRlJykgREVTQ1xuICAgIGApXG4gICAgLy8gRGVjb3JhdGUgZGF0YSBhcyAndGhpcyBpcyBhIGpzb24gZG9jdW1lbnQnXG4gICAgLmpzb25Qcm9wZXJ0aWVzKCdkYXRhJylcblxuLyogU3RhdGljIGRhdGEgLSBubyBkYXRhYmFzZSBuZWVkZWQsIGlkZWFsIGZvciBmaXhlZCBsaXN0cyAqL1xuYXBwLmFkZERhdGFzb3VyY2Uuc3RhdGljKERBVEFTT1VSQ0UuRVhQRU5TRV9DQVRFR09SSUVTLCBbXG4gIHsgaWQ6ICdmb29kJywgbmFtZTogJ0Zvb2QgJiBEaW5pbmcnIH0sXG4gIHsgaWQ6ICd0cmFuc3BvcnQnLCBuYW1lOiAnVHJhbnNwb3J0YXRpb24nIH0sXG4gIHsgaWQ6ICdhY2NvbW1vZGF0aW9uJywgbmFtZTogJ0FjY29tbW9kYXRpb24nIH0sXG4gIHsgaWQ6ICdvZmZpY2UnLCBuYW1lOiAnT2ZmaWNlIFN1cHBsaWVzJyB9LFxuICB7IGlkOiAndGVjaG5vbG9neScsIG5hbWU6ICdUZWNobm9sb2d5JyB9LFxuICB7IGlkOiAnZW50ZXJ0YWlubWVudCcsIG5hbWU6ICdFbnRlcnRhaW5tZW50JyB9LFxuICB7IGlkOiAnaGVhbHRoJywgbmFtZTogJ0hlYWx0aCAmIE1lZGljYWwnIH0sXG4gIHsgaWQ6ICdlZHVjYXRpb24nLCBuYW1lOiAnRWR1Y2F0aW9uICYgVHJhaW5pbmcnIH0sXG4gIHsgaWQ6ICdvdGhlcicsIG5hbWU6ICdPdGhlcicgfVxuXSlcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gSE9NRSBTQ1JFRU4gLSBNYWluIGRhc2hib2FyZFxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG5jb25zdCBob21lU2NyZWVuID0gYXBwLmFkZFNjcmVlblxuICAuZGVmYXVsdChTQ1JFRU4uSE9NRSwgJ0V4cGVuc2UgVHJhY2tlcicpXG57XG4gIC8vIFNjcmVlbi1zcGVjaWZpYyBkYXRhc291cmNlIGZvciBhZ2dyZWdhdGUgc3RhdHNcbiAgaG9tZVNjcmVlbi5hZGREYXRhc291cmNlLnNxbGl0ZShEQVRBU09VUkNFLkVYUEVOU0VfU1RBVFMsICdkeW5hbWljJylcbiAgICAuZW50aXR5KEVOVElUWS5FWFBFTlNFUylcbiAgICAucXVlcnkoLyogc3FsICovYFxuICAgICAgU0VMRUNUIFxuICAgICAgICBDT1VOVCgxKSBBUyBjb3VudCxcbiAgICAgICAgU1VNKGpzb25fZXh0cmFjdChleHAuZGF0YSwgJyQuYW1vdW50JykpIEFTIHRvdGFsX2Ftb3VudCxcbiAgICAgICAgQVZHKGpzb25fZXh0cmFjdChleHAuZGF0YSwgJyQuYW1vdW50JykpIEFTIGF2Z19hbW91bnQsXG4gICAgICAgIE1BWChqc29uX2V4dHJhY3QoZXhwLmRhdGEsICckLmFtb3VudCcpKSBBUyBtYXhfYW1vdW50XG4gICAgICBGUk9NIFske0VOVElUWS5FWFBFTlNFU31dIEFTIGV4cFxuICAgIGApXG4gICAgLy8uaXNEb2N1bWVudCgpIC8vIFJldHVybiBzaW5nbGUgZG9jdW1lbnQgaW5zdGVhZCBvZiBhcnJheVxuXG4gIC8vIENhcmQgY29tcG9uZW50IC0gY29udGFpbmVyIGZvciByZWxhdGVkIGZpZWxkc1xuICBjb25zdCBjYXJkID0gaG9tZVNjcmVlbi5hZGRDb250cm9sLmNhcmQoKVxuICB7XG4gICAgY2FyZC5hZGRDb250cm9sLmxpc3QoKVxuICAgICAgLmRhdGEoREFUQVNPVVJDRS5FWFBFTlNFX1NUQVRTKVxuICAgICAgLmFkZENvbnRyb2wubGlzdEl0ZW0oKVxuICAgICAgLy8gQ2FsbCBnbG9iYWwgZnVuY3Rpb24gdG8gZm9ybWF0IGN1cnJlbmN5XG4gICAgICAudGl0bGUoJz0kZm9ybWF0Q3VycmVuY3koQGN0eC5jdXJyZW50Lml0ZW0udG90YWxfYW1vdW50KSAmIFwiIChcIiAmIEBjdHguY3VycmVudC5pdGVtLmNvdW50ICYgXCIgaXRlbXMpXCInKVxuICB9XG5cbiAgLy8gUmVjZW50IGl0ZW1zIChMT0NBTCBEQVRBU09VUkNFKVxuICBob21lU2NyZWVuLmFkZERhdGFzb3VyY2Uuc3FsaXRlKERBVEFTT1VSQ0UuUkVDRU5UX0VYUEVOU0VTLCAnZHluYW1pYycpXG4gICAgLmVudGl0eShFTlRJVFkuRVhQRU5TRVMpXG4gICAgLnF1ZXJ5KC8qIHNxbCAqL2BcbiAgICAgIFNFTEVDVCBcbiAgICAgICAgZXhwLmlkLFxuICAgICAgICBleHAuZGF0YVxuICAgICAgRlJPTSBbJHtFTlRJVFkuRVhQRU5TRVN9XSBBUyBleHBcbiAgICAgIE9SREVSIEJZXG4gICAgICAgIGpzb25fZXh0cmFjdChleHAuZGF0YSwgJyQuZGF0ZScpIERFU0NcbiAgICAgIExJTUlUIDVcbiAgICBgKVxuICAgIC5qc29uUHJvcGVydGllcygnZGF0YScpXG5cbiAgLy8gTGlzdCBjb21wb25lbnQgLSBkaXNwbGF5cyBjb2xsZWN0aW9uXG4gIGNvbnN0IGxpc3QgPSBob21lU2NyZWVuLmFkZENvbnRyb2xcbiAgICAubGlzdCgncmVjZW50LWxpc3QnKVxuICAgIC5kYXRhKERBVEFTT1VSQ0UuUkVDRU5UX0VYUEVOU0VTKSAvLyBCaW5kIHRvIGRhdGFzb3VyY2VcblxuICBsaXN0LmFkZENvbnRyb2wubGlzdEl0ZW0oKVxuICAgIC50aXRsZSgnPUBjdHguY3VycmVudC5pdGVtLmRhdGEudGl0bGUnKVxuICAgIC5zdWJ0aXRsZSgnPUBjdHguY3VycmVudC5pdGVtLmRhdGEuY2F0ZWdvcnlJZCAmIFwiIOKAoiBcIiAmICRmb3JtYXREYXRlKEBjdHguY3VycmVudC5pdGVtLmRhdGEuZGF0ZSknKVxuICAgIC5kZXNjcmlwdGlvbignPSRmb3JtYXRDdXJyZW5jeShAY3R4LmN1cnJlbnQuaXRlbS5kYXRhLmFtb3VudCknKVxuICAgIC5vblByZXNzXG4gICAgICAuZ290byhTQ1JFRU4uRURJVF9FWFBFTlNFKSAvLyBOYXZpZ2F0ZSB0byBzY3JlZW5cbiAgICAgIC5wYXJhbWV0ZXIoJ2lkJywgJz1AY3R4LmN1cnJlbnQuaXRlbS5pZCcpXG4gICAgICAucGFyYW1ldGVyKCdleHBlbnNlSXRlbScsICc9QGN0eC5jdXJyZW50Lml0ZW0uZGF0YScpXG5cbiAgLy8gQnV0dG9ucyBhdCBib3R0b20gb2Ygc2NyZWVuXG4gIGNvbnN0IGhvbWVCdXR0b25zID0gaG9tZVNjcmVlbi5ib3R0b21QYW5lbC5idXR0b25zKDIpIC8vIFNob3cgMiBvZiAzIG1heFxuICBob21lQnV0dG9ucy5hZGQuZ290bygnQWRkIEV4cGVuc2UnLCBTQ1JFRU4uQUREX0VYUEVOU0UpXG4gIGhvbWVCdXR0b25zLmFkZC5nb3RvKCdWaWV3IEFsbCcsIFNDUkVFTi5FWFBFTlNFX0xJU1QpIC8vIFNob3cgYWxsIGV4cGVuc2VzXG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIEVYUEVOU0UgTElTVCBTQ1JFRU5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyogRXhwZW5zZSBsaXN0IC0gYWxsIHRyYW5zYWN0aW9ucyAqL1xuY29uc3QgZXhwZW5zZUxpc3RTY3JlZW4gPSBhcHAuYWRkU2NyZWVuXG4gIC5kZWZhdWx0KFNDUkVFTi5FWFBFTlNFX0xJU1QsICdBbGwgRXhwZW5zZXMnKVxue1xuICAvLyBMT0NBTCBkYXRhc291cmNlIChvbmx5IGF2YWlsYWJsZSBpbiB0aGlzIHNjcmVlbilcbiAgZXhwZW5zZUxpc3RTY3JlZW4uYWRkRGF0YXNvdXJjZS5zcWxpdGUoREFUQVNPVVJDRS5FWFBFTlNFU19TVU1NQVJZLCAnZHluYW1pYycpXG4gICAgLmVudGl0eShFTlRJVFkuRVhQRU5TRVMpXG4gICAgLnF1ZXJ5KC8qIHNxbCAqL2BcbiAgICAgIFNFTEVDVCBcbiAgICAgICAganNvbl9leHRyYWN0KGV4cC5kYXRhLCAnJC5jYXRlZ29yeUlkJykgQVMgY2F0ZWdvcnlJZCxcbiAgICAgICAgU1VNKGpzb25fZXh0cmFjdChleHAuZGF0YSwgJyQuYW1vdW50JykpIEFTIHRvdGFsX2Ftb3VudCxcbiAgICAgICAgQ09VTlQoMSkgQVMgY291bnRcbiAgICAgIEZST00gWyR7RU5USVRZLkVYUEVOU0VTfV0gQVMgZXhwXG4gICAgICBHUk9VUCBCWSBjYXRlZ29yeUlkXG4gICAgICBPUkRFUiBCWSB0b3RhbF9hbW91bnQgREVTQ1xuICAgIGApXG4gICAgLmlzRG9jdW1lbnQoZmFsc2UpIC8vIEluIHRoaXMgY2FzZSB3ZSB3YW50IHRoZSBhcnJheSwgc2luY2UgaXQgaXMgbXVsdGlwbGUgY2F0ZWdvcmllc1xuXG4gIC8vIFN1bW1hcnkgY2FyZFxuICBjb25zdCBzdW1tYXJ5Q2FyZCA9IGV4cGVuc2VMaXN0U2NyZWVuLmFkZENvbnRyb2wuY2FyZCgpXG4gIHtcbiAgICBzdW1tYXJ5Q2FyZC5hZGRDb250cm9sLnRleHRGaWVsZCgpXG4gICAgICAubGFiZWwoJ1N1bW1hcnkgYnkgQ2F0ZWdvcnknKVxuICAgICAgLnZhbHVlKCdDYXRlZ29yeSBCcmVha2Rvd24nKVxuICAgICAgLmlzRGlzYWJsZWQoKVxuXG4gICAgc3VtbWFyeUNhcmQuYWRkQ29udHJvbC5saXN0KClcbiAgICAgIC5kYXRhKERBVEFTT1VSQ0UuRVhQRU5TRVNfU1VNTUFSWSlcbiAgICAgIC5hZGRDb250cm9sLmxpc3RJdGVtKClcbiAgICAgIC50aXRsZSgnPUBjdHguY3VycmVudC5pdGVtLmNhdGVnb3J5SWQnKVxuICAgICAgLnN1YnRpdGxlKCc9JGZvcm1hdEN1cnJlbmN5KEBjdHguY3VycmVudC5pdGVtLnRvdGFsX2Ftb3VudCkgJiBcIiDigKIgXCIgJiBAY3R4LmN1cnJlbnQuaXRlbS5jb3VudCAmIFwiIGV4cGVuc2VzXCInKVxuICB9XG5cbiAgLy8gRXhwZW5zZSBsaXN0XG4gIGNvbnN0IGV4cGVuc2VMaXN0ID0gZXhwZW5zZUxpc3RTY3JlZW4uYWRkQ29udHJvbC5saXN0KCdleHBlbnNlLWxpc3QnKVxuICAgIC5kYXRhKERBVEFTT1VSQ0UuRVhQRU5TRVMpIC8vIEJpbmQgdG8gZ2xvYmFsIGRhdGFzb3VyY2VcblxuICBleHBlbnNlTGlzdC5hZGRDb250cm9sLmxpc3RJdGVtKClcbiAgICAudGl0bGUoJz1AY3R4LmN1cnJlbnQuaXRlbS5kYXRhLnRpdGxlJylcbiAgICAuc3VidGl0bGUoJz1AY3R4LmN1cnJlbnQuaXRlbS5kYXRhLmNhdGVnb3J5SWQgJiBcIiDigKIgXCIgJiBAY3R4LmN1cnJlbnQuaXRlbS5kYXRhLmRhdGUnKVxuICAgIC5kZXNjcmlwdGlvbignPSRmb3JtYXRDdXJyZW5jeShAY3R4LmN1cnJlbnQuaXRlbS5kYXRhLmFtb3VudCknKVxuICAgIC5vblByZXNzXG4gICAgICAuZ290byhTQ1JFRU4uRURJVF9FWFBFTlNFKVxuICAgICAgLy8gQWN0dWFsIHBhcmFtZXRlcnNcbiAgICAgIC5wYXJhbWV0ZXIoJ2lkJywgJz1AY3R4LmN1cnJlbnQuaXRlbS5pZCcpXG4gICAgICAucGFyYW1ldGVyKCdleHBlbnNlSXRlbScsICc9QGN0eC5jdXJyZW50Lml0ZW0uZGF0YScpXG5cbiAgLy8gQWN0aW9uc1xuICBjb25zdCBleHBlbnNlTGlzdEJ1dHRvbnMgPSBleHBlbnNlTGlzdFNjcmVlbi5ib3R0b21QYW5lbC5idXR0b25zKClcbiAgZXhwZW5zZUxpc3RCdXR0b25zLmFkZC5nb3RvKCdBZGQgTmV3JywgU0NSRUVOLkFERF9FWFBFTlNFKVxufVxuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBBREQgRVhQRU5TRSBTQ1JFRU5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyogQWRkIGV4cGVuc2UgZm9ybSAqL1xuY29uc3QgYWRkRXhwZW5zZVNjcmVlbiA9IGFwcC5hZGRTY3JlZW5cbiAgLmRlZmF1bHQoU0NSRUVOLkFERF9FWFBFTlNFLCAnQWRkIEV4cGVuc2UnKVxue1xuICAvLyBDbGVhciBwcmV2aW91cyBmb3JtIHN0YXRlIG9uIHNjcmVlbiBmb2N1cyAoZWxzZSB3aWxsIHByZS1wb3B1bGF0ZSB1c2luZyBvbGQgdmFsdWVzKVxuICBjb25zdCBmb3JtSW5zdGFuY2VJZCA9ICdhZGQtZXhwZW5zZS1mb3JtJ1xuICBhZGRFeHBlbnNlU2NyZWVuLm9uRm9jdXNcbiAgICAuY2xlYXJTdGF0ZShgPUBjdHguY29tcG9uZW50cy4ke2Zvcm1JbnN0YW5jZUlkfS5zdGF0ZS52YWx1ZWApXG5cbiAgLy8gRm9ybSBjb250YWluZXJcbiAgY29uc3QgZm9ybSA9IGFkZEV4cGVuc2VTY3JlZW4uYWRkQ29udHJvbFxuICAgIC5mb3JtKHsgaW5zdGFuY2VJZDogZm9ybUluc3RhbmNlSWQgfSlcbiAge1xuXG4gICAgLy8gSWYgc2V0IHRvIHRydWUsIHdpbGwgd2FybiB1c2VyIG9uIGJhY2sgbmF2aWdhdGlvblxuICAgIGZvcm0uZGlzY2FyZENoYW5nZXNBbGVydChmYWxzZSlcblxuICAgIC8vIEZvcm0gZmllbGRzIC0gVGl0bGVcbiAgICBmb3JtLmFkZENvbnRyb2xcbiAgICAgIC8vIFVzZSBpbnN0YW5jZUlkIGZvciBzdGF0ZSB0cmFja2luZyBsYXRlciwgZWcgYD1AY3R4LmNvbXBvbmVudHMudGl0bGUuc3RhdGUudmFsdWVgXG4gICAgICAudGV4dEZpZWxkKHsgaW5zdGFuY2VJZDogJ3RpdGxlJyB9KVxuICAgICAgLmxhYmVsKCdUaXRsZScpXG4gICAgICAucmVxdWlyZWQodHJ1ZSlcbiAgICAgIC5pc0F1dG9Gb2N1c2VkKClcbiAgICAgIC5pc0F1dG9Db3JyZWN0ZWQoKVxuICAgICAgLmF1dG9DYXBpdGFsaXplKCdzZW50ZW5jZXMnKVxuXG4gICAgLy8gQW1vdW50XG4gICAgZm9ybS5hZGRDb250cm9sXG4gICAgICAubnVtYmVyRmllbGQoeyBpbnN0YW5jZUlkOiAnYW1vdW50JyB9KVxuICAgICAgLmxhYmVsKCdBbW91bnQnKVxuICAgICAgLnJlcXVpcmVkKHRydWUpXG4gICAgICAuZm9ybWF0KHsgbnVtYmVyU3R5bGU6ICdjdXJyZW5jeScgfSkgLy8gRm9ybWF0IGFzIGN1cnJlbmN5XG5cbiAgICAvLyBDYXRlZ29yeVxuICAgIGNvbnN0IGNhdGVnb3J5ID0gZm9ybS5hZGRDb250cm9sXG4gICAgICAuZHJvcGRvd24oeyBpc1JlcXVpcmVkOiB0cnVlLCBpbnN0YW5jZUlkOiAnY2F0ZWdvcnlJZCcgfSlcbiAgICAgIC5sYWJlbCgnQ2F0ZWdvcnknKVxuICAgICAgLy8gRHJvcGRvd24gYm91bmQgdG8gc3RhdGljIGRhdGFzb3VyY2VcbiAgICAgIC5kYXRhKERBVEFTT1VSQ0UuRVhQRU5TRV9DQVRFR09SSUVTKVxuICAgIHtcbiAgICAgIC8vIFRlbXBsYXRlIGZvciBkcm9wZG93biBtZW1iZXJzIChwcm9wZXJ0eSBuYW1lcyBmcm9tIGRhdGFzb3VyY2UpXG4gICAgICBjYXRlZ29yeS5pdGVtKClcbiAgICAgICAgLnZhbHVlKCc9QGN0eC5jdXJyZW50Lml0ZW0uaWQnKSAvLyBTdG9yZWQga2V5XG4gICAgICAgIC50aXRsZSgnPUBjdHguY3VycmVudC5pdGVtLm5hbWUnKSAvLyBEaXNwbGF5IHRleHRcbiAgICB9XG5cbiAgICAvLyBEZXNjcmlwdGlvblxuICAgIGZvcm0uYWRkQ29udHJvbFxuICAgICAgLnRleHRGaWVsZCh7IGluc3RhbmNlSWQ6ICdkZXNjcmlwdGlvbicgfSlcbiAgICAgIC5sYWJlbCgnRGVzY3JpcHRpb24nKVxuICAgICAgLnJlcXVpcmVkKGZhbHNlKVxuICAgICAgLmlzTXVsdGlsaW5lKHRydWUpXG4gICAgICAuYXV0b0NhcGl0YWxpemUoJ3NlbnRlbmNlcycpXG4gICAgICAuaXNPcHRpb25hbExhYmVsSGlkZGVuKCkgLy8gSGlkZSBcIm9wdGlvbmFsXCIgbGFiZWxcblxuICAgIC8vIERhdGVcbiAgICBmb3JtLmFkZENvbnRyb2wuZGF0ZVBpY2tlcih7IGlzUmVxdWlyZWQ6IHRydWUsIG1vZGU6ICdkYXRlJywgaW5zdGFuY2VJZDogJ2RhdGUnIH0pXG4gICAgICAubGFiZWwoJ0RhdGUnKVxuICAgICAgLmluaXRpYWxWYWx1ZSgnPSRub3coKScpIC8vIEpzb25hdGEgZXhwcmVzc2lvblxuICB9XG5cbiAgLy8gU2F2ZSB0byBkYXRhYmFzZVxuICBjb25zdCBhZGRFeHBlbnNlQnV0dG9ucyA9IGFkZEV4cGVuc2VTY3JlZW4uYm90dG9tUGFuZWwuYnV0dG9ucygpXG4gIGFkZEV4cGVuc2VCdXR0b25zLmFkZC5leGVjdXRlRW50aXR5KCdTYXZlIEV4cGVuc2UnKVxuICAgIC5keW5hbWljRGF0YShFTlRJVFkuRVhQRU5TRVMsICdjcmVhdGUnKVxuICAgIC5kYXRhKHtcbiAgICAgIC8vIENvbXBvbmVudCBzdGF0ZSBtYXBwZWQgdmlhIGluc3RhbmNlSWRcbiAgICAgIHRpdGxlOiAnPUBjdHguY29tcG9uZW50cy50aXRsZS5zdGF0ZS52YWx1ZScsXG4gICAgICBhbW91bnQ6ICc9QGN0eC5jb21wb25lbnRzLmFtb3VudC5zdGF0ZS52YWx1ZScsXG4gICAgICBjYXRlZ29yeUlkOiAnPUBjdHguY29tcG9uZW50cy5jYXRlZ29yeUlkLnN0YXRlLnZhbHVlJyxcbiAgICAgIGRhdGU6ICc9QGN0eC5jb21wb25lbnRzLmRhdGUuc3RhdGUudmFsdWUnLFxuICAgICAgZGVzY3JpcHRpb246ICc9QGN0eC5jb21wb25lbnRzLmRlc2NyaXB0aW9uLnN0YXRlLnZhbHVlJyxcbiAgICAgIHRpbWVzdGFtcDogJz0kbm93KCknXG4gICAgfSlcbiAgICAuZ29CYWNrKCdwcmV2aW91cycpIC8vIE5hdmlnYXRlIGJhY2sgYWZ0ZXIgc2F2ZSAoc2VlIGRpc2NhcmRDaGFuZ2VzQWxlcnQgYWJvdmUpXG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIEVESVQgRVhQRU5TRSBTQ1JFRU4gIFxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKiBFZGl0IGV4cGVuc2UgZm9ybSAqL1xuY29uc3QgZWRpdEV4cGVuc2VTY3JlZW4gPSBhcHAuYWRkU2NyZWVuXG4gIC5kZWZhdWx0KFNDUkVFTi5FRElUX0VYUEVOU0UsICdFZGl0IEV4cGVuc2UnKVxue1xuICAvLyBGb3JtYWwgcGFyYW1ldGVyc1xuICBlZGl0RXhwZW5zZVNjcmVlblxuICAgIC5pbnB1dChJTlAuc3RyaW5nKCdpZCcpLnJlcXVpcmVkKCkpXG4gICAgLmlucHV0KElOUC5vYmplY3QoJ2V4cGVuc2VJdGVtJykucmVxdWlyZWQoKSlcblxuICBjb25zdCBmb3JtSW5zdGFuY2VJZCA9ICdlZGl0LWV4cGVuc2UtZm9ybSdcblxuICAvLyBFZGl0IGZvcm0gLSBwcmUtcG9wdWxhdGVkXG4gIGNvbnN0IGZvcm0gPSBlZGl0RXhwZW5zZVNjcmVlbi5hZGRDb250cm9sLmZvcm0oeyBpbnN0YW5jZUlkOiBmb3JtSW5zdGFuY2VJZCB9KVxuICB7XG4gICAgZm9ybS5kaXNjYXJkQ2hhbmdlc0FsZXJ0KGZhbHNlKVxuXG4gICAgLy8gRm9ybSBmaWVsZHMgLSBUaXRsZVxuICAgIGZvcm0uYWRkQ29udHJvbFxuICAgICAgLnRleHRGaWVsZCh7IGluc3RhbmNlSWQ6ICd0aXRsZScgfSlcbiAgICAgIC5sYWJlbCgnVGl0bGUnKVxuICAgICAgLnJlcXVpcmVkKHRydWUpXG4gICAgICAuaXNBdXRvRm9jdXNlZCgpXG4gICAgICAuaXNBdXRvQ29ycmVjdGVkKClcbiAgICAgIC5hdXRvQ2FwaXRhbGl6ZSgnc2VudGVuY2VzJylcbiAgICAgIC8vIFByZS1wb3B1bGF0ZSBmcm9tIGlucHV0cy4gQnkgY29udmVudGlvbiwgaW5zdGFuY2VJZCBtYXRjaGVzIGRhdGEtZmllbGQvc3RhdGVcbiAgICAgIC5pbml0aWFsVmFsdWUoYD1AY3R4LmlucHV0cy5leHBlbnNlSXRlbS50aXRsZWApXG5cbiAgICAvLyBBbW91bnRcbiAgICBmb3JtLmFkZENvbnRyb2xcbiAgICAgIC5udW1iZXJGaWVsZCh7IGluc3RhbmNlSWQ6ICdhbW91bnQnIH0pXG4gICAgICAubGFiZWwoJ0Ftb3VudCcpXG4gICAgICAucmVxdWlyZWQodHJ1ZSlcbiAgICAgIC5mb3JtYXQoeyBudW1iZXJTdHlsZTogJ2N1cnJlbmN5JyB9KVxuICAgICAgLmluaXRpYWxWYWx1ZShgPUBjdHguaW5wdXRzLmV4cGVuc2VJdGVtLmFtb3VudGApXG5cbiAgICAvLyBDYXRlZ29yeVxuICAgIGNvbnN0IGNhdGVnb3J5ID0gZm9ybS5hZGRDb250cm9sXG4gICAgICAuZHJvcGRvd24oeyBpc1JlcXVpcmVkOiB0cnVlLCBpbnN0YW5jZUlkOiAnY2F0ZWdvcnlJZCcgfSlcbiAgICAgIC5sYWJlbCgnQ2F0ZWdvcnknKVxuICAgICAgLmRhdGEoREFUQVNPVVJDRS5FWFBFTlNFX0NBVEVHT1JJRVMpXG4gICAgICAuaW5pdGlhbFZhbHVlKGA9QGN0eC5pbnB1dHMuZXhwZW5zZUl0ZW0uY2F0ZWdvcnlJZGApXG4gICAge1xuICAgICAgY2F0ZWdvcnkuaXRlbSgpXG4gICAgICAgIC52YWx1ZSgnPUBjdHguY3VycmVudC5pdGVtLmlkJylcbiAgICAgICAgLnRpdGxlKCc9QGN0eC5jdXJyZW50Lml0ZW0ubmFtZScpXG4gICAgfVxuXG4gICAgLy8gRGVzY3JpcHRpb25cbiAgICBmb3JtLmFkZENvbnRyb2xcbiAgICAgIC50ZXh0RmllbGQoeyBpbnN0YW5jZUlkOiAnZGVzY3JpcHRpb24nIH0pXG4gICAgICAubGFiZWwoJ0Rlc2NyaXB0aW9uJylcbiAgICAgIC5yZXF1aXJlZChmYWxzZSlcbiAgICAgIC5pc011bHRpbGluZSh0cnVlKVxuICAgICAgLmF1dG9DYXBpdGFsaXplKCdzZW50ZW5jZXMnKVxuICAgICAgLmluaXRpYWxWYWx1ZSgnPUBjdHguaW5wdXRzLmV4cGVuc2VJdGVtLmRlc2NyaXB0aW9uJylcblxuICAgIC8vIERhdGVcbiAgICBmb3JtLmFkZENvbnRyb2xcbiAgICAgIC5kYXRlUGlja2VyKHsgaXNSZXF1aXJlZDogdHJ1ZSwgbW9kZTogJ2RhdGUnLCBpbnN0YW5jZUlkOiAnZGF0ZScgfSlcbiAgICAgIC5sYWJlbCgnRGF0ZScpXG4gICAgICAuaW5pdGlhbFZhbHVlKGA9QGN0eC5pbnB1dHMuZXhwZW5zZUl0ZW0uZGF0ZWApXG4gIH1cblxuICAvLyBCdXR0b25zL2FjdGlvbnNcbiAgY29uc3QgZWRpdEJ1dHRvbnMgPSBlZGl0RXhwZW5zZVNjcmVlbi5ib3R0b21QYW5lbC5idXR0b25zKDIpXG5cbiAgLy8gQnV0dG9uL2FjdGlvbiB0byBVUERBVEUgc2VsZWN0ZWQgZXhwZW5zZVxuICBlZGl0QnV0dG9ucy5hZGRcbiAgICAuZXhlY3V0ZUVudGl0eSgnVXBkYXRlJylcbiAgICAuZHluYW1pY0RhdGEoRU5USVRZLkVYUEVOU0VTLCAndXBkYXRlJykgLy8gJ3VwZGF0ZScgPSBtb2RpZnkgZXhpc3RpbmdcbiAgICAuZGF0YSh7XG4gICAgICBpZDogYD1AY3R4LmlucHV0cy5pZGAsIC8vIExvY2F0b3JcbiAgICAgIC8vIEluY2x1ZGUgYWxsIGZpZWxkcyBmb3IgdXBkYXRlXG4gICAgICB0aXRsZTogJz1AY3R4LmNvbXBvbmVudHMudGl0bGUuc3RhdGUudmFsdWUnLFxuICAgICAgYW1vdW50OiAnPUBjdHguY29tcG9uZW50cy5hbW91bnQuc3RhdGUudmFsdWUnLFxuICAgICAgY2F0ZWdvcnlJZDogJz1AY3R4LmNvbXBvbmVudHMuY2F0ZWdvcnlJZC5zdGF0ZS52YWx1ZScsXG4gICAgICBkYXRlOiAnPUBjdHguY29tcG9uZW50cy5kYXRlLnN0YXRlLnZhbHVlJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnPUBjdHguY29tcG9uZW50cy5kZXNjcmlwdGlvbi5zdGF0ZS52YWx1ZScsXG4gICAgICB0aW1lc3RhbXA6ICc9JG5vdygpJ1xuICAgIH0pXG4gICAgLmdvQmFjaygncHJldmlvdXMnKSAvLyBOYXZpZ2F0ZSBiYWNrIGFmdGVyIHVwZGF0ZSAoc2VlIGRpc2NhcmRDaGFuZ2VzQWxlcnQgYWJvdmUpXG5cbiAgLy8gQWRkIGJ1dHRvbiB0byBERUxFVEUgd2l0aCBjb25maXJtYXRpb24gbW9kYWxcbiAgY29uc3QgZGVsZXRlQnV0dG9uID0gZWRpdEJ1dHRvbnMuYWRkLmNvbmZpcm0oJ0RlbGV0ZScpXG4gIGRlbGV0ZUJ1dHRvbi5tb2RhbCh7IHRleHQ6ICdEZWxldGUgRXhwZW5zZT8nIH0pIC8vIFVzZXIgbXVzdCBjb25maXJtXG4gICAgLmNvbmZpcm1UZXh0KCdEZWxldGUnKVxuICAgIC5jYW5jZWxUZXh0KCdDYW5jZWwnKVxuICBkZWxldGVCdXR0b24ub25Db25maXJtZWQuZXhlY3V0ZUVudGl0eSgpXG4gICAgLmR5bmFtaWNEYXRhKEVOVElUWS5FWFBFTlNFUywgJ2RlbGV0ZScpIC8vICdkZWxldGUnID0gcmVtb3ZlIHJlY29yZFxuICAgIC5kYXRhKHtcbiAgICAgIGlkOiBgPUBjdHguaW5wdXRzLmlkYCAvLyBMb2NhdG9yXG4gICAgfSlcbiAgICAuZ29CYWNrKCdwcmV2aW91cycpXG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIE5BVklHQVRJT04gLSBUYWIgYmFyIGNvbmZpZ3VyYXRpb25cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLy8gQm90dG9tIG5hdmlnYXRpb24gLSBlbnRyeSBwb2ludFxuYXBwLmFkZFRhYihUQUJTLkhPTUUsICdjcmVkaXQtY2FyZCcpIC8vIEljb24gbmFtZVxuICAubGFiZWwoJ0V4cGVuc2VzJykgLy8gVGFiIHRleHRcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gQlVJTEQgLSBHZW5lcmF0ZSBvdXRwdXRcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLy8gQ1JJVElDQUw6IE1VU1QgY2FsbCBidWlsZCgpXG5hcHAuYnVpbGQoKVxuIl19