Merge branch 'master' into freearhey-patch-2

This commit is contained in:
freearhey 2023-09-22 17:57:20 +03:00
commit b9e1265a10
62 changed files with 3557 additions and 1106 deletions

38
.eslintrc.json Normal file
View File

@ -0,0 +1,38 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"no-case-declarations": "off",
"indent": [
"error",
2,
{ "SwitchCase": 1 }
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"never"
]
}
}

View File

@ -12,7 +12,7 @@ jobs:
- uses: actions/checkout@v3
with:
fetch-depth: 2
- uses: tibdex/github-app-token@v2
- uses: tibdex/github-app-token@v1.8.2
if: ${{ !env.ACT }}
id: create-app-token
with:

View File

@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: tibdex/github-app-token@v2
- uses: tibdex/github-app-token@v1.8.2
if: ${{ !env.ACT }}
id: create-app-token
with:

View File

@ -141,6 +141,7 @@ To run scripts use the `npm run <script-name>` command.
- `update`: (shorthand) sequentially runs the `api:load`, `playlist:generate`, `api:generate` and `readme:update` commands.
- `deploy`: (shorthand) sequentially runs the `playlist:deploy` and `api:deploy` commands.
- `report`: (shorthand) sequentially runs the `api:load` and `report:create` commands.
- `lint`: сhecks the scripts for syntax errors.
- `test`: runs a test of all the scripts described above.
## Workflows

View File

@ -118,7 +118,7 @@ Same thing, but split up into separate files:
<tr><td align="left">Albanian</td><td align="right">58</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/sqi.m3u</code></td></tr>
<tr><td align="left">Alemannic</td><td align="right">1</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/gsw.m3u</code></td></tr>
<tr><td align="left">Amharic</td><td align="right">5</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/amh.m3u</code></td></tr>
<tr><td align="left">Arabic</td><td align="right">367</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/ara.m3u</code></td></tr>
<tr><td align="left">Arabic</td><td align="right">368</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/ara.m3u</code></td></tr>
<tr><td align="left">Armenian</td><td align="right">29</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/hye.m3u</code></td></tr>
<tr><td align="left">Assamese</td><td align="right">7</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/asm.m3u</code></td></tr>
<tr><td align="left">Assyrian Neo-Aramaic</td><td align="right">1</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/aii.m3u</code></td></tr>
@ -142,7 +142,7 @@ Same thing, but split up into separate files:
<tr><td align="left">Dholuo</td><td align="right">1</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/luo.m3u</code></td></tr>
<tr><td align="left">Dimili</td><td align="right">1</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/zza.m3u</code></td></tr>
<tr><td align="left">Dutch</td><td align="right">191</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/nld.m3u</code></td></tr>
<tr><td align="left">English</td><td align="right">2174</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/eng.m3u</code></td></tr>
<tr><td align="left">English</td><td align="right">2175</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/eng.m3u</code></td></tr>
<tr><td align="left">Estonian</td><td align="right">9</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/est.m3u</code></td></tr>
<tr><td align="left">Ewe</td><td align="right">1</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/ewe.m3u</code></td></tr>
<tr><td align="left">Faroese</td><td align="right">1</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/fao.m3u</code></td></tr>
@ -238,7 +238,7 @@ Same thing, but split up into separate files:
<tr><td align="left">Wolof</td><td align="right">2</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/wol.m3u</code></td></tr>
<tr><td align="left">Yucatec Maya</td><td align="right">1</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/yua.m3u</code></td></tr>
<tr><td align="left">Yue Chinese</td><td align="right">10</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/yue.m3u</code></td></tr>
<tr><td align="left">Undefined</td><td align="right">1253</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/undefined.m3u</code></td></tr>
<tr><td align="left">Undefined</td><td align="right">1252</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/languages/undefined.m3u</code></td></tr>
</tbody>
</table>

2212
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -20,14 +20,15 @@
"update": "npm run api:load && npm run playlist:generate && npm run api:generate && npm run readme:update",
"deploy": "npm run playlist:deploy && npm run api:deploy",
"report": "npm run api:load && npm run report:create",
"lint": "npx eslint ./scripts/**/*.ts ./tests/**/*.ts",
"test": "jest --runInBand",
"ts-node": "node --no-warnings=ExperimentalWarning --experimental-specifier-resolution=node --loader ts-node/esm"
},
"jest": {
"transform": {
"^.*test.(ts|js)$": "ts-jest"
"^.+\\.ts$": "ts-jest"
},
"testRegex": "tests/(.*?/)?.*test.(ts|js)$",
"testRegex": "tests/(.*?/)?.*test.ts$",
"setupFilesAfterEnv": [
"jest-expect-message"
]
@ -40,8 +41,8 @@
"author": "Arhey",
"private": true,
"license": "MIT",
"type": "module",
"dependencies": {
"@freearhey/core": "^0.1.2",
"@octokit/core": "^4.2.1",
"@octokit/plugin-paginate-rest": "^7.1.2",
"@octokit/plugin-rest-endpoint-methods": "^7.1.3",
@ -68,5 +69,10 @@
"transliteration": "^2.3.5",
"ts-jest": "^29.1.1",
"typescript": "^5.2.2"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^6.7.2",
"@typescript-eslint/parser": "^6.7.2",
"eslint": "^8.49.0"
}
}

View File

@ -1,5 +1,6 @@
import { Logger, Storage } from '@freearhey/core'
import { API_DIR, STREAMS_DIR } from '../../constants'
import { Logger, PlaylistParser, Storage } from '../../core'
import { PlaylistParser } from '../../core'
import { Stream } from '../../models'
async function main() {

View File

@ -1,5 +1,6 @@
import { Logger, Storage, Collection } from '@freearhey/core'
import { STREAMS_DIR, DATA_DIR } from '../../constants'
import { Storage, Logger, PlaylistParser, Collection } from '../../core'
import { PlaylistParser } from '../../core'
import { Stream, Playlist, Channel } from '../../models'
import { program } from 'commander'

View File

@ -1,7 +1,6 @@
import { File, PlaylistParser, Storage } from '../../core'
import { Logger, Storage, Collection, File } from '@freearhey/core'
import { PlaylistParser } from '../../core'
import { Stream, Category, Channel, Language, Country, Region, Subdivision } from '../../models'
import { Collection } from '../../core/collection'
import { Logger } from '../../core/logger'
import _ from 'lodash'
import {
CategoriesGenerator,
@ -124,7 +123,7 @@ async function loadStreams({
if (channel.logo) stream.logo = channel.logo
} else {
const file = new File(stream.filepath)
const [_, countryCode] = file.getFilename().match(/^([a-z]{2})(_|$)/) || [null, null]
const [_, countryCode] = file.name().match(/^([a-z]{2})(_|$)/) || [null, null]
const defaultBroadcastArea = countryCode ? [`c/${countryCode.toUpperCase()}`] : []
stream.broadcastArea = new Collection(defaultBroadcastArea)

View File

@ -1,5 +1,6 @@
import { Logger, Storage, Collection, Dictionary } from '@freearhey/core'
import { DATA_DIR, STREAMS_DIR } from '../../constants'
import { Storage, Logger, Collection, Dictionary, IssueLoader, PlaylistParser } from '../../core'
import { IssueLoader, PlaylistParser } from '../../core'
import { Stream, Playlist, Channel, Issue } from '../../models'
let processedIssues = new Collection()

View File

@ -1,4 +1,5 @@
import { Logger, Storage, PlaylistParser, Collection, File, Dictionary } from '../../core'
import { Logger, Storage, Collection, Dictionary, File } from '@freearhey/core'
import { PlaylistParser } from '../../core'
import { Channel, Stream, Blocked } from '../../models'
import { program } from 'commander'
import chalk from 'chalk'

View File

@ -1,5 +1,6 @@
import { Logger } from '@freearhey/core'
import { CategoryTable, CountryTable, LanguageTable, RegionTable } from '../../tables'
import { Logger, Markdown } from '../../core'
import { Markdown } from '../../core'
import { README_DIR } from '../../constants'
import path from 'path'

View File

@ -1,5 +1,6 @@
import { Logger, Storage, Collection, Dictionary } from '@freearhey/core'
import { DATA_DIR, STREAMS_DIR } from '../../constants'
import { Collection, Dictionary, IssueLoader, Storage, Logger, PlaylistParser } from '../../core'
import { IssueLoader, PlaylistParser } from '../../core'
import { Blocked, Channel, Issue, Stream } from '../../models'
async function main() {

View File

@ -1,175 +0,0 @@
import _ from 'lodash'
import { orderBy, Order } from 'natural-orderby'
import { Dictionary } from './'
type Iteratee = (value: any, value2?: any) => void
export class Collection {
_items: any[]
constructor(items?: any[]) {
this._items = Array.isArray(items) ? items : []
}
first(predicate?: Iteratee) {
if (predicate) {
return this._items.find(predicate)
}
return this._items[0]
}
last(predicate?: Iteratee) {
if (predicate) {
return _.findLast(this._items, predicate)
}
return this._items[this._items.length - 1]
}
find(iteratee: Iteratee): Collection {
const found = this._items.filter(iteratee)
return new Collection(found)
}
add(data: any) {
this._items.push(data)
return this
}
intersects(collection: Collection): boolean {
return _.intersection(this._items, collection.all()).length > 0
}
count() {
return this._items.length
}
join(separator: string) {
return this._items.join(separator)
}
indexOf(value: string) {
return this._items.indexOf(value)
}
push(data: any) {
this.add(data)
}
uniq() {
const items = _.uniq(this._items)
return new Collection(items)
}
reduce(iteratee: Iteratee, accumulator: any) {
const items = _.reduce(this._items, iteratee, accumulator)
return new Collection(items)
}
filter(iteratee: Iteratee) {
const items = _.filter(this._items, iteratee)
return new Collection(items)
}
forEach(iteratee: Iteratee) {
for (let item of this._items) {
iteratee(item)
}
return this
}
remove(iteratee: Iteratee): Collection {
const removed = _.remove(this._items, iteratee)
return new Collection(removed)
}
concat(collection: Collection) {
const items = this._items.concat(collection._items)
return new Collection(items)
}
isEmpty(): boolean {
return this._items.length === 0
}
notEmpty(): boolean {
return this._items.length > 0
}
sort() {
const items = this._items.sort()
return new Collection(items)
}
orderBy(iteratees: Iteratee | Iteratee[], orders?: Order | Order[]) {
const items = orderBy(this._items, iteratees, orders)
return new Collection(items)
}
keyBy(iteratee: Iteratee) {
const items = _.keyBy(this._items, iteratee)
return new Dictionary(items)
}
empty() {
return this._items.length === 0
}
includes(value: any) {
if (typeof value === 'function') {
const found = this._items.find(value)
return !!found
}
return this._items.includes(value)
}
missing(value: any) {
if (typeof value === 'function') {
const found = this._items.find(value)
return !found
}
return !this._items.includes(value)
}
uniqBy(iteratee: Iteratee) {
const items = _.uniqBy(this._items, iteratee)
return new Collection(items)
}
groupBy(iteratee: Iteratee) {
const object = _.groupBy(this._items, iteratee)
return new Dictionary(object)
}
map(iteratee: Iteratee) {
const items = this._items.map(iteratee)
return new Collection(items)
}
all() {
return this._items
}
toJSON() {
return JSON.stringify(this._items)
}
}

View File

@ -1,31 +0,0 @@
export class Dictionary {
dict: any
constructor(dict?: any) {
this.dict = dict || {}
}
set(key: string, value: any) {
this.dict[key] = value
}
has(key: string): boolean {
return !!this.dict[key]
}
missing(key: string): boolean {
return !this.dict[key]
}
get(key: string): any {
return this.dict[key] ? this.dict[key] : undefined
}
keys(): string[] {
return Object.keys(this.dict)
}
data() {
return this.dict
}
}

View File

@ -1,31 +0,0 @@
import * as path from 'path'
export class File {
filepath: string
content: string
constructor(filepath: string, content?: string) {
this.filepath = path.normalize(filepath)
this.content = content || ''
}
getFilename() {
return path.parse(this.filepath).name
}
dirname() {
return path.dirname(this.filepath)
}
basename() {
return path.basename(this.filepath)
}
append(data: string) {
this.content = this.content + data
}
extension() {
return this.filepath.split('.').pop()
}
}

View File

@ -19,19 +19,19 @@ export class HTMLTable {
let output = '<table>\n'
output += ' <thead>\n <tr>'
for (let column of this.columns) {
for (const column of this.columns) {
output += `<th align="left">${column.name}</th>`
}
output += '</tr>\n </thead>\n'
output += ' <tbody>\n'
for (let item of this.data) {
for (const item of this.data) {
output += ' <tr>'
let i = 0
for (let prop in item) {
for (const prop in item) {
const column = this.columns[i]
let nowrap = column.nowrap ? ` nowrap` : ''
let align = column.align ? ` align="${column.align}"` : ''
const nowrap = column.nowrap ? ' nowrap' : ''
const align = column.align ? ` align="${column.align}"` : ''
output += `<td${align}${nowrap}>${item[prop]}</td>`
i++
}

View File

@ -1,13 +1,7 @@
export * from './logger'
export * from './playlistParser'
export * from './numberParser'
export * from './logParser'
export * from './markdown'
export * from './file'
export * from './collection'
export * from './dictionary'
export * from './storage'
export * from './url'
export * from './issueLoader'
export * from './issueParser'
export * from './htmlTable'

View File

@ -1,7 +1,8 @@
import { Collection } from '@freearhey/core'
import { restEndpointMethods } from '@octokit/plugin-rest-endpoint-methods'
import { paginateRest } from '@octokit/plugin-paginate-rest'
import { Octokit } from '@octokit/core'
import { Collection, IssueParser } from './'
import { IssueParser } from './'
import { TESTING, OWNER, REPO } from '../constants'
const CustomOctokit = Octokit.plugin(paginateRest, restEndpointMethods)
@ -10,27 +11,26 @@ const octokit = new CustomOctokit()
export class IssueLoader {
async load({ labels }: { labels: string[] | string }) {
labels = Array.isArray(labels) ? labels.join(',') : labels
let issues: any[] = []
let issues: object[] = []
if (TESTING) {
switch (labels) {
case 'streams:add':
issues = (await import('../../tests/__data__/input/issues/streams_add')).default
issues = require('../../tests/__data__/input/issues/streams_add.js')
break
case 'streams:edit':
issues = (await import('../../tests/__data__/input/issues/streams_edit')).default
issues = require('../../tests/__data__/input/issues/streams_edit.js')
break
case 'broken stream':
issues = (await import('../../tests/__data__/input/issues/broken_stream')).default
issues = require('../../tests/__data__/input/issues/broken_stream.js')
break
case 'streams:add,approved':
issues = (await import('../../tests/__data__/input/issues/streams_add_approved')).default
issues = require('../../tests/__data__/input/issues/streams_add_approved.js')
break
case 'streams:edit,approved':
issues = (await import('../../tests/__data__/input/issues/streams_edit_approved')).default
issues = require('../../tests/__data__/input/issues/streams_edit_approved.js')
break
case 'streams:remove,approved':
issues = (await import('../../tests/__data__/input/issues/streams_remove_approved'))
.default
issues = require('../../tests/__data__/input/issues/streams_remove_approved.js')
break
}
} else {

View File

@ -1,6 +1,5 @@
import { Dictionary } from './'
import { Dictionary } from '@freearhey/core'
import { Issue } from '../models'
import _ from 'lodash'
const FIELDS = new Dictionary({
'Channel ID': 'channel_id',
@ -21,7 +20,7 @@ const FIELDS = new Dictionary({
})
export class IssueParser {
parse(issue: any): Issue {
parse(issue: { number: number; body: string; labels: { name: string }[] }): Issue {
const fields = issue.body.split('###')
const data = new Dictionary()

View File

@ -4,7 +4,7 @@ export type LogItem = {
}
export class LogParser {
parse(content: string): any[] {
parse(content: string): LogItem[] {
if (!content) return []
const lines = content.split('\n')

View File

@ -1,9 +0,0 @@
import signale from 'signale'
const { Signale } = signale
export class Logger extends Signale {
constructor(options?: any) {
super(options)
}
}

View File

@ -1,6 +1,6 @@
import { Collection, Storage } from '@freearhey/core'
import parser from 'iptv-playlist-parser'
import { Stream } from '../models'
import { Collection, Storage } from './'
import path from 'path'
import { STREAMS_DIR } from '../constants'
@ -14,7 +14,7 @@ export class PlaylistParser {
async parse(files: string[]): Promise<Collection> {
let streams = new Collection()
for (let filepath of files) {
for (const filepath of files) {
const relativeFilepath = filepath.replace(path.normalize(STREAMS_DIR), '')
const _streams: Collection = await this.parseFile(relativeFilepath)
streams = streams.concat(_streams)
@ -26,7 +26,7 @@ export class PlaylistParser {
async parseFile(filepath: string): Promise<Collection> {
const streams = new Collection()
const content = await this.storage.read(filepath)
const content = await this.storage.load(filepath)
const parsed: parser.Playlist = parser.parse(content)
parsed.items.forEach((item: parser.PlaylistItem) => {

View File

@ -1,84 +0,0 @@
import { File, Collection } from './'
import * as path from 'path'
import fs from 'fs-extra'
import { glob } from 'glob'
export class Storage {
rootDir: string
constructor(rootDir?: string) {
this.rootDir = path.normalize(rootDir || './')
}
async list(pattern: string): Promise<string[]> {
const files = await glob(pattern, {
cwd: this.rootDir
})
return files.sort()
}
async createDir(dir: string): Promise<void> {
if (await fs.exists(dir)) return
await fs.mkdir(dir, { recursive: true }).catch(console.error)
}
async load(filepath: string): Promise<any> {
return this.read(filepath)
}
async read(filepath: string): Promise<any> {
const absFilepath = path.join(this.rootDir, filepath)
return await fs.readFile(absFilepath, { encoding: 'utf8' })
}
async json(filepath: string): Promise<any> {
const absFilepath = path.join(this.rootDir, filepath)
const content = await fs.readFile(absFilepath, { encoding: 'utf8' })
return JSON.parse(content)
}
async exists(filepath: string): Promise<boolean> {
const absFilepath = path.join(this.rootDir, filepath)
return await fs.exists(absFilepath)
}
async write(filepath: string, data: string = ''): Promise<void> {
const absFilepath = path.join(this.rootDir, filepath)
const dir = path.dirname(absFilepath)
await this.createDir(dir)
await fs.writeFile(absFilepath, data, { encoding: 'utf8', flag: 'w' })
}
async append(filepath: string, data: string = ''): Promise<void> {
const absFilepath = path.join(this.rootDir, filepath)
await fs.appendFile(absFilepath, data, { encoding: 'utf8', flag: 'w' })
}
async clear(filepath: string): Promise<void> {
await this.write(filepath)
}
async createStream(filepath: string): Promise<NodeJS.WriteStream> {
const absFilepath = path.join(this.rootDir, filepath)
const dir = path.dirname(absFilepath)
await this.createDir(dir)
return fs.createWriteStream(absFilepath) as unknown as NodeJS.WriteStream
}
async save(filepath: string, content: string): Promise<void> {
await this.write(filepath, content)
}
async saveFile(file: File): Promise<void> {
await this.write(file.filepath, file.content)
}
}

View File

@ -1,20 +0,0 @@
import normalizeUrl from 'normalize-url'
export class URL {
url: string
constructor(url: string) {
this.url = url
}
normalize(): URL {
const normalized = normalizeUrl(this.url, { stripWWW: false })
this.url = decodeURIComponent(normalized).replace(/\s/g, '+')
return this
}
toString(): string {
return this.url
}
}

View File

@ -1,55 +1,53 @@
import { Generator } from './generator'
import { Collection, Storage, Logger } from '../core'
import { Stream, Category, Playlist } from '../models'
import { PUBLIC_DIR } from '../constants'
type CategoriesGeneratorProps = {
streams: Collection
categories: Collection
logger: Logger
}
export class CategoriesGenerator implements Generator {
streams: Collection
categories: Collection
storage: Storage
logger: Logger
constructor({ streams, categories, logger }: CategoriesGeneratorProps) {
this.streams = streams
this.categories = categories
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate() {
const streams = this.streams.orderBy([(stream: Stream) => stream.getTitle()])
this.categories.forEach(async (category: Category) => {
let categoryStreams = streams
.filter((stream: Stream) => stream.hasCategory(category))
.map((stream: Stream) => {
const groupTitle = stream.categories
? stream.categories
.map((category: Category) => category.name)
.sort()
.join(';')
: ''
stream.groupTitle = groupTitle
return stream
})
const playlist = new Playlist(categoryStreams, { public: true })
const filepath = `categories/${category.id}.m3u`
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
})
const undefinedStreams = streams.filter((stream: Stream) => stream.noCategories())
const playlist = new Playlist(undefinedStreams, { public: true })
const filepath = `categories/undefined.m3u`
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
}
import { Generator } from './generator'
import { Collection, Storage, Logger } from '@freearhey/core'
import { Stream, Category, Playlist } from '../models'
import { PUBLIC_DIR } from '../constants'
type CategoriesGeneratorProps = {
streams: Collection
categories: Collection
logger: Logger
}
export class CategoriesGenerator implements Generator {
streams: Collection
categories: Collection
storage: Storage
logger: Logger
constructor({ streams, categories, logger }: CategoriesGeneratorProps) {
this.streams = streams
this.categories = categories
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate() {
const streams = this.streams.orderBy([(stream: Stream) => stream.getTitle()])
this.categories.forEach(async (category: Category) => {
const categoryStreams = streams
.filter((stream: Stream) => stream.hasCategory(category))
.map((stream: Stream) => {
const streamCategories = stream.categories
.map((category: Category) => category.name)
.sort()
const groupTitle = stream.categories ? streamCategories.join(';') : ''
stream.groupTitle = groupTitle
return stream
})
const playlist = new Playlist(categoryStreams, { public: true })
const filepath = `categories/${category.id}.m3u`
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
})
const undefinedStreams = streams.filter((stream: Stream) => stream.noCategories())
const playlist = new Playlist(undefinedStreams, { public: true })
const filepath = 'categories/undefined.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
}

View File

@ -1,85 +1,85 @@
import { Generator } from './generator'
import { Collection, Storage, Logger } from '../core'
import { Country, Region, Subdivision, Stream, Playlist } from '../models'
import { PUBLIC_DIR } from '../constants'
type CountriesGeneratorProps = {
streams: Collection
regions: Collection
subdivisions: Collection
countries: Collection
logger: Logger
}
export class CountriesGenerator implements Generator {
streams: Collection
countries: Collection
regions: Collection
subdivisions: Collection
storage: Storage
logger: Logger
constructor({ streams, countries, regions, subdivisions, logger }: CountriesGeneratorProps) {
this.streams = streams
this.countries = countries
this.regions = regions
this.subdivisions = subdivisions
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
let streams = this.streams
.orderBy([stream => stream.getTitle()])
.filter((stream: Stream) => stream.isSFW())
let regions = this.regions.filter((region: Region) => region.code !== 'INT')
this.countries.forEach(async (country: Country) => {
const countrySubdivisions = this.subdivisions.filter(
(subdivision: Subdivision) => subdivision.country === country.code
)
const countrySubdivisionsCodes = countrySubdivisions.map(
(subdivision: Subdivision) => `s/${subdivision.code}`
)
const countryAreaCodes = regions
.filter((region: Region) => region.countries.includes(country.code))
.map((region: Region) => `r/${region.code}`)
.concat(countrySubdivisionsCodes)
.add(`c/${country.code}`)
const countryStreams = streams.filter(stream =>
stream.broadcastArea.intersects(countryAreaCodes)
)
if (countryStreams.isEmpty()) return
const playlist = new Playlist(countryStreams, { public: true })
const filepath = `countries/${country.code.toLowerCase()}.m3u`
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
countrySubdivisions.forEach(async (subdivision: Subdivision) => {
const subdivisionStreams = streams.filter(stream =>
stream.broadcastArea.includes(`s/${subdivision.code}`)
)
if (subdivisionStreams.isEmpty()) return
const playlist = new Playlist(subdivisionStreams, { public: true })
const filepath = `subdivisions/${subdivision.code.toLowerCase()}.m3u`
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
})
})
const internationalStreams = streams.filter(stream => stream.isInternational())
if (internationalStreams.notEmpty()) {
const playlist = new Playlist(internationalStreams, { public: true })
const filepath = `countries/int.m3u`
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
}
}
import { Generator } from './generator'
import { Collection, Storage, Logger } from '@freearhey/core'
import { Country, Region, Subdivision, Stream, Playlist } from '../models'
import { PUBLIC_DIR } from '../constants'
type CountriesGeneratorProps = {
streams: Collection
regions: Collection
subdivisions: Collection
countries: Collection
logger: Logger
}
export class CountriesGenerator implements Generator {
streams: Collection
countries: Collection
regions: Collection
subdivisions: Collection
storage: Storage
logger: Logger
constructor({ streams, countries, regions, subdivisions, logger }: CountriesGeneratorProps) {
this.streams = streams
this.countries = countries
this.regions = regions
this.subdivisions = subdivisions
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
const streams = this.streams
.orderBy([stream => stream.getTitle()])
.filter((stream: Stream) => stream.isSFW())
const regions = this.regions.filter((region: Region) => region.code !== 'INT')
this.countries.forEach(async (country: Country) => {
const countrySubdivisions = this.subdivisions.filter(
(subdivision: Subdivision) => subdivision.country === country.code
)
const countrySubdivisionsCodes = countrySubdivisions.map(
(subdivision: Subdivision) => `s/${subdivision.code}`
)
const countryAreaCodes = regions
.filter((region: Region) => region.countries.includes(country.code))
.map((region: Region) => `r/${region.code}`)
.concat(countrySubdivisionsCodes)
.add(`c/${country.code}`)
const countryStreams = streams.filter(stream =>
stream.broadcastArea.intersects(countryAreaCodes)
)
if (countryStreams.isEmpty()) return
const playlist = new Playlist(countryStreams, { public: true })
const filepath = `countries/${country.code.toLowerCase()}.m3u`
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
countrySubdivisions.forEach(async (subdivision: Subdivision) => {
const subdivisionStreams = streams.filter(stream =>
stream.broadcastArea.includes(`s/${subdivision.code}`)
)
if (subdivisionStreams.isEmpty()) return
const playlist = new Playlist(subdivisionStreams, { public: true })
const filepath = `subdivisions/${subdivision.code.toLowerCase()}.m3u`
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
})
})
const internationalStreams = streams.filter(stream => stream.isInternational())
if (internationalStreams.notEmpty()) {
const playlist = new Playlist(internationalStreams, { public: true })
const filepath = 'countries/int.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
}
}

View File

@ -1,10 +1,10 @@
export * from './categoriesGenerator'
export * from './countriesGenerator'
export * from './languagesGenerator'
export * from './regionsGenerator'
export * from './indexGenerator'
export * from './indexNsfwGenerator'
export * from './indexCategoryGenerator'
export * from './indexCountryGenerator'
export * from './indexLanguageGenerator'
export * from './indexRegionGenerator'
export * from './categoriesGenerator'
export * from './countriesGenerator'
export * from './languagesGenerator'
export * from './regionsGenerator'
export * from './indexGenerator'
export * from './indexNsfwGenerator'
export * from './indexCategoryGenerator'
export * from './indexCountryGenerator'
export * from './indexLanguageGenerator'
export * from './indexRegionGenerator'

View File

@ -1,53 +1,53 @@
import { Generator } from './generator'
import { Collection, Storage, Logger } from '../core'
import { Stream, Playlist, Category } from '../models'
import { PUBLIC_DIR } from '../constants'
type IndexCategoryGeneratorProps = {
streams: Collection
logger: Logger
}
export class IndexCategoryGenerator implements Generator {
streams: Collection
storage: Storage
logger: Logger
constructor({ streams, logger }: IndexCategoryGeneratorProps) {
this.streams = streams
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
const streams = this.streams
.orderBy(stream => stream.getTitle())
.filter(stream => stream.isSFW())
let groupedStreams = new Collection()
streams.forEach((stream: Stream) => {
if (stream.noCategories()) {
const streamClone = stream.clone()
streamClone.groupTitle = 'Undefined'
groupedStreams.add(streamClone)
return
}
stream.categories.forEach((category: Category) => {
const streamClone = stream.clone()
streamClone.groupTitle = category.name
groupedStreams.push(streamClone)
})
})
groupedStreams = groupedStreams.orderBy(stream => {
if (stream.groupTitle === 'Undefined') return 'ZZ'
return stream.groupTitle
})
const playlist = new Playlist(groupedStreams, { public: true })
const filepath = 'index.category.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
}
import { Generator } from './generator'
import { Collection, Storage, Logger } from '@freearhey/core'
import { Stream, Playlist, Category } from '../models'
import { PUBLIC_DIR } from '../constants'
type IndexCategoryGeneratorProps = {
streams: Collection
logger: Logger
}
export class IndexCategoryGenerator implements Generator {
streams: Collection
storage: Storage
logger: Logger
constructor({ streams, logger }: IndexCategoryGeneratorProps) {
this.streams = streams
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
const streams = this.streams
.orderBy(stream => stream.getTitle())
.filter(stream => stream.isSFW())
let groupedStreams = new Collection()
streams.forEach((stream: Stream) => {
if (stream.noCategories()) {
const streamClone = stream.clone()
streamClone.groupTitle = 'Undefined'
groupedStreams.add(streamClone)
return
}
stream.categories.forEach((category: Category) => {
const streamClone = stream.clone()
streamClone.groupTitle = category.name
groupedStreams.push(streamClone)
})
})
groupedStreams = groupedStreams.orderBy(stream => {
if (stream.groupTitle === 'Undefined') return 'ZZ'
return stream.groupTitle
})
const playlist = new Playlist(groupedStreams, { public: true })
const filepath = 'index.category.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
}

View File

@ -1,104 +1,104 @@
import { Generator } from './generator'
import { Collection, Storage, Logger } from '../core'
import { Stream, Playlist, Country, Subdivision, Region } from '../models'
import { PUBLIC_DIR } from '../constants'
type IndexCountryGeneratorProps = {
streams: Collection
regions: Collection
countries: Collection
subdivisions: Collection
logger: Logger
}
export class IndexCountryGenerator implements Generator {
streams: Collection
countries: Collection
regions: Collection
subdivisions: Collection
storage: Storage
logger: Logger
constructor({ streams, regions, countries, subdivisions, logger }: IndexCountryGeneratorProps) {
this.streams = streams
this.countries = countries
this.regions = regions
this.subdivisions = subdivisions
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
let groupedStreams = new Collection()
this.streams
.orderBy(stream => stream.getTitle())
.filter(stream => stream.isSFW())
.forEach(stream => {
if (stream.noBroadcastArea()) {
const streamClone = stream.clone()
streamClone.groupTitle = 'Undefined'
groupedStreams.add(streamClone)
return
}
if (stream.isInternational()) {
const streamClone = stream.clone()
streamClone.groupTitle = 'International'
groupedStreams.add(streamClone)
}
this.getStreamBroadcastCountries(stream).forEach((country: Country) => {
const streamClone = stream.clone()
streamClone.groupTitle = country.name
groupedStreams.add(streamClone)
})
})
groupedStreams = groupedStreams.orderBy((stream: Stream) => {
if (stream.groupTitle === 'International') return 'ZZ'
if (stream.groupTitle === 'Undefined') return 'ZZZ'
return stream.groupTitle
})
const playlist = new Playlist(groupedStreams, { public: true })
const filepath = 'index.country.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
getStreamBroadcastCountries(stream: Stream) {
const groupedRegions = this.regions.keyBy((region: Region) => region.code)
const groupedCountries = this.countries.keyBy((country: Country) => country.code)
const groupedSubdivisions = this.subdivisions.keyBy(
(subdivision: Subdivision) => subdivision.code
)
let broadcastCountries = new Collection()
stream.broadcastArea.forEach(broadcastAreaCode => {
const [type, code] = broadcastAreaCode.split('/')
switch (type) {
case 'c':
broadcastCountries.add(code)
break
case 'r':
if (code !== 'INT' && groupedRegions.has(code)) {
broadcastCountries = broadcastCountries.concat(groupedRegions.get(code).countries)
}
break
case 's':
if (groupedSubdivisions.has(code)) {
broadcastCountries.add(groupedSubdivisions.get(code).country)
}
break
}
})
return broadcastCountries
.uniq()
.map(code => groupedCountries.get(code))
.filter(Boolean)
}
}
import { Generator } from './generator'
import { Collection, Storage, Logger } from '@freearhey/core'
import { Stream, Playlist, Country, Subdivision, Region } from '../models'
import { PUBLIC_DIR } from '../constants'
type IndexCountryGeneratorProps = {
streams: Collection
regions: Collection
countries: Collection
subdivisions: Collection
logger: Logger
}
export class IndexCountryGenerator implements Generator {
streams: Collection
countries: Collection
regions: Collection
subdivisions: Collection
storage: Storage
logger: Logger
constructor({ streams, regions, countries, subdivisions, logger }: IndexCountryGeneratorProps) {
this.streams = streams
this.countries = countries
this.regions = regions
this.subdivisions = subdivisions
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
let groupedStreams = new Collection()
this.streams
.orderBy(stream => stream.getTitle())
.filter(stream => stream.isSFW())
.forEach(stream => {
if (stream.noBroadcastArea()) {
const streamClone = stream.clone()
streamClone.groupTitle = 'Undefined'
groupedStreams.add(streamClone)
return
}
if (stream.isInternational()) {
const streamClone = stream.clone()
streamClone.groupTitle = 'International'
groupedStreams.add(streamClone)
}
this.getStreamBroadcastCountries(stream).forEach((country: Country) => {
const streamClone = stream.clone()
streamClone.groupTitle = country.name
groupedStreams.add(streamClone)
})
})
groupedStreams = groupedStreams.orderBy((stream: Stream) => {
if (stream.groupTitle === 'International') return 'ZZ'
if (stream.groupTitle === 'Undefined') return 'ZZZ'
return stream.groupTitle
})
const playlist = new Playlist(groupedStreams, { public: true })
const filepath = 'index.country.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
getStreamBroadcastCountries(stream: Stream) {
const groupedRegions = this.regions.keyBy((region: Region) => region.code)
const groupedCountries = this.countries.keyBy((country: Country) => country.code)
const groupedSubdivisions = this.subdivisions.keyBy(
(subdivision: Subdivision) => subdivision.code
)
let broadcastCountries = new Collection()
stream.broadcastArea.forEach(broadcastAreaCode => {
const [type, code] = broadcastAreaCode.split('/')
switch (type) {
case 'c':
broadcastCountries.add(code)
break
case 'r':
if (code !== 'INT' && groupedRegions.has(code)) {
broadcastCountries = broadcastCountries.concat(groupedRegions.get(code).countries)
}
break
case 's':
if (groupedSubdivisions.has(code)) {
broadcastCountries.add(groupedSubdivisions.get(code).country)
}
break
}
})
return broadcastCountries
.uniq()
.map(code => groupedCountries.get(code))
.filter(Boolean)
}
}

View File

@ -1,32 +1,32 @@
import { Collection, Logger, Storage } from '../core'
import { Stream, Playlist } from '../models'
import { Generator } from './generator'
import { PUBLIC_DIR } from '../constants'
type IndexGeneratorProps = {
streams: Collection
logger: Logger
}
export class IndexGenerator implements Generator {
streams: Collection
storage: Storage
logger: Logger
constructor({ streams, logger }: IndexGeneratorProps) {
this.streams = streams
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
const sfwStreams = this.streams
.orderBy(stream => stream.getTitle())
.filter((stream: Stream) => stream.isSFW())
const playlist = new Playlist(sfwStreams, { public: true })
const filepath = 'index.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
}
import { Collection, Logger, Storage } from '@freearhey/core'
import { Stream, Playlist } from '../models'
import { Generator } from './generator'
import { PUBLIC_DIR } from '../constants'
type IndexGeneratorProps = {
streams: Collection
logger: Logger
}
export class IndexGenerator implements Generator {
streams: Collection
storage: Storage
logger: Logger
constructor({ streams, logger }: IndexGeneratorProps) {
this.streams = streams
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
const sfwStreams = this.streams
.orderBy(stream => stream.getTitle())
.filter((stream: Stream) => stream.isSFW())
const playlist = new Playlist(sfwStreams, { public: true })
const filepath = 'index.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
}

View File

@ -1,52 +1,52 @@
import { Generator } from './generator'
import { Collection, Storage, Logger } from '../core'
import { Stream, Playlist, Language } from '../models'
import { PUBLIC_DIR } from '../constants'
type IndexLanguageGeneratorProps = {
streams: Collection
logger: Logger
}
export class IndexLanguageGenerator implements Generator {
streams: Collection
storage: Storage
logger: Logger
constructor({ streams, logger }: IndexLanguageGeneratorProps) {
this.streams = streams
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
let groupedStreams = new Collection()
this.streams
.orderBy(stream => stream.getTitle())
.filter(stream => stream.isSFW())
.forEach(stream => {
if (stream.noLanguages()) {
const streamClone = stream.clone()
streamClone.groupTitle = 'Undefined'
groupedStreams.add(streamClone)
return
}
stream.languages.forEach((language: Language) => {
const streamClone = stream.clone()
streamClone.groupTitle = language.name
groupedStreams.add(streamClone)
})
})
groupedStreams = groupedStreams.orderBy((stream: Stream) => {
if (stream.groupTitle === 'Undefined') return 'ZZ'
return stream.groupTitle
})
const playlist = new Playlist(groupedStreams, { public: true })
const filepath = 'index.language.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
}
import { Generator } from './generator'
import { Collection, Storage, Logger } from '@freearhey/core'
import { Stream, Playlist, Language } from '../models'
import { PUBLIC_DIR } from '../constants'
type IndexLanguageGeneratorProps = {
streams: Collection
logger: Logger
}
export class IndexLanguageGenerator implements Generator {
streams: Collection
storage: Storage
logger: Logger
constructor({ streams, logger }: IndexLanguageGeneratorProps) {
this.streams = streams
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
let groupedStreams = new Collection()
this.streams
.orderBy(stream => stream.getTitle())
.filter(stream => stream.isSFW())
.forEach(stream => {
if (stream.noLanguages()) {
const streamClone = stream.clone()
streamClone.groupTitle = 'Undefined'
groupedStreams.add(streamClone)
return
}
stream.languages.forEach((language: Language) => {
const streamClone = stream.clone()
streamClone.groupTitle = language.name
groupedStreams.add(streamClone)
})
})
groupedStreams = groupedStreams.orderBy((stream: Stream) => {
if (stream.groupTitle === 'Undefined') return 'ZZ'
return stream.groupTitle
})
const playlist = new Playlist(groupedStreams, { public: true })
const filepath = 'index.language.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
}

View File

@ -1,30 +1,30 @@
import { Collection, Logger, Storage } from '../core'
import { Stream, Playlist } from '../models'
import { Generator } from './generator'
import { PUBLIC_DIR } from '../constants'
type IndexNsfwGeneratorProps = {
streams: Collection
logger: Logger
}
export class IndexNsfwGenerator implements Generator {
streams: Collection
storage: Storage
logger: Logger
constructor({ streams, logger }: IndexNsfwGeneratorProps) {
this.streams = streams
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
const allStreams = this.streams.orderBy((stream: Stream) => stream.getTitle())
const playlist = new Playlist(allStreams, { public: true })
const filepath = 'index.nsfw.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
}
import { Collection, Logger, Storage } from '@freearhey/core'
import { Stream, Playlist } from '../models'
import { Generator } from './generator'
import { PUBLIC_DIR } from '../constants'
type IndexNsfwGeneratorProps = {
streams: Collection
logger: Logger
}
export class IndexNsfwGenerator implements Generator {
streams: Collection
storage: Storage
logger: Logger
constructor({ streams, logger }: IndexNsfwGeneratorProps) {
this.streams = streams
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
const allStreams = this.streams.orderBy((stream: Stream) => stream.getTitle())
const playlist = new Playlist(allStreams, { public: true })
const filepath = 'index.nsfw.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
}

View File

@ -1,83 +1,83 @@
import { Generator } from './generator'
import { Collection, Storage, Logger } from '../core'
import { Stream, Playlist, Region } from '../models'
import { PUBLIC_DIR } from '../constants'
type IndexRegionGeneratorProps = {
streams: Collection
regions: Collection
logger: Logger
}
export class IndexRegionGenerator implements Generator {
streams: Collection
regions: Collection
storage: Storage
logger: Logger
constructor({ streams, regions, logger }: IndexRegionGeneratorProps) {
this.streams = streams
this.regions = regions
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
let groupedStreams = new Collection()
this.streams
.orderBy((stream: Stream) => stream.getTitle())
.filter((stream: Stream) => stream.isSFW())
.forEach((stream: Stream) => {
if (stream.noBroadcastArea()) {
const streamClone = stream.clone()
streamClone.groupTitle = 'Undefined'
groupedStreams.push(streamClone)
return
}
this.getStreamRegions(stream).forEach((region: Region) => {
const streamClone = stream.clone()
streamClone.groupTitle = region.name
groupedStreams.push(streamClone)
})
})
groupedStreams = groupedStreams.orderBy((stream: Stream) => {
if (stream.groupTitle === 'Undefined') return 'ZZ'
return stream.groupTitle
})
const playlist = new Playlist(groupedStreams, { public: true })
const filepath = 'index.region.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
getStreamRegions(stream: Stream) {
let streamRegions = new Collection()
stream.broadcastArea.forEach(broadcastAreaCode => {
const [type, code] = broadcastAreaCode.split('/')
switch (type) {
case 'r':
const groupedRegions = this.regions.keyBy((region: Region) => region.code)
streamRegions.add(groupedRegions.get(code))
break
case 's':
const [countryCode] = code.split('-')
const subdivisionRegions = this.regions.filter((region: Region) =>
region.countries.includes(countryCode)
)
streamRegions = streamRegions.concat(subdivisionRegions)
break
case 'c':
const countryRegions = this.regions.filter((region: Region) =>
region.countries.includes(code)
)
streamRegions = streamRegions.concat(countryRegions)
break
}
})
return streamRegions
}
}
import { Generator } from './generator'
import { Collection, Storage, Logger } from '@freearhey/core'
import { Stream, Playlist, Region } from '../models'
import { PUBLIC_DIR } from '../constants'
type IndexRegionGeneratorProps = {
streams: Collection
regions: Collection
logger: Logger
}
export class IndexRegionGenerator implements Generator {
streams: Collection
regions: Collection
storage: Storage
logger: Logger
constructor({ streams, regions, logger }: IndexRegionGeneratorProps) {
this.streams = streams
this.regions = regions
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
let groupedStreams = new Collection()
this.streams
.orderBy((stream: Stream) => stream.getTitle())
.filter((stream: Stream) => stream.isSFW())
.forEach((stream: Stream) => {
if (stream.noBroadcastArea()) {
const streamClone = stream.clone()
streamClone.groupTitle = 'Undefined'
groupedStreams.push(streamClone)
return
}
this.getStreamRegions(stream).forEach((region: Region) => {
const streamClone = stream.clone()
streamClone.groupTitle = region.name
groupedStreams.push(streamClone)
})
})
groupedStreams = groupedStreams.orderBy((stream: Stream) => {
if (stream.groupTitle === 'Undefined') return 'ZZ'
return stream.groupTitle
})
const playlist = new Playlist(groupedStreams, { public: true })
const filepath = 'index.region.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
getStreamRegions(stream: Stream) {
let streamRegions = new Collection()
stream.broadcastArea.forEach(broadcastAreaCode => {
const [type, code] = broadcastAreaCode.split('/')
switch (type) {
case 'r':
const groupedRegions = this.regions.keyBy((region: Region) => region.code)
streamRegions.add(groupedRegions.get(code))
break
case 's':
const [countryCode] = code.split('-')
const subdivisionRegions = this.regions.filter((region: Region) =>
region.countries.includes(countryCode)
)
streamRegions = streamRegions.concat(subdivisionRegions)
break
case 'c':
const countryRegions = this.regions.filter((region: Region) =>
region.countries.includes(code)
)
streamRegions = streamRegions.concat(countryRegions)
break
}
})
return streamRegions
}
}

View File

@ -1,50 +1,52 @@
import { Generator } from './generator'
import { Collection, Storage, Logger } from '../core'
import { Playlist, Language, Stream } from '../models'
import { PUBLIC_DIR } from '../constants'
type LanguagesGeneratorProps = { streams: Collection; logger: Logger }
export class LanguagesGenerator implements Generator {
streams: Collection
storage: Storage
logger: Logger
constructor({ streams, logger }: LanguagesGeneratorProps) {
this.streams = streams
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
let streams = this.streams.orderBy(stream => stream.getTitle()).filter(stream => stream.isSFW())
let languages = new Collection()
streams.forEach((stream: Stream) => {
languages = languages.concat(stream.languages)
})
languages
.uniqBy((language: Language) => language.code)
.orderBy((language: Language) => language.name)
.forEach(async (language: Language) => {
const languageStreams = streams.filter(stream => stream.hasLanguage(language))
if (languageStreams.isEmpty()) return
const playlist = new Playlist(languageStreams, { public: true })
const filepath = `languages/${language.code}.m3u`
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
})
const undefinedStreams = streams.filter(stream => stream.noLanguages())
if (undefinedStreams.isEmpty()) return
const playlist = new Playlist(undefinedStreams, { public: true })
const filepath = 'languages/undefined.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
}
import { Generator } from './generator'
import { Collection, Storage, Logger } from '@freearhey/core'
import { Playlist, Language, Stream } from '../models'
import { PUBLIC_DIR } from '../constants'
type LanguagesGeneratorProps = { streams: Collection; logger: Logger }
export class LanguagesGenerator implements Generator {
streams: Collection
storage: Storage
logger: Logger
constructor({ streams, logger }: LanguagesGeneratorProps) {
this.streams = streams
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
const streams = this.streams
.orderBy(stream => stream.getTitle())
.filter(stream => stream.isSFW())
let languages = new Collection()
streams.forEach((stream: Stream) => {
languages = languages.concat(stream.languages)
})
languages
.uniqBy((language: Language) => language.code)
.orderBy((language: Language) => language.name)
.forEach(async (language: Language) => {
const languageStreams = streams.filter(stream => stream.hasLanguage(language))
if (languageStreams.isEmpty()) return
const playlist = new Playlist(languageStreams, { public: true })
const filepath = `languages/${language.code}.m3u`
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
})
const undefinedStreams = streams.filter(stream => stream.noLanguages())
if (undefinedStreams.isEmpty()) return
const playlist = new Playlist(undefinedStreams, { public: true })
const filepath = 'languages/undefined.m3u'
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
}
}

View File

@ -1,51 +1,53 @@
import { Generator } from './generator'
import { Collection, Storage, Logger } from '../core'
import { Playlist, Subdivision, Region } from '../models'
import { PUBLIC_DIR } from '../constants'
type RegionsGeneratorProps = {
streams: Collection
regions: Collection
subdivisions: Collection
logger: Logger
}
export class RegionsGenerator implements Generator {
streams: Collection
regions: Collection
subdivisions: Collection
storage: Storage
logger: Logger
constructor({ streams, regions, subdivisions, logger }: RegionsGeneratorProps) {
this.streams = streams
this.regions = regions
this.subdivisions = subdivisions
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
let streams = this.streams.orderBy(stream => stream.getTitle()).filter(stream => stream.isSFW())
this.regions.forEach(async (region: Region) => {
if (region.code === 'INT') return
const regionSubdivisionsCodes = this.subdivisions
.filter((subdivision: Subdivision) => region.countries.indexOf(subdivision.country) > -1)
.map((subdivision: Subdivision) => `s/${subdivision.code}`)
const regionCodes = region.countries
.map((code: string) => `c/${code}`)
.concat(regionSubdivisionsCodes)
.add(`r/${region.code}`)
const regionStreams = streams.filter(stream => stream.broadcastArea.intersects(regionCodes))
const playlist = new Playlist(regionStreams, { public: true })
const filepath = `regions/${region.code.toLowerCase()}.m3u`
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
})
}
}
import { Generator } from './generator'
import { Collection, Storage, Logger } from '@freearhey/core'
import { Playlist, Subdivision, Region } from '../models'
import { PUBLIC_DIR } from '../constants'
type RegionsGeneratorProps = {
streams: Collection
regions: Collection
subdivisions: Collection
logger: Logger
}
export class RegionsGenerator implements Generator {
streams: Collection
regions: Collection
subdivisions: Collection
storage: Storage
logger: Logger
constructor({ streams, regions, subdivisions, logger }: RegionsGeneratorProps) {
this.streams = streams
this.regions = regions
this.subdivisions = subdivisions
this.storage = new Storage(PUBLIC_DIR)
this.logger = logger
}
async generate(): Promise<void> {
const streams = this.streams
.orderBy(stream => stream.getTitle())
.filter(stream => stream.isSFW())
this.regions.forEach(async (region: Region) => {
if (region.code === 'INT') return
const regionSubdivisionsCodes = this.subdivisions
.filter((subdivision: Subdivision) => region.countries.indexOf(subdivision.country) > -1)
.map((subdivision: Subdivision) => `s/${subdivision.code}`)
const regionCodes = region.countries
.map((code: string) => `c/${code}`)
.concat(regionSubdivisionsCodes)
.add(`r/${region.code}`)
const regionStreams = streams.filter(stream => stream.broadcastArea.intersects(regionCodes))
const playlist = new Playlist(regionStreams, { public: true })
const filepath = `regions/${region.code.toLowerCase()}.m3u`
await this.storage.save(filepath, playlist.toString())
this.logger.info(JSON.stringify({ filepath, count: playlist.streams.count() }))
})
}
}

View File

@ -1,4 +1,4 @@
import { Collection } from '../core'
import { Collection } from '@freearhey/core'
type ChannelProps = {
id: string

View File

@ -1,4 +1,4 @@
import { Dictionary } from '../core'
import { Dictionary } from '@freearhey/core'
type IssueProps = {
number: number

View File

@ -1,4 +1,4 @@
import { Collection } from '../core'
import { Collection } from '@freearhey/core'
import { Stream } from '../models'
type PlaylistOptions = {
@ -17,10 +17,10 @@ export class Playlist {
}
toString() {
let output = `#EXTM3U\n`
let output = '#EXTM3U\n'
this.streams.forEach((stream: Stream) => {
output += stream.toString(this.options) + `\n`
output += stream.toString(this.options) + '\n'
})
return output

View File

@ -1,4 +1,4 @@
import { Collection } from '../core'
import { Collection } from '@freearhey/core'
type RegionProps = {
code: string

View File

@ -1,4 +1,4 @@
import { URL, Collection } from '../core'
import { URL, Collection } from '@freearhey/core'
import { Category, Language } from './index'
type StreamProps = {
@ -86,7 +86,7 @@ export class Stream {
}
noCategories(): boolean {
return this.categories.empty()
return this.categories.isEmpty()
}
hasCategory(category: Category): boolean {
@ -94,7 +94,7 @@ export class Stream {
}
noLanguages(): boolean {
return this.languages.empty()
return this.languages.isEmpty()
}
hasLanguage(language: Language): boolean {
@ -102,7 +102,7 @@ export class Stream {
}
noBroadcastArea(): boolean {
return this.broadcastArea.empty()
return this.broadcastArea.isEmpty()
}
isInternational(): boolean {

View File

@ -1,4 +1,5 @@
import { Storage, HTMLTable, Collection, LogParser, LogItem, File } from '../core'
import { Storage, Collection, File } from '@freearhey/core'
import { HTMLTable, LogParser, LogItem } from '../core'
import { Category } from '../models'
import { DATA_DIR, LOGS_DIR, README_DIR } from '../constants'
import { Table } from './table'
@ -13,7 +14,7 @@ export class CategoryTable implements Table {
const parser = new LogParser()
const logsStorage = new Storage(LOGS_DIR)
const generatorsLog = await logsStorage.read('generators.log')
const generatorsLog = await logsStorage.load('generators.log')
let data = new Collection()
parser
@ -21,7 +22,7 @@ export class CategoryTable implements Table {
.filter((logItem: LogItem) => logItem.filepath.includes('categories/'))
.forEach((logItem: LogItem) => {
const file = new File(logItem.filepath)
const categoryId = file.getFilename()
const categoryId = file.name()
const category: Category = categories.first(
(category: Category) => category.id === categoryId
)

View File

@ -1,4 +1,5 @@
import { Storage, HTMLTable, Collection, LogParser, LogItem, File } from '../core'
import { Storage, Collection, File } from '@freearhey/core'
import { HTMLTable, LogParser, LogItem } from '../core'
import { Country, Subdivision } from '../models'
import { DATA_DIR, LOGS_DIR, README_DIR } from '../constants'
import { Table } from './table'
@ -17,7 +18,7 @@ export class CountryTable implements Table {
const parser = new LogParser()
const logsStorage = new Storage(LOGS_DIR)
const generatorsLog = await logsStorage.read('generators.log')
const generatorsLog = await logsStorage.load('generators.log')
let data = new Collection()
parser
@ -28,7 +29,7 @@ export class CountryTable implements Table {
)
.forEach((logItem: LogItem) => {
const file = new File(logItem.filepath)
const code = file.getFilename().toUpperCase()
const code = file.name().toUpperCase()
const [countryCode, subdivisionCode] = code.split('-') || ['', '']
if (subdivisionCode) {
@ -47,7 +48,7 @@ export class CountryTable implements Table {
} else if (countryCode === 'INT') {
data.add([
'ZZ',
`🌍 International`,
'🌍 International',
logItem.count,
`<code>https://iptv-org.github.io/iptv/${logItem.filepath}</code>`
])

View File

@ -1,4 +1,5 @@
import { Storage, HTMLTable, Collection, LogParser, LogItem, File } from '../core'
import { Storage, Collection, File } from '@freearhey/core'
import { HTMLTable, LogParser, LogItem } from '../core'
import { Language } from '../models'
import { DATA_DIR, LOGS_DIR, README_DIR } from '../constants'
import { Table } from './table'
@ -13,7 +14,7 @@ export class LanguageTable implements Table {
const parser = new LogParser()
const logsStorage = new Storage(LOGS_DIR)
const generatorsLog = await logsStorage.read('generators.log')
const generatorsLog = await logsStorage.load('generators.log')
let data = new Collection()
parser
@ -21,7 +22,7 @@ export class LanguageTable implements Table {
.filter((logItem: LogItem) => logItem.filepath.includes('languages/'))
.forEach((logItem: LogItem) => {
const file = new File(logItem.filepath)
const languageCode = file.getFilename()
const languageCode = file.name()
const language: Language = languages.first(
(language: Language) => language.code === languageCode
)

View File

@ -1,4 +1,5 @@
import { Storage, HTMLTable, Collection, LogParser, LogItem, File } from '../core'
import { Storage, Collection, File } from '@freearhey/core'
import { HTMLTable, LogParser, LogItem } from '../core'
import { Region } from '../models'
import { DATA_DIR, LOGS_DIR, README_DIR } from '../constants'
import { Table } from './table'
@ -13,7 +14,7 @@ export class RegionTable implements Table {
const parser = new LogParser()
const logsStorage = new Storage(LOGS_DIR)
const generatorsLog = await logsStorage.read('generators.log')
const generatorsLog = await logsStorage.load('generators.log')
let data = new Collection()
parser
@ -21,7 +22,7 @@ export class RegionTable implements Table {
.filter((logItem: LogItem) => logItem.filepath.includes('regions/'))
.forEach((logItem: LogItem) => {
const file = new File(logItem.filepath)
const regionCode = file.getFilename().toUpperCase()
const regionCode = file.name().toUpperCase()
const region: Region = regions.first((region: Region) => region.code === regionCode)
if (region) {

View File

@ -155,7 +155,7 @@ https://weyyak-live.akamaized.net/weyyak_drama/index.m3u8
https://weyyak-live.akamaized.net/weyyak_mix/index.m3u8
#EXTINF:-1 tvg-id="WeyyakNawaem.ae",Weyyak Nawaem (720p)
https://weyyak-live.akamaized.net/weyyak_nawaem/index.m3u8
#EXTINF:-1 tvg-id="",Yas (1080p)
#EXTINF:-1 tvg-id="YasTV.ae",Yas TV (1080p)
https://admdn1.cdn.mangomolo.com/yastv/smil:yastv.stream.smil/playlist.m3u8
#EXTINF:-1 tvg-id="ZeeAflam.in",Zee Aflam (720p) [Not 24/7]
https://weyyak-live.akamaized.net/weyyak_zee_aflam/index.m3u8

View File

@ -59,8 +59,6 @@ https://ythls.onrender.com/channel/UC2ZoBzeCXrZcMz4f82eMe-g.m3u8
#EXTINF:-1 tvg-id="GarshomTV.uk",Garshom TV (360p) [Not 24/7]
https://og2qd3aal7an-hls-live.5centscdn.com/garshomtv/d0dbe915091d400bd8ee7f27f0791303.sdp/playlist.m3u8
#EXTINF:-1 tvg-id="GBNews.uk",GB News (1080p)
https://live-gbnews-ssai.simplestreamcdn.com/v1/master/82267e84b9e5053b3fd0ade12cb1a146df74169a/gbnews-live/live.m3u8
#EXTINF:-1 tvg-id="GBNews.uk",GB News (1080p)
https://live-gbnews.simplestreamcdn.com/live5/gbnews/bitrate1.isml/manifest.m3u8
#EXTINF:-1 tvg-id="GBNews.uk",GB News (1080p)
https://ythls.onrender.com/channel/UC0vn8ISa4LKMunLbzaXLnOQ.m3u8

View File

@ -1,4 +1,4 @@
export default [
module.exports = [
{
url: 'https://api.github.com/repos/iptv-org/iptv/issues/14140',
repository_url: 'https://api.github.com/repos/iptv-org/iptv',

View File

@ -1,4 +1,4 @@
export default [
module.exports = [
{
url: 'https://api.github.com/repos/iptv-org/iptv/issues/14179',
repository_url: 'https://api.github.com/repos/iptv-org/iptv',

View File

@ -1,4 +1,4 @@
export default [
module.exports = [
{
url: 'https://api.github.com/repos/iptv-org/iptv/issues/14179',
repository_url: 'https://api.github.com/repos/iptv-org/iptv',

View File

@ -1,4 +1,4 @@
export default [
module.exports = [
{
url: 'https://api.github.com/repos/iptv-org/iptv/issues/14110',
repository_url: 'https://api.github.com/repos/iptv-org/iptv',

View File

@ -1,4 +1,4 @@
export default [
module.exports = [
{
url: 'https://api.github.com/repos/iptv-org/iptv/issues/14110',
repository_url: 'https://api.github.com/repos/iptv-org/iptv',

View File

@ -1,4 +1,4 @@
export default [
module.exports = [
{
url: 'https://api.github.com/repos/iptv-org/iptv/issues/14151',
repository_url: 'https://api.github.com/repos/iptv-org/iptv',

View File

@ -4,14 +4,14 @@ import fs from 'fs-extra'
beforeEach(() => {
fs.emptyDirSync('tests/__data__/output')
const stdout = execSync(
execSync(
'STREAMS_DIR=tests/__data__/input/streams_generate API_DIR=tests/__data__/output/.api npm run api:generate',
{ encoding: 'utf8' }
)
})
it('can create streams.json', () => {
expect(content(`output/.api/streams.json`)).toMatchObject(content(`expected/.api/streams.json`))
expect(content('output/.api/streams.json')).toMatchObject(content('expected/.api/streams.json'))
})
function content(filepath: string) {

View File

@ -8,7 +8,7 @@ beforeEach(() => {
})
it('can format playlists', () => {
const stdout = execSync('STREAMS_DIR=tests/__data__/output/streams npm run playlist:format', {
execSync('STREAMS_DIR=tests/__data__/output/streams npm run playlist:format', {
encoding: 'utf8'
})

View File

@ -5,7 +5,7 @@ import * as glob from 'glob'
beforeEach(() => {
fs.emptyDirSync('tests/__data__/output')
const stdout = execSync(
execSync(
'STREAMS_DIR=tests/__data__/input/streams_generate DATA_DIR=tests/__data__/input/data PUBLIC_DIR=tests/__data__/output/.gh-pages LOGS_DIR=tests/__data__/output/logs npm run playlist:generate',
{ encoding: 'utf8' }
)
@ -20,8 +20,8 @@ it('can generate playlists and logs', () => {
expect(content(`output/${filepath}`), filepath).toBe(content(`expected/${filepath}`))
})
expect(content(`output/logs/generators.log`).split('\n').sort()).toStrictEqual(
content(`expected/logs/generators.log`).split('\n').sort()
expect(content('output/logs/generators.log').split('\n').sort()).toStrictEqual(
content('expected/logs/generators.log').split('\n').sort()
)
})

View File

@ -26,7 +26,7 @@ it('can format playlists', () => {
})
expect(stdout).toBe(
`OUTPUT=closes #14151, closes #14140, closes #14139, closes #14110, closes #14179, closes #14178\n`
'OUTPUT=closes #14151, closes #14140, closes #14139, closes #14110, closes #14179, closes #14178\n'
)
})

View File

@ -10,11 +10,13 @@ it('show an error if channel name in the blocklist', () => {
)
console.log(stdout)
process.exit(1)
} catch (error: any) {
} catch (error: unknown) {
// @ts-ignore
expect(error.status).toBe(1)
expect(
// @ts-ignore
error.stdout.includes(
`us_blocked.m3u\n 2 error "Fox Sports 2 Asia (Thai)" is on the blocklist due to claims of copyright holders (https://github.com/iptv-org/iptv/issues/0000)\n\n1 problems (1 errors, 0 warnings)\n`
'us_blocked.m3u\n 2 error "Fox Sports 2 Asia (Thai)" is on the blocklist due to claims of copyright holders (https://github.com/iptv-org/iptv/issues/0000)\n\n1 problems (1 errors, 0 warnings)\n'
)
).toBe(true)
}
@ -30,7 +32,7 @@ it('show a warning if channel has wrong id', () => {
expect(
stdout.includes(
`wrong_id.m3u\n 2 warning "qib22lAq1L.us" is not in the database\n\n1 problems (0 errors, 1 warnings)\n`
'wrong_id.m3u\n 2 warning "qib22lAq1L.us" is not in the database\n\n1 problems (0 errors, 1 warnings)\n'
)
).toBe(true)
})

View File

@ -14,7 +14,7 @@ beforeEach(() => {
'tests/__data__/output/.readme/template.md'
)
const stdout = execSync(
execSync(
'DATA_DIR=tests/__data__/input/data LOGS_DIR=tests/__data__/input/logs README_DIR=tests/__data__/output/.readme npm run readme:update',
{ encoding: 'utf8' }
)

View File

@ -1,9 +1,9 @@
{
"compilerOptions": {
"strict": true,
"target": "es2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "es2020",
"module": "nodeNext",
"moduleResolution": "nodeNext",
"typeRoots": [
"./scripts/types",
"./node_modules/@types"

660
yarn.lock
View File

@ -2,6 +2,11 @@
# yarn lockfile v1
"@aashutoshrathi/word-wrap@^1.2.3":
version "1.2.6"
resolved "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz"
integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
"@ampproject/remapping@^2.0.0":
version "2.1.1"
resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.1.tgz"
@ -301,6 +306,71 @@
dependencies:
"@jridgewell/trace-mapping" "0.3.9"
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0"
resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz"
integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
dependencies:
eslint-visitor-keys "^3.3.0"
"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1":
version "4.8.1"
resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz"
integrity sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==
"@eslint/eslintrc@^2.1.2":
version "2.1.2"
resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz"
integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==
dependencies:
ajv "^6.12.4"
debug "^4.3.2"
espree "^9.6.0"
globals "^13.19.0"
ignore "^5.2.0"
import-fresh "^3.2.1"
js-yaml "^4.1.0"
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@eslint/js@8.49.0":
version "8.49.0"
resolved "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz"
integrity sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==
"@freearhey/core@^0.1.2":
version "0.1.2"
resolved "https://registry.npmjs.org/@freearhey/core/-/core-0.1.2.tgz"
integrity sha512-o6FaLHiKED0JkUXxqHcUUM2P+Jd4kvE96p0MZlLjhCFAPii/lVr5mTwrHehKMzve/r7AbDUX7kyNLNG8Qec6uw==
dependencies:
"@types/fs-extra" "^11.0.2"
"@types/lodash" "^4.14.198"
fs-extra "^11.1.1"
glob "^10.3.4"
lodash "^4.17.21"
natural-orderby "^3.0.2"
normalize-url "^6.1.0"
signale "^1.4.0"
"@humanwhocodes/config-array@^0.11.11":
version "0.11.11"
resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz"
integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==
dependencies:
"@humanwhocodes/object-schema" "^1.2.1"
debug "^4.1.1"
minimatch "^3.0.5"
"@humanwhocodes/module-importer@^1.0.1":
version "1.0.1"
resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz"
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
"@humanwhocodes/object-schema@^1.2.1":
version "1.2.1"
resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@isaacs/cliui@^8.0.2":
version "8.0.2"
resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz"
@ -555,6 +625,27 @@
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"
integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
dependencies:
"@nodelib/fs.stat" "2.0.5"
run-parallel "^1.1.9"
"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5":
version "2.0.5"
resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8":
version "1.2.8"
resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz"
integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
dependencies:
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
"@octokit/auth-token@^3.0.0":
version "3.0.4"
resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz"
@ -742,10 +833,10 @@
dependencies:
"@babel/types" "^7.20.7"
"@types/fs-extra@^11.0.1":
version "11.0.1"
resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.1.tgz"
integrity sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==
"@types/fs-extra@^11.0.1", "@types/fs-extra@^11.0.2":
version "11.0.2"
resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.2.tgz"
integrity sha512-c0hrgAOVYr21EX8J0jBMXGLMgJqVf/v6yxi0dLaJboW9aQPh16Id+z6w2Tx1hm+piJOLv8xPfVKZCLfjPw/IMQ==
dependencies:
"@types/jsonfile" "*"
"@types/node" "*"
@ -799,6 +890,11 @@
expect "^29.0.0"
pretty-format "^29.0.0"
"@types/json-schema@^7.0.12":
version "7.0.13"
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz"
integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==
"@types/jsonfile@*":
version "6.1.1"
resolved "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.1.tgz"
@ -821,6 +917,11 @@
resolved "https://registry.npmjs.org/@types/node/-/node-17.0.18.tgz"
integrity sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA==
"@types/semver@^7.5.0":
version "7.5.2"
resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.2.tgz"
integrity sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==
"@types/signale@^1.4.4":
version "1.4.4"
resolved "https://registry.npmjs.org/@types/signale/-/signale-1.4.4.tgz"
@ -845,15 +946,115 @@
dependencies:
"@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^6.7.2":
version "6.7.2"
resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz"
integrity sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q==
dependencies:
"@eslint-community/regexpp" "^4.5.1"
"@typescript-eslint/scope-manager" "6.7.2"
"@typescript-eslint/type-utils" "6.7.2"
"@typescript-eslint/utils" "6.7.2"
"@typescript-eslint/visitor-keys" "6.7.2"
debug "^4.3.4"
graphemer "^1.4.0"
ignore "^5.2.4"
natural-compare "^1.4.0"
semver "^7.5.4"
ts-api-utils "^1.0.1"
"@typescript-eslint/parser@^6.0.0 || ^6.0.0-alpha", "@typescript-eslint/parser@^6.7.2":
version "6.7.2"
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.2.tgz"
integrity sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw==
dependencies:
"@typescript-eslint/scope-manager" "6.7.2"
"@typescript-eslint/types" "6.7.2"
"@typescript-eslint/typescript-estree" "6.7.2"
"@typescript-eslint/visitor-keys" "6.7.2"
debug "^4.3.4"
"@typescript-eslint/scope-manager@6.7.2":
version "6.7.2"
resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.2.tgz"
integrity sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw==
dependencies:
"@typescript-eslint/types" "6.7.2"
"@typescript-eslint/visitor-keys" "6.7.2"
"@typescript-eslint/type-utils@6.7.2":
version "6.7.2"
resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.2.tgz"
integrity sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ==
dependencies:
"@typescript-eslint/typescript-estree" "6.7.2"
"@typescript-eslint/utils" "6.7.2"
debug "^4.3.4"
ts-api-utils "^1.0.1"
"@typescript-eslint/types@6.7.2":
version "6.7.2"
resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.2.tgz"
integrity sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg==
"@typescript-eslint/typescript-estree@6.7.2":
version "6.7.2"
resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.2.tgz"
integrity sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ==
dependencies:
"@typescript-eslint/types" "6.7.2"
"@typescript-eslint/visitor-keys" "6.7.2"
debug "^4.3.4"
globby "^11.1.0"
is-glob "^4.0.3"
semver "^7.5.4"
ts-api-utils "^1.0.1"
"@typescript-eslint/utils@6.7.2":
version "6.7.2"
resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.2.tgz"
integrity sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ==
dependencies:
"@eslint-community/eslint-utils" "^4.4.0"
"@types/json-schema" "^7.0.12"
"@types/semver" "^7.5.0"
"@typescript-eslint/scope-manager" "6.7.2"
"@typescript-eslint/types" "6.7.2"
"@typescript-eslint/typescript-estree" "6.7.2"
semver "^7.5.4"
"@typescript-eslint/visitor-keys@6.7.2":
version "6.7.2"
resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.2.tgz"
integrity sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ==
dependencies:
"@typescript-eslint/types" "6.7.2"
eslint-visitor-keys "^3.4.1"
acorn-jsx@^5.3.2:
version "5.3.2"
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
acorn-walk@^8.1.1:
version "8.2.0"
resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
acorn@^8.4.1:
version "8.7.0"
resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz"
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.4.1, acorn@^8.9.0:
version "8.10.0"
resolved "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz"
integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==
ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ansi-escapes@^4.2.1:
version "4.3.2"
@ -916,6 +1117,11 @@ argparse@^1.0.7:
dependencies:
sprintf-js "~1.0.2"
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
array-union@^1.0.1:
version "1.0.2"
resolved "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz"
@ -923,6 +1129,11 @@ array-union@^1.0.1:
dependencies:
array-uniq "^1.0.1"
array-union@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
array-uniq@^1.0.1:
version "1.0.3"
resolved "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz"
@ -1201,7 +1412,7 @@ create-require@^1.1.0:
resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-spawn@^7.0.0, cross-spawn@^7.0.3:
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@ -1210,10 +1421,10 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
debug@^4.1.0, debug@^4.1.1:
version "4.3.3"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
@ -1222,6 +1433,11 @@ dedent@^1.0.0:
resolved "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz"
integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==
deep-is@^0.1.3:
version "0.1.4"
resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
deepmerge@^4.2.2:
version "4.3.1"
resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz"
@ -1247,6 +1463,20 @@ diff@^4.0.1:
resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz"
integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
dependencies:
path-type "^4.0.0"
doctrine@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz"
integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
dependencies:
esutils "^2.0.2"
eastasianwidth@^0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz"
@ -1304,11 +1534,105 @@ escape-string-regexp@^2.0.0:
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
escape-string-regexp@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
eslint-scope@^7.2.2:
version "7.2.2"
resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz"
integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==
dependencies:
esrecurse "^4.3.0"
estraverse "^5.2.0"
eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
version "3.4.3"
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
"eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", eslint@^8.49.0:
version "8.49.0"
resolved "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz"
integrity sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==
dependencies:
"@eslint-community/eslint-utils" "^4.2.0"
"@eslint-community/regexpp" "^4.6.1"
"@eslint/eslintrc" "^2.1.2"
"@eslint/js" "8.49.0"
"@humanwhocodes/config-array" "^0.11.11"
"@humanwhocodes/module-importer" "^1.0.1"
"@nodelib/fs.walk" "^1.2.8"
ajv "^6.12.4"
chalk "^4.0.0"
cross-spawn "^7.0.2"
debug "^4.3.2"
doctrine "^3.0.0"
escape-string-regexp "^4.0.0"
eslint-scope "^7.2.2"
eslint-visitor-keys "^3.4.3"
espree "^9.6.1"
esquery "^1.4.2"
esutils "^2.0.2"
fast-deep-equal "^3.1.3"
file-entry-cache "^6.0.1"
find-up "^5.0.0"
glob-parent "^6.0.2"
globals "^13.19.0"
graphemer "^1.4.0"
ignore "^5.2.0"
imurmurhash "^0.1.4"
is-glob "^4.0.0"
is-path-inside "^3.0.3"
js-yaml "^4.1.0"
json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.4.1"
lodash.merge "^4.6.2"
minimatch "^3.1.2"
natural-compare "^1.4.0"
optionator "^0.9.3"
strip-ansi "^6.0.1"
text-table "^0.2.0"
espree@^9.6.0, espree@^9.6.1:
version "9.6.1"
resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz"
integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
dependencies:
acorn "^8.9.0"
acorn-jsx "^5.3.2"
eslint-visitor-keys "^3.4.1"
esprima@^4.0.0:
version "4.0.1"
resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
esquery@^1.4.2:
version "1.5.0"
resolved "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz"
integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==
dependencies:
estraverse "^5.1.0"
esrecurse@^4.3.0:
version "4.3.0"
resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz"
integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
dependencies:
estraverse "^5.2.0"
estraverse@^5.1.0, estraverse@^5.2.0:
version "5.3.0"
resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz"
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
esutils@^2.0.2:
version "2.0.3"
resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
execa@^5.0.0:
version "5.1.1"
resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz"
@ -1340,11 +1664,39 @@ expect@^29.0.0, expect@^29.6.4:
jest-message-util "^29.6.3"
jest-util "^29.6.3"
fast-json-stable-stringify@^2.1.0, fast-json-stable-stringify@2.x:
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-glob@^3.2.9:
version "3.3.1"
resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz"
integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3"
glob-parent "^5.1.2"
merge2 "^1.3.0"
micromatch "^4.0.4"
fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0, fast-json-stable-stringify@2.x:
version "2.1.0"
resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
fast-levenshtein@^2.0.6:
version "2.0.6"
resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz"
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
fastq@^1.6.0:
version "1.15.0"
resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz"
integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==
dependencies:
reusify "^1.0.4"
fb-watchman@^2.0.0:
version "2.0.2"
resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz"
@ -1359,6 +1711,13 @@ figures@^2.0.0:
dependencies:
escape-string-regexp "^1.0.5"
file-entry-cache@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz"
integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
dependencies:
flat-cache "^3.0.4"
filename-reserved-regex@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz"
@ -1404,6 +1763,28 @@ find-up@^4.0.0, find-up@^4.1.0:
locate-path "^5.0.0"
path-exists "^4.0.0"
find-up@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz"
integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
dependencies:
locate-path "^6.0.0"
path-exists "^4.0.0"
flat-cache@^3.0.4:
version "3.1.0"
resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz"
integrity sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==
dependencies:
flatted "^3.2.7"
keyv "^4.5.3"
rimraf "^3.0.2"
flatted@^3.2.7:
version "3.2.9"
resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz"
integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==
foreground-child@^3.1.0:
version "3.1.1"
resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz"
@ -1478,6 +1859,20 @@ gh-pages@^5.0.0:
fs-extra "^8.1.0"
globby "^6.1.0"
glob-parent@^5.1.2:
version "5.1.2"
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
glob-parent@^6.0.2:
version "6.0.2"
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz"
integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
dependencies:
is-glob "^4.0.3"
glob@^10.3.4:
version "10.3.4"
resolved "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz"
@ -1542,6 +1937,25 @@ globals@^11.1.0:
resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
globals@^13.19.0:
version "13.22.0"
resolved "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz"
integrity sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==
dependencies:
type-fest "^0.20.2"
globby@^11.1.0:
version "11.1.0"
resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz"
integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
dependencies:
array-union "^2.1.0"
dir-glob "^3.0.1"
fast-glob "^3.2.9"
ignore "^5.2.0"
merge2 "^1.4.1"
slash "^3.0.0"
globby@^6.1.0:
version "6.1.0"
resolved "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz"
@ -1558,6 +1972,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9:
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz"
integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
graphemer@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz"
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz"
@ -1585,6 +2004,19 @@ human-signals@^2.1.0:
resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
ignore@^5.2.0, ignore@^5.2.4:
version "5.2.4"
resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz"
integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
import-fresh@^3.2.1:
version "3.3.0"
resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz"
integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
dependencies:
parent-module "^1.0.0"
resolve-from "^4.0.0"
import-local@^3.0.2:
version "3.1.0"
resolved "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz"
@ -1636,6 +2068,11 @@ is-extglob@^1.0.0:
resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz"
integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz"
@ -1653,6 +2090,27 @@ is-glob@^2.0.0:
dependencies:
is-extglob "^1.0.0"
is-glob@^4.0.0:
version "4.0.3"
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"
is-glob@^4.0.1:
version "4.0.3"
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"
is-glob@^4.0.3:
version "4.0.3"
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"
is-invalid-path@^0.1.0:
version "0.1.0"
resolved "https://registry.npmjs.org/is-invalid-path/-/is-invalid-path-0.1.0.tgz"
@ -1665,6 +2123,11 @@ is-number@^7.0.0:
resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
is-path-inside@^3.0.3:
version "3.0.3"
resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz"
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
is-plain-object@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz"
@ -2126,11 +2589,23 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies:
argparse "^2.0.1"
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz"
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
json-buffer@3.0.1:
version "3.0.1"
resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz"
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
json-parse-better-errors@^1.0.1:
version "1.0.2"
resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz"
@ -2141,6 +2616,16 @@ json-parse-even-better-errors@^2.3.0:
resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz"
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz"
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
json5@^2.1.2, json5@^2.2.3:
version "2.2.3"
resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz"
@ -2162,6 +2647,13 @@ jsonfile@^6.0.1:
optionalDependencies:
graceful-fs "^4.1.6"
keyv@^4.5.3:
version "4.5.3"
resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz"
integrity sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==
dependencies:
json-buffer "3.0.1"
kleur@^3.0.3:
version "3.0.3"
resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz"
@ -2172,6 +2664,14 @@ leven@^3.1.0:
resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz"
integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
levn@^0.4.1:
version "0.4.1"
resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz"
integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
dependencies:
prelude-ls "^1.2.1"
type-check "~0.4.0"
lines-and-columns@^1.1.6:
version "1.2.4"
resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz"
@ -2202,11 +2702,23 @@ locate-path@^5.0.0:
dependencies:
p-locate "^4.1.0"
locate-path@^6.0.0:
version "6.0.0"
resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz"
integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
dependencies:
p-locate "^5.0.0"
lodash.memoize@4.x:
version "4.1.2"
resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz"
integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
@ -2271,6 +2783,11 @@ merge-stream@^2.0.0:
resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
merge2@^1.3.0, merge2@^1.4.1:
version "1.4.1"
resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
micromatch@^4.0.4:
version "4.0.5"
resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz"
@ -2284,7 +2801,7 @@ mimic-fn@^2.1.0:
resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
minimatch@^3.0.4, minimatch@^3.1.1:
minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@ -2340,6 +2857,11 @@ normalize-path@^3.0.0:
resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
normalize-url@^6.1.0:
version "6.1.0"
resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz"
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
normalize-url@^7.2.0:
version "7.2.0"
resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-7.2.0.tgz"
@ -2371,6 +2893,18 @@ onetime@^5.1.2:
dependencies:
mimic-fn "^2.1.0"
optionator@^0.9.3:
version "0.9.3"
resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz"
integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==
dependencies:
"@aashutoshrathi/word-wrap" "^1.2.3"
deep-is "^0.1.3"
fast-levenshtein "^2.0.6"
levn "^0.4.1"
prelude-ls "^1.2.1"
type-check "^0.4.0"
p-limit@^1.1.0:
version "1.3.0"
resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz"
@ -2385,6 +2919,13 @@ p-limit@^2.2.0:
dependencies:
p-try "^2.0.0"
p-limit@^3.0.2:
version "3.1.0"
resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz"
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
dependencies:
yocto-queue "^0.1.0"
p-limit@^3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz"
@ -2406,6 +2947,13 @@ p-locate@^4.1.0:
dependencies:
p-limit "^2.2.0"
p-locate@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz"
integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
dependencies:
p-limit "^3.0.2"
p-try@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz"
@ -2416,6 +2964,13 @@ p-try@^2.0.0:
resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz"
integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
dependencies:
callsites "^3.0.0"
parse-json@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz"
@ -2467,6 +3022,11 @@ path-scurry@^1.10.1:
lru-cache "^9.1.1 || ^10.0.0"
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz"
@ -2519,6 +3079,11 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0:
dependencies:
find-up "^4.0.0"
prelude-ls@^1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
pretty-format@^29.0.0, pretty-format@^29.6.3:
version "29.6.3"
resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz"
@ -2536,6 +3101,11 @@ prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.5"
punycode@^2.1.0:
version "2.3.0"
resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz"
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
pure-rand@^6.0.0:
version "6.0.3"
resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.3.tgz"
@ -2546,6 +3116,11 @@ q@^1.2.0:
resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz"
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
react-is@^18.0.0:
version "18.2.0"
resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz"
@ -2563,6 +3138,11 @@ resolve-cwd@^3.0.0:
dependencies:
resolve-from "^5.0.0"
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
resolve-from@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
@ -2582,6 +3162,25 @@ resolve@^1.20.0:
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
reusify@^1.0.4:
version "1.0.4"
resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
dependencies:
glob "^7.1.3"
run-parallel@^1.1.9:
version "1.2.0"
resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz"
integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
dependencies:
queue-microtask "^1.2.2"
safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
@ -2795,6 +3394,11 @@ test-exclude@^6.0.0:
glob "^7.1.4"
minimatch "^3.0.4"
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
tmpl@1.0.5:
version "1.0.5"
resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz"
@ -2831,6 +3435,11 @@ trim-repeated@^1.0.0:
dependencies:
escape-string-regexp "^1.0.2"
ts-api-utils@^1.0.1:
version "1.0.3"
resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz"
integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==
ts-jest@^29.1.1:
version "29.1.1"
resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz"
@ -2864,17 +3473,29 @@ ts-node@>=9.0.0:
v8-compile-cache-lib "^3.0.1"
yn "3.1.1"
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz"
integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
dependencies:
prelude-ls "^1.2.1"
type-detect@4.0.8:
version "4.0.8"
resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
type-fest@^0.20.2:
version "0.20.2"
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
type-fest@^0.21.3:
version "0.21.3"
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
typescript@^5.2.2, typescript@>=2.7, "typescript@>=4.3 <6":
typescript@^5.2.2, typescript@>=2.7, typescript@>=4.2.0, "typescript@>=4.3 <6":
version "5.2.2"
resolved "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz"
integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==
@ -2894,6 +3515,13 @@ universalify@^2.0.0:
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz"
integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
dependencies:
punycode "^2.1.0"
v8-compile-cache-lib@^3.0.1:
version "3.0.1"
resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz"