fix: type-checking in multiple files

This commit is contained in:
tglide
2023-01-30 20:02:32 +00:00
parent 483f7efec0
commit 100fc7a14a
27 changed files with 138 additions and 72 deletions
+1 -1
View File
@@ -13,7 +13,7 @@
registerTheme('light', { ...base, ...light });
registerTheme('dark', { ...base, ...dark });
export let options: EChartsOption;
export let options: EChartsOption | null;
export let series: (BarSeriesOption | LineSeriesOption)[];
let chart: ECharts;
+2 -1
View File
@@ -15,10 +15,11 @@
} from '$lib/constants';
import { tooltip } from '$lib/actions/tooltip';
import { objectKeys } from '$lib/helpers/object';
import type { Nullable } from '$lib/helpers/type';
// Props
export let show = false;
export let initialValue = '';
export let initialValue: Nullable<string> = '';
// Constants
const serviceNames = services.map((s) => s.name);
+7 -4
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { tooltip } from '$lib/actions/tooltip';
import type { PromiseValue } from '$lib/helpers/type';
import { sdkForProject } from '$lib/stores/sdk';
import type { Models } from '@aw-labs/appwrite-console';
import { tick } from 'svelte';
@@ -9,14 +10,16 @@
export let role: string;
let content: HTMLDivElement;
let data = null;
let data: PromiseValue<ReturnType<typeof getData>> | null = null;
let isFetching = false;
async function getData(
permission: string
): Promise<Partial<Models.User<Record<string, unknown>> & Models.Team>> {
): Promise<Partial<Models.User<Record<string, unknown>> & Models.Team> | undefined> {
const role = permission.split(':')[0];
const id = permission.split(':')[1].split('/')[0];
const id = permission.split(':')[1]?.split('/')[0];
if (!id) return;
if (role === 'user') {
const user = await sdkForProject.users.get(id);
return user;
@@ -88,7 +91,7 @@
? data?.phone
: '-'}
</span>
<Output value={data.$id}>{role}</Output>
<Output value={data.$id ?? ''}>{role}</Output>
</span>
{#if (isUser && (data?.email || data?.phone)) || isTeam}
<span class="user-profile-sep" />
+3 -2
View File
@@ -1,16 +1,17 @@
<script lang="ts">
import type { Nullable } from '$lib/helpers/type';
import { FormItem, Helper } from '.';
export let label: string;
export let optionalText: string | undefined = undefined;
export let showLabel = true;
export let id: string;
export let value = false;
export let value: Nullable<boolean> = false;
export let required = false;
export let disabled = false;
let element: HTMLInputElement;
let error: string;
let error: Nullable<string>;
const handleInvalid = (event: Event) => {
event.preventDefault();
+2 -1
View File
@@ -1,11 +1,12 @@
<script lang="ts">
import type { Nullable } from '$lib/helpers/type';
import { FormItem, Helper } from '.';
export let id: string;
export let label: string;
export let optionalText: string | undefined = undefined;
export let showLabel = true;
export let value: string | null | undefined;
export let value: Nullable<unknown>;
export let placeholder = '';
export let required = false;
export let disabled = false;
+4 -2
View File
@@ -1,11 +1,13 @@
<script lang="ts">
import type { Nullable } from '$lib/helpers/type';
import { onMount } from 'svelte';
import { FormItem, Helper } from '.';
export let label: string;
export let showLabel = true;
export let id: string;
export let value = '';
export let value: Nullable<string> = '';
export let placeholder = '';
export let required = false;
export let disabled = false;
@@ -15,7 +17,7 @@
export let maxlength: number | null = null;
let element: HTMLInputElement;
let error: string;
let error: Nullable<string>;
onMount(() => {
if (element && autofocus) {
+14
View File
@@ -2,6 +2,8 @@ import { objectEntries } from './object';
export type Nullable<T> = T | null | undefined;
export type PromiseValue<T> = T extends Promise<infer U> ? U : T;
export function isNonNullable<T>(value: T): value is NonNullable<T> {
return value !== null && value !== undefined;
}
@@ -136,3 +138,15 @@ export const isObjectType = <T>(
return true;
};
/**
* Casts value as any. Useful for Svelte templates, since they don't support
* type-casting and TS expressions in general.
*
* @param {unknown} value
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function Anyify(value: unknown): any {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return value as any;
}
+1 -1
View File
@@ -17,7 +17,7 @@
}
updater = setInterval(() => {
const randomStep = stepSizes[Math.floor(Math.random() * stepSizes.length)];
const step = getIncrement(width) + randomStep;
const step = getIncrement(width) + (randomStep ?? 0);
if (width < maximum) {
width = width + step;
}
+1 -1
View File
@@ -116,7 +116,7 @@
{/each}
<div class="form-footer">
<div class="u-flex u-main-end u-gap-12">
{#if !isLastStep && sortedSteps[currentStep - 1][1].optional}
{#if !isLastStep && sortedSteps[currentStep - 1]?.[1].optional}
<Button text on:click={() => dispatch('finish')}>
Skip optional steps
</Button>
+1 -1
View File
@@ -5,7 +5,7 @@
/**
* Pass callback that is run before the Form submit, can be canceled with throwing an exception.
*/
export let beforeSubmit: () => Promise<void> | null = null;
export let beforeSubmit: (() => Promise<void>) | null = null;
if (beforeSubmit) {
wizard.setInterceptor(beforeSubmit);
+3 -1
View File
@@ -120,7 +120,9 @@ function createServices() {
{
label: 'GraphQL',
method: 'graphql',
value: project.serviceStatusForGraphql
// TODO: verify this is correct
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: (project as any).serviceStatusForGraphql
}
];
set({ list });
+12 -12
View File
@@ -1,20 +1,20 @@
<script lang="ts">
import { afterNavigate, goto } from '$app/navigation';
import { page } from '$app/stores';
import { user } from '$lib/stores/user';
import { onCLS, onFID, onLCP, onFCP, onINP, onTTFB } from 'web-vitals';
import { reportWebVitals } from '$lib/helpers/vitals';
import { onMount } from 'svelte';
import { base } from '$app/paths';
import { browser, dev } from '$app/environment';
import { app } from '$lib/stores/app';
import { Progress, Notifications } from '$lib/layout';
import { loading } from './store';
import Loading from './loading.svelte';
import { afterNavigate, goto } from '$app/navigation';
import { base } from '$app/paths';
import { page } from '$app/stores';
import { trackPageView } from '$lib/actions/analytics';
import { isString } from '$lib/helpers/type';
import { reportWebVitals } from '$lib/helpers/vitals';
import { Notifications, Progress } from '$lib/layout';
import { app } from '$lib/stores/app';
import { user } from '$lib/stores/user';
import * as Sentry from '@sentry/svelte';
import { BrowserTracing } from '@sentry/tracing';
import { isString } from '$lib/helpers/type';
import { onMount } from 'svelte';
import { onCLS, onFCP, onFID, onINP, onLCP, onTTFB } from 'web-vitals';
import Loading from './loading.svelte';
import { loading } from './store';
if (browser) {
window.VERCEL_ANALYTICS_ID = import.meta.env.VERCEL_ANALYTICS_ID?.toString() ?? false;
@@ -12,6 +12,7 @@
import { trackEvent } from '$lib/actions/analytics';
import { timeToSeconds } from '$lib/helpers/timeConversion';
import { writable } from 'svelte/store';
import { Anyify } from '$lib/helpers/type';
const projectId = $project.$id;
let isLimited = $project.authLimit === 0 ? 'unlimited' : 'limited';
@@ -20,7 +21,8 @@
let timeInSec = time;
let period = writable('s');
let btnActive = false;
let maxSessions = $project.authSessionsLimit;
// TODO: verify if this is the correct way to get the max sessions
let maxSessions = ($project as any).authSessionsLimit;
let options = [
{ label: 'days', value: 'd' },
@@ -84,7 +86,8 @@
}
async function updateSessionsLimit() {
try {
await sdkForConsole.projects.updateAuthSessionsLimit(projectId, maxSessions);
// TODO: verify if this is the correct way to update the max sessions
await (sdkForConsole.projects as any)?.updateAuthSessionsLimit(projectId, maxSessions);
addNotification({
type: 'success',
@@ -227,7 +230,9 @@
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={maxSessions === $project.authSessionsLimit} submit>Update</Button>
<!-- TODO: verify types for $project -->
<Button disabled={maxSessions === Anyify($project).authSessionsLimit} submit
>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -8,23 +8,29 @@
import Attribute from './attribute.svelte';
export let attributes: Attributes[] = [];
export let formValues: object = {};
// Svelte templates do not support type casting, making type-checking impossible
// in certain scenarios. For example, the following has a type error that could be
// confidently fixed by type-casting:
// {# if formValues[attribute.key] } <input bind:value={formValues[attribute.key][index]} /> {/if}
export let formValues: any = {};
export let customId: string | null | undefined = undefined;
export let gap: '16' | '32' = '32';
let showCustomId = false;
function removeArrayItem(key: string, index: number) {
if (!formValues[key]) return;
formValues = {
...formValues,
[key]: formValues[key].filter((_, i) => i !== index)
[key]: formValues[key]?.filter((_: any, i: any) => i !== index)
};
}
function addArrayItem(key: string) {
formValues = {
...formValues,
[key]: [...formValues[key], null]
[key]: [...(formValues[key] ?? []), null]
};
}
@@ -32,6 +38,11 @@
if (isAttributeEnum(attribute)) return 'Enum';
return `${capitalize(attribute.type)}${attribute.array ? '[]' : ''}`;
}
function keys(arr?: unknown[]): number[] {
if (!arr) return [];
return [...arr.keys()];
}
</script>
{#if attributes.length}
@@ -39,7 +50,7 @@
{#each attributes as attribute}
{@const label = attribute.required ? `${attribute.key}*` : attribute.key}
{#if attribute.array}
{#if formValues[attribute.key].length === 0}
{#if formValues[attribute.key]?.length === 0}
<div class="u-flex u-cross-center u-main-space-between">
<span class="label u-margin-0">
{label}
@@ -55,19 +66,21 @@
</div>
{/if}
{#if formValues[attribute.key].length !== 0}
{#if formValues[attribute.key]?.length !== 0}
<ul class="u-grid u-gap-8">
{#each [...formValues[attribute.key].keys()] as index}
{#each keys(formValues[attribute.key]) as index}
<li class="form-item is-multiple">
<div class="form-item-part u-stretch">
<Attribute
{attribute}
id={`${attribute.key}-${index}`}
optionalText={index === 0
? getAttributeType(attribute)
: undefined}
label={index === 0 ? label : ''}
bind:value={formValues[attribute.key][index]} />
{#if formValues[attribute.key]}
<Attribute
{attribute}
id={`${attribute.key}-${index}`}
optionalText={index === 0
? getAttributeType(attribute)
: undefined}
label={index === 0 ? label : ''}
bind:value={formValues[attribute.key][index]} />
{/if}
</div>
<div class="form-item-part u-cross-child-end">
<Button
@@ -1,10 +1,11 @@
<script lang="ts">
import { InputCheckbox, InputSelect } from '$lib/elements/forms';
import type { Nullable } from '$lib/helpers/type';
import type { Models } from '@aw-labs/appwrite-console';
export let id: string;
export let label: string;
export let value: boolean;
export let value: Nullable<boolean>;
export let attribute: Models.AttributeBoolean;
export let optionalText: string | undefined = undefined;
</script>
@@ -1,10 +1,11 @@
<script lang="ts">
import InputURL from '$lib/elements/forms/inputURL.svelte';
import type { Nullable } from '$lib/helpers/type';
import type { Models } from '@aw-labs/appwrite-console';
export let id: string;
export let label: string;
export let value: string | null;
export let value: Nullable<string>;
export let attribute: Models.AttributeUrl;
</script>
@@ -5,6 +5,7 @@
import { CardGrid, Heading } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, InputText } from '$lib/elements/forms';
import { isNonNullableObject, type Nullable } from '$lib/helpers/type';
import { addNotification } from '$lib/stores/notifications';
import { sdkForProject } from '$lib/stores/sdk';
import { onMount } from 'svelte';
@@ -12,18 +13,20 @@
const databaseId = $page.params.database;
let collectionName: string = null;
let collectionName: Nullable<string> = null;
onMount(() => {
collectionName ??= $collection.name;
});
async function updateName() {
const args = { databaseId, collectionName };
if (!isNonNullableObject(args)) return;
try {
await sdkForProject.databases.updateCollection(
databaseId,
args.databaseId,
$collection.$id,
collectionName,
args.collectionName,
$collection.$permissions,
$collection.documentSecurity,
$collection.enabled
@@ -7,6 +7,7 @@
import { Dependencies } from '$lib/constants';
import { Button } from '$lib/elements/forms';
import { symmetricDifference } from '$lib/helpers/array';
import { isNonNullable } from '$lib/helpers/type';
import { addNotification } from '$lib/stores/notifications';
import { sdkForProject } from '$lib/stores/sdk';
import { onMount } from 'svelte';
@@ -16,19 +17,20 @@
let arePermsDisabled = true;
let collectionPermissions: string[] = null;
let collectionPermissions: string[] | null = null;
onMount(() => {
collectionPermissions ??= $collection.$permissions;
});
async function updatePermissions() {
if (!isNonNullable(databaseId)) return;
try {
await sdkForProject.databases.updateCollection(
databaseId,
$collection.$id,
$collection.name,
collectionPermissions,
collectionPermissions ?? undefined,
$collection.documentSecurity,
$collection.enabled
);
@@ -5,6 +5,7 @@
import { CardGrid, Heading } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, FormList, InputSwitch } from '$lib/elements/forms';
import { isNonNullable, type Nullable } from '$lib/helpers/type';
import { addNotification } from '$lib/stores/notifications';
import { sdkForProject } from '$lib/stores/sdk';
import { onMount } from 'svelte';
@@ -12,20 +13,21 @@
const databaseId = $page.params.database;
let collectionDocumentSecurity: boolean = null;
let collectionDocumentSecurity: Nullable<boolean> = null;
onMount(() => {
collectionDocumentSecurity ??= $collection.documentSecurity;
});
async function updateSecurity() {
if (!isNonNullable(databaseId)) return;
try {
await sdkForProject.databases.updateCollection(
databaseId,
$collection.$id,
$collection.name,
$collection.$permissions,
collectionDocumentSecurity,
collectionDocumentSecurity ?? undefined,
$collection.enabled
);
invalidate(Dependencies.COLLECTION);
@@ -6,6 +6,7 @@
import { Dependencies } from '$lib/constants';
import { Button, InputSwitch } from '$lib/elements/forms';
import { toLocaleDateTime } from '$lib/helpers/date';
import { isNonNullable, type Nullable } from '$lib/helpers/type';
import { addNotification } from '$lib/stores/notifications';
import { sdkForProject } from '$lib/stores/sdk';
import { onMount } from 'svelte';
@@ -13,13 +14,14 @@
const databaseId = $page.params.database;
let enabled: boolean = null;
let enabled: Nullable<boolean> = null;
onMount(() => {
enabled ??= $collection.enabled;
});
async function toggleCollection() {
if (!isNonNullable(databaseId)) return;
try {
await sdkForProject.databases.updateCollection(
databaseId,
@@ -27,7 +29,7 @@
$collection.name,
$collection.$permissions,
$collection.documentSecurity,
enabled
enabled ?? undefined
);
invalidate(Dependencies.COLLECTION);
addNotification({
@@ -19,15 +19,17 @@
import { Button } from '$lib/elements/forms';
import DropList from '$lib/components/dropList.svelte';
import DropListItem from '$lib/components/dropListItem.svelte';
import { isNonNullable, type Nullable } from '$lib/helpers/type';
const functionId = $page.params.function;
const eventSet: Writable<Set<string>> = writable(new Set($func.events));
let showEvents = false;
let eventValue: string;
let eventValue: Nullable<string>;
let isDisabled = true;
let showDropdown: boolean[] = [];
async function updateEvents() {
if (!isNonNullable(functionId)) return;
try {
await sdkForProject.functions.update(
functionId,
@@ -7,6 +7,7 @@
import { Button, Form } from '$lib/elements/forms';
import { TableCell, TableCellText, TableList } from '$lib/elements/table';
import { symmetricDifference } from '$lib/helpers/array';
import { isNonNullable } from '$lib/helpers/type';
import { addNotification } from '$lib/stores/notifications';
import { sdkForConsole } from '$lib/stores/sdk';
import { onMount } from 'svelte';
@@ -24,6 +25,7 @@
});
async function updateEvents() {
if (!isNonNullable(projectId)) return;
try {
await sdkForConsole.projects.updateWebhook(
projectId,
@@ -5,24 +5,27 @@
import { CardGrid, Heading } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, FormList, InputText } from '$lib/elements/forms';
import { isNonNullableObject, type Nullable } from '$lib/helpers/type';
import { addNotification } from '$lib/stores/notifications';
import { sdkForConsole } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { webhook } from './store';
const projectId = $page.params.project;
let name: string = null;
let name: Nullable<string> = null;
onMount(async () => {
name ??= $webhook.name;
});
async function updateName() {
const args = { projectId, name };
if (!isNonNullableObject(args)) return;
try {
await sdkForConsole.projects.updateWebhook(
projectId,
args.projectId,
$webhook.$id,
name,
args.name,
$webhook.events,
$webhook.url,
$webhook.security,
@@ -12,14 +12,15 @@
InputPassword,
InputText
} from '$lib/elements/forms';
import { isNonNullableObject, type Nullable } from '$lib/helpers/type';
import { addNotification } from '$lib/stores/notifications';
import { sdkForConsole } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { webhook } from './store';
const projectId = $page.params.project;
let httpUser: string = null;
let httpPass: string = null;
let httpUser: string | null = null;
let httpPass: Nullable<string> = null;
let security = false;
onMount(async () => {
@@ -29,16 +30,18 @@
});
async function updateSecurity() {
const args = { projectId, httpUser, httpPass };
if (!isNonNullableObject(args)) return;
try {
await sdkForConsole.projects.updateWebhook(
projectId,
args.projectId,
$webhook.$id,
$webhook.name,
$webhook.events,
$webhook.url,
security,
httpUser,
httpPass
args.httpUser,
args.httpPass
);
invalidate(Dependencies.WEBHOOK);
addNotification({
@@ -5,26 +5,29 @@
import { CardGrid, Heading } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, FormList, InputText } from '$lib/elements/forms';
import { isNonNullableObject } from '$lib/helpers/type';
import { addNotification } from '$lib/stores/notifications';
import { sdkForConsole } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { webhook } from './store';
const projectId = $page.params.project;
let url: string = null;
let url: string | null = null;
onMount(async () => {
url ??= $webhook.url;
});
async function updateUrl() {
const args = { projectId, url };
if (!isNonNullableObject(args)) return;
try {
await sdkForConsole.projects.updateWebhook(
projectId,
args.projectId,
$webhook.$id,
$webhook.name,
$webhook.events,
url,
args.url,
$webhook.security,
$webhook.httpUser,
$webhook.httpPass
@@ -1,7 +1,7 @@
import { derived, writable } from 'svelte/store';
import { page } from '$app/stores';
import type { Models } from '@aw-labs/appwrite-console';
import type { BarSeriesOption } from 'echarts/charts';
import { derived, writable } from 'svelte/store';
export const project = derived(page, ($page) => $page.data.project as Models.Project);
export const onboarding = derived(
@@ -14,11 +14,11 @@ function createStats() {
return {
subscribe,
add: (projectId: string, data: BarSeriesOption['data'][number]) =>
add: (projectId: string, data: Array<string | number | Date>) =>
update((n) => {
let series = n.get(projectId) ?? [];
if (series.length === 0) {
if (series.length === 0 && data[0]) {
const date = new Date(data[0]);
for (let i = 0; i < 12; i++) {
series.push([date.toISOString(), 0]);
+2 -2
View File
@@ -27,7 +27,7 @@ self.addEventListener('fetch', (event: FetchEvent) => {
if (url.origin === location.origin && cachedFiles.includes(url.pathname)) {
// always return build files from cache
event.respondWith(cached);
event.respondWith(cached as Promise<Response>);
} else if (url.protocol === 'https:' || location.hostname === 'localhost') {
// hit the network for everything else...
const promise = fetch(request);
@@ -44,6 +44,6 @@ self.addEventListener('fetch', (event: FetchEvent) => {
});
// ...but if it fails, fall back to cache if available
event.respondWith(promise.catch(() => cached || promise));
event.respondWith(promise.catch(() => (cached as Promise<Response>) || promise));
}
});