+ return urlObject.href.replace(/\/$/, '')
+}
+
+// Don't import remote scheme from constants because we are in core utils
+function sanitizeHost (host: string, remoteScheme: string) {
+ const toRemove = remoteScheme === 'https' ? 443 : 80
+
+ return host.replace(new RegExp(`:${toRemove}$`), '')
+}
+
+// ---------------------------------------------------------------------------
+
+function isTestInstance () {
+ return process.env.NODE_ENV === 'test'
+}
+
+function isDevInstance () {
+ return process.env.NODE_ENV === 'dev'
+}
+
+function isTestOrDevInstance () {
+ return isTestInstance() || isDevInstance()
+}
+
+function isProdInstance () {
+ return process.env.NODE_ENV === 'production'
+}
+
+function getAppNumber () {
+ return process.env.NODE_APP_INSTANCE || ''
+}
+
+// ---------------------------------------------------------------------------
+
+// Consistent with .length, lodash truncate function is not
+function peertubeTruncate (str: string, options: { length: number, separator?: RegExp, omission?: string }) {
+ const truncatedStr = truncate(str, options)
+
+ // The truncated string is okay, we can return it
+ if (truncatedStr.length <= options.length) return truncatedStr
+
+ // Lodash takes into account all UTF characters, whereas String.prototype.length does not: some characters have a length of 2
+ // We always use the .length so we need to truncate more if needed
+ options.length -= truncatedStr.length - options.length
+ return truncate(str, options)
+}
+
+function pageToStartAndCount (page: number, itemsPerPage: number) {
+ const start = (page - 1) * itemsPerPage
+
+ return { start, count: itemsPerPage }
+}
+
+// ---------------------------------------------------------------------------
+
+type SemVersion = { major: number, minor: number, patch: number }
+function parseSemVersion (s: string) {
+ const parsed = s.match(/^v?(\d+)\.(\d+)\.(\d+)$/i)
+
+ return {
+ major: parseInt(parsed[1]),
+ minor: parseInt(parsed[2]),
+ patch: parseInt(parsed[3])
+ } as SemVersion
+}
+
+// ---------------------------------------------------------------------------
+
+function execShell (command: string, options?: ExecOptions) {
+ return new Promise<{ err?: Error, stdout: string, stderr: string }>((res, rej) => {
+ exec(command, options, (err, stdout, stderr) => {
+ // eslint-disable-next-line prefer-promise-reject-errors
+ if (err) return rej({ err, stdout, stderr })
+
+ return res({ stdout, stderr })
+ })
+ })