mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 09:09:36 +02:00
Enable TS noImplicitAny in web (#31636)
GitOrigin-RevId: 18881694770f2476c475f8fef4c6a2678a2a12fe
This commit is contained in:
committed by
Copybot
parent
b3489a6792
commit
6113c6c291
@@ -8,13 +8,20 @@ const READ_PREFERENCE_SECONDARY =
|
||||
: ReadPreference.secondaryPreferred.mode
|
||||
|
||||
const ONE_MONTH_IN_MS = 1000 * 60 * 60 * 24 * 31
|
||||
/** @type {ObjectId | null} */
|
||||
let ID_EDGE_PAST
|
||||
const ID_EDGE_FUTURE = objectIdFromMs(Date.now() + 1000)
|
||||
/** @type {boolean} */
|
||||
let BATCH_DESCENDING
|
||||
/** @type {number} */
|
||||
let BATCH_SIZE
|
||||
/** @type {boolean} */
|
||||
let VERBOSE_LOGGING
|
||||
/** @type {ObjectId} */
|
||||
let BATCH_RANGE_START
|
||||
/** @type {ObjectId} */
|
||||
let BATCH_RANGE_END
|
||||
/** @type {number} */
|
||||
let BATCH_MAX_TIME_SPAN_IN_MS
|
||||
let BATCHED_UPDATE_RUNNING = false
|
||||
|
||||
@@ -55,7 +62,7 @@ function refreshGlobalOptionsForBatchedUpdate(options = {}) {
|
||||
if (BATCH_DESCENDING) {
|
||||
BATCH_RANGE_START = ID_EDGE_FUTURE
|
||||
} else {
|
||||
BATCH_RANGE_START = ID_EDGE_PAST
|
||||
BATCH_RANGE_START = /** @type {ObjectId} */ (ID_EDGE_PAST)
|
||||
}
|
||||
}
|
||||
BATCH_MAX_TIME_SPAN_IN_MS = parseInt(
|
||||
@@ -66,7 +73,7 @@ function refreshGlobalOptionsForBatchedUpdate(options = {}) {
|
||||
BATCH_RANGE_END = objectIdFromInput(options.BATCH_RANGE_END)
|
||||
} else {
|
||||
if (BATCH_DESCENDING) {
|
||||
BATCH_RANGE_END = ID_EDGE_PAST
|
||||
BATCH_RANGE_END = /** @type {ObjectId} */ (ID_EDGE_PAST)
|
||||
} else {
|
||||
BATCH_RANGE_END = ID_EDGE_FUTURE
|
||||
}
|
||||
|
||||
@@ -7,3 +7,4 @@ mongo-utils
|
||||
--node-version=24.13.0
|
||||
--pipeline-owner=32
|
||||
--public-repo=False
|
||||
--tsconfig-no-implicit-any=True
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @import { MongoClient } from 'mongodb'
|
||||
* @import { MongoClient as LegacyMongoClient } from 'mongodb-legacy'
|
||||
* @typedef {import('mongodb').MongoClient} MongoClient
|
||||
* @typedef {import('mongodb-legacy').MongoClient} LegacyMongoClient
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.backend.json",
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true
|
||||
},
|
||||
"include": ["**/*.js", "**/*.cjs", "**/*.ts"]
|
||||
}
|
||||
|
||||
@@ -7,3 +7,4 @@ object-persistor
|
||||
--node-version=24.13.0
|
||||
--pipeline-owner=32
|
||||
--public-repo=False
|
||||
--tsconfig-no-implicit-any=True
|
||||
|
||||
@@ -21,7 +21,7 @@ const hkdf = promisify(Crypto.hkdf)
|
||||
const AES256_KEY_LENGTH = 32
|
||||
|
||||
/**
|
||||
* @typedef {Object} Settings
|
||||
* @typedef {Object} EncryptionSettings
|
||||
* @property {boolean} automaticallyRotateDEKEncryption
|
||||
* @property {string} dataEncryptionKeyBucketName
|
||||
* @property {boolean} ignoreErrorsFromDEKReEncryption
|
||||
@@ -29,6 +29,26 @@ const AES256_KEY_LENGTH = 32
|
||||
* @property {() => Promise<Array<RootKeyEncryptionKey>>} getRootKeyEncryptionKeys
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} S3PersistorSettings
|
||||
* @property {Object<string, import('@aws-sdk/client-s3').StorageClass>} [storageClass]
|
||||
* @property {string} [key]
|
||||
* @property {string} [secret]
|
||||
* @property {string} [region]
|
||||
* @property {string} [endpoint]
|
||||
* @property {boolean} [pathStyle]
|
||||
* @property {number} [maxRetries]
|
||||
* @property {Object} [httpOptions]
|
||||
* @property {string} [ca]
|
||||
* @property {number} [signedUrlExpiryInMs]
|
||||
* @property {number} [partSize]
|
||||
* @property {Object<string, {auth_key: string, auth_secret: string}>} [bucketCreds]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {S3PersistorSettings & EncryptionSettings} Settings
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('./types').ListDirectoryResult} ListDirectoryResult
|
||||
*/
|
||||
@@ -105,10 +125,6 @@ class PerProjectEncryptedS3Persistor extends S3Persistor {
|
||||
})
|
||||
}
|
||||
|
||||
async ensureKeyEncryptionKeysLoaded() {
|
||||
await this.#availableKeyEncryptionKeysPromise
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} bucketName
|
||||
* @param {string} path
|
||||
@@ -289,6 +305,13 @@ class PerProjectEncryptedS3Persistor extends S3Persistor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} bucketName
|
||||
* @param {string} path
|
||||
* @param {NodeJS.ReadableStream} sourceStream
|
||||
* @param {import('./S3Persistor.js').StreamOptions} opts
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async sendStream(bucketName, path, sourceStream, opts = {}) {
|
||||
const ssecOptions =
|
||||
opts.ssecOptions ||
|
||||
@@ -299,6 +322,13 @@ class PerProjectEncryptedS3Persistor extends S3Persistor {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} bucketName
|
||||
* @param {string} path
|
||||
* @param {Object} opts
|
||||
* @param {SSECOptions} [opts.ssecOptions]
|
||||
* @return {Promise<NodeJS.ReadableStream>}
|
||||
*/
|
||||
async getObjectStream(bucketName, path, opts = {}) {
|
||||
const ssecOptions =
|
||||
opts.ssecOptions ||
|
||||
@@ -309,6 +339,13 @@ class PerProjectEncryptedS3Persistor extends S3Persistor {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} bucketName
|
||||
* @param {string} path
|
||||
* @param {Object} opts
|
||||
* @param {SSECOptions} [opts.ssecOptions]
|
||||
* @return {Promise<number>}
|
||||
*/
|
||||
async getObjectSize(bucketName, path, opts = {}) {
|
||||
const ssecOptions =
|
||||
opts.ssecOptions ||
|
||||
@@ -316,6 +353,13 @@ class PerProjectEncryptedS3Persistor extends S3Persistor {
|
||||
return await super.getObjectSize(bucketName, path, { ...opts, ssecOptions })
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} bucketName
|
||||
* @param {string} path
|
||||
* @param {Object} opts
|
||||
* @param {SSECOptions} [opts.ssecOptions]
|
||||
* @return {Promise<string | undefined>}
|
||||
*/
|
||||
async getObjectStorageClass(bucketName, path, opts = {}) {
|
||||
const ssecOptions =
|
||||
opts.ssecOptions ||
|
||||
@@ -326,11 +370,23 @@ class PerProjectEncryptedS3Persistor extends S3Persistor {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} bucketName
|
||||
* @param {string} path
|
||||
* @param {string} [continuationToken]
|
||||
* @return {Promise<number>}
|
||||
*/
|
||||
async directorySize(bucketName, path, continuationToken) {
|
||||
// Note: Listing a bucket does not require SSE-C credentials.
|
||||
return await super.directorySize(bucketName, path, continuationToken)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} bucketName
|
||||
* @param {string} path
|
||||
* @param {string} [continuationToken]
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async deleteDirectory(bucketName, path, continuationToken) {
|
||||
// Let [Settings.pathToProjectFolder] validate the project path before deleting things.
|
||||
const { projectFolder, dekPath } = this.#buildProjectPaths(bucketName, path)
|
||||
@@ -344,12 +400,27 @@ class PerProjectEncryptedS3Persistor extends S3Persistor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} bucketName
|
||||
* @param {string} path
|
||||
* @param {Object} opts
|
||||
* @return {Promise<string>}
|
||||
*/
|
||||
async getObjectMd5Hash(bucketName, path, opts = {}) {
|
||||
// The ETag in object metadata is not the MD5 content hash, skip the HEAD request.
|
||||
opts = { ...opts, etagIsNotMD5: true }
|
||||
return await super.getObjectMd5Hash(bucketName, path, opts)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} bucketName
|
||||
* @param {string} sourcePath
|
||||
* @param {string} destinationPath
|
||||
* @param {Object} opts
|
||||
* @param {SSECOptions} [opts.ssecOptions]
|
||||
* @param {SSECOptions} [opts.ssecSrcOptions]
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async copyObject(bucketName, sourcePath, destinationPath, opts = {}) {
|
||||
const ssecOptions =
|
||||
opts.ssecOptions ||
|
||||
|
||||
@@ -77,14 +77,53 @@ class SSECOptions {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('@aws-sdk/client-s3').StorageClass} StorageClass
|
||||
* @typedef {import('@aws-sdk/client-s3').PutObjectCommandInput} PutObjectCommandInput
|
||||
* @typedef {import('@aws-sdk/client-s3').HeadObjectCommandOutput} HeadObjectCommandOutput
|
||||
* @typedef {import('@aws-sdk/client-s3').GetObjectCommandOutput} GetObjectCommandOutput
|
||||
* @typedef {import('@aws-sdk/client-s3').S3ClientConfig} S3ClientConfig
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} StreamOptions
|
||||
* @property {string} [contentType]
|
||||
* @property {string} [contentEncoding]
|
||||
* @property {number} [contentLength]
|
||||
* @property {'*'} [ifNoneMatch]
|
||||
* @property {SSECOptions} [ssecOptions]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} S3PersistorSettings
|
||||
* @property {Object<string, StorageClass>} [storageClass]
|
||||
* @property {string} [key]
|
||||
* @property {string} [secret]
|
||||
* @property {string} [region]
|
||||
* @property {string} [endpoint]
|
||||
* @property {boolean} [pathStyle]
|
||||
* @property {number} [maxRetries]
|
||||
* @property {Object} [httpOptions]
|
||||
* @property {string} [ca]
|
||||
* @property {number} [signedUrlExpiryInMs]
|
||||
* @property {number} [partSize]
|
||||
* @property {Object<string, {auth_key: string, auth_secret: string}>} [bucketCreds]
|
||||
*/
|
||||
|
||||
class S3Persistor extends AbstractPersistor {
|
||||
/** @type {Map<string, S3Client>} */
|
||||
#clients = new Map()
|
||||
/** @type {S3PersistorSettings} */
|
||||
settings
|
||||
|
||||
/**
|
||||
* @param {S3PersistorSettings} [settings]
|
||||
*/
|
||||
constructor(settings = {}) {
|
||||
super()
|
||||
|
||||
settings.storageClass = settings.storageClass || {}
|
||||
settings.storageClass = /** @type {Object<string, StorageClass>} */ (
|
||||
settings.storageClass || {}
|
||||
)
|
||||
this.settings = settings
|
||||
}
|
||||
|
||||
@@ -102,12 +141,7 @@ class S3Persistor extends AbstractPersistor {
|
||||
* @param {string} bucketName
|
||||
* @param {string} key
|
||||
* @param {NodeJS.ReadableStream} readStream
|
||||
* @param {Object} opts
|
||||
* @param {string} [opts.contentType]
|
||||
* @param {string} [opts.contentEncoding]
|
||||
* @param {number} [opts.contentLength]
|
||||
* @param {'*'} [opts.ifNoneMatch]
|
||||
* @param {SSECOptions} [opts.ssecOptions]
|
||||
* @param {StreamOptions} opts
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async sendStream(bucketName, key, readStream, opts = {}) {
|
||||
@@ -128,8 +162,11 @@ class S3Persistor extends AbstractPersistor {
|
||||
Body: observer,
|
||||
}
|
||||
|
||||
if (this.settings.storageClass[bucketName]) {
|
||||
uploadOptions.StorageClass = this.settings.storageClass[bucketName]
|
||||
const storageClass = /** @type {Object<string, StorageClass>} */ (
|
||||
this.settings.storageClass
|
||||
)
|
||||
if (storageClass[bucketName]) {
|
||||
uploadOptions.StorageClass = storageClass[bucketName]
|
||||
}
|
||||
|
||||
if ('sourceMd5' in opts) {
|
||||
@@ -239,7 +276,9 @@ class S3Persistor extends AbstractPersistor {
|
||||
* @return {Promise<string>}
|
||||
*/
|
||||
async getRedirectUrl(bucketName, key) {
|
||||
const expiresSeconds = Math.round(this.settings.signedUrlExpiryInMs / 1000)
|
||||
const expiresSeconds = Math.round(
|
||||
/** @type {number} */ (this.settings.signedUrlExpiryInMs) / 1000
|
||||
)
|
||||
try {
|
||||
return await getSignedUrl(
|
||||
this._getClientForBucket(bucketName),
|
||||
@@ -311,6 +350,7 @@ class S3Persistor extends AbstractPersistor {
|
||||
*/
|
||||
async #listDirectory(bucketName, key, continuationToken) {
|
||||
let response
|
||||
/** @type {{ Bucket: string, Prefix: string, ContinuationToken?: string }} */
|
||||
const options = { Bucket: bucketName, Prefix: key }
|
||||
if (continuationToken) {
|
||||
options.ContinuationToken = continuationToken
|
||||
@@ -501,6 +541,7 @@ class S3Persistor extends AbstractPersistor {
|
||||
*/
|
||||
async directorySize(bucketName, key, continuationToken) {
|
||||
try {
|
||||
/** @type {{ Bucket: string, Prefix: string, ContinuationToken?: string }} */
|
||||
const options = {
|
||||
Bucket: bucketName,
|
||||
Prefix: key,
|
||||
@@ -621,7 +662,7 @@ class S3Persistor extends AbstractPersistor {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} bucketCredentials
|
||||
* @param {{auth_key: string, auth_secret: string} | undefined} bucketCredentials
|
||||
* @param {import('@aws-sdk/client-s3').S3ClientConfig} clientOptions
|
||||
* @return {import('@aws-sdk/client-s3').S3ClientConfig}
|
||||
* @private
|
||||
@@ -637,7 +678,7 @@ class S3Persistor extends AbstractPersistor {
|
||||
} else if (this.settings.key) {
|
||||
options.credentials = {
|
||||
accessKeyId: this.settings.key,
|
||||
secretAccessKey: this.settings.secret,
|
||||
secretAccessKey: /** @type {string} */ (this.settings.secret),
|
||||
}
|
||||
} else {
|
||||
// Use the default credentials provider (process.env -> SSP -> ini -> IAM)
|
||||
@@ -709,6 +750,9 @@ class S3Persistor extends AbstractPersistor {
|
||||
}
|
||||
|
||||
// test-only
|
||||
/**
|
||||
* @param {string} bucketName
|
||||
*/
|
||||
_createBucket(bucketName) {
|
||||
return this._getClientForBucket(bucketName).send(
|
||||
new CreateBucketCommand({ Bucket: bucketName })
|
||||
@@ -716,6 +760,10 @@ class S3Persistor extends AbstractPersistor {
|
||||
}
|
||||
|
||||
// test-only
|
||||
/**
|
||||
* @param {string} bucketName
|
||||
* @param {import('@aws-sdk/client-s3').PutObjectCommandInput} uploadOptions
|
||||
*/
|
||||
_upload(bucketName, uploadOptions) {
|
||||
return this._getClientForBucket(bucketName).send(
|
||||
new PutObjectCommand(uploadOptions)
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.backend.json",
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true
|
||||
},
|
||||
"include": ["**/*.js", "**/*.cjs", "**/*.ts"]
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ const crypto = require('node:crypto')
|
||||
const os = require('node:os')
|
||||
const { promisify } = require('node:util')
|
||||
|
||||
// @ts-ignore - ioredis types may not be available in all contexts
|
||||
const Redis = require('ioredis')
|
||||
|
||||
const {
|
||||
@@ -20,6 +21,9 @@ const PID = process.pid
|
||||
const RND = crypto.randomBytes(4).toString('hex')
|
||||
let COUNT = 0
|
||||
|
||||
/**
|
||||
* @param {any} opts
|
||||
*/
|
||||
function createClient(opts) {
|
||||
const standardOpts = Object.assign({}, opts)
|
||||
delete standardOpts.key_schema
|
||||
@@ -42,6 +46,7 @@ function createClient(opts) {
|
||||
client = new Redis(standardOpts)
|
||||
}
|
||||
monkeyPatchIoRedisExec(client)
|
||||
/** @param {any} callback */
|
||||
client.healthCheck = callback => {
|
||||
if (callback) {
|
||||
// callback based invocation
|
||||
@@ -54,6 +59,9 @@ function createClient(opts) {
|
||||
return client
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} client
|
||||
*/
|
||||
async function healthCheck(client) {
|
||||
// check the redis connection by storing and retrieving a unique key/value pair
|
||||
const uniqueToken = `host=${HOST}:pid=${PID}:random=${RND}:time=${Date.now()}:count=${COUNT++}`
|
||||
@@ -71,6 +79,11 @@ async function healthCheck(client) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} client
|
||||
* @param {any} uniqueToken
|
||||
* @param {any} context
|
||||
*/
|
||||
async function runCheck(client, uniqueToken, context) {
|
||||
const healthCheckKey = `_redis-wrapper:healthCheckKey:{${uniqueToken}}`
|
||||
const healthCheckValue = `_redis-wrapper:healthCheckValue:{${uniqueToken}}`
|
||||
@@ -79,9 +92,11 @@ async function runCheck(client, uniqueToken, context) {
|
||||
context.stage = 'write'
|
||||
const writeAck = await client
|
||||
.set(healthCheckKey, healthCheckValue, 'EX', 60)
|
||||
.catch(err => {
|
||||
throw new RedisHealthCheckWriteError('write errored', context, err)
|
||||
})
|
||||
.catch(
|
||||
/** @param {any} err */ err => {
|
||||
throw new RedisHealthCheckWriteError('write errored', context, err)
|
||||
}
|
||||
)
|
||||
if (writeAck !== 'OK') {
|
||||
context.writeAck = writeAck
|
||||
throw new RedisHealthCheckWriteError('write failed', context)
|
||||
@@ -94,9 +109,15 @@ async function runCheck(client, uniqueToken, context) {
|
||||
.get(healthCheckKey)
|
||||
.del(healthCheckKey)
|
||||
.exec()
|
||||
.catch(err => {
|
||||
throw new RedisHealthCheckVerifyError('read/delete errored', context, err)
|
||||
})
|
||||
.catch(
|
||||
/** @param {any} err */ err => {
|
||||
throw new RedisHealthCheckVerifyError(
|
||||
'read/delete errored',
|
||||
context,
|
||||
err
|
||||
)
|
||||
}
|
||||
)
|
||||
if (roundTrippedHealthCheckValue !== healthCheckValue) {
|
||||
context.roundTrippedHealthCheckValue = roundTrippedHealthCheckValue
|
||||
throw new RedisHealthCheckVerifyError('read failed', context)
|
||||
@@ -107,6 +128,10 @@ async function runCheck(client, uniqueToken, context) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} result
|
||||
* @param {any} callback
|
||||
*/
|
||||
function unwrapMultiResult(result, callback) {
|
||||
// ioredis exec returns a results like:
|
||||
// [ [null, 42], [null, "foo"] ]
|
||||
@@ -130,19 +155,30 @@ function unwrapMultiResult(result, callback) {
|
||||
}
|
||||
const unwrapMultiResultPromisified = promisify(unwrapMultiResult)
|
||||
|
||||
/**
|
||||
* @param {any} client
|
||||
*/
|
||||
function monkeyPatchIoRedisExec(client) {
|
||||
const _multi = client.multi
|
||||
client.multi = function () {
|
||||
const multi = _multi.apply(client, arguments)
|
||||
const _exec = multi.exec
|
||||
/** @param {any} callback */
|
||||
multi.exec = callback => {
|
||||
if (callback) {
|
||||
// callback based invocation
|
||||
_exec.call(multi, (error, result) => {
|
||||
// The command can fail all-together due to syntax errors
|
||||
if (error) return callback(error)
|
||||
unwrapMultiResult(result, callback)
|
||||
})
|
||||
_exec.call(
|
||||
multi,
|
||||
/**
|
||||
* @param {any} error
|
||||
* @param {any} result
|
||||
*/
|
||||
(error, result) => {
|
||||
// The command can fail all-together due to syntax errors
|
||||
if (error) return callback(error)
|
||||
unwrapMultiResult(result, callback)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
// Promise based invocation
|
||||
return _exec.call(multi).then(unwrapMultiResultPromisified)
|
||||
@@ -152,7 +188,11 @@ function monkeyPatchIoRedisExec(client) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{runner: any, timeout: any, context: any}} param0
|
||||
*/
|
||||
async function runWithTimeout({ runner, timeout, context }) {
|
||||
/** @type {any} */
|
||||
let healthCheckDeadline
|
||||
await Promise.race([
|
||||
new Promise((resolve, reject) => {
|
||||
|
||||
@@ -45,7 +45,7 @@ if (!settingsExist) {
|
||||
console.warn("No settings or defaults found. I'm flying blind.")
|
||||
}
|
||||
|
||||
module.exports = settings
|
||||
module.exports = /** @type {any} */ (settings)
|
||||
|
||||
function pathIfExists(path) {
|
||||
if (path && fs.existsSync(path)) {
|
||||
|
||||
Reference in New Issue
Block a user