🏡 index : quine-global/webdriverio.git

import type { AddressInfo } from 'node:net'
import polka from 'polka'
import { json } from '@polka/parse'

import type { JsonCompatible, JsonPrimitive, JsonObject, JsonArray } from '@wdio/types'

const store: JsonObject = {}
const resourcePoolStore: Map<string, JsonArray> = new Map()
/**
 * @private
 */
export const __store = store
export const __resourcePoolStore = resourcePoolStore

const validateBody: NextFn = (req, res, next) => {
    if (!req.path.endsWith('/get') && !req.path.endsWith('/set')) {
        return next()
    }
    if (req.method === 'POST' && typeof req.body.key !== 'string') {
        res.end(JSON.stringify({ error: 'Invalid payload, key is required.' }))
    }
    next()
}

const MAX_TIMEOUT = 15000
const DEFAULT_TIMEOUT = 1000

export const startServer = () => new Promise<{ port: number, app: PolkaInstance }>((resolve, reject) => {
    const app = polka()
        /**
         * middleware
         * `json` middleware transforms body to json for every request or returns empty object
         */
        .use(json(), validateBody)

        // routes
        .post('/', (req, res) => {
            const key = req.body.key as string

            if (key === '*') {
                throw new Error('You can\'t set a value with key "*" as this is a reserved key')
            }

            store[key] = req.body.value as JsonCompatible | JsonPrimitive
            return res.end()
        })
        .get('/:key', (req, res) => {
            const key = req.params.key as string
            const value = key === '*'
                ? store
                : store[key]
            res.end(JSON.stringify({ value }))
        })
        .post('/pool', (req, res, next) => {
            const key = req.body.key as string
            const value = req.body.value as JsonCompatible | JsonPrimitive

            if (!Array.isArray(value)) {
                return next('Resource pool must be an array of values')
            }

            resourcePoolStore.set(key, value)
            return res.end()
        })
        .get('/pool/:key', async (req, res, next) => {
            const key = req.params.key as string

            if (!resourcePoolStore.has(key)) {
                return next(`'${key}' resource pool does not exist. Set it first using 'setResourcePool'`)
            }

            let pool = resourcePoolStore.get(key) || []

            if (pool.length > 0) {
                return res.end(JSON.stringify({ value: pool.shift() }))
            }

            const timeout = Math.min(parseInt(req.query.timeout as string) || DEFAULT_TIMEOUT, MAX_TIMEOUT)

            try {
                const result = await new Promise((resolve, reject) => {
                    setTimeout(function secondAttempt() {
                        pool = resourcePoolStore.get(key) || []
                        if (pool.length > 0) {
                            resolve({ value: pool.shift() })
                        }

                        reject(`'${key}' resource pool is empty. Set values to it first using 'setResourcePool' or 'addValueToPool'`)
                    }, timeout)
                })
                res.end(JSON.stringify(result))
            } catch (err) {
                return next(err)
            }
        })
        .post('/pool/:key', (req, res, next) => {
            const key = req.params.key as string
            const value = req.body.value as JsonCompatible | JsonPrimitive
            const pool = resourcePoolStore.get(key)

            if (!pool) {
                return next(`'${key}' resource pool does not exist. Set it first using 'setResourcePool'`)
            }

            pool.push(value)
            return res.end()
        })

    /**
     * run server on a random port, `0` stands for random port
     * > If port is omitted or is 0, the operating system will assign
     * > an arbitrary unused port, which can be retrieved by using `server.address().port`
     * https://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback
     */
    app.listen(0, (err: Error) => {
        /* istanbul ignore next */
        if (err) {
            return reject(err)
        }
        resolve({ app, port: (app.server.address() as AddressInfo).port })
    })
})