Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move utility files to common package #11863

Draft
wants to merge 18 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions app/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,29 @@
"./src/load": "./src/load.ts",
"./src/backendQuery": "./src/backendQuery.ts",
"./src/queryClient": "./src/queryClient.ts",
"./src/utilities/convertCSSUnits": "./src/utilities/convertCSSUnits.ts",
"./src/utilities/data/array": "./src/utilities/data/array.ts",
"./src/utilities/data/dateTime": "./src/utilities/data/dateTime.ts",
"./src/utilities/data/email": "./src/utilities/data/email.ts",
"./src/utilities/data/fileInfo": "./src/utilities/data/fileInfo.ts",
"./src/utilities/data/iter": "./src/utilities/data/iter.ts",
"./src/utilities/data/newtype": "./src/utilities/data/newtype.ts",
"./src/utilities/data/nullable": "./src/utilities/data/nullable.ts",
"./src/utilities/data/object": "./src/utilities/data/object.ts",
"./src/utilities/data/path": "./src/utilities/data/path.ts",
"./src/utilities/data/set": "./src/utilities/data/set.ts",
"./src/utilities/data/string": "./src/utilities/data/string.ts",
"./src/utilities/data/iter": "./src/utilities/data/iter.ts",
"./src/utilities/download": "./src/utilities/download.ts",
"./src/utilities/equalities": "./src/utilities/equalities.ts",
"./src/utilities/error": "./src/utilities/error.ts",
"./src/utilities/functions": "./src/utilities/functions.ts",
"./src/utilities/github": "./src/utilities/github.ts",
"./src/utilities/input": "./src/utilities/input.ts",
"./src/utilities/permissions": "./src/utilities/permissions.ts",
"./src/utilities/style/tabBar": "./src/utilities/style/tabBar.ts",
"./src/utilities/uniqueString": "./src/utilities/uniqueString.ts",
"./src/text": "./src/text/index.ts",
"./src/utilities/permissions": "./src/utilities/permissions.ts",
"./src/services/Backend": "./src/services/Backend.ts",
"./src/text": "./src/text/index.ts",
"./src/types": "./src/types.d.ts"
},
"scripts": {
Expand All @@ -42,5 +54,8 @@
"react": "^18.3.1",
"vitest": "^1.3.1",
"vue": "^3.5.2"
},
"devDependencies": {
"@fast-check/vitest": "^0.0.8"
}
}
16 changes: 0 additions & 16 deletions app/common/src/detect.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
/** @file Helper functions for environment detection. */

// ===================
// === IS_DEV_MODE ===
// ===================

/** Return whether the current build is in development mode */
export const IS_DEV_MODE = process.env.NODE_ENV === 'development'

// ================
// === Platform ===
// ================

/** Possible platforms that the app may run on. */
export enum Platform {
unknown = 'Unknown platform',
Expand Down Expand Up @@ -82,10 +74,6 @@ export function isOnUnknownOS() {
return platform() === Platform.unknown
}

// ===============
// === Browser ===
// ===============

/** Possible browsers that the app may run on. */
export enum Browser {
unknown = 'Unknown browser',
Expand Down Expand Up @@ -160,10 +148,6 @@ export function isOnUnknownBrowser() {
return browser() === Browser.unknown
}

// ====================
// === Architecture ===
// ====================

let detectedArchitecture: string | null = null
// Only implemented by Chromium.
// @ts-expect-error This API exists, but no typings exist for it yet.
Expand Down
64 changes: 4 additions & 60 deletions app/common/src/services/Backend.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/** @file Type definitions common between all backends. */

import * as array from '../utilities/data/array'
import * as dateTime from '../utilities/data/dateTime'
import * as newtype from '../utilities/data/newtype'
Expand All @@ -9,10 +8,6 @@ import * as uniqueString from '../utilities/uniqueString'
/** The size, in bytes, of the chunks which the backend accepts. */
export const S3_CHUNK_SIZE_BYTES = 10_000_000

// ================
// === Newtypes ===
// ================

/** Unique identifier for an organization. */
export type OrganizationId = newtype.Newtype<string, 'OrganizationId'>
export const OrganizationId = newtype.newtypeConstructor<OrganizationId>()
Expand Down Expand Up @@ -145,10 +140,6 @@ export function newPlaceholderUserGroupId() {
return UserGroupId(`${PLACEHOLDER_USER_GROUP_PREFIX}${uniqueString.uniqueString()}`)
}

// =============
// === Types ===
// =============

/** The {@link Backend} variant. If a new variant is created, it should be added to this enum. */
export enum BackendType {
local = 'local',
Expand Down Expand Up @@ -685,10 +676,6 @@ export function findLeastUsedColor(labels: Iterable<Label>) {
return minColor == null ? COLORS[0] : COLOR_STRING_TO_COLOR.get(minColor) ?? COLORS[0]
}

// =================
// === AssetType ===
// =================

export enum SpecialAssetType {
loading = 'specialLoading',
empty = 'specialEmpty',
Expand Down Expand Up @@ -747,10 +734,6 @@ export const ASSET_TYPE_ORDER: Readonly<Record<AssetType, number>> = {
[AssetType.specialError]: 1000,
}

// =============
// === Asset ===
// =============

/**
* Metadata uniquely identifying a directory entry.
* These can be Projects, Files, Secrets, or other directories.
Expand Down Expand Up @@ -1136,10 +1119,6 @@ export interface AssetVersions {
readonly versions: S3ObjectVersion[]
}

// ===============================
// === compareAssetPermissions ===
// ===============================

/**
* Return a positive number when `a > b`, a negative number when `a < b`, and `0`
* when `a === b`.
Expand Down Expand Up @@ -1167,10 +1146,6 @@ export function compareAssetPermissions(a: AssetPermission, b: AssetPermission)
}
}

// =================
// === Endpoints ===
// =================

/** HTTP request body for the "set username" endpoint. */
export interface CreateUserRequestBody {
readonly userName: string
Expand Down Expand Up @@ -1380,10 +1355,6 @@ export interface UploadPictureRequestParams {
readonly fileName: string | null
}

// ==============================
// === detectVersionLifecycle ===
// ==============================

/** Extract the {@link VersionLifecycle} from a version string. */
export function detectVersionLifecycle(version: string) {
if (/rc/i.test(version)) {
Expand All @@ -1397,10 +1368,6 @@ export function detectVersionLifecycle(version: string) {
}
}

// =====================
// === compareAssets ===
// =====================

/** Return a positive number if `a > b`, a negative number if `a < b`, and zero if `a === b`. */
export function compareAssets(a: AnyAsset, b: AnyAsset) {
const relativeTypeOrder = ASSET_TYPE_ORDER[a.type] - ASSET_TYPE_ORDER[b.type]
Expand Down Expand Up @@ -1430,10 +1397,6 @@ export function compareAssets(a: AnyAsset, b: AnyAsset) {
}
}

// ==================
// === getAssetId ===
// ==================

/**
* A convenience function to get the `id` of an {@link Asset}.
* This is useful to avoid React re-renders as it is not re-created on each function call.
Expand All @@ -1442,10 +1405,6 @@ export function getAssetId<Type extends AssetType>(asset: Asset<Type>) {
return asset.id
}

// ================================
// === userHasUserAndTeamSpaces ===
// ================================

/** Whether a user's root directory has the "Users" and "Teams" subdirectories. */
export function userHasUserAndTeamSpaces(user: User | null) {
switch (user?.plan ?? null) {
Expand All @@ -1461,10 +1420,6 @@ export function userHasUserAndTeamSpaces(user: User | null) {
}
}

// =====================
// === fileIsProject ===
// =====================

/** A subset of properties of the JS `File` type. */
interface JSFile {
readonly name: string
Expand All @@ -1484,10 +1439,6 @@ export function fileIsNotProject(file: JSFile) {
return !fileIsProject(file)
}

// =============================
// === stripProjectExtension ===
// =============================

/** Remove the extension of the project file name (if any). */
export function stripProjectExtension(name: string) {
return name.replace(/[.](?:tar[.]gz|zip|enso-project)$/, '')
Expand Down Expand Up @@ -1531,7 +1482,7 @@ export function isNewTitleValid(
/** Network error class. */
export class NetworkError extends Error {
/**
* Create a new instance of the {@link NetworkError} class.
* Create a new {@link NetworkError}.
* @param message - The error message.
* @param status - The HTTP status code.
*/
Expand All @@ -1542,13 +1493,10 @@ export class NetworkError extends Error {
super(message)
}
}

/** Error class for when the user is not authorized to access a resource. */
export class NotAuthorizedError extends NetworkError {}

// ===============
// === Backend ===
// ===============

/** Interface for sending requests to a backend that manages assets and runs projects. */
export default abstract class Backend {
abstract readonly type: BackendType
Expand Down Expand Up @@ -1765,15 +1713,11 @@ export default abstract class Backend {
abstract resolveProjectAssetPath(projectId: ProjectId, relativePath: string): Promise<string>
}

// ==============================
// ====== Custom Errors =========
// ==============================
export { Backend }

/** Error thrown when a directory does not exist. */
export class DirectoryDoesNotExistError extends Error {
/**
* Create a new instance of the {@link DirectoryDoesNotExistError} class.
*/
/** Create a new {@link DirectoryDoesNotExistError}. */
constructor() {
super('Directory does not exist.')
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest'
import { Rfc3339DateTime } from '../../utilities/dateTime'

import { Rfc3339DateTime } from '../../utilities/data/dateTime'
import { AssetType, compareAssets, type AnyAsset } from '../Backend'

describe('Backend', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
/** @file Tests for `error.ts`. */
import * as v from 'vitest'

import * as error from '#/utilities/error'

// =============
// === Tests ===
// =============
import { tryGetMessage } from '../error'

const MESSAGE = 'A custom error message.'

Expand All @@ -17,7 +13,7 @@ v.describe('`error.tryGetMessage`', () => {
{ errorObject: {}, message: null },
{ errorObject: null, message: null },
])('should correctly parse error objects', ({ errorObject, message }) => {
v.expect(error.tryGetMessage<unknown>(errorObject)).toBe(message)
v.expect(tryGetMessage<unknown>(errorObject)).toBe(message)
})

v.test.each([
Expand All @@ -29,7 +25,7 @@ v.describe('`error.tryGetMessage`', () => {
])(
'should return default message if failed to extract it from the error object',
({ errorObject, message, defaultMessage }) => {
v.expect(error.tryGetMessage<unknown, string>(errorObject, defaultMessage)).toBe(message)
v.expect(tryGetMessage<unknown, string>(errorObject, defaultMessage)).toBe(message)
},
)
})
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
const DUMMY_RECT = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
const WIDTH = DUMMY_RECT.width.baseVal
const MODES = {
// eslint-disable-next-line @typescript-eslint/naming-convention
'%': WIDTH.SVG_LENGTHTYPE_PERCENTAGE,
em: WIDTH.SVG_LENGTHTYPE_EMS,
ex: WIDTH.SVG_LENGTHTYPE_EXS,
Expand Down Expand Up @@ -55,7 +54,6 @@ export function convertCSSUnitString(
}
default: {
// This is SAFE, as the regex ensures that the only valid values are CSS units.
// eslint-disable-next-line no-restricted-syntax
return convertCSSUnits(numericValue, from as CSSUnit, to, parent)
}
}
Expand Down
28 changes: 26 additions & 2 deletions app/common/src/utilities/data/__tests__/array.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as fc from '@fast-check/vitest'
import { expect, test } from 'vitest'
import * as array from '../array'

import { includesPredicate, shallowEqual, transpose } from '../array'

interface TransposeCase {
matrix: number[][]
Expand All @@ -26,6 +28,28 @@ const transposeCases: TransposeCase[] = [
]

test.each(transposeCases)('transpose: case %#', ({ matrix, expected }) => {
const transposed = array.transpose(matrix)
const transposed = transpose(matrix)
expect(transposed).toStrictEqual(expected)
})

fc.test.prop({ theArray: fc.fc.array(fc.fc.anything()) })(
'`array.shallowEqual`',
({ theArray }) => {
expect(shallowEqual(theArray, [...theArray]))
},
)

fc.test.prop({
theArray: fc.fc.array(fc.fc.anything(), { minLength: 1 }).chain(theArray =>
fc.fc.record({
theArray: fc.fc.constant(theArray),
i: fc.fc.nat(theArray.length - 1),
}),
),
})('`array.includesPredicate`', ({ theArray: { theArray, i } }) => {
expect(
includesPredicate(theArray)(theArray[i]),
`'${JSON.stringify(theArray)}' should include '${JSON.stringify(theArray[i])}'`,
).toBe(true)
expect(includesPredicate(theArray)({}), 'unique object should not be in array').toBe(false)
})
Loading
Loading