Merge branch 'game' into refactor-trpc

This commit is contained in:
Justin Lin committed 2025-04-18 14:27:41 +08:00
commit 57d8f24930
23 files changed
+616 -13

No files matched your search

+8
View File
@@ -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: [
{
+12
View File
@@ -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"
}
}
+17
View File
@@ -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)
}
}
+37
View File
@@ -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<boolean> {
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)
}
}
@@ -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)
}
}
+43
View File
@@ -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<boolean> {
if (this.matchers.length === 0) return true
return checkFiles(checker, this.files)
}
}
@@ -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 || '')
}
}
@@ -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)
}
+5
View File
@@ -0,0 +1,5 @@
import { ICommand } from '../types.js'
export interface Matcher {
match(command: ICommand): boolean
}
+10
View File
@@ -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)
}
}
+10
View File
@@ -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))
}
}
@@ -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())
}
}
+10
View File
@@ -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
}
}
Whitespace-only changes.
+54
View File
@@ -0,0 +1,54 @@
import { z } from 'zod'
export const requirementsSchema = z.object({
requirements: z.array(z.string()),
})
export type IRequirements = z.infer<typeof requirementsSchema>
export const fileInputSchema = z.object({
path: z.string(),
type: z.enum(['file', 'folder']),
})
export type IFileInput = z.infer<typeof fileInputSchema>
export const fileExistenceInputSchema = fileInputSchema.extend({
exists: z.boolean(),
})
export type IFileExistenceInput = z.infer<typeof fileExistenceInputSchema>
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<typeof baseConditionSchema> & {
or?: ICondition[]
not?: ICondition
}
export const conditionSchema: z.ZodType<ICondition> =
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<typeof stageSchema>
Whitespace-only changes.
View File
Whitespace-only changes.
+45
View File
@@ -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<boolean>
}
export interface IStage {
id: string
name?: string
requirements?: string[]
condition: ICondition
}
export interface ISession {
completedStages: string[]
}
+107
View File
@@ -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<boolean> {
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)
})
+94
View File
@@ -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()
})
+88
View File
@@ -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)
})
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"]
}
+13 -13
View File
@@ -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: