diff --git a/.eslintrc.js b/.eslintrc.js index f95277a..a2e5bd1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -72,6 +72,14 @@ module.exports = { tsx: 'never', }, ], + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': 'error', + 'no-useless-constructor': 'off', + '@typescript-eslint/no-useless-constructor': 'error', + 'no-empty-function': 'off', + '@typescript-eslint/no-empty-function': 'error', + 'no-shadow': 'off', + '@typescript-eslint/no-shadow': 'error', }, overrides: [ { diff --git a/packages/game/package.json b/packages/game/package.json new file mode 100644 index 0000000..5854459 --- /dev/null +++ b/packages/game/package.json @@ -0,0 +1,12 @@ +{ + "name": "@linux-odyssey/game", + "version": "1.0.0", + "main": "dist/index.ts", + "license": "MIT", + "scripts": { + "build": "tsc -b" + }, + "dependencies": { + "zod": "^3.23.8" + } +} diff --git a/packages/game/src/Session.ts b/packages/game/src/Session.ts new file mode 100644 index 0000000..e8b5417 --- /dev/null +++ b/packages/game/src/Session.ts @@ -0,0 +1,17 @@ +import { ISession } from './types' + +export class Session implements ISession { + private completed: string[] = [] + + constructor({ completedStages }: ISession) { + this.completed = completedStages + } + + get completedStages() { + return this.completed + } + + complete(stageId: string) { + this.completed.push(stageId) + } +} diff --git a/packages/game/src/Stage.ts b/packages/game/src/Stage.ts new file mode 100644 index 0000000..e440488 --- /dev/null +++ b/packages/game/src/Stage.ts @@ -0,0 +1,37 @@ +import { IStage, ICommand, IFileExistenceChecker, ISession } from './types' +import { Condition } from './condition/Condition' + +export class Stage { + id: string + name: string + private condition: Condition + private requirements: string[] + + constructor({ id, name, condition, requirements }: IStage) { + this.id = id + this.name = name || '' + this.condition = new Condition(condition) + this.requirements = requirements || [] + } + + async satisfies( + command: ICommand, + checker: IFileExistenceChecker + ): Promise { + return this.condition.match(command) && this.condition.check(checker) + } + + private checkRequirements(session: ISession): boolean { + return this.requirements.every((req) => + session.completedStages.includes(req) + ) + } + + private finished(session: ISession): boolean { + return session.completedStages.includes(this.id) + } + + active(session: ISession): boolean { + return !this.finished(session) && this.checkRequirements(session) + } +} diff --git a/packages/game/src/condition/CommandMatcher.ts b/packages/game/src/condition/CommandMatcher.ts new file mode 100644 index 0000000..3f71bcd --- /dev/null +++ b/packages/game/src/condition/CommandMatcher.ts @@ -0,0 +1,13 @@ +import { ICommand } from '../types.js' +import { Matcher } from './Matcher.js' + +export class CommandMatcher implements Matcher { + private matcher: RegExp + constructor(command: string, flags?: string) { + this.matcher = new RegExp(command, flags) + } + + match(command: ICommand): boolean { + return !!command.command && this.matcher.test(command.command) + } +} diff --git a/packages/game/src/condition/Condition.ts b/packages/game/src/condition/Condition.ts new file mode 100644 index 0000000..82c6c57 --- /dev/null +++ b/packages/game/src/condition/Condition.ts @@ -0,0 +1,43 @@ +import { + ICommand, + ICondition, + IFileExistenceChecker, + FileExistenceInput, +} from '../types.js' +import { PwdMatcher } from './PwdMatcher.js' +import { CommandMatcher } from './CommandMatcher.js' +import { ErrorMatcher } from './ErrorMatcher.js' +import { Matcher } from './Matcher.js' +import { OutputMatcher } from './OutputMatcher.js' +import { OrMatcher } from './OrMatcher.js' +import { NotMatcher } from './NotMatcher.js' +import { checkFiles } from './FileMatcher.js' + +export class Condition { + private files: FileExistenceInput[] = [] + private matchers: Matcher[] = [] + + constructor({ command, output, error, pwd, or, not, files }: ICondition) { + if (command) this.matchers.push(new CommandMatcher(command)) + if (output) this.matchers.push(new OutputMatcher(output)) + if (pwd) this.matchers.push(new PwdMatcher(pwd)) + if (or) { + const conditions = or.map((condition) => new Condition(condition)) + this.matchers.push(new OrMatcher(conditions)) + } + if (not) this.matchers.push(new NotMatcher(new Condition(not))) + if (files) this.files = files + this.matchers.push(new ErrorMatcher(error)) // always check unexpected error + } + + match(command: ICommand): boolean { + return this.matchers.every((matcher) => { + return matcher.match(command) + }) + } + + async check(checker: IFileExistenceChecker): Promise { + if (this.matchers.length === 0) return true + return checkFiles(checker, this.files) + } +} diff --git a/packages/game/src/condition/ErrorMatcher.ts b/packages/game/src/condition/ErrorMatcher.ts new file mode 100644 index 0000000..6fd1d15 --- /dev/null +++ b/packages/game/src/condition/ErrorMatcher.ts @@ -0,0 +1,18 @@ +import { Matcher } from './Matcher.js' +import { ICommand } from '../types.js' + +export class ErrorMatcher implements Matcher { + private matcher?: RegExp + constructor(errorPattern?: string) { + if (errorPattern) { + this.matcher = new RegExp(errorPattern) + } + } + + match(command: ICommand): boolean { + if (!this.matcher) { + return !command.error + } + return this.matcher.test(command.error || '') + } +} diff --git a/packages/game/src/condition/FileMatcher.ts b/packages/game/src/condition/FileMatcher.ts new file mode 100644 index 0000000..c364e09 --- /dev/null +++ b/packages/game/src/condition/FileMatcher.ts @@ -0,0 +1,11 @@ +import { FileExistenceInput, IFileExistenceChecker } from '../types' + +export async function checkFiles( + checker: IFileExistenceChecker, + files: FileExistenceInput[] +) { + const existences = await Promise.all( + files.map((file) => checker.exists(file)) + ) + return existences.every((exists, index) => exists === files[index].exists) +} diff --git a/packages/game/src/condition/Matcher.ts b/packages/game/src/condition/Matcher.ts new file mode 100644 index 0000000..0af7c33 --- /dev/null +++ b/packages/game/src/condition/Matcher.ts @@ -0,0 +1,5 @@ +import { ICommand } from '../types.js' + +export interface Matcher { + match(command: ICommand): boolean +} diff --git a/packages/game/src/condition/NotMatcher.ts b/packages/game/src/condition/NotMatcher.ts new file mode 100644 index 0000000..1e9a846 --- /dev/null +++ b/packages/game/src/condition/NotMatcher.ts @@ -0,0 +1,10 @@ +import { ICommand } from '../types.js' +import { Matcher } from './Matcher.js' + +export class NotMatcher implements Matcher { + constructor(private condition: Matcher) {} + + match(command: ICommand): boolean { + return !this.condition.match(command) + } +} diff --git a/packages/game/src/condition/OrMatcher.ts b/packages/game/src/condition/OrMatcher.ts new file mode 100644 index 0000000..70c2091 --- /dev/null +++ b/packages/game/src/condition/OrMatcher.ts @@ -0,0 +1,10 @@ +import { ICommand } from '../types.js' +import { Matcher } from './Matcher.js' + +export class OrMatcher implements Matcher { + constructor(private conditions: Matcher[]) {} + + match(command: ICommand): boolean { + return this.conditions.some((condition) => condition.match(command)) + } +} diff --git a/packages/game/src/condition/OutputMatcher.ts b/packages/game/src/condition/OutputMatcher.ts new file mode 100644 index 0000000..89aa9a2 --- /dev/null +++ b/packages/game/src/condition/OutputMatcher.ts @@ -0,0 +1,13 @@ +import { ICommand } from '../types.js' +import { Matcher } from './Matcher.js' + +export class OutputMatcher implements Matcher { + private matcher: RegExp + constructor(pattern: string, args?: string) { + this.matcher = new RegExp(pattern, args) + } + + match(command: ICommand): boolean { + return !!command.output && this.matcher.test(command.output.trim()) + } +} diff --git a/packages/game/src/condition/PwdMatcher.ts b/packages/game/src/condition/PwdMatcher.ts new file mode 100644 index 0000000..781a7c5 --- /dev/null +++ b/packages/game/src/condition/PwdMatcher.ts @@ -0,0 +1,10 @@ +import { ICommand } from '../types.js' +import { Matcher } from './Matcher.js' + +export class PwdMatcher implements Matcher { + constructor(private pwd: string) {} + + match(command: ICommand): boolean { + return command.pwd === this.pwd + } +} diff --git a/packages/game/src/schema/condition.ts b/packages/game/src/schema/condition.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/game/src/schema/index.ts b/packages/game/src/schema/index.ts new file mode 100644 index 0000000..f21337b --- /dev/null +++ b/packages/game/src/schema/index.ts @@ -0,0 +1,54 @@ +import { z } from 'zod' + +export const requirementsSchema = z.object({ + requirements: z.array(z.string()), +}) + +export type IRequirements = z.infer + +export const fileInputSchema = z.object({ + path: z.string(), + type: z.enum(['file', 'folder']), +}) + +export type IFileInput = z.infer + +export const fileExistenceInputSchema = fileInputSchema.extend({ + exists: z.boolean(), +}) + +export type IFileExistenceInput = z.infer + +const baseConditionSchema = z.object({ + command: z.string().optional(), + output: z.string().optional(), + error: z.string().optional(), + pwd: z.string().optional(), + files: z.array(fileExistenceInputSchema).optional(), +}) + +export type ICondition = z.infer & { + or?: ICondition[] + not?: ICondition +} + +export const conditionSchema: z.ZodType = + baseConditionSchema.extend({ + or: z.lazy(() => z.array(conditionSchema)).optional(), + not: z.lazy(() => conditionSchema).optional(), + }) + +export const responseSchema = z.object({ + type: z.enum(['narrative', 'dialogue']), + content: z.string(), +}) + +export const stageSchema = z.object({ + id: z.string().regex(/^[a-zA-Z0-9_-]+$/), + name: z.string().optional(), + requirements: z.array(z.string()), + condition: conditionSchema, + response: responseSchema, +}) + +export type IStage = z.infer diff --git a/packages/game/src/schema/requirements.ts b/packages/game/src/schema/requirements.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/game/src/schema/stage.ts b/packages/game/src/schema/stage.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/game/src/types.ts b/packages/game/src/types.ts new file mode 100644 index 0000000..972200f --- /dev/null +++ b/packages/game/src/types.ts @@ -0,0 +1,45 @@ +export interface ICommand { + command?: string + output?: string + error?: string + pwd?: string +} + +export interface ICondition { + command?: string + output?: string + error?: string + pwd?: string + or?: ICondition[] + not?: ICondition + files?: FileExistenceInput[] +} + +export enum FileType { + FILE = 'file', + FOLDER = 'folder', +} + +export interface FileInput { + path: string + type: FileType +} + +export interface FileExistenceInput extends FileInput { + exists: boolean +} + +export interface IFileExistenceChecker { + exists(file: FileInput): Promise +} + +export interface IStage { + id: string + name?: string + requirements?: string[] + condition: ICondition +} + +export interface ISession { + completedStages: string[] +} diff --git a/packages/game/test/asyncCondition.test.ts b/packages/game/test/asyncCondition.test.ts new file mode 100644 index 0000000..8ad2630 --- /dev/null +++ b/packages/game/test/asyncCondition.test.ts @@ -0,0 +1,107 @@ +import { test, expect } from '@jest/globals' +import { IFileExistenceChecker, FileType, FileInput } from '../src/types.js' +import { checkFiles } from '../src/condition/FileMatcher.js' +import { Condition } from '../src/condition/Condition.js' + +export class MockFileChecker implements IFileExistenceChecker { + private files: FileInput[] = [ + { + path: '/home/user/hello.txt', + type: FileType.FILE, + }, + { + path: '/home/user/world.txt', + type: FileType.FILE, + }, + { + path: '/home/user/Downloads', + type: FileType.FOLDER, + }, + ] + + async exists(file: FileInput): Promise { + return this.files.some((f) => f.path === file.path && f.type === file.type) + } +} + +test('file matcher', async () => { + const checker = new MockFileChecker() + expect( + await checkFiles(checker, [ + { + path: '/home/user/hello.txt', + type: FileType.FILE, + exists: true, + }, + ]) + ).toBe(true) + expect( + await checkFiles(checker, [ + { + path: '/home/user/not-exists.txt', + type: FileType.FILE, + exists: true, + }, + ]) + ).toBe(false) + expect( + await checkFiles(checker, [ + { + path: '/home/user/not-exists.txt', + type: FileType.FILE, + exists: false, + }, + ]) + ).toBe(true) + expect( + await checkFiles(checker, [ + { + path: '/home/user/Downloads', + type: FileType.FOLDER, + exists: true, + }, + ]) + ).toBe(true) + expect( + await checkFiles(checker, [ + { + path: '/home/user/hello.txt', + type: FileType.FILE, + exists: true, + }, + { + path: '/home/user/Downloads', + type: FileType.FOLDER, + exists: true, + }, + ]) + ).toBe(true) + expect( + await checkFiles(checker, [ + { + path: '/home/user/hello.txt', + type: FileType.FILE, + exists: true, + }, + { + path: '/home/user/not-exist.txt', + type: FileType.FILE, + exists: true, + }, + ]) + ).toBe(false) +}) + +test('condition with file matcher', async () => { + const checker = new MockFileChecker() + const condition = new Condition({ + files: [ + { + path: '/home/user/hello.txt', + type: FileType.FILE, + exists: true, + }, + ], + }) + expect(await condition.check(checker)).toBe(true) +}) diff --git a/packages/game/test/condition.test.ts b/packages/game/test/condition.test.ts new file mode 100644 index 0000000..ad05f5c --- /dev/null +++ b/packages/game/test/condition.test.ts @@ -0,0 +1,94 @@ +import { test, expect } from '@jest/globals' +import { Condition } from '../src/condition/Condition' +import { OutputMatcher } from '../src/condition/OutputMatcher' +import { PwdMatcher } from '../src/condition/PwdMatcher' +import { CommandMatcher } from '../src/condition/CommandMatcher' +import { ErrorMatcher } from '../src/condition/ErrorMatcher.js' +import { conditionSchema } from '../src/schema' + +test('command match', () => { + const matcher = new CommandMatcher('^echo start$') + expect(matcher.match({ command: 'echo start' })).toBe(true) + expect(matcher.match({ command: 'echo start1' })).toBe(false) + // Currently not supported + expect(matcher.match({ command: 'echo "start"' })).toBe(false) +}) + +test('error matcher', () => { + const matcher = new ErrorMatcher() + expect(matcher.match({ error: '' })).toBe(true) + expect(matcher.match({ error: 'permission denied' })).toBe(false) +}) + +test('error matcher with specific error', () => { + const matcher = new ErrorMatcher('permission denied') + expect(matcher.match({ error: '' })).toBe(false) + expect(matcher.match({ error: 'file not found' })).toBe(false) + expect(matcher.match({ error: 'cannot open files: permission denied' })).toBe( + true + ) +}) + +test('pwd matcher', () => { + const matcher = new PwdMatcher('/home/user') + expect(matcher.match({ pwd: '/home/user' })).toBe(true) + expect(matcher.match({ pwd: '/home/user1' })).toBe(false) + expect(matcher.match({ pwd: '/home/user/anotherpath' })).toBe(false) + expect(matcher.match({ pwd: '/var/home/user' })).toBe(false) +}) + +test('output matcher', () => { + const matcher = new OutputMatcher('^start$') + expect(matcher.match({ output: 'start' })).toBe(true) + expect(matcher.match({ output: 'start1' })).toBe(false) + expect(matcher.match({ output: 'start\n' })).toBe(true) +}) + +test('condition match command', () => { + const condition = new Condition({ + command: '^echo start$', + }) + + expect( + condition.match({ + command: 'echo start', + }) + ).toBe(true) + expect( + condition.match({ + command: 'echo hello', + }) + ).toBe(false) + expect( + condition.match({ command: 'echo start', error: 'permission denied' }) + ).toBe(false) +}) + +test('condition with OR', () => { + const condition = new Condition({ + or: [{ command: '^echo start$' }, { command: '^echo hello$' }], + }) + expect(condition.match({ command: 'echo start' })).toBe(true) + expect(condition.match({ command: 'echo hello' })).toBe(true) + expect(condition.match({ command: 'echo start1' })).toBe(false) +}) + +test('condition with NOT', () => { + const condition = new Condition({ + not: { command: '^echo start$' }, + }) + + expect(condition.match({ command: 'echo start' })).toBe(false) + expect(condition.match({ command: 'echo hello' })).toBe(true) +}) + +it('should parse condition', () => { + const condition = { + command: 'echo start', + output: 'start', + not: { + pwd: '/home/user', + }, + } + expect(conditionSchema.safeParse(condition).error).toBeUndefined() +}) diff --git a/packages/game/test/stage.test.ts b/packages/game/test/stage.test.ts new file mode 100644 index 0000000..de81ade --- /dev/null +++ b/packages/game/test/stage.test.ts @@ -0,0 +1,88 @@ +import { test, expect } from '@jest/globals' +import { Stage } from '../src/Stage' +import { FileType } from '../src/types' +import { MockFileChecker } from './asyncCondition.test' +import { Session } from '../src/Session' + +const checker = new MockFileChecker() + +test('check stage condition', async () => { + const stage = new Stage({ + id: 'stage1', + name: 'List the files using `ls`', + condition: { + command: 'ls', + pwd: '/home/user', + }, + }) + expect( + await stage.satisfies( + { + command: 'ls', + pwd: '/home/user', + }, + checker + ) + ).toBe(true) + expect( + await stage.satisfies( + { + command: 'ls', + pwd: '/home/user/Downloads', + }, + checker + ) + ).toBe(false) +}) + +test('check stage with async condition truthy', async () => { + const stage = new Stage({ + id: 'stage1', + name: 'Create file', + condition: { + files: [ + { + path: '/home/user/hello.txt', + type: FileType.FILE, + exists: true, + }, + ], + }, + }) + expect(await stage.satisfies({}, checker)).toBe(true) +}) + +test('check stage with async condition falsy', async () => { + const stage = new Stage({ + id: 'stage1', + name: 'Create file', + condition: { + files: [ + { + path: '/home/user/not-exists.txt', + type: FileType.FILE, + exists: true, + }, + ], + }, + }) + expect(await stage.satisfies({}, checker)).toBe(false) +}) + +test('stage active checks', () => { + const session = new Session({ + completedStages: ['stage1'], + }) + const stage3 = new Stage({ + id: 'stage3', + requirements: ['stage1', 'stage2'], + condition: {}, + }) + expect(stage3.active(session)).toBe(false) + + session.complete('stage2') + expect(stage3.active(session)).toBe(true) + + session.complete('stage3') + expect(stage3.active(session)).toBe(false) +}) diff --git a/packages/game/tsconfig.json b/packages/game/tsconfig.json new file mode 100644 index 0000000..49e05ce --- /dev/null +++ b/packages/game/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src"] +} diff --git a/yarn.lock b/yarn.lock index 1ecf491..44dfc84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -35,7 +35,7 @@ "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9", "@babel/core@^7.24.0": version "7.24.0" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.0.tgz#56cbda6b185ae9d9bed369816a8f4423c5f2ff1b" integrity sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw== dependencies: "@ampproject/remapping" "^2.2.0" @@ -178,12 +178,12 @@ "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.24.0" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz#945681931a52f15ce879fd5b86ce2dae6d3d7f2a" integrity sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w== "@babel/helper-remap-async-to-generator@^7.22.20": version "7.22.20" - resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" @@ -192,7 +192,7 @@ "@babel/helper-replace-supers@^7.22.20": version "7.22.20" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793" integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw== dependencies: "@babel/helper-environment-visitor" "^7.22.20" @@ -246,7 +246,7 @@ "@babel/helpers@^7.24.0": version "7.24.0" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.0.tgz#a3dd462b41769c95db8091e49cfe019389a9409b" integrity sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA== dependencies: "@babel/template" "^7.24.0" @@ -264,19 +264,19 @@ "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.0": version "7.24.0" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.0.tgz#26a3d1ff49031c53a97d03b604375f028746a9ac" integrity sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3": version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz#5cd1c87ba9380d0afb78469292c954fee5d2411a" integrity sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.23.3": version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz#f6652bb16b94f8f9c20c50941e16e9756898dc5d" integrity sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -285,7 +285,7 @@ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.23.7": version "7.23.7" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz#516462a95d10a9618f197d39ad291a9b47ae1d7b" integrity sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw== dependencies: "@babel/helper-environment-visitor" "^7.22.20" @@ -293,7 +293,7 @@ "@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": version "7.21.0-placeholder-for-preset-env.2" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== "@babel/plugin-syntax-async-generators@^7.8.4": @@ -987,7 +987,7 @@ "@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.24.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.24.0" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w== dependencies: "@babel/helper-string-parser" "^7.23.4" @@ -3573,7 +3573,7 @@ cookie@0.6.0: core-js-compat@^3.31.0, core-js-compat@^3.34.0: version "3.36.0" - resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.0.tgz" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.36.0.tgz#087679119bc2fdbdefad0d45d8e5d307d45ba190" integrity sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw== dependencies: browserslist "^4.22.3" @@ -9788,7 +9788,7 @@ yocto-queue@^0.1.0: zod@^3.23.8: version "3.23.8" - resolved "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== zod@^3.24.2: