chore: resolve no-explicit-any and no-base-to-string lint warnings (#12217)

This commit is contained in:
Tom Moor
2026-04-29 17:45:02 -04:00
committed by GitHub
parent 281b778b2d
commit 4c8a1c89b2
44 changed files with 162 additions and 110 deletions
+6 -2
View File
@@ -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 = {
+2 -2
View File
@@ -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();
+10 -9
View File
@@ -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
View File
@@ -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,
+3 -1
View File
@@ -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;
+3 -1
View File
@@ -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.
+1 -1
View File
@@ -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();
+3 -1
View File
@@ -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
View File
@@ -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>;
+6 -6
View File
@@ -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))
+7 -4
View File
@@ -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;
}
+3 -1
View File
@@ -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 = [],
+1 -1
View File
@@ -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
View File
@@ -57,7 +57,7 @@ if (element) {
const App = () => (
<StrictMode>
<HelmetProvider>
<Provider {...stores}>
<Provider rootStore={stores}>
<Analytics>
<Router history={history}>
<Theme>
+4 -3
View File
@@ -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,
});
})
);
}
}
+2 -2
View File
@@ -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();
+7 -7
View File
@@ -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 dont 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;
+2 -2
View File
@@ -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;
+27 -19
View File
@@ -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");
}
+4 -4
View File
@@ -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);
}
});
+1 -1
View File
@@ -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);
}
});
+1
View File
@@ -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];
+7 -3
View File
@@ -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];
+3
View File
@@ -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
View File
@@ -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 & {
+6 -2
View File
@@ -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,
+1
View File
@@ -13,6 +13,7 @@ type LogCategory =
| "plugins"
| "policies";
// oxlint-disable-next-line no-explicit-any
type Extra = Record<string, any>;
class Logger {
+2 -3
View File
@@ -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
View File
@@ -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;
}>;
+2 -2
View File
@@ -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)}`
)
),
})
+3 -3
View File
@@ -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);
}
});
}
+7 -2
View File
@@ -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)}`
+2 -2
View File
@@ -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;
+1 -1
View File
@@ -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;
+3 -3
View File
@@ -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) {
-2
View File
@@ -13,6 +13,4 @@ export class MutexLock {
}),
};
}
private static redlock: any;
}
+7 -2
View File
@@ -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 });
+3 -2
View File
@@ -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)};`;
}
}
+1 -1
View File
@@ -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;
}
+1 -1
View File
@@ -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.
+3 -3
View File
@@ -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
View File
@@ -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 (