dodanie do formularza końca tury pola na relacje dyplomatyczne. Poprawienie wiadomości o przebiegu obsługi końca tury. Naprawienie wartości user w formularzu końca tury przy obsłudze państw. Dodanie do nagłówka ChartTooltip "Tura: ". Poprawienie przycisku Browse... w inputach typu file. Dodanie wykresów do własności miast w kategorii misc.

This commit is contained in:
installer
2024-11-18 00:38:56 +01:00
parent 46f76e3c64
commit 142561a2dd
16 changed files with 300 additions and 55 deletions

View File

@@ -11,7 +11,7 @@ RUN bun run build-only
FROM alpine:latest
LABEL maintainer="garandplg@garandplg.com"
LABEL version="0.0.16"
LABEL version="0.0.17"
LABEL description="Garand's WG. Pocketbase + Bun + Vue3 + Vite + TypeScript + TailwindCSS + Shadcn-vue"
WORKDIR /pb

View File

@@ -1,6 +1,6 @@
{
"name": "garands-world-game",
"version": "0.0.16",
"version": "0.0.17",
"private": true,
"type": "module",
"scripts": {

View File

@@ -0,0 +1,29 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("ytob8a12887fu67")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "hmr8zx60",
"name": "relations",
"type": "json",
"required": true,
"presentable": false,
"unique": false,
"options": {
"maxSize": 2000000
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("ytob8a12887fu67")
// remove
collection.schema.removeField("hmr8zx60")
return dao.saveCollection(collection)
})

View File

@@ -0,0 +1,40 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("ytob8a12887fu67")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "hmr8zx60",
"name": "relations",
"type": "json",
"required": false,
"presentable": false,
"unique": false,
"options": {
"maxSize": 2000000
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("ytob8a12887fu67")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "hmr8zx60",
"name": "relations",
"type": "json",
"required": true,
"presentable": false,
"unique": false,
"options": {
"maxSize": 2000000
}
}))
return dao.saveCollection(collection)
})

View File

@@ -13,16 +13,24 @@ const props = defineProps<{
description: string;
}>();
let turn = (await pb.collection('turns').getFullList({ sort: '-value', limit: 1 }))[0];
const data = cacheFullListResponses();
let turns = await data.fetchTurnsData().then(turns => turns.reverse());
let turn = turns[0];
if (turn === undefined)
try {
turn = await pb.collection('turns').create({ value: 0 });
turns = await data.fetchTurnsData().then(turns => turns.reverse());
} catch (error: unknown) {
if (error instanceof Error) throw error;
else console.error('Błąd przy tworzeniu tury zerowej:', 'Nieznany błąd.');
}
const statesFromPreviousTurn = await data
.fetchStatesData()
.then(states => states.filter(state => state.turn === turns[1].id));
const formSchema = toTypedSchema(
// FIXME: Naprawić walidację zod'a dla pól plikowych
z.object({
@@ -42,6 +50,9 @@ const formSchema = toTypedSchema(
provincesCSV: z
.instanceof(File)
.refine(file => file.type === 'text/csv', 'Tylko pliki CSV są dozwolone'),
relationsCSV: z
.instanceof(File)
.refine(file => file.type === 'text/csv', 'Tylko pliki CSV są dozwolone'),
statesByNumberOfCellsGroupedByBiomeCSV: z
.instanceof(File)
.refine(file => file.type === 'text/csv', 'Tylko pliki CSV są dozwolone'),
@@ -68,7 +79,8 @@ const CSVFilesForms = [
createCSVForm('burgsCSV', 'Miast', 'get-burgs-csv', 'Plik CSV'),
createCSVForm('culturesCSV', 'Kultur', 'get-cultures-csv', 'Plik CSV'),
createCSVForm('religionsCSV', 'Religii', 'get-religions-csv', 'Plik CSV'),
createCSVForm('provincesCSV', 'Prowincje', 'get-provinces-csv', 'Plik CSV')
createCSVForm('provincesCSV', 'Prowincje', 'get-provinces-csv', 'Plik CSV'),
createCSVForm('relationsCSV', 'Relacje dyplomatyczne', 'get-provinces-csv', 'Plik CSV')
];
const GroupedCSVFilesForms = [
@@ -120,6 +132,7 @@ const onSubmit = handleSubmit(async values => {
const culturesData = await parseCSVFile(values.culturesCSV);
const religionsData = await parseCSVFile(values.religionsCSV);
const provincesData = await parseCSVFile(values.provincesCSV);
const relationsData = await parseCSVFile(values.relationsCSV);
const statesByNumberOfCellsGroupedByBiomeData = await parseCSVFile(
values.statesByNumberOfCellsGroupedByBiomeCSV
);
@@ -138,6 +151,7 @@ const onSubmit = handleSubmit(async values => {
!culturesData ||
!religionsData ||
!provincesData ||
!relationsData ||
!statesByNumberOfCellsGroupedByBiomeData ||
!statesByNumberOfCellsGroupedByReligionsData ||
!statesByNumberOfCellsGroupedByCulturesData ||
@@ -151,6 +165,13 @@ const onSubmit = handleSubmit(async values => {
const religionsResponses: Record<string, string> = {};
const culturesResponses: Record<string, string> = {};
const relationsFinalData = relationsData.reduce((acc, relation) => {
const state = relation[''];
const { ['']: _1, [state]: _2, ...rest } = relation;
acc[state] = rest;
return acc;
}, {});
const statesByNumberOfCellsGroupedByCulturesFinalData =
statesByNumberOfCellsGroupedByCulturesData.reduce((acc, current) => {
acc[current.Name] = acc[current.Name] || {};
@@ -182,6 +203,8 @@ const onSubmit = handleSubmit(async values => {
return acc;
}, {});
const startTimeAll = new Date().getTime();
let startTime = new Date().getTime();
for (let i = 0; i < religionsData.length; i++) {
try {
const response = await pb.collection('religions').create({
@@ -202,6 +225,14 @@ const onSubmit = handleSubmit(async values => {
}
}
let endTime = new Date().getTime();
toast({
title: 'Religie zostały dodane',
description: `Dodano ${religionsData.length} religii w czasie: ${((endTime - startTime) / 1000).toFixed(3)} s`
});
startTime = new Date().getTime();
for (let i = 0; i < culturesData.length; i++) {
try {
const response = await pb.collection('cultures').create({
@@ -220,13 +251,23 @@ const onSubmit = handleSubmit(async values => {
else console.error('Błąd przy tworzeniu kultury:', 'Nieznany błąd.');
}
}
endTime = new Date().getTime();
toast({
title: 'Kultury zostały dodane',
description: `Dodano ${culturesData.length} kultur w czasie: ${((endTime - startTime) / 1000).toFixed(3)} s`
});
startTime = new Date().getTime();
for (let i = 0; i < statesData.length; i++) {
try {
// const user = await pb.collection('states').getFirstListItem()
const stateFromPreviousTurn = statesFromPreviousTurn.find(
state => state.name === statesData[i].State
);
const user = stateFromPreviousTurn?.user ? stateFromPreviousTurn.user : null;
const response = await pb.collection('states').create({
user: 'esfa1bg2ovpxd0h', // TODO: poprawić pobieranie usera
user: user,
turn: turn.id,
name: statesData[i].State,
nameFull: statesData[i]['Full Name'],
@@ -240,7 +281,8 @@ const onSubmit = handleSubmit(async values => {
culture: culturesResponses[statesData[i].Culture],
culturesCells: statesByNumberOfCellsGroupedByCulturesFinalData[statesData[i].State],
religionsCells: statesByNumberOfCellsGroupedByReligionsFinalData[statesData[i].State],
biomesCells: statesByNumberOfCellsGroupedByBiomeFinalData[statesData[i].State]
biomesCells: statesByNumberOfCellsGroupedByBiomeFinalData[statesData[i].State],
relations: relationsFinalData[statesData[i].State]
});
statesResponses[response.name] = response.id;
@@ -249,7 +291,14 @@ const onSubmit = handleSubmit(async values => {
else console.error('Błąd przy tworzeniu państwa:', 'Nieznany błąd.');
}
}
endTime = new Date().getTime();
toast({
title: 'Państwa zostały dodane',
description: `Dodano ${statesData.length} państw w czasie: ${((endTime - startTime) / 1000).toFixed(3)} s`
});
startTime = new Date().getTime();
for (let i = 0; i < provincesData.length; i++) {
try {
const response = await pb.collection('provinces').create({
@@ -272,7 +321,14 @@ const onSubmit = handleSubmit(async values => {
else console.error('Błąd przy tworzeniu prowincji:', 'Nieznany błąd.');
}
}
endTime = new Date().getTime();
toast({
title: 'Prowincje zostały dodane',
description: `Dodano ${provincesData.length} prowincji w czasie: ${((endTime - startTime) / 1000).toFixed(3)} s`
});
startTime = new Date().getTime();
for (let i = 0; i < burgsData.length; i++) {
try {
await pb.collection('burgs').create({
@@ -298,13 +354,22 @@ const onSubmit = handleSubmit(async values => {
else console.error('Błąd przy tworzeniu miasta:', 'Nieznany błąd.');
}
}
endTime = new Date().getTime();
toast({
title: 'Miasta zostały dodane',
description: `Dodano ${burgsData.length} miast w czasie: ${((endTime - startTime) / 1000).toFixed(3)} s`
});
// FIXME: Poprawić drawio, żeby znowu odwzorowywał obecną bazę danych
startTime = new Date().getTime();
try {
const nextTurn = await pb.collection('turns').create({ value: turn.value + 1 });
turn = await pb.collection('turns').create({ value: turn.value + 1 });
turns = await pb.collection('turns').getFullList({ sort: '-value' });
await pb.collection('maps').create({
turn: nextTurn.id,
turn: turn.id,
name: values.map.name.split(/ (?=\d{4}-\d{2}-\d{2}-\d{2}-\d{2})/)[0],
file: values.map
});
@@ -312,10 +377,17 @@ const onSubmit = handleSubmit(async values => {
if (error instanceof Error) throw error;
else console.error('Błąd przy tworzeniu mapy:', 'Nieznany błąd.');
}
endTime = new Date().getTime();
const endTimeAll = new Date().getTime();
toast({
title: 'Mapa i nowa tura zostały dodane!',
description: `Dodano mapę i nową turę w czasie: ${((endTime - startTime) / 1000).toFixed(3)} s`
});
toast({
title: 'Udało się zakończyć turę!',
description: 'Dane z plików csv zostały wgrane do bazy danych.'
description: `Zakończono turę w czasie: ${((endTimeAll - startTimeAll) / 1000).toFixed(3)} s`
});
});

View File

@@ -16,9 +16,7 @@ props.data.sort((a, b) => b.value - a.value);
<template>
<Card class="text-sm">
<CardHeader v-if="title" class="border-b p-3">
<CardTitle>
{{ title }}
</CardTitle>
<CardTitle> <span class="text-primary">Tura</span>: {{ title }} </CardTitle>
</CardHeader>
<CardContent class="flex min-w-[180px] flex-col gap-1 p-3">
<ScrollArea class="h-full w-full">

View File

@@ -20,7 +20,6 @@ const modelValue = useVModel(props, 'modelValue', emits, {
</script>
<template>
<!-- TODO: Spróbować zmienić kolor Browse... w inputach typu file na --primary -->
<input
v-model="modelValue"
:class="
@@ -31,3 +30,9 @@ const modelValue = useVModel(props, 'modelValue', emits, {
"
/>
</template>
<style>
input[type='file']::file-selector-button {
color: var(--primary);
}
</style>

View File

@@ -2,7 +2,7 @@ import type { Component, VNode } from 'vue';
import { computed, ref } from 'vue';
import type { ToastProps } from '.';
const TOAST_LIMIT = 1;
const TOAST_LIMIT = 5;
const TOAST_REMOVE_DELAY = 1000000;
export type StringOrVNode = string | VNode | (() => VNode);

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<title>Garand's WG</title>
</head>
<body>
<div id="app"></div>

View File

@@ -57,6 +57,15 @@ const dataForCharts: DataForCharts = {
population: [],
burgs: []
}
},
misc: {
config: {
categories: ['Capitals', 'Ports', 'Citadels', 'Walls', 'Plazas', 'Temples', 'Shanty Towns'],
colors: ['#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF', '#FFFFFF']
},
charts: {
features: []
}
}
};
@@ -82,6 +91,12 @@ for (let i = 0; i < configsDataSets.length; i++) {
for (let i = 0; i < turnsData.length - 1; i++) {
const turn = turnsData[i];
const statesDataInTurn = statesData.filter(state => state.turn === turn.id);
const provincesDataInTurn = provincesData.filter(province => province.turn === turn.id);
const buildingsFeaturesDataItems: ChartData[] = [{ turn: turn.value }];
const burgsDataInTurn = burgsData.filter(burg => burg.turn === turn.id);
const culturesDataInTurn = culturesData.filter(culture => culture.turn === turn.id);
const religionsDataInTurn = religionsData.filter(religion => religion.turn === turn.id);
const statesDataItems: ChartData[] = [
{ turn: turn.value },
@@ -91,12 +106,12 @@ for (let i = 0; i < turnsData.length - 1; i++) {
{ turn: turn.value }
];
for (let i = 0; i < statesData.length; i++) {
const state = statesData[i];
for (let j = 0; j < statesDataInTurn.length; j++) {
const state = statesDataInTurn[j];
statesDataItems[0][state.name] = state.burgs;
statesDataItems[1][state.name] = provincesData.filter(
province => province.turn === turn.id && province.state === state.id
province => province.state === state.id
).length;
statesDataItems[2][state.name] = state.cells;
statesDataItems[3][state.name] = state.ruralPopulation;
@@ -116,8 +131,8 @@ for (let i = 0; i < turnsData.length - 1; i++) {
{ turn: turn.value }
];
for (let i = 0; i < provincesData.length; i++) {
const province = provincesData[i];
for (let j = 0; j < provincesDataInTurn.length; j++) {
const province = provincesDataInTurn[j];
provincesDataItems[0][province.name] = province.burgs;
provincesDataItems[1][province.name] = province.cells;
@@ -130,35 +145,29 @@ for (let i = 0; i < turnsData.length - 1; i++) {
dataForCharts.provinces.charts.ruralPopulation.push(provincesDataItems[2]);
dataForCharts.provinces.charts.urbanPopulation.push(provincesDataItems[3]);
const burgsDataItems: ChartData[] = [
{ turn: turn.value }
// { turn: turn.value }
];
const burgsDataItems: ChartData[] = [{ turn: turn.value }];
const burgsBuildings = [0, 0, 0, 0, 0, 0, 0];
for (let i = 0; i < burgsData.length; i++) {
const burg = burgsData[i];
const buildingsFeatures = [0, 0, 0, 0, 0, 0, 0];
for (let j = 0; j < burgsDataInTurn.length; j++) {
const burg = burgsData[j];
burgsDataItems[0][burg.name] = burg.population;
// if (burg.isCapital) burgsBuildings[0]++;
// if (burg.isPort) burgsBuildings[1]++;
// if (burg.isCitadel) burgsBuildings[2]++;
// if (burg.isWalls) burgsBuildings[3]++;
// if (burg.isPlaza) burgsBuildings[4]++;
// if (burg.isTemple) burgsBuildings[5]++;
// if (burg.isShantyTown) burgsBuildings[6]++;
if (burg.isCapital) buildingsFeatures[0]++;
if (burg.isPort) buildingsFeatures[1]++;
if (burg.isCitadel) buildingsFeatures[2]++;
if (burg.isWalls) buildingsFeatures[3]++;
if (burg.isPlaza) buildingsFeatures[4]++;
if (burg.isTemple) buildingsFeatures[5]++;
if (burg.isShantyTown) buildingsFeatures[6]++;
// burgsDataItems[1]['Capitals'] = burgsBuildings[0];
// burgsDataItems[1]['Ports'] = burgsBuildings[1];
// burgsDataItems[1]['Citadels'] = burgsBuildings[2];
// burgsDataItems[1]['Walls'] = burgsBuildings[3];
// burgsDataItems[1]['Plazas'] = burgsBuildings[4];
// burgsDataItems[1]['Temples'] = burgsBuildings[5];
// burgsDataItems[1]['Shanty Towns'] = burgsBuildings[6];
const categories = dataForCharts.misc.config.categories;
for (let k = 0; k < categories.length; k++) {
buildingsFeaturesDataItems[0][categories[k]] = buildingsFeatures[k];
}
}
dataForCharts.burgs.charts.population.push(burgsDataItems[0]);
// dataForCharts.burgs.charts.features.push(burgsDataItems[1]);
dataForCharts.misc.charts.features.push(buildingsFeaturesDataItems[0]);
const culturesDataItems: ChartData[] = [
{ turn: turn.value },
@@ -166,9 +175,9 @@ for (let i = 0; i < turnsData.length - 1; i++) {
{ turn: turn.value }
];
for (let i = 0; i < culturesData.length; i++) {
const culture = culturesData[i];
const burgsCount = burgsData.filter(burg => burg.culture === culture.id).length;
for (let j = 0; j < culturesDataInTurn.length; j++) {
const culture = culturesDataInTurn[j];
const burgsCount = burgsDataInTurn.filter(burg => burg.culture === culture.id).length;
culturesDataItems[0][culture.name] = culture.cells;
culturesDataItems[1][culture.name] = culture.population;
@@ -185,9 +194,9 @@ for (let i = 0; i < turnsData.length - 1; i++) {
{ turn: turn.value }
];
for (let i = 0; i < religionsData.length; i++) {
const religion = religionsData[i];
const burgsCount = burgsData.filter(burg => burg.religion === religion.id).length;
for (let j = 0; j < religionsDataInTurn.length; j++) {
const religion = religionsDataInTurn[j];
const burgsCount = burgsDataInTurn.filter(burg => burg.religion === religion.id).length;
religionsDataItems[0][religion.name] = religion.cells;
religionsDataItems[1][religion.name] = religion.population;
@@ -269,10 +278,18 @@ const tabs: {
labels: ['Ilość kratek', 'Ilość populacji', 'Ilość miast'],
charts: dataForCharts.religions.charts
}
},
{
name: 'misc',
label: 'Pozostałe',
description: 'pozostałe statystyki',
props: {
config: dataForCharts.misc.config,
labels: ['Własności miast'],
charts: dataForCharts.misc.charts
}
}
];
console.log(tabs);
</script>
<template>

View File

@@ -4,6 +4,7 @@ useHead({
title: "Garand's WG"
});
// const pb = usePocketBase();
const { toast } = useToast();
</script>
<template>
@@ -12,7 +13,7 @@ useHead({
>Witaj na <span class="text-primary">Garandowym WG</span>!</h1
>
<p class="mb-4 text-sm">Wersja: <span class="text-primary">0.0.16 Indev</span></p>
<p class="mb-4 text-sm">Wersja: <span class="text-primary">0.0.17 Indev</span></p>
<a
href="https://discord.gg/vhBmFVpR3w"

View File

@@ -1,5 +1,4 @@
const ONE_HOUR = 60 * 60 * 1000; // 1 godzina w milisekundach
// TODO: Napisać unit testy dla cacheFullListResponses
export const cacheFullListResponses = defineStore('cacheFullListResponses', () => {
const pb = usePocketBase();

View File

@@ -41,6 +41,12 @@ interface DataForCharts {
burgs: ChartData[];
};
};
misc: {
config: Config;
charts: {
features: ChartData[];
};
};
}
interface ChartData {

View File

@@ -2,7 +2,7 @@
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {};
export {}
/* prettier-ignore */
declare module 'vue' {

View File

@@ -286,6 +286,7 @@ export enum StatesFormOptions {
export type StatesRecord<
TbiomesCells = unknown,
TculturesCells = unknown,
Trelations = unknown,
TreligionsCells = unknown
> = {
biomesCells?: null | TbiomesCells;
@@ -298,6 +299,7 @@ export type StatesRecord<
form?: StatesFormOptions;
name: string;
nameFull?: string;
relations?: null | Trelations;
religionsCells?: null | TreligionsCells;
ruralPopulation?: number;
turn: RecordIdString;
@@ -338,9 +340,10 @@ export type ReportsResponse<Texpand = unknown> = Required<ReportsRecord> &
export type StatesResponse<
TbiomesCells = unknown,
TculturesCells = unknown,
Trelations = unknown,
TreligionsCells = unknown,
Texpand = unknown
> = Required<StatesRecord<TbiomesCells, TculturesCells, TreligionsCells>> &
> = Required<StatesRecord<TbiomesCells, TculturesCells, Trelations, TreligionsCells>> &
BaseSystemFields<Texpand>;
export type TurnsResponse<Texpand = unknown> = Required<TurnsRecord> & BaseSystemFields<Texpand>;
export type UsersResponse<Texpand = unknown> = Required<UsersRecord> & AuthSystemFields<Texpand>;

View File

@@ -0,0 +1,75 @@
// vite.config.ts
import path from "path";
import { unheadVueComposablesImports } from "file:///home/garand_plg/Projects/garands-world-game/node_modules/@unhead/vue/dist/index.mjs";
import vue from "file:///home/garand_plg/Projects/garands-world-game/node_modules/@vitejs/plugin-vue/dist/index.mjs";
import AutoImport from "file:///home/garand_plg/Projects/garands-world-game/node_modules/unplugin-auto-import/dist/vite.js";
import Components from "file:///home/garand_plg/Projects/garands-world-game/node_modules/unplugin-vue-components/dist/vite.js";
import VueRouter from "file:///home/garand_plg/Projects/garands-world-game/node_modules/unplugin-vue-router/dist/vite.js";
import { defineConfig } from "file:///home/garand_plg/Projects/garands-world-game/node_modules/vite/dist/node/index.js";
import vueDevTools from "file:///home/garand_plg/Projects/garands-world-game/node_modules/vite-plugin-vue-devtools/dist/vite.mjs";
import autoprefixer from "file:///home/garand_plg/Projects/garands-world-game/node_modules/autoprefixer/lib/autoprefixer.js";
import tailwind from "file:///home/garand_plg/Projects/garands-world-game/node_modules/tailwindcss/lib/index.js";
var __vite_injected_original_dirname = "/home/garand_plg/Projects/garands-world-game";
var vite_config_default = defineConfig({
plugins: [
VueRouter({
dts: "./src/types/typed-router.d.ts",
importMode: "async"
}),
vue(),
vueDevTools(),
AutoImport({
dts: "./types/auto-imports.d.ts",
dirs: ["./composables", "./stores", "./components/ui/**/*.ts"],
imports: [
"vue",
"vue-router",
"pinia",
"@vueuse/core",
"vitest",
unheadVueComposablesImports
],
eslintrc: {
enabled: true
},
packagePresets: ["pocketbase"]
}),
Components({
dts: "./types/components.d.ts",
dirs: ["./components/**"],
directoryAsNamespace: true,
collapseSamePrefixes: true,
extensions: ["vue"],
deep: true,
include: [/\.vue$/, /\.vue\?vue/],
exclude: [/node_modules/]
})
],
resolve: {
alias: {
"@": path.resolve(__vite_injected_original_dirname, "./src")
}
},
css: {
postcss: {
plugins: [tailwind(), autoprefixer()]
}
},
server: {
proxy: {
"/pb": {
target: "http://localhost:8090/",
changeOrigin: true,
rewrite: (path2) => path2.replace(/^\/pb/, "")
}
}
},
root: "src",
build: {
outDir: "../dist"
}
});
export {
vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvaG9tZS9nYXJhbmRfcGxnL1Byb2plY3RzL2dhcmFuZHMtd29ybGQtZ2FtZVwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL2hvbWUvZ2FyYW5kX3BsZy9Qcm9qZWN0cy9nYXJhbmRzLXdvcmxkLWdhbWUvdml0ZS5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL2hvbWUvZ2FyYW5kX3BsZy9Qcm9qZWN0cy9nYXJhbmRzLXdvcmxkLWdhbWUvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcblxuaW1wb3J0IHsgdW5oZWFkVnVlQ29tcG9zYWJsZXNJbXBvcnRzIH0gZnJvbSAnQHVuaGVhZC92dWUnO1xuaW1wb3J0IHZ1ZSBmcm9tICdAdml0ZWpzL3BsdWdpbi12dWUnO1xuaW1wb3J0IEF1dG9JbXBvcnQgZnJvbSAndW5wbHVnaW4tYXV0by1pbXBvcnQvdml0ZSc7XG5pbXBvcnQgQ29tcG9uZW50cyBmcm9tICd1bnBsdWdpbi12dWUtY29tcG9uZW50cy92aXRlJztcbmltcG9ydCBWdWVSb3V0ZXIgZnJvbSAndW5wbHVnaW4tdnVlLXJvdXRlci92aXRlJztcbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gJ3ZpdGUnO1xuaW1wb3J0IHZ1ZURldlRvb2xzIGZyb20gJ3ZpdGUtcGx1Z2luLXZ1ZS1kZXZ0b29scyc7XG4vLyBpbXBvcnQgeyBWdWVSb3V0ZXJBdXRvSW1wb3J0cyB9IGZyb20gJ3VucGx1Z2luLXZ1ZS1yb3V0ZXInO1xuXG5pbXBvcnQgYXV0b3ByZWZpeGVyIGZyb20gJ2F1dG9wcmVmaXhlcic7XG5pbXBvcnQgdGFpbHdpbmQgZnJvbSAndGFpbHdpbmRjc3MnO1xuXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcblx0cGx1Z2luczogW1xuXHRcdFZ1ZVJvdXRlcih7XG5cdFx0XHRkdHM6ICcuL3NyYy90eXBlcy90eXBlZC1yb3V0ZXIuZC50cycsXG5cdFx0XHRpbXBvcnRNb2RlOiAnYXN5bmMnXG5cdFx0fSksXG5cdFx0dnVlKCksXG5cdFx0dnVlRGV2VG9vbHMoKSxcblx0XHRBdXRvSW1wb3J0KHtcblx0XHRcdGR0czogJy4vdHlwZXMvYXV0by1pbXBvcnRzLmQudHMnLFxuXHRcdFx0ZGlyczogWycuL2NvbXBvc2FibGVzJywgJy4vc3RvcmVzJywgJy4vY29tcG9uZW50cy91aS8qKi8qLnRzJ10sXG5cdFx0XHRpbXBvcnRzOiBbXG5cdFx0XHRcdCd2dWUnLFxuXHRcdFx0XHQndnVlLXJvdXRlcicsXG5cdFx0XHRcdCdwaW5pYScsXG5cdFx0XHRcdCdAdnVldXNlL2NvcmUnLFxuXHRcdFx0XHQndml0ZXN0Jyxcblx0XHRcdFx0dW5oZWFkVnVlQ29tcG9zYWJsZXNJbXBvcnRzXG5cdFx0XHRdLFxuXHRcdFx0ZXNsaW50cmM6IHtcblx0XHRcdFx0ZW5hYmxlZDogdHJ1ZVxuXHRcdFx0fSxcblx0XHRcdHBhY2thZ2VQcmVzZXRzOiBbJ3BvY2tldGJhc2UnXVxuXHRcdH0pLFxuXHRcdENvbXBvbmVudHMoe1xuXHRcdFx0ZHRzOiAnLi90eXBlcy9jb21wb25lbnRzLmQudHMnLFxuXHRcdFx0ZGlyczogWycuL2NvbXBvbmVudHMvKionXSxcblx0XHRcdGRpcmVjdG9yeUFzTmFtZXNwYWNlOiB0cnVlLFxuXHRcdFx0Y29sbGFwc2VTYW1lUHJlZml4ZXM6IHRydWUsXG5cdFx0XHRleHRlbnNpb25zOiBbJ3Z1ZSddLFxuXHRcdFx0ZGVlcDogdHJ1ZSxcblx0XHRcdGluY2x1ZGU6IFsvXFwudnVlJC8sIC9cXC52dWVcXD92dWUvXSxcblx0XHRcdGV4Y2x1ZGU6IFsvbm9kZV9tb2R1bGVzL11cblx0XHR9KVxuXHRdLFxuXHRyZXNvbHZlOiB7XG5cdFx0YWxpYXM6IHtcblx0XHRcdCdAJzogcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgJy4vc3JjJylcblx0XHR9XG5cdH0sXG5cdGNzczoge1xuXHRcdHBvc3Rjc3M6IHtcblx0XHRcdHBsdWdpbnM6IFt0YWlsd2luZCgpLCBhdXRvcHJlZml4ZXIoKV1cblx0XHR9XG5cdH0sXG5cdHNlcnZlcjoge1xuXHRcdHByb3h5OiB7XG5cdFx0XHQnL3BiJzoge1xuXHRcdFx0XHR0YXJnZXQ6ICdodHRwOi8vbG9jYWxob3N0OjgwOTAvJyxcblx0XHRcdFx0Y2hhbmdlT3JpZ2luOiB0cnVlLFxuXHRcdFx0XHRyZXdyaXRlOiBwYXRoID0+IHBhdGgucmVwbGFjZSgvXlxcL3BiLywgJycpXG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXHRyb290OiAnc3JjJyxcblx0YnVpbGQ6IHtcblx0XHRvdXREaXI6ICcuLi9kaXN0J1xuXHR9XG59KTtcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBc1QsT0FBTyxVQUFVO0FBRXZVLFNBQVMsbUNBQW1DO0FBQzVDLE9BQU8sU0FBUztBQUNoQixPQUFPLGdCQUFnQjtBQUN2QixPQUFPLGdCQUFnQjtBQUN2QixPQUFPLGVBQWU7QUFDdEIsU0FBUyxvQkFBb0I7QUFDN0IsT0FBTyxpQkFBaUI7QUFHeEIsT0FBTyxrQkFBa0I7QUFDekIsT0FBTyxjQUFjO0FBWnJCLElBQU0sbUNBQW1DO0FBZXpDLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzNCLFNBQVM7QUFBQSxJQUNSLFVBQVU7QUFBQSxNQUNULEtBQUs7QUFBQSxNQUNMLFlBQVk7QUFBQSxJQUNiLENBQUM7QUFBQSxJQUNELElBQUk7QUFBQSxJQUNKLFlBQVk7QUFBQSxJQUNaLFdBQVc7QUFBQSxNQUNWLEtBQUs7QUFBQSxNQUNMLE1BQU0sQ0FBQyxpQkFBaUIsWUFBWSx5QkFBeUI7QUFBQSxNQUM3RCxTQUFTO0FBQUEsUUFDUjtBQUFBLFFBQ0E7QUFBQSxRQUNBO0FBQUEsUUFDQTtBQUFBLFFBQ0E7QUFBQSxRQUNBO0FBQUEsTUFDRDtBQUFBLE1BQ0EsVUFBVTtBQUFBLFFBQ1QsU0FBUztBQUFBLE1BQ1Y7QUFBQSxNQUNBLGdCQUFnQixDQUFDLFlBQVk7QUFBQSxJQUM5QixDQUFDO0FBQUEsSUFDRCxXQUFXO0FBQUEsTUFDVixLQUFLO0FBQUEsTUFDTCxNQUFNLENBQUMsaUJBQWlCO0FBQUEsTUFDeEIsc0JBQXNCO0FBQUEsTUFDdEIsc0JBQXNCO0FBQUEsTUFDdEIsWUFBWSxDQUFDLEtBQUs7QUFBQSxNQUNsQixNQUFNO0FBQUEsTUFDTixTQUFTLENBQUMsVUFBVSxZQUFZO0FBQUEsTUFDaEMsU0FBUyxDQUFDLGNBQWM7QUFBQSxJQUN6QixDQUFDO0FBQUEsRUFDRjtBQUFBLEVBQ0EsU0FBUztBQUFBLElBQ1IsT0FBTztBQUFBLE1BQ04sS0FBSyxLQUFLLFFBQVEsa0NBQVcsT0FBTztBQUFBLElBQ3JDO0FBQUEsRUFDRDtBQUFBLEVBQ0EsS0FBSztBQUFBLElBQ0osU0FBUztBQUFBLE1BQ1IsU0FBUyxDQUFDLFNBQVMsR0FBRyxhQUFhLENBQUM7QUFBQSxJQUNyQztBQUFBLEVBQ0Q7QUFBQSxFQUNBLFFBQVE7QUFBQSxJQUNQLE9BQU87QUFBQSxNQUNOLE9BQU87QUFBQSxRQUNOLFFBQVE7QUFBQSxRQUNSLGNBQWM7QUFBQSxRQUNkLFNBQVMsQ0FBQUEsVUFBUUEsTUFBSyxRQUFRLFNBQVMsRUFBRTtBQUFBLE1BQzFDO0FBQUEsSUFDRDtBQUFBLEVBQ0Q7QUFBQSxFQUNBLE1BQU07QUFBQSxFQUNOLE9BQU87QUFBQSxJQUNOLFFBQVE7QUFBQSxFQUNUO0FBQ0QsQ0FBQzsiLAogICJuYW1lcyI6IFsicGF0aCJdCn0K