Add pretty formatting (#29)

* Test

* Add pretty formatting

* Fix Tests

* Fix Tests

* Fix Tests

* Fix

* Add pretty formatting fix

* Fix

* Test

* Fix tests

* Clean typeckech

* Add prettier check

* Fix api tsconfig

* Fix api tsconfig

* Fix tsconfig

* Fix

* Fix

* Prettier
This commit is contained in:
Martin Braquet
2026-02-20 17:32:27 +01:00
committed by GitHub
parent 1994697fa1
commit ba9b3cfb06
695 changed files with 22382 additions and 23209 deletions

View File

@@ -1,8 +1,4 @@
export function binarySearch(
min: number,
max: number,
comparator: (x: number) => number
) {
export function binarySearch(min: number, max: number, comparator: (x: number) => number) {
let mid = 0
let i = 0
while (true) {
@@ -22,8 +18,7 @@ export function binarySearch(
i++
if (i > 100000) {
throw new Error(
'Binary search exceeded max iterations' +
JSON.stringify({ min, max, mid, i }, null, 2)
'Binary search exceeded max iterations' + JSON.stringify({min, max, mid, i}, null, 2),
)
}
}

View File

@@ -1,8 +1,8 @@
import { API, APIParams, APIPath, APIResponse } from 'common/api/schema'
import { APIError, getApiUrl } from 'common/api/utils'
import { forEach } from 'lodash'
import { removeUndefinedProps } from 'common/util/object'
import { User } from 'firebase/auth'
import {API, APIParams, APIPath, APIResponse} from 'common/api/schema'
import {APIError, getApiUrl} from 'common/api/utils'
import {removeUndefinedProps} from 'common/util/object'
import {User} from 'firebase/auth'
import {forEach} from 'lodash'
export function unauthedApi<P extends APIPath>(path: P, params: APIParams<P>) {
return typedAPICall(path, params, null)
@@ -11,7 +11,7 @@ export function unauthedApi<P extends APIPath>(path: P, params: APIParams<P>) {
export const typedAPICall = <P extends APIPath>(
path: P,
params: APIParams<P>,
user: User | null
user: User | null,
) => {
// parse any params that should part of the path (like market/:id)
const newParams: any = {}
@@ -51,7 +51,7 @@ export async function baseApiCall(props: {
params: any
user: User | null
}) {
const { url, method, params, user } = props
const {url, method, params, user} = props
const actualUrl = method === 'POST' ? url : appendQuery(url, params)
const headers: HeadersInit = {
@@ -64,12 +64,11 @@ export async function baseApiCall(props: {
const req = new Request(actualUrl, {
headers,
method: method,
body:
params == null || method === 'GET' ? undefined : JSON.stringify(params),
body: params == null || method === 'GET' ? undefined : JSON.stringify(params),
})
// console.log('Request', req)
return fetch(req).then(async (resp) => {
const json = (await resp.json()) as { [k: string]: any }
const json = (await resp.json()) as {[k: string]: any}
if (!resp.ok) {
throw new APIError(resp.status as any, json?.message, json?.details)
}

View File

@@ -1,7 +1,6 @@
import { compact, flattenDeep, isEqual } from 'lodash'
import {compact, flattenDeep, isEqual} from 'lodash'
export const arrify = <T>(maybeArr: T | T[]) =>
Array.isArray(maybeArr) ? maybeArr : [maybeArr]
export const arrify = <T>(maybeArr: T | T[]) => (Array.isArray(maybeArr) ? maybeArr : [maybeArr])
export function filterDefined<T>(array: (T | null | undefined)[]) {
return array.filter((item) => item !== null && item !== undefined) as T[]
@@ -19,12 +18,12 @@ export function groupConsecutive<T, U>(xs: T[], key: (x: T) => U) {
return []
}
const result = []
let curr = { key: key(xs[0]), items: [xs[0]] }
let curr = {key: key(xs[0]), items: [xs[0]]}
for (const x of xs.slice(1)) {
const k = key(x)
if (!isEqual(k, curr.key)) {
result.push(curr)
curr = { key: k, items: [x] }
curr = {key: k, items: [x]}
} else {
curr.items.push(x)
}

View File

@@ -1,4 +1,5 @@
export const cleanUsername = (name: string, maxLength = 25) => {
// Test guidance: do not mock this method (pure, deterministic transformations with no side effects)
return name
.replace(/\s+/g, '')
.normalize('NFD') // split an accented letter in the base letter and the accent
@@ -8,5 +9,6 @@ export const cleanUsername = (name: string, maxLength = 25) => {
}
export const cleanDisplayName = (displayName: string, maxLength = 30) => {
// Test guidance: do not mock this method (pure, deterministic transformations with no side effects)
return displayName.replace(/\s+/g, ' ').substring(0, maxLength).trim()
}

View File

@@ -1,4 +1,4 @@
import { sortBy, sum } from 'lodash'
import {sortBy, sum} from 'lodash'
export const logInterpolation = (min: number, max: number, value: number) => {
if (value <= min) return 0

View File

@@ -1,4 +1,4 @@
import { max, sumBy } from 'lodash'
import {max, sumBy} from 'lodash'
// each row has [column, value] pairs
type SparseMatrix = [number, number][][]
@@ -23,7 +23,7 @@ export function factorizeMatrix(
ITERS = 5000,
LEARNING_RATE = 0.0002,
REGULARIZATION_RATE = 0.02,
THRESHOLD = 0.001
THRESHOLD = 0.001,
) {
const initCell = () => (2 * Math.random()) / FEATURES
const m = TARGET_MATRIX.length

View File

@@ -1,4 +1,4 @@
import { isEqual, mapValues, union } from 'lodash'
import {isEqual, mapValues, union} from 'lodash'
export const removeUndefinedProps = <T extends object>(obj: T): T => {
const newObj: any = {}
@@ -9,10 +9,7 @@ export const removeUndefinedProps = <T extends object>(obj: T): T => {
return newObj
}
export const removeNullOrUndefinedProps = <T extends object>(
obj: T,
exceptions?: string[]
): T => {
export const removeNullOrUndefinedProps = <T extends object>(obj: T, exceptions?: string[]): T => {
const newObj: any = {}
for (const key of Object.keys(obj)) {
@@ -25,10 +22,7 @@ export const removeNullOrUndefinedProps = <T extends object>(
return newObj
}
export const addObjects = <T extends { [key: string]: number }>(
obj1: T,
obj2: T
) => {
export const addObjects = <T extends {[key: string]: number}>(obj1: T, obj2: T) => {
const keys = union(Object.keys(obj1), Object.keys(obj2))
const newObj = {} as any
@@ -39,10 +33,7 @@ export const addObjects = <T extends { [key: string]: number }>(
return newObj as T
}
export const subtractObjects = <T extends { [key: string]: number }>(
obj1: T,
obj2: T
) => {
export const subtractObjects = <T extends {[key: string]: number}>(obj1: T, obj2: T) => {
const keys = union(Object.keys(obj1), Object.keys(obj2))
const newObj = {} as any
@@ -61,18 +52,14 @@ export const hasChanges = <T extends object>(obj: T, partial: Partial<T>) => {
export const hasSignificantDeepChanges = <T extends object>(
obj: T,
partial: Partial<T>,
epsilonForNumbers: number
epsilonForNumbers: number,
): boolean => {
const compareValues = (currValue: any, partialValue: any): boolean => {
if (typeof currValue === 'number' && typeof partialValue === 'number') {
return Math.abs(currValue - partialValue) > epsilonForNumbers
}
if (typeof currValue === 'object' && typeof partialValue === 'object') {
return hasSignificantDeepChanges(
currValue,
partialValue,
epsilonForNumbers
)
return hasSignificantDeepChanges(currValue, partialValue, epsilonForNumbers)
}
return !isEqual(currValue, partialValue)
}

View File

@@ -5,10 +5,9 @@ import {DOMAIN} from 'common/envs/constants'
export function buildOgUrl<P extends Record<string, string>>(
props: P,
endpoint: string,
domain?: string
domain?: string,
) {
const generateUrlParams = (params: P) =>
new URLSearchParams(params).toString()
const generateUrlParams = (params: P) => new URLSearchParams(params).toString()
return `https://${domain ?? DOMAIN}/api/og/${endpoint}?` + generateUrlParams(props)
}

View File

@@ -1,14 +1,15 @@
import {getSchema, getText, getTextSerializersFromSchema, JSONContent,} from '@tiptap/core'
import {Node as ProseMirrorNode} from '@tiptap/pm/model'
import {StarterKit} from '@tiptap/starter-kit'
import {getSchema, getText, getTextSerializersFromSchema, JSONContent} from '@tiptap/core'
import {Image} from '@tiptap/extension-image'
import {Link} from '@tiptap/extension-link'
import {Mention} from '@tiptap/extension-mention'
import Iframe from './tiptap-iframe'
import {Node as ProseMirrorNode} from '@tiptap/pm/model'
import {StarterKit} from '@tiptap/starter-kit'
import {find} from 'linkifyjs'
import {uniq} from 'lodash'
import {compareTwoStrings} from 'string-similarity'
import Iframe from './tiptap-iframe'
/** get first url in text. like "notion.so " -> "http://notion.so" "notion" -> null */
export function getUrl(text: string) {
const results = find(text, 'url')
@@ -46,8 +47,7 @@ export const extensions = [
Image.extend({renderText: () => '[image]'}),
Mention, // user @mention
Iframe.extend({
renderText: ({node}) =>
'[embed]' + node.attrs.src ? `(${node.attrs.src})` : '',
renderText: ({node}) => ('[embed]' + node.attrs.src ? `(${node.attrs.src})` : ''),
}),
]
@@ -76,7 +76,7 @@ export function urlBase64ToUint8Array(base64String: string) {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/')
const rawData = window.atob(base64)
return new Uint8Array([...rawData].map(c => c.charCodeAt(0)))
return new Uint8Array([...rawData].map((c) => c.charCodeAt(0)))
}
export function cleanDoc(doc: JSONContent) {
@@ -89,52 +89,53 @@ export function cleanDoc(doc: JSONContent) {
}
function _cleanDoc(doc: JSONContent) {
if (!doc || !Array.isArray(doc.content)) return doc;
if (!doc || !Array.isArray(doc.content)) return doc
let content = [...doc.content];
let content = [...doc.content]
const isEmptyParagraph = (node: JSONContent) =>
node.type === "paragraph" &&
(!node.content || node.content.length === 0);
node.type === 'paragraph' && (!node.content || node.content.length === 0)
// Remove empty paragraphs at the start
while (content.length > 0 && isEmptyParagraph(content[0])) {
content.shift();
content.shift()
}
// Remove empty paragraphs at the end
while (content.length > 0 && isEmptyParagraph(content[content.length - 1])) {
content.pop();
content.pop()
}
// Trim leading/trailing hardBreaks within first and last paragraphs
const trimHardBreaks = (paragraph: JSONContent, start: boolean, end: boolean) => {
if (!paragraph.content) return paragraph;
if (!paragraph.content) return paragraph
const nodes = [...paragraph.content];
const nodes = [...paragraph.content]
// Remove hardBreaks at the start
while (start && nodes.length > 0 && nodes[0].type === "hardBreak") {
nodes.shift();
while (start && nodes.length > 0 && nodes[0].type === 'hardBreak') {
nodes.shift()
}
// Remove hardBreaks at the end
while (end && nodes.length > 0 && nodes[nodes.length - 1].type === "hardBreak") {
nodes.pop();
while (end && nodes.length > 0 && nodes[nodes.length - 1].type === 'hardBreak') {
nodes.pop()
}
return { ...paragraph, content: nodes };
};
return {...paragraph, content: nodes}
}
if (content.length > 0) {
content[0] = trimHardBreaks(content[0], true, false);
content[0] = trimHardBreaks(content[0], true, false)
if (content.length > 1) {
content[content.length - 1] = trimHardBreaks(content[content.length - 1], false, true);
content[content.length - 1] = trimHardBreaks(content[content.length - 1], false, true)
}
}
// Remove any now-empty paragraphs created by hardBreak trimming
content = content.filter(node => !(node.type === "paragraph" && (!node.content || node.content.length === 0)));
content = content.filter(
(node) => !(node.type === 'paragraph' && (!node.content || node.content.length === 0)),
)
return { ...doc, content };
return {...doc, content}
}

View File

@@ -29,15 +29,13 @@ export async function withRetries<T>(q: PromiseLike<T>, policy?: RetryPolicy) {
export const mapAsyncChunked = async <T, U>(
items: T[],
f: (item: T, index: number) => Promise<U>,
chunkSize = 100
chunkSize = 100,
) => {
const results: U[] = []
for (let i = 0; i < items.length; i += chunkSize) {
const chunk = items.slice(i, i + chunkSize)
const chunkResults = await Promise.all(
chunk.map((item, index) => f(item, i + index))
)
const chunkResults = await Promise.all(chunk.map((item, index) => f(item, i + index)))
results.push(...chunkResults)
}
@@ -47,7 +45,7 @@ export const mapAsyncChunked = async <T, U>(
export const mapAsync = <T, U>(
items: T[],
f: (item: T, index: number) => Promise<U>,
maxConcurrentRequests = 100
maxConcurrentRequests = 100,
) => {
let index = 0
let currRequests = 0

View File

@@ -7,7 +7,7 @@ export function genHash(str: string) {
// xmur3
// Route around compiler bug by using object?
const o = { h: 1779033703 ^ str.length }
const o = {h: 1779033703 ^ str.length}
for (let i = 0; i < str.length; i++) {
let h = o.h
@@ -51,4 +51,4 @@ export const shuffle = (array: unknown[], rand: () => number) => {
const swapIndex = i + Math.floor(rand() * (array.length - i))
;[array[i], array[swapIndex]] = [array[swapIndex], array[i]]
}
}
}

View File

@@ -1,8 +1,4 @@
export const slugify = (
text: string,
separator = '-',
maxLength = 35
): string => {
export const slugify = (text: string, separator = '-', maxLength = 35): string => {
return text
.toString()
.normalize('NFD') // split an accented letter in the base letter and the acent

View File

@@ -1,4 +1,4 @@
import {MAX_INT} from "common/constants";
import {MAX_INT} from 'common/constants'
export function getSortedOptions(options: string[], order: string[] | Record<string, string>) {
let parsedOrder: string[]
@@ -7,14 +7,12 @@ export function getSortedOptions(options: string[], order: string[] | Record<str
} else {
parsedOrder = Object.keys(order)
}
return options
.slice()
.sort((a, b) => {
const ia = parsedOrder.indexOf(a as any)
const ib = parsedOrder.indexOf(b as any)
const sa = ia === -1 ? MAX_INT : ia
const sb = ib === -1 ? MAX_INT : ib
if (sa !== sb) return sa - sb
return String(a).localeCompare(String(b))
});
}
return options.slice().sort((a, b) => {
const ia = parsedOrder.indexOf(a as any)
const ib = parsedOrder.indexOf(b as any)
const sa = ia === -1 ? MAX_INT : ia
const sb = ib === -1 ? MAX_INT : ib
if (sa !== sb) return sa - sb
return String(a).localeCompare(String(b))
})
}

View File

@@ -2,6 +2,6 @@ export function removeEmojis(str: string) {
return str.replace(
// eslint-disable-next-line no-misleading-character-class
/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F700}-\u{1F77F}\u{1F780}-\u{1F7FF}\u{1F800}-\u{1F8FF}\u{1F900}-\u{1F9FF}\u{1FA00}-\u{1FA6F}\u{1FA70}-\u{1FAFF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{2300}-\u{23FF}\u{2B50}\u{2B55}\u{2934}\u{2935}\u{2B05}\u{2B06}\u{2B07}\u{2B1B}\u{2B1C}\u{2B50}\u{2B55}\u{3030}\u{303D}\u{3297}\u{3299}\u{FE0F}]/gu,
''
'',
)
}
}

View File

@@ -6,5 +6,4 @@ export const MONTH_MS = 30 * DAY_MS
export const YEAR_MS = 365 * DAY_MS
export const HOUR_SECONDS = 60 * 60
export const sleep = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms))
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))

View File

@@ -1,6 +1,6 @@
// Adopted from https://github.com/ueberdosis/tiptap/blob/main/demos/src/Experiments/Embeds/Vue/iframe.ts
import { Node, mergeAttributes } from '@tiptap/core'
import {mergeAttributes, Node} from '@tiptap/core'
export interface IframeOptions {
HTMLAttributes: {
@@ -11,7 +11,7 @@ export interface IframeOptions {
declare module '@tiptap/core' {
interface Commands<ReturnType> {
iframe: {
setIframe: (options: { src: string }) => ReturnType
setIframe: (options: {src: string}) => ReturnType
}
}
}
@@ -46,15 +46,12 @@ export default Node.create<IframeOptions>({
},
parseHTML() {
return [{ tag: 'iframe' }]
return [{tag: 'iframe'}]
},
renderHTML({ HTMLAttributes }) {
const iframeAttributes = mergeAttributes(
this.options.HTMLAttributes,
HTMLAttributes
)
const { src } = HTMLAttributes
renderHTML({HTMLAttributes}) {
const iframeAttributes = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)
const {src} = HTMLAttributes
// This is a hack to prevent native from opening the iframe in an in-app browser
// and mobile in another tab. In native, links with target='_blank' open in the in-app browser.
@@ -93,9 +90,9 @@ export default Node.create<IframeOptions>({
addCommands() {
return {
setIframe:
(options: { src: string }) =>
({ tr, dispatch }) => {
const { selection } = tr
(options: {src: string}) =>
({tr, dispatch}) => {
const {selection} = tr
const node = this.type.create(options)
if (dispatch) {

View File

@@ -1,7 +1,7 @@
export const tryCatch = async <T, E = Error>(promise: Promise<T>) => {
try {
return { data: await promise, error: null }
return {data: await promise, error: null}
} catch (e) {
return { data: null, error: e as E }
return {data: null, error: e as E}
}
}

View File

@@ -1,6 +1,4 @@
export function assertUnreachable(x: never, message?: string): never {
if (message) throw new Error(message)
throw new Error(
`Expected unreachable value, instead got: ${JSON.stringify(x)}`
)
throw new Error(`Expected unreachable value, instead got: ${JSON.stringify(x)}`)
}