Added reservation and username lookup
All checks were successful
Build BAB Application Deployment Artifact / build (push) Successful in 2m13s

This commit is contained in:
2024-05-13 10:49:03 -04:00
parent 4a273ccb2f
commit 78211a33ae
17 changed files with 180 additions and 72 deletions

View File

@@ -17,6 +17,7 @@
"@quasar/quasar-app-extension-qcalendar": "https://github.com/ptoal/quasar-ui-qcalendar/releases/download/v4.0.0-beta.19/app-extension.tgz", "@quasar/quasar-app-extension-qcalendar": "https://github.com/ptoal/quasar-ui-qcalendar/releases/download/v4.0.0-beta.19/app-extension.tgz",
"@quasar/quasar-ui-qcalendar": "https://github.com/ptoal/quasar-ui-qcalendar/releases/download/v4.0.0-beta.19/qcalendar-ui.tgz", "@quasar/quasar-ui-qcalendar": "https://github.com/ptoal/quasar-ui-qcalendar/releases/download/v4.0.0-beta.19/qcalendar-ui.tgz",
"appwrite": "^14.0.1", "appwrite": "^14.0.1",
"axios": "^1.6.8",
"file": "^0.2.2", "file": "^0.2.2",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"vue": "3", "vue": "3",

View File

@@ -102,6 +102,12 @@ module.exports = configure(function (/* ctx */) {
secure: false, secure: false,
rewrite: (path) => path.replace(/^\/api/, ''), rewrite: (path) => path.replace(/^\/api/, ''),
}, },
'/function': {
target: 'https://6640382951eacb568371.f.appwrite.toal.ca/',
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/function/, ''),
},
}, },
// For reverse-proxying via haproxy // For reverse-proxying via haproxy
// hmr: { // hmr: {

View File

@@ -16,6 +16,7 @@ onMounted(async () => {
await useAuthStore().init(); await useAuthStore().init();
await useScheduleStore().fetchIntervalTemplates(); await useScheduleStore().fetchIntervalTemplates();
await useScheduleStore().fetchIntervals(); await useScheduleStore().fetchIntervals();
await useScheduleStore().fetchReservations();
await useBoatStore().fetchBoats(); await useBoatStore().fetchBoats();
}); });
</script> </script>

View File

@@ -1,5 +1,5 @@
import { boot } from 'quasar/wrappers'; import { boot } from 'quasar/wrappers';
import { Client, Account, Databases, ID } from 'appwrite'; import { Client, Account, Databases, Functions, ID } from 'appwrite';
import { useAuthStore } from 'src/stores/auth'; import { useAuthStore } from 'src/stores/auth';
import { Dialog, Notify } from 'quasar'; import { Dialog, Notify } from 'quasar';
import type { Router } from 'vue-router'; import type { Router } from 'vue-router';
@@ -22,10 +22,11 @@ if (process.env.APPWRITE_API_ENDPOINT && process.env.APPWRITE_API_PROJECT)
const AppwriteIds = { const AppwriteIds = {
databaseId: '65ee1cbf9c2493faf15f', databaseId: '65ee1cbf9c2493faf15f',
collection: { collection: {
boat: '66341910003e287cd71c',
reservation: '663f8847000b8f5e29bb',
skillTags: '66072582a74d94a4bd01',
task: '65ee1cd5b550023fae4f', task: '65ee1cd5b550023fae4f',
taskTags: '65ee21d72d5c8007c34c', taskTags: '65ee21d72d5c8007c34c',
skillTags: '66072582a74d94a4bd01',
boat: '66341910003e287cd71c',
timeBlock: '66361869002883fb4c4b', timeBlock: '66361869002883fb4c4b',
timeBlockTemplate: '66361f480007fdd639af', timeBlockTemplate: '66361f480007fdd639af',
}, },
@@ -33,6 +34,8 @@ const AppwriteIds = {
const account = new Account(client); const account = new Account(client);
const databases = new Databases(client); const databases = new Databases(client);
const functions = new Functions(client);
let appRouter: Router; let appRouter: Router;
export default boot(async ({ router }) => { export default boot(async ({ router }) => {
@@ -98,4 +101,13 @@ function login(email: string, password: string) {
}); });
}); });
} }
export { client, account, databases, ID, AppwriteIds, login, logout }; export {
client,
account,
databases,
functions,
ID,
AppwriteIds,
login,
logout,
};

View File

@@ -111,6 +111,7 @@ import {
parseTimestamp, parseTimestamp,
addToDate, addToDate,
Timestamp, Timestamp,
parsed,
} from '@quasar/quasar-ui-qcalendar'; } from '@quasar/quasar-ui-qcalendar';
import { Boat, useBoatStore } from 'src/stores/boat'; import { Boat, useBoatStore } from 'src/stores/boat';
import { useScheduleStore } from 'src/stores/schedule'; import { useScheduleStore } from 'src/stores/schedule';
@@ -178,7 +179,7 @@ function getEvents(scope: ResourceIntervalScope) {
return resourceEvents.map((event) => { return resourceEvents.map((event) => {
return { return {
left: scope.timeStartPosX(parseDate(event.start)), left: scope.timeStartPosX(parsed(event.start)),
width: scope.timeDurationWidth( width: scope.timeDurationWidth(
date.getDateDiff(event.end, event.start, 'minutes') date.getDateDiff(event.end, event.start, 'minutes')
), ),

View File

@@ -155,7 +155,6 @@ function onDragStart(e: DragEvent, template: IntervalTemplate) {
} }
} }
const saveTemplate = (evt: Event, template: IntervalTemplate | undefined) => { const saveTemplate = (evt: Event, template: IntervalTemplate | undefined) => {
console.log(template);
if (!template) return false; if (!template) return false;
overlapped.value = timeTuplesOverlapped(template.timeTuples); overlapped.value = timeTuplesOverlapped(template.timeTuples);
if (overlapped.value.length > 0) { if (overlapped.value.length > 0) {
@@ -163,7 +162,6 @@ const saveTemplate = (evt: Event, template: IntervalTemplate | undefined) => {
} else { } else {
edit.value = false; edit.value = false;
if (template.$id && template.$id !== 'unsaved') { if (template.$id && template.$id !== 'unsaved') {
console.log(template.$id);
scheduleStore.updateIntervalTemplate(template, template.$id); scheduleStore.updateIntervalTemplate(template, template.$id);
} else { } else {
scheduleStore.createIntervalTemplate(template); scheduleStore.createIntervalTemplate(template);

View File

@@ -38,23 +38,23 @@
{{ selectedBlock?.$id === block.$id ? 'Selected' : 'Available' }} {{ selectedBlock?.$id === block.$id ? 'Selected' : 'Available' }}
</div> </div>
</div> </div>
<!-- <div <div
v-for="r in boats[scope.columnIndex].reservations" v-for="reservation in getBoatReservations(scope)"
:key="r.id" :key="reservation.$id"
> >
<div <div
class="reservation" class="reservation"
:style=" :style="
reservationStyles( reservationStyles(
r, reservation,
scope.timeStartPos, scope.timeStartPos,
scope.timeDurationHeight scope.timeDurationHeight
) )
" "
> >
{{ r.user }} {{ getUserName(reservation.user) || 'loading...' }}
</div>
</div> </div>
</div> -->
</template> </template>
</QCalendarDay> </QCalendarDay>
</div> </div>
@@ -76,7 +76,8 @@ import CalendarHeaderComponent from './CalendarHeaderComponent.vue';
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { useBoatStore } from 'src/stores/boat'; import { useBoatStore } from 'src/stores/boat';
import { useScheduleStore } from 'src/stores/schedule'; import { useScheduleStore } from 'src/stores/schedule';
import { Interval } from 'src/stores/schedule.types'; import { useAuthStore } from 'src/stores/auth';
import { Interval, Reservation } from 'src/stores/schedule.types';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
const scheduleStore = useScheduleStore(); const scheduleStore = useScheduleStore();
@@ -89,18 +90,22 @@ const calendar = ref<QCalendarDay | null>(null);
function handleSwipe({ ...event }) { function handleSwipe({ ...event }) {
event.direction === 'right' ? calendar.value?.prev() : calendar.value?.next(); event.direction === 'right' ? calendar.value?.prev() : calendar.value?.next();
} }
// function reservationStyles( function reservationStyles(
// reservation: Reservation, reservation: Reservation,
// timeStartPos: (t: string) => string, timeStartPos: (t: string) => string,
// timeDurationHeight: (d: number) => string timeDurationHeight: (d: number) => string
// ) { ) {
// return genericBlockStyle( return genericBlockStyle(
// parseDate(reservation.start) as Timestamp, parseDate(new Date(reservation.start)) as Timestamp,
// parseDate(reservation.end) as Timestamp, parseDate(new Date(reservation.end)) as Timestamp,
// timeStartPos, timeStartPos,
// timeDurationHeight timeDurationHeight
// ); );
// } }
function getUserName(userid: string) {
return useAuthStore().getUserNameById(userid);
}
function blockStyles( function blockStyles(
block: Interval, block: Interval,
@@ -176,8 +181,13 @@ const boatBlocks = computed((): BoatBlocks => {
}); });
function getBoatBlocks(scope: DayBodyScope): Interval[] { function getBoatBlocks(scope: DayBodyScope): Interval[] {
return boats.value[scope.columnIndex] const boat = boats.value[scope.columnIndex];
? boatBlocks.value[boats.value[scope.columnIndex].$id] return boat ? boatBlocks.value[boat.$id] : [];
}
function getBoatReservations(scope: DayBodyScope): Reservation[] {
const boat = boats.value[scope.columnIndex];
return boat
? scheduleStore.getBoatReservations(scope.timestamp, boat.$id)
: []; : [];
} }

View File

@@ -252,14 +252,11 @@ const dateRule = (val: string) => {
const router = useRouter(); const router = useRouter();
async function onSubmit() { async function onSubmit() {
//console.log(modifiedTask);
try { try {
if (modifiedTask.$id) { if (modifiedTask.$id) {
await taskStore.updateTask(modifiedTask); await taskStore.updateTask(modifiedTask);
console.log('Updated Task: ' + modifiedTask.$id);
} else { } else {
await taskStore.addTask(modifiedTask); await taskStore.addTask(modifiedTask);
console.log('Created Task');
} }
router.go(-1); router.go(-1);
} catch (error) { } catch (error) {

View File

@@ -136,7 +136,6 @@ watch(timeblock, (tb_new) => {
); );
bookingForm.value.startDate = date.formatDate(tb_new?.start, dateFormat); bookingForm.value.startDate = date.formatDate(tb_new?.start, dateFormat);
bookingForm.value.endDate = date.formatDate(tb_new?.end, dateFormat); bookingForm.value.endDate = date.formatDate(tb_new?.end, dateFormat);
console.log(tb_new);
}); });
// //TODO: Turn this into a validator. // //TODO: Turn this into a validator.

View File

@@ -37,7 +37,9 @@
<span class="title q-calendar__ellipsis"> <span class="title q-calendar__ellipsis">
{{ event.user }} {{ event.user }}
<q-tooltip>{{ <q-tooltip>{{
event.start + ' - ' + event.resource.name event.start +
' - ' +
boatStore.getBoatById(event.resource)?.name
}}</q-tooltip> }}</q-tooltip>
</span> </span>
</div> </div>
@@ -54,12 +56,14 @@ import { useScheduleStore } from 'src/stores/schedule';
import { Reservation } from 'src/stores/schedule.types'; import { Reservation } from 'src/stores/schedule.types';
import { ref } from 'vue'; import { ref } from 'vue';
const scheduleStore = useScheduleStore(); const scheduleStore = useScheduleStore();
import { TimestampOrNull, parseDate, today } from '@quasar/quasar-ui-qcalendar'; import { TimestampOrNull, parsed, today } from '@quasar/quasar-ui-qcalendar';
import { QCalendarDay } from '@quasar/quasar-ui-qcalendar'; import { QCalendarDay } from '@quasar/quasar-ui-qcalendar';
import { date } from 'quasar'; import { date } from 'quasar';
import { Timestamp } from '@quasar/quasar-ui-qcalendar'; import { Timestamp } from '@quasar/quasar-ui-qcalendar';
import { useBoatStore } from 'src/stores/boat';
const selectedDate = ref(today()); const selectedDate = ref(today());
const boatStore = useBoatStore();
// Method declarations // Method declarations
@@ -74,7 +78,7 @@ function slotStyle(
'align-items': 'flex-start', 'align-items': 'flex-start',
}; };
if (timeStartPos && timeDurationHeight) { if (timeStartPos && timeDurationHeight) {
s.top = timeStartPos(parseDate(event.start)) + 'px'; s.top = timeStartPos(parsed(event.start)) + 'px';
s.height = s.height =
timeDurationHeight(date.getDateDiff(event.end, event.start, 'minutes')) + timeDurationHeight(date.getDateDiff(event.end, event.start, 'minutes')) +
'px'; 'px';

View File

@@ -260,7 +260,6 @@ function onDrop(
) )
) )
); );
console.log(overlapped);
if (overlapped.value.length === 0) { if (overlapped.value.length === 0) {
boats.value.map((b) => createIntervals(b, templateId, date)); boats.value.map((b) => createIntervals(b, templateId, date));
} else { } else {

View File

@@ -1,10 +1,11 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { ID, account } from 'boot/appwrite'; import { ID, account, functions } from 'boot/appwrite';
import { OAuthProvider, type Models } from 'appwrite'; import { ExecutionMethod, OAuthProvider, type Models } from 'appwrite';
import { ref } from 'vue'; import { ref } from 'vue';
export const useAuthStore = defineStore('auth', () => { export const useAuthStore = defineStore('auth', () => {
const currentUser = ref<Models.User<Models.Preferences> | null>(null); const currentUser = ref<Models.User<Models.Preferences> | null>(null);
const userNames = ref<Record<string, string>>({});
async function init() { async function init() {
try { try {
@@ -31,9 +32,39 @@ export const useAuthStore = defineStore('auth', () => {
currentUser.value = await account.get(); currentUser.value = await account.get();
} }
function getUserNameById(id: string) {
try {
if (!userNames.value[id]) {
userNames.value[id] = '';
functions
.createExecution(
'664038294b5473ef0c8d',
'',
false,
'/userinfo/' + id,
ExecutionMethod.GET
)
.then(
(res) => (userNames.value[id] = JSON.parse(res.responseBody).name)
);
}
} catch (e) {
console.log('Failed to get username. Error: ' + e);
}
return userNames.value[id];
}
function logout() { function logout() {
return account.deleteSession('current').then((currentUser.value = null)); return account.deleteSession('current').then((currentUser.value = null));
} }
return { currentUser, register, login, googleLogin, logout, init }; return {
currentUser,
getUserNameById,
register,
login,
googleLogin,
logout,
init,
};
}); });

View File

@@ -39,5 +39,9 @@ export const useBoatStore = defineStore('boat', () => {
} }
} }
return { boats, fetchBoats }; const getBoatById = (id: string): Boat | null => {
return boats.value.find((b) => b.$id === id) || null;
};
return { boats, fetchBoats, getBoatById };
}); });

View File

@@ -69,7 +69,7 @@ export function getSampleReservations(): Reservation[] {
user: 'John Smith', user: 'John Smith',
start: '7:00', start: '7:00',
end: '10:00', end: '10:00',
boat: '1', boat: '66359729003825946ae1',
status: 'confirmed', status: 'confirmed',
}, },
{ {
@@ -77,7 +77,7 @@ export function getSampleReservations(): Reservation[] {
user: 'Bob Barker', user: 'Bob Barker',
start: '16:00', start: '16:00',
end: '19:00', end: '19:00',
boat: '1', boat: '66359729003825946ae1',
status: 'confirmed', status: 'confirmed',
}, },
{ {
@@ -85,7 +85,7 @@ export function getSampleReservations(): Reservation[] {
user: 'Peter Parker', user: 'Peter Parker',
start: '7:00', start: '7:00',
end: '13:00', end: '13:00',
boat: '4', boat: '663597030029b71c7a9b',
status: 'tentative', status: 'tentative',
}, },
{ {
@@ -93,7 +93,7 @@ export function getSampleReservations(): Reservation[] {
user: 'Vince McMahon', user: 'Vince McMahon',
start: '10:00', start: '10:00',
end: '13:00', end: '13:00',
boat: '2', boat: '663597030029b71c7a9b',
status: 'pending', status: 'pending',
}, },
{ {
@@ -101,7 +101,7 @@ export function getSampleReservations(): Reservation[] {
user: 'Heather Graham', user: 'Heather Graham',
start: '13:00', start: '13:00',
end: '19:00', end: '19:00',
boat: '4', boat: '663596b9000235ffea55',
status: 'confirmed', status: 'confirmed',
}, },
{ {
@@ -109,7 +109,7 @@ export function getSampleReservations(): Reservation[] {
user: 'Lawrence Fishburne', user: 'Lawrence Fishburne',
start: '13:00', start: '13:00',
end: '16:00', end: '16:00',
boat: '3', boat: '663596b9000235ffea55',
}, },
]; ];
const boatStore = useBoatStore(); const boatStore = useBoatStore();
@@ -131,9 +131,11 @@ export function getSampleReservations(): Reservation[] {
return { return {
id: entry.id, id: entry.id,
user: entry.user, user: entry.user,
start: date.adjustDate(now, makeOpts(splitTime(entry.start))), start: date
end: date.adjustDate(now, makeOpts(splitTime(entry.end))), .adjustDate(now, makeOpts(splitTime(entry.start)))
resource: boat, .toISOString(),
end: date.adjustDate(now, makeOpts(splitTime(entry.end))).toISOString(),
resource: boat.$id,
reservationDate: now, reservationDate: now,
status: entry.status as StatusTypes, status: entry.status as StatusTypes,
}; };

View File

@@ -108,19 +108,32 @@ export const useScheduleStore = defineStore('schedule', () => {
}); });
}; };
async function fetchReservations() {
try {
const response = await databases.listDocuments(
AppwriteIds.databaseId,
AppwriteIds.collection.reservation
);
reservations.value = response.documents as Reservation[];
} catch (error) {
console.error('Failed to fetch timeblocks', error);
}
}
const getBoatReservations = ( const getBoatReservations = (
searchDate: Timestamp, searchDate: Timestamp,
boat?: string boat?: string
): Reservation[] => { ): Reservation[] => {
return reservations.value.filter((x) => { const result = reservations.value.filter((x) => {
return ( return (
((parseDate(x.start)?.date == searchDate.date || ((parsed(x.start)?.date == searchDate.date ||
parseDate(x.end)?.date == searchDate.date) && // Part of reservation falls on day parsed(x.end)?.date == searchDate.date) && // Part of reservation falls on day
x.resource != undefined && // A boat is defined x.resource != undefined && // A boat is defined
!boat) || !boat) ||
x.resource.$id == boat // A specific boat has been passed, and matches x.resource == boat // A specific boat has been passed, and matches
); );
}); });
return result;
}; };
async function fetchIntervals() { async function fetchIntervals() {
@@ -170,21 +183,21 @@ export const useScheduleStore = defineStore('schedule', () => {
// }; // };
const getConflictingReservations = ( const getConflictingReservations = (
resource: Boat, resource: string,
start: Date, start: Date,
end: Date end: Date
): Reservation[] => { ): Reservation[] => {
const overlapped = reservations.value.filter( const overlapped = reservations.value.filter(
(entry: Reservation) => (entry: Reservation) =>
entry.resource.$id == resource.$id && entry.resource == resource &&
entry.start < end && new Date(entry.start) < end &&
entry.end > start new Date(entry.end) > start
); );
return overlapped; return overlapped;
}; };
const isResourceTimeOverlapped = ( const isResourceTimeOverlapped = (
resource: Boat, resource: string,
start: Date, start: Date,
end: Date end: Date
): boolean => { ): boolean => {
@@ -192,7 +205,11 @@ export const useScheduleStore = defineStore('schedule', () => {
}; };
const isReservationOverlapped = (res: Reservation): boolean => { const isReservationOverlapped = (res: Reservation): boolean => {
return isResourceTimeOverlapped(res.resource, res.start, res.end); return isResourceTimeOverlapped(
res.resource,
new Date(res.start),
new Date(res.end)
);
}; };
const getNewId = (): string => { const getNewId = (): string => {
@@ -235,7 +252,6 @@ export const useScheduleStore = defineStore('schedule', () => {
{ ...interval, $id: undefined } { ...interval, $id: undefined }
); );
timeblocks.value.push(response as Interval); timeblocks.value.push(response as Interval);
console.log(interval, response);
} else { } else {
console.error('Update interval called without an ID'); console.error('Update interval called without an ID');
} }
@@ -315,19 +331,20 @@ export const useScheduleStore = defineStore('schedule', () => {
timeblockTemplates, timeblockTemplates,
getBoatReservations, getBoatReservations,
getConflictingReservations, getConflictingReservations,
addOrCreateReservation,
isReservationOverlapped,
isResourceTimeOverlapped,
fetchReservations,
getIntervalsForDate, getIntervalsForDate,
getIntervals, getIntervals,
fetchIntervals, fetchIntervals,
fetchIntervalTemplates, fetchIntervalTemplates,
getNewId, getNewId,
addOrCreateReservation,
createInterval, createInterval,
updateInterval, updateInterval,
deleteInterval, deleteInterval,
createIntervalTemplate, createIntervalTemplate,
deleteIntervalTemplate, deleteIntervalTemplate,
updateIntervalTemplate, updateIntervalTemplate,
isReservationOverlapped,
isResourceTimeOverlapped,
}; };
}); });

View File

@@ -1,16 +1,14 @@
import { Models } from 'appwrite'; import { Models } from 'appwrite';
import type { Boat } from './boat';
export type StatusTypes = 'tentative' | 'confirmed' | 'pending' | undefined; export type StatusTypes = 'tentative' | 'confirmed' | 'pending' | undefined;
export interface Reservation { export type Reservation = Partial<Models.Document> & {
id: string;
user: string; user: string;
start: Date; start: string; // ISODate
end: Date; end: string; //ISODate
resource: Boat; resource: string; // Boat ID
reservationDate: Date;
status?: StatusTypes; status?: StatusTypes;
} };
// 24 hrs in advance only 2 weekday, and 1 weekend slot // 24 hrs in advance only 2 weekday, and 1 weekend slot
// Within 24 hrs, any available slot // Within 24 hrs, any available slot
/* TODO: Figure out how best to separate out where qcalendar bits should be. /* TODO: Figure out how best to separate out where qcalendar bits should be.

View File

@@ -1808,6 +1808,15 @@ available-typed-arrays@^1.0.7:
dependencies: dependencies:
possible-typed-array-names "^1.0.0" possible-typed-array-names "^1.0.0"
axios@^1.6.8:
version "1.6.8"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66"
integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==
dependencies:
follow-redirects "^1.15.6"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
babel-plugin-polyfill-corejs2@^0.4.10: babel-plugin-polyfill-corejs2@^0.4.10:
version "0.4.11" version "0.4.11"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz#30320dfe3ffe1a336c15afdcdafd6fd615b25e33" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz#30320dfe3ffe1a336c15afdcdafd6fd615b25e33"
@@ -2086,7 +2095,7 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
combined-stream@^1.0.6: combined-stream@^1.0.6, combined-stream@^1.0.8:
version "1.0.8" version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
@@ -3079,6 +3088,11 @@ flatted@^3.2.9:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
follow-redirects@^1.15.6:
version "1.15.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
for-each@^0.3.3: for-each@^0.3.3:
version "0.3.3" version "0.3.3"
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
@@ -3095,6 +3109,15 @@ form-data@^2.3.2:
combined-stream "^1.0.6" combined-stream "^1.0.6"
mime-types "^2.1.12" mime-types "^2.1.12"
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
forwarded@0.2.0: forwarded@0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
@@ -4282,6 +4305,11 @@ proxy-addr@~2.0.7:
forwarded "0.2.0" forwarded "0.2.0"
ipaddr.js "1.9.1" ipaddr.js "1.9.1"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
punycode@^2.1.0: punycode@^2.1.0:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"