mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
chore: resolve no-explicit-any and no-base-to-string lint warnings (#12217)
This commit is contained in:
@@ -17,8 +17,12 @@ import Analytics from "~/utils/Analytics";
|
||||
import history from "~/utils/history";
|
||||
import type { Action as KbarAction } from "kbar";
|
||||
|
||||
export function resolve<T>(value: any, context: ActionContext): T {
|
||||
return typeof value === "function" ? value(context) : value;
|
||||
export function resolve<T>(value: unknown, context: ActionContext): T {
|
||||
return (
|
||||
typeof value === "function"
|
||||
? (value as (context: ActionContext) => T)(context)
|
||||
: value
|
||||
) as T;
|
||||
}
|
||||
|
||||
export const ActionSeparator: TActionSeparator = {
|
||||
|
||||
@@ -29,7 +29,7 @@ import { useDialogContext } from "~/components/DialogContext";
|
||||
|
||||
const IconPicker = createLazyComponent(() => import("~/components/IconPicker"));
|
||||
|
||||
export interface FormData {
|
||||
export type FormData = {
|
||||
name: string;
|
||||
icon: string;
|
||||
color: string | null;
|
||||
@@ -37,7 +37,7 @@ export interface FormData {
|
||||
permission: CollectionPermission | undefined;
|
||||
commenting?: boolean | null;
|
||||
templateManagement: CollectionPermission;
|
||||
}
|
||||
};
|
||||
|
||||
const useIconColor = (collection?: Collection) => {
|
||||
const { collections } = useStores();
|
||||
|
||||
@@ -87,22 +87,23 @@ const ContentEditable = React.forwardRef(function ContentEditable_(
|
||||
}));
|
||||
|
||||
const wrappedEvent =
|
||||
(
|
||||
callback:
|
||||
| React.FocusEventHandler<HTMLSpanElement>
|
||||
| React.FormEventHandler<HTMLSpanElement>
|
||||
| React.KeyboardEventHandler<HTMLSpanElement>
|
||||
| undefined
|
||||
<E extends React.SyntheticEvent<HTMLSpanElement>>(
|
||||
callback: ((event: E) => void) | undefined
|
||||
) =>
|
||||
(event: any) => {
|
||||
(event: E) => {
|
||||
if (readOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
const text = event.currentTarget.textContent || "";
|
||||
|
||||
if (maxLength && isPrintableKeyEvent(event) && text.length >= maxLength) {
|
||||
event?.preventDefault();
|
||||
if (
|
||||
maxLength &&
|
||||
event.nativeEvent instanceof KeyboardEvent &&
|
||||
isPrintableKeyEvent(event.nativeEvent) &&
|
||||
text.length >= maxLength
|
||||
) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as React from "react";
|
||||
import lazyWithRetry from "~/utils/lazyWithRetry";
|
||||
|
||||
// oxlint-disable no-explicit-any -- ComponentType<any> is the standard React pattern for generic component constraints
|
||||
export interface LazyComponent<T extends React.ComponentType<any>> {
|
||||
Component: React.LazyExoticComponent<T>;
|
||||
preload: () => Promise<{ default: T }>;
|
||||
|
||||
@@ -13,7 +13,7 @@ import Switch from "../Switch";
|
||||
import EventBoundary from "@shared/components/EventBoundary";
|
||||
import { InputClientType } from "./InputClientType";
|
||||
|
||||
export interface FormData {
|
||||
export type FormData = {
|
||||
name: string;
|
||||
developerName: string;
|
||||
developerUrl: string;
|
||||
@@ -22,7 +22,7 @@ export interface FormData {
|
||||
redirectUris: string[];
|
||||
published: boolean;
|
||||
clientType: "confidential" | "public";
|
||||
}
|
||||
};
|
||||
|
||||
export const OAuthClientForm = observer(function OAuthClientForm_({
|
||||
handleSubmit,
|
||||
|
||||
@@ -7,7 +7,9 @@ import PaginatedList from "~/components/PaginatedList";
|
||||
|
||||
type Props = {
|
||||
documents: Document[];
|
||||
fetch: (options: any) => Promise<Document[] | undefined>;
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
fetch: (options: Record<string, any>) => Promise<Document[] | undefined>;
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
options?: Record<string, any>;
|
||||
heading?: React.ReactNode;
|
||||
empty?: JSX.Element;
|
||||
|
||||
@@ -35,10 +35,12 @@ interface Props<
|
||||
* @param options Pagination and other query options
|
||||
*/
|
||||
fetch?: (
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
options: Record<string, any> | undefined
|
||||
) => Promise<unknown[] | undefined> | undefined;
|
||||
|
||||
/** Additional options to pass to the fetch function */
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
options?: Record<string, any>;
|
||||
|
||||
/** Optional header content to display above the list */
|
||||
@@ -78,7 +80,7 @@ interface Props<
|
||||
* Function to render section headings (typically date-based)
|
||||
* @param name The heading text or element to render
|
||||
*/
|
||||
renderHeading?: (name: React.ReactElement<any> | string) => React.ReactNode;
|
||||
renderHeading?: (name: React.ReactElement | string) => React.ReactNode;
|
||||
|
||||
/**
|
||||
* Function to determine if an item is a duplicate of the previous item.
|
||||
|
||||
@@ -64,7 +64,7 @@ type Props = {
|
||||
};
|
||||
|
||||
const Tabs: React.FC = ({ children }: Props) => {
|
||||
const ref = React.useRef<any>();
|
||||
const ref = React.useRef<HTMLElement>(null);
|
||||
const [shadowVisible, setShadow] = React.useState(false);
|
||||
const { width } = useWindowSize();
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Toaster, useSonner } from "sonner";
|
||||
import styled, { useTheme } from "styled-components";
|
||||
import { useWebHaptics } from "web-haptics/react";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import type { ResolvedTheme } from "~/stores/UiStore";
|
||||
|
||||
function Toasts() {
|
||||
const { ui } = useStores();
|
||||
@@ -26,7 +27,8 @@ function Toasts() {
|
||||
|
||||
return (
|
||||
<StyledToaster
|
||||
theme={ui.resolvedTheme as any}
|
||||
// @ts-expect-error styled-components overrides sonner's theme prop with DefaultTheme
|
||||
theme={ui.resolvedTheme as ResolvedTheme}
|
||||
closeButton
|
||||
toastOptions={{
|
||||
duration: 5000,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// oxlint-disable no-explicit-any -- window.env is a server-injected boundary with mixed value types
|
||||
declare global {
|
||||
interface Window {
|
||||
env: Record<string, any>;
|
||||
|
||||
@@ -69,15 +69,15 @@ export const ActionContextProvider = observer(function ActionContextProvider_({
|
||||
activeDocumentId: stores.ui.activeDocumentId ?? undefined,
|
||||
|
||||
getActiveModels: <T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
modelClass: new (...args: never[]) => T
|
||||
): T[] => stores.ui.getActiveModels<T>(modelClass),
|
||||
|
||||
getActiveModel: <T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
modelClass: new (...args: never[]) => T
|
||||
): T | undefined => stores.ui.getActiveModels<T>(modelClass)[0],
|
||||
|
||||
getActivePolicies: <T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
modelClass: new (...args: never[]) => T
|
||||
): Policy[] =>
|
||||
stores.ui
|
||||
.getActiveModels<T>(modelClass)
|
||||
@@ -97,7 +97,7 @@ export const ActionContextProvider = observer(function ActionContextProvider_({
|
||||
// Override model accessors when models are provided in value
|
||||
const getActiveModels =
|
||||
valueModels && valueModels.length > 0
|
||||
? <T extends Model>(modelClass: new (...args: any[]) => T): T[] => {
|
||||
? <T extends Model>(modelClass: new (...args: never[]) => T): T[] => {
|
||||
const matching = valueModels.filter(
|
||||
(model): model is T => model instanceof modelClass
|
||||
);
|
||||
@@ -108,11 +108,11 @@ export const ActionContextProvider = observer(function ActionContextProvider_({
|
||||
: baseContext.getActiveModels;
|
||||
|
||||
const getActiveModel = <T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
modelClass: new (...args: never[]) => T
|
||||
): T | undefined => getActiveModels(modelClass)[0];
|
||||
|
||||
const getActivePolicies = <T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
modelClass: new (...args: never[]) => T
|
||||
): Policy[] =>
|
||||
getActiveModels(modelClass)
|
||||
.map((node) => stores.policies.get(node.id))
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { MobXProviderContext } from "mobx-react";
|
||||
import { useContext } from "react";
|
||||
import type RootStore from "~/stores";
|
||||
import type RootStore from "~/stores/RootStore";
|
||||
|
||||
/**
|
||||
* Hook to access the MobX stores from the React context.
|
||||
*
|
||||
* @returns The root store containing all application stores
|
||||
* @returns The root store containing all application stores.
|
||||
*/
|
||||
export default function useStores() {
|
||||
return useContext(MobXProviderContext) as typeof RootStore;
|
||||
export default function useStores(): RootStore {
|
||||
const { rootStore } = useContext(MobXProviderContext) as {
|
||||
rootStore: RootStore;
|
||||
};
|
||||
return rootStore;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ const defaultOptions: ThrottleSettings = {
|
||||
* @param dependencies The dependencies to watch for changes
|
||||
* @param options The throttle options
|
||||
*/
|
||||
export default function useThrottledCallback<T extends (...args: any[]) => any>(
|
||||
export default function useThrottledCallback<
|
||||
T extends (...args: never[]) => unknown,
|
||||
>(
|
||||
fn: T,
|
||||
wait = 250,
|
||||
dependencies: React.DependencyList = [],
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useRef, useEffect } from "react";
|
||||
*
|
||||
* @param callback Function to be called on component unmount
|
||||
*/
|
||||
const useUnmount = (callback: (...args: Array<any>) => any) => {
|
||||
const useUnmount = (callback: () => void) => {
|
||||
const ref = useRef(callback);
|
||||
ref.current = callback;
|
||||
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ if (element) {
|
||||
const App = () => (
|
||||
<StrictMode>
|
||||
<HelmetProvider>
|
||||
<Provider {...stores}>
|
||||
<Provider rootStore={stores}>
|
||||
<Analytics>
|
||||
<Router history={history}>
|
||||
<Theme>
|
||||
|
||||
@@ -37,11 +37,12 @@ class AuthenticationProvider extends Model {
|
||||
@AfterDelete
|
||||
static afterDelete(model: AuthenticationProvider) {
|
||||
// Restore a placeholder record to allow re-connection
|
||||
return (model.store as AuthenticationProvidersStore).add({
|
||||
...model,
|
||||
return (model.store as AuthenticationProvidersStore).add(
|
||||
Object.assign({}, model, {
|
||||
isEnabled: false,
|
||||
isConnected: false,
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ type SaveOptions = JSONObject & {
|
||||
export default class Document extends ArchivableModel implements Searchable {
|
||||
static modelName = "Document";
|
||||
|
||||
constructor(fields: Record<string, any>, store: DocumentsStore) {
|
||||
constructor(fields: Record<string, unknown>, store: DocumentsStore) {
|
||||
super(fields, store);
|
||||
|
||||
this.embedsDisabled = Storage.get(`embedsDisabled-${this.id}`) ?? false;
|
||||
@@ -570,7 +570,7 @@ export default class Document extends ArchivableModel implements Searchable {
|
||||
);
|
||||
|
||||
// if saving is successful set the new values on the model itself
|
||||
set(this, { ...params, ...model });
|
||||
set(this, Object.assign({}, params, model));
|
||||
|
||||
this.persistedAttributes = this.toAPI();
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ export default abstract class Model {
|
||||
|
||||
store: Store<Model>;
|
||||
|
||||
constructor(fields: Record<string, any>, store: Store<Model>) {
|
||||
constructor(fields: Record<string, unknown>, store: Store<Model>) {
|
||||
this.store = store;
|
||||
this.updateData(fields);
|
||||
this.isNew = !this.id;
|
||||
@@ -43,7 +43,7 @@ export default abstract class Model {
|
||||
async loadRelations(
|
||||
this: Model,
|
||||
options: { withoutPolicies?: boolean } = {}
|
||||
): Promise<any> {
|
||||
): Promise<unknown> {
|
||||
// this is to ensure that multiple loads don’t happen in parallel
|
||||
if (this.loadingRelations) {
|
||||
return this.loadingRelations;
|
||||
@@ -90,7 +90,7 @@ export default abstract class Model {
|
||||
* @returns A promise that resolves with the updated model
|
||||
*/
|
||||
save = async (
|
||||
params?: Record<string, any>,
|
||||
params?: Record<string, unknown>,
|
||||
options?: Record<string, string | boolean | number | undefined>
|
||||
): Promise<Model> => {
|
||||
const isNew = this.isNew;
|
||||
@@ -120,7 +120,7 @@ export default abstract class Model {
|
||||
);
|
||||
|
||||
// if saving is successful set the new values on the model itself
|
||||
this.updateData({ ...params, ...model });
|
||||
this.updateData(Object.assign({}, params, model));
|
||||
|
||||
if (isNew) {
|
||||
LifecycleManager.executeHooks(this.constructor, "afterCreate", this);
|
||||
@@ -134,7 +134,7 @@ export default abstract class Model {
|
||||
}
|
||||
};
|
||||
|
||||
updateData = action((data: Partial<Model>) => {
|
||||
updateData = action((data: Record<string, unknown>) => {
|
||||
if (this.initialized) {
|
||||
LifecycleManager.executeHooks(this.constructor, "beforeChange", this);
|
||||
}
|
||||
@@ -197,7 +197,7 @@ export default abstract class Model {
|
||||
*
|
||||
* @returns A plain object representation of the model
|
||||
*/
|
||||
toAPI = (): Record<string, any> => {
|
||||
toAPI = (): Partial<Model> => {
|
||||
const fields = getFieldsForModel(this);
|
||||
return pick(this, fields);
|
||||
};
|
||||
@@ -247,7 +247,7 @@ export default abstract class Model {
|
||||
protected persistedAttributes: Partial<Model> = {};
|
||||
|
||||
/** A promise that resolves when all relations have been loaded. */
|
||||
private loadingRelations: Promise<any[]> | undefined;
|
||||
private loadingRelations: Promise<unknown[]> | undefined;
|
||||
|
||||
/** A boolean representing if the constructor has been called. */
|
||||
private initialized = false;
|
||||
|
||||
@@ -12,9 +12,9 @@ export const getFieldsForModel = <T extends Model>(target: T) =>
|
||||
* @param target
|
||||
* @param propertyKey
|
||||
*/
|
||||
const Field = <T>(target: any, propertyKey: keyof T) => {
|
||||
const Field = (target: Model, propertyKey: string | symbol) => {
|
||||
const className = target.constructor.name;
|
||||
fields.set(className, [...(fields.get(className) || []), propertyKey]);
|
||||
fields.set(className, [...(fields.get(className) ?? []), propertyKey]);
|
||||
};
|
||||
|
||||
export default Field;
|
||||
|
||||
@@ -1,24 +1,32 @@
|
||||
export class LifecycleManager {
|
||||
private static hooks = new Map();
|
||||
type ModelClass = { readonly name: string };
|
||||
type Hook = (...args: unknown[]) => unknown;
|
||||
|
||||
public static getHooks(target: any, lifecycle: string) {
|
||||
export class LifecycleManager {
|
||||
private static hooks = new Map<string, Map<string, string[]>>();
|
||||
|
||||
public static getHooks(target: ModelClass, lifecycle: string): string[] {
|
||||
const key = `lifecycle:${lifecycle}`;
|
||||
const modelHooks = this.hooks.get(target.name);
|
||||
return modelHooks?.get(key) || [];
|
||||
return modelHooks?.get(key) ?? [];
|
||||
}
|
||||
|
||||
public static executeHooks(target: any, lifecycle: string, ...args: any[]) {
|
||||
public static executeHooks(
|
||||
target: ModelClass,
|
||||
lifecycle: string,
|
||||
...args: unknown[]
|
||||
): void {
|
||||
const hooks = this.getHooks(target, lifecycle);
|
||||
hooks.forEach((hook: keyof typeof target) => {
|
||||
target[hook](...args);
|
||||
hooks.forEach((hook) => {
|
||||
const fn = (target as unknown as Record<string, Hook>)[hook];
|
||||
fn(...args);
|
||||
});
|
||||
}
|
||||
|
||||
public static registerHook(
|
||||
target: any,
|
||||
target: ModelClass,
|
||||
propertyKey: string,
|
||||
lifecycle: string
|
||||
) {
|
||||
): void {
|
||||
const key = `lifecycle:${lifecycle}`;
|
||||
let modelHooks = this.hooks.get(target.name);
|
||||
|
||||
@@ -37,42 +45,42 @@ export class LifecycleManager {
|
||||
}
|
||||
}
|
||||
|
||||
export function BeforeCreate(target: any, propertyKey: string) {
|
||||
export function BeforeCreate(target: ModelClass, propertyKey: string) {
|
||||
LifecycleManager.registerHook(target, propertyKey, "beforeCreate");
|
||||
}
|
||||
|
||||
export function AfterCreate(target: any, propertyKey: string) {
|
||||
export function AfterCreate(target: ModelClass, propertyKey: string) {
|
||||
LifecycleManager.registerHook(target, propertyKey, "afterCreate");
|
||||
}
|
||||
|
||||
export function BeforeUpdate(target: any, propertyKey: string) {
|
||||
export function BeforeUpdate(target: ModelClass, propertyKey: string) {
|
||||
LifecycleManager.registerHook(target, propertyKey, "beforeUpdate");
|
||||
}
|
||||
|
||||
export function AfterUpdate(target: any, propertyKey: string) {
|
||||
export function AfterUpdate(target: ModelClass, propertyKey: string) {
|
||||
LifecycleManager.registerHook(target, propertyKey, "afterUpdate");
|
||||
}
|
||||
|
||||
export function BeforeChange(target: any, propertyKey: string) {
|
||||
export function BeforeChange(target: ModelClass, propertyKey: string) {
|
||||
LifecycleManager.registerHook(target, propertyKey, "beforeChange");
|
||||
}
|
||||
|
||||
export function AfterChange(target: any, propertyKey: string) {
|
||||
export function AfterChange(target: ModelClass, propertyKey: string) {
|
||||
LifecycleManager.registerHook(target, propertyKey, "afterChange");
|
||||
}
|
||||
|
||||
export function BeforeRemove(target: any, propertyKey: string) {
|
||||
export function BeforeRemove(target: ModelClass, propertyKey: string) {
|
||||
LifecycleManager.registerHook(target, propertyKey, "beforeRemove");
|
||||
}
|
||||
|
||||
export function AfterRemove(target: any, propertyKey: string) {
|
||||
export function AfterRemove(target: ModelClass, propertyKey: string) {
|
||||
LifecycleManager.registerHook(target, propertyKey, "afterRemove");
|
||||
}
|
||||
|
||||
export function BeforeDelete(target: any, propertyKey: string) {
|
||||
export function BeforeDelete(target: ModelClass, propertyKey: string) {
|
||||
LifecycleManager.registerHook(target, propertyKey, "beforeDelete");
|
||||
}
|
||||
|
||||
export function AfterDelete(target: any, propertyKey: string) {
|
||||
export function AfterDelete(target: ModelClass, propertyKey: string) {
|
||||
LifecycleManager.registerHook(target, propertyKey, "afterDelete");
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ export default function Relation<T extends typeof Model>(
|
||||
classResolver: () => T,
|
||||
options?: RelationOptions
|
||||
) {
|
||||
return function (target: any, propertyKey: string) {
|
||||
return function (target: Model, propertyKey: string) {
|
||||
const idKey = options?.multiple
|
||||
? `${String(singular(propertyKey))}Ids`
|
||||
: `${String(propertyKey)}Id`;
|
||||
@@ -96,16 +96,16 @@ export default function Relation<T extends typeof Model>(
|
||||
// TODO: requestAnimationFrame is a temporary solution to a bug in rolldown compiled code that
|
||||
// will place static methods _after_ decorators. Temporary fix is to delay the registration until
|
||||
// the next frame.
|
||||
const modelName = (target.constructor as typeof Model).modelName;
|
||||
requestAnimationFrame(() => {
|
||||
if (options) {
|
||||
const configForClass =
|
||||
relations.get(target.constructor.modelName) || new Map();
|
||||
const configForClass = relations.get(modelName) ?? new Map();
|
||||
configForClass.set(propertyKey, {
|
||||
options,
|
||||
relationClassResolver: classResolver,
|
||||
idKey,
|
||||
});
|
||||
relations.set(target.constructor.modelName, configForClass);
|
||||
relations.set(modelName, configForClass);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ function Login({ children, onBack }: Props) {
|
||||
const handleGoSubdomain = React.useCallback(async (event) => {
|
||||
event.preventDefault();
|
||||
const data = Object.fromEntries(new FormData(event.target));
|
||||
await navigateToSubdomain(data.subdomain.toString());
|
||||
await navigateToSubdomain(data.subdomain as string);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -104,7 +104,7 @@ function AuthenticationProvider(props: Props) {
|
||||
const input = document.createElement("input");
|
||||
input.type = "hidden";
|
||||
input.name = fieldName;
|
||||
input.value = String(value);
|
||||
input.value = String(value as string | number | boolean);
|
||||
formRef.current?.appendChild(input);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import Event from "~/models/Event";
|
||||
import type RootStore from "./RootStore";
|
||||
import Store, { RPCAction } from "./base/Store";
|
||||
|
||||
// oxlint-disable no-explicit-any -- Event generic must be `any` because the store holds events for all model types
|
||||
export default class EventsStore extends Store<Event<any>> {
|
||||
actions = [RPCAction.List];
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ export enum SystemTheme {
|
||||
Dark = "dark",
|
||||
}
|
||||
|
||||
export type ResolvedTheme = "light" | "dark" | "system";
|
||||
|
||||
type PersistedData = Pick<
|
||||
UiStore,
|
||||
| "languagePromptDismissed"
|
||||
@@ -209,7 +211,9 @@ class UiStore {
|
||||
* @param modelClass the model class to filter by.
|
||||
* @returns array of active models of the specified type.
|
||||
*/
|
||||
getActiveModels<T extends Model>(modelClass: new (...args: any[]) => T): T[] {
|
||||
getActiveModels<T extends Model>(
|
||||
modelClass: new (...args: never[]) => T
|
||||
): T[] {
|
||||
return Array.from(this.activeModels.values()).filter(
|
||||
(model) => model.constructor === modelClass
|
||||
) as T[];
|
||||
@@ -231,7 +235,7 @@ class UiStore {
|
||||
* @param modelClass optional model class to filter by.
|
||||
*/
|
||||
@action
|
||||
clearActiveModels(modelClass?: new (...args: any[]) => Model): void {
|
||||
clearActiveModels(modelClass?: new (...args: never[]) => Model): void {
|
||||
if (modelClass) {
|
||||
const modelsToRemove = this.getActiveModels(modelClass);
|
||||
modelsToRemove.forEach((model) => this.activeModels.delete(model.id));
|
||||
@@ -247,7 +251,7 @@ class UiStore {
|
||||
* @returns the most recently added model of the specified type.
|
||||
*/
|
||||
getPrimaryActiveModel<T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
modelClass: new (...args: never[]) => T
|
||||
): T | undefined {
|
||||
const models = this.getActiveModels<T>(modelClass);
|
||||
return models[models.length - 1];
|
||||
|
||||
@@ -44,6 +44,7 @@ export type PaginatedResponse<T> = T[] & {
|
||||
};
|
||||
};
|
||||
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
export type FetchPageParams = PaginationParams & Record<string, any>;
|
||||
|
||||
export default abstract class Store<T extends Model> {
|
||||
@@ -59,6 +60,7 @@ export default abstract class Store<T extends Model> {
|
||||
@observable
|
||||
isLoaded = false;
|
||||
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
requests: Map<string, Promise<any>> = new Map();
|
||||
|
||||
model: typeof Model;
|
||||
@@ -430,6 +432,7 @@ export default abstract class Store<T extends Model> {
|
||||
|
||||
@action
|
||||
fetchAll = async (
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
params?: Record<string, any>
|
||||
): Promise<PaginatedResponse<T>> => {
|
||||
const limit = params?.limit ?? Pagination.defaultLimit;
|
||||
|
||||
+4
-4
@@ -116,13 +116,13 @@ export type ActionContext = {
|
||||
|
||||
// New API - work directly with Model instances
|
||||
getActiveModels: <T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
modelClass: new (...args: never[]) => T
|
||||
) => T[];
|
||||
getActiveModel: <T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
modelClass: new (...args: never[]) => T
|
||||
) => T | undefined;
|
||||
getActivePolicies: <T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
modelClass: new (...args: never[]) => T
|
||||
) => Policy[];
|
||||
isModelActive: (model: Model) => boolean;
|
||||
activeModels: ReadonlySet<Model>;
|
||||
@@ -160,7 +160,7 @@ export type Action = BaseAction & {
|
||||
tooltip?:
|
||||
| ((context: ActionContext) => React.ReactChild | undefined)
|
||||
| React.ReactChild;
|
||||
perform: (context: ActionContext) => any;
|
||||
perform: (context: ActionContext) => unknown;
|
||||
};
|
||||
|
||||
export type InternalLinkAction = BaseAction & {
|
||||
|
||||
@@ -47,6 +47,7 @@ class ApiClient {
|
||||
shareId?: string;
|
||||
|
||||
/** Map of in-flight POST requests for deduplication, keyed by path + body. */
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
private inflightRequests = new Map<string, Promise<any>>();
|
||||
|
||||
constructor(options: Options = {}) {
|
||||
@@ -57,6 +58,7 @@ class ApiClient {
|
||||
this.shareId = shareId;
|
||||
};
|
||||
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
fetch = async <T = any>(
|
||||
path: string,
|
||||
method: string,
|
||||
@@ -93,7 +95,7 @@ class ApiClient {
|
||||
// toggling Content-Type to application/json
|
||||
if (
|
||||
typeof data === "object" &&
|
||||
(data || "").toString() === "[object Object]"
|
||||
Object.prototype.toString.call(data) === "[object Object]"
|
||||
) {
|
||||
body = JSON.stringify(data);
|
||||
}
|
||||
@@ -206,7 +208,7 @@ class ApiClient {
|
||||
const error: {
|
||||
message?: string;
|
||||
error?: string;
|
||||
data?: Record<string, any>;
|
||||
data?: Record<string, unknown>;
|
||||
} = {};
|
||||
|
||||
try {
|
||||
@@ -277,12 +279,14 @@ class ApiClient {
|
||||
throw err;
|
||||
};
|
||||
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
get = <T = any>(
|
||||
path: string,
|
||||
data: JSONObject | undefined,
|
||||
options?: FetchOptions
|
||||
) => this.fetch<T>(path, "GET", data, options);
|
||||
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
post = <T = any>(
|
||||
path: string,
|
||||
data?: JSONObject | FormData,
|
||||
|
||||
@@ -13,6 +13,7 @@ type LogCategory =
|
||||
| "plugins"
|
||||
| "policies";
|
||||
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
type Extra = Record<string, any>;
|
||||
|
||||
class Logger {
|
||||
|
||||
@@ -41,9 +41,8 @@ export default function download(
|
||||
}
|
||||
|
||||
// go ahead and download dataURLs right away
|
||||
if (String(x).match(/^data:[\w+-]+\/[\w+-]+[,;]/)) {
|
||||
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
|
||||
return saver(x); // everyone else can save dataURLs un-processed
|
||||
if (typeof x === "string" && x.match(/^data:[\w+-]+\/[\w+-]+[,;]/)) {
|
||||
return saver(x);
|
||||
}
|
||||
|
||||
// end if dataURL passed?
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as React from "react";
|
||||
|
||||
// oxlint-disable no-explicit-any -- ComponentType<any> is the standard React pattern for generic component constraints
|
||||
type ComponentPromise<T extends React.ComponentType<any>> = Promise<{
|
||||
default: T;
|
||||
}>;
|
||||
|
||||
@@ -60,8 +60,8 @@ class Logger {
|
||||
winston.format.printf(
|
||||
({ message, level, label, ...extra }) =>
|
||||
`${level}: ${
|
||||
label ? styleText("bold", `[${String(label)}] `) : ""
|
||||
}${String(message)} ${isEmpty(extra) ? "" : JSON.stringify(extra)}`
|
||||
label ? styleText("bold", `[${label as string}] `) : ""
|
||||
}${message as string} ${isEmpty(extra) ? "" : JSON.stringify(extra)}`
|
||||
)
|
||||
),
|
||||
})
|
||||
|
||||
@@ -371,13 +371,13 @@ export class ProsemirrorHelper extends SharedProsemirrorHelper {
|
||||
|
||||
function replaceAttachmentUrls(node: ProsemirrorData) {
|
||||
if (node.attrs?.src) {
|
||||
node.attrs.src = getMapping(String(node.attrs.src));
|
||||
node.attrs.src = getMapping(node.attrs.src as string);
|
||||
} else if (node.attrs?.href) {
|
||||
node.attrs.href = getMapping(String(node.attrs.href));
|
||||
node.attrs.href = getMapping(node.attrs.href as string);
|
||||
} else if (node.marks) {
|
||||
node.marks.forEach((mark) => {
|
||||
if (mark.attrs?.href) {
|
||||
mark.attrs.href = getMapping(String(mark.attrs.href));
|
||||
mark.attrs.href = getMapping(mark.attrs.href as string);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -63,9 +63,14 @@ export function withAPIContext<T>(
|
||||
* @param obj Object to convert to form-urlencoded string
|
||||
* @returns Form-urlencoded string representation of the object
|
||||
*/
|
||||
export function toFormData(obj: Record<string, any>): string {
|
||||
export function toFormData(
|
||||
obj: Record<string, string | number | boolean | null | undefined>
|
||||
): string {
|
||||
return Object.entries(obj)
|
||||
.filter(([_, value]) => value !== undefined)
|
||||
.filter(
|
||||
(entry): entry is [string, string | number | boolean] =>
|
||||
entry[1] !== undefined && entry[1] !== null
|
||||
)
|
||||
.map(
|
||||
([key, value]) =>
|
||||
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
|
||||
|
||||
@@ -356,7 +356,7 @@ export function documentTools(server: McpServer, scopes: string[]) {
|
||||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
text: String(text ?? ""),
|
||||
text: typeof text === "string" ? text : "",
|
||||
},
|
||||
],
|
||||
} satisfies CallToolResult;
|
||||
@@ -606,7 +606,7 @@ export function documentTools(server: McpServer, scopes: string[]) {
|
||||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
text: String(text ?? ""),
|
||||
text: typeof text === "string" ? text : "",
|
||||
},
|
||||
],
|
||||
} satisfies CallToolResult;
|
||||
|
||||
@@ -119,7 +119,7 @@ export function fetchTool(server: McpServer, scopes: string[]) {
|
||||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
text: String(text ?? ""),
|
||||
text: typeof text === "string" ? text : "",
|
||||
},
|
||||
],
|
||||
} satisfies CallToolResult;
|
||||
|
||||
@@ -299,9 +299,9 @@ export class DocumentConverter {
|
||||
|
||||
// Replace the content-location with a data URI for each attachment.
|
||||
for (const attachment of parsed.attachments) {
|
||||
const contentLocation = String(
|
||||
attachment.headers.get("content-location") ?? ""
|
||||
);
|
||||
const contentLocation =
|
||||
(attachment.headers.get("content-location") as string | undefined) ??
|
||||
"";
|
||||
|
||||
const id = contentLocation.split("/").pop();
|
||||
if (!id) {
|
||||
|
||||
@@ -13,6 +13,4 @@ export class MutexLock {
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
private static redlock: any;
|
||||
}
|
||||
|
||||
@@ -195,10 +195,15 @@ export const MentionURL = (props: IssueUrlProps) => {
|
||||
...attrs
|
||||
} = getAttributesFromNode(node);
|
||||
|
||||
const url = String(attrs.href);
|
||||
const unfurl = unfurls.get(url)?.data ?? unfurlAttr;
|
||||
const url = typeof attrs.href === "string" ? attrs.href : undefined;
|
||||
const unfurl = url ? (unfurls.get(url)?.data ?? unfurlAttr) : undefined;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!url) {
|
||||
setLoaded(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchUnfurl = async () => {
|
||||
try {
|
||||
const unfurlModel = await unfurls.fetchUnfurl({ url });
|
||||
|
||||
@@ -142,7 +142,8 @@ export function setCellAttrs(node: Node): Attrs {
|
||||
attrs["data-colwidth"] = node.attrs.colwidth.map(Number).join(",");
|
||||
} else {
|
||||
attrs.style =
|
||||
(attrs.style ?? "") + `min-width: ${Number(node.attrs.colwidth[0])}px;`;
|
||||
((attrs.style as string) ?? "") +
|
||||
`min-width: ${Number(node.attrs.colwidth[0])}px;`;
|
||||
}
|
||||
}
|
||||
if (Array.isArray(node.attrs.marks)) {
|
||||
@@ -156,7 +157,7 @@ export function setCellAttrs(node: Node): Attrs {
|
||||
const color = backgroundMark.attrs!.color as string;
|
||||
attrs["data-bgcolor"] = color;
|
||||
attrs.style =
|
||||
(attrs.style ?? "") +
|
||||
((attrs.style as string) ?? "") +
|
||||
`--cell-bg-color: ${color}; --cell-text-color: ${readableColor(color)};`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@ import { MobXProviderContext } from "mobx-react";
|
||||
import * as React from "react";
|
||||
|
||||
export default function useStores() {
|
||||
return React.useContext(MobXProviderContext);
|
||||
return React.useContext(MobXProviderContext).rootStore;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export const ellipsis = () => `
|
||||
*/
|
||||
export const s =
|
||||
(key: keyof DefaultTheme) => (props: { theme: DefaultTheme }) =>
|
||||
String(props.theme[key]);
|
||||
props.theme[key] as string;
|
||||
|
||||
/**
|
||||
* Mixin to hide scrollbars.
|
||||
|
||||
@@ -506,19 +506,19 @@ export class ProsemirrorHelper {
|
||||
if (
|
||||
node.type === "image" &&
|
||||
node.attrs?.src &&
|
||||
regex.test(String(node.attrs.src))
|
||||
regex.test(node.attrs.src as string)
|
||||
) {
|
||||
node.attrs.src = env.URL + node.attrs.src;
|
||||
} else if (
|
||||
node.type === "video" &&
|
||||
node.attrs?.src &&
|
||||
regex.test(String(node.attrs.src))
|
||||
regex.test(node.attrs.src as string)
|
||||
) {
|
||||
node.attrs.src = env.URL + node.attrs.src;
|
||||
} else if (
|
||||
node.type === "attachment" &&
|
||||
node.attrs?.href &&
|
||||
regex.test(String(node.attrs.href))
|
||||
regex.test(node.attrs.href as string)
|
||||
) {
|
||||
node.attrs.href = env.URL + node.attrs.href;
|
||||
}
|
||||
|
||||
+4
-1
@@ -40,7 +40,10 @@ export class CSVHelper {
|
||||
return "";
|
||||
}
|
||||
|
||||
const stringValue = String(value);
|
||||
const stringValue =
|
||||
typeof value === "object"
|
||||
? JSON.stringify(value)
|
||||
: String(value as string | number | boolean);
|
||||
|
||||
// If the value contains comma, quote, or newline, wrap it in quotes and escape internal quotes
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user