mineflayer 4.20.1 → 4.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -52,7 +52,7 @@ jobs:
52
52
  - name: Setup Java JDK
53
53
  uses: actions/setup-java@v1.4.3
54
54
  with:
55
- java-version: 17
55
+ java-version: 21
56
56
  java-package: jre
57
57
  - name: Install Dependencies
58
58
  run: npm install
package/.gitpod.yml CHANGED
@@ -1,2 +1,2 @@
1
1
  tasks:
2
- - command: npm install
2
+ - command: npm install && sdk install java
package/README.md CHANGED
@@ -17,7 +17,7 @@ First time using Node.js? You may want to start with the [tutorial](tutorial.md)
17
17
 
18
18
  ## Features
19
19
 
20
- * Supports Minecraft 1.8 to 1.20.4 (1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19 and 1.20)
20
+ * Supports Minecraft 1.8 to 1.20.5 (1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19 and 1.20 upto 1.20.6)
21
21
  * Entity knowledge and tracking.
22
22
  * Block knowledge. You can query the world around you. Milliseconds to find any block.
23
23
  * Physics and movement - handle all bounding boxes
@@ -43,7 +43,7 @@ npm install mineflayer
43
43
 
44
44
  To update mineflayer (or any Node.js) package and its dependencies, use
45
45
  ```bash
46
- npm update --depth 9999
46
+ npm update
47
47
  ```
48
48
 
49
49
  ## Documentation
package/docs/README.md CHANGED
@@ -17,7 +17,7 @@ First time using Node.js? You may want to start with the [tutorial](tutorial.md)
17
17
 
18
18
  ## Features
19
19
 
20
- * Supports Minecraft 1.8 to 1.20.4 (1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19 and 1.20)
20
+ * Supports Minecraft 1.8 to 1.20.5 (1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19 and 1.20 upto 1.20.6)
21
21
  * Entity knowledge and tracking.
22
22
  * Block knowledge. You can query the world around you. Milliseconds to find any block.
23
23
  * Physics and movement - handle all bounding boxes
@@ -43,7 +43,7 @@ npm install mineflayer
43
43
 
44
44
  To update mineflayer (or any Node.js) package and its dependencies, use
45
45
  ```bash
46
- npm update --depth 9999
46
+ npm update
47
47
  ```
48
48
 
49
49
  ## Documentation
package/docs/api.md CHANGED
@@ -808,7 +808,7 @@ Create and return an instance of the class bot.
808
808
  * checkTimeoutInterval : default to `30*1000` (30s), check if keepalive received at that period, disconnect otherwise.
809
809
  * loadInternalPlugins : defaults to true
810
810
  * storageBuilder : an optional function, takes as argument version and worldName and return an instance of something with the same API as prismarine-provider-anvil. Will be used to save the world.
811
- * client : an instance of node-minecraft-protocol, if not specified, mineflayer makes it's own client. This can be used to enable using mineflayer through a proxy of many clients or a vanilla client and a mineflayer client.
811
+ * client : an instance of node-minecraft-protocol, if not specified, mineflayer makes its own client. This can be used to enable using mineflayer through a proxy of many clients or a vanilla client and a mineflayer client.
812
812
  * brand : the brand name for the client to use. Defaults to vanilla. Can be used to simulate custom clients for servers that require it.
813
813
  * respawn : when set to false disables bot from automatically respawning, defaults to true.
814
814
  * plugins : object : defaults to {}
@@ -974,7 +974,7 @@ Default true, whether or not you receive color codes in chats from the server.
974
974
 
975
975
  #### bot.settings.viewDistance
976
976
 
977
- Can be a string listed below or a postive number.
977
+ Can be a string listed below or a positive number.
978
978
  Choices:
979
979
  * `far` (default)
980
980
  * `normal`
@@ -1035,7 +1035,7 @@ saturation of 5.0. Eating food increases the saturation as well as the food bar.
1035
1035
 
1036
1036
  #### bot.oxygenLevel
1037
1037
 
1038
- Number in the range [0, 20] respresenting the number of water-icons known as oxygen level.
1038
+ Number in the range [0, 20] representing the number of water-icons known as oxygen level.
1039
1039
 
1040
1040
  #### bot.physics
1041
1041
 
@@ -1554,7 +1554,7 @@ Fires when a particle is created
1554
1554
 
1555
1555
  #### bot.blockAt(point, extraInfos=true)
1556
1556
 
1557
- Returns the block at `point` or `null` if that point is not loaded. If `extraInfos` set to true, also returns informations about signs, paintings and block entities (slower).
1557
+ Returns the block at `point` or `null` if that point is not loaded. If `extraInfos` set to true, also returns information about signs, paintings and block entities (slower).
1558
1558
  See `Block`.
1559
1559
 
1560
1560
  #### bot.waitForChunksToLoad()
@@ -1596,8 +1596,8 @@ Finds the closest blocks from the given point.
1596
1596
  - `point` - The start position of the search (center). Default is the bot position.
1597
1597
  - `matching` - A function that returns true if the given block is a match. Also supports this value being a block id or array of block ids.
1598
1598
  - `useExtraInfo` - To preserve backward compatibility can result in two behavior depending on the type
1599
- - **boolean** - Provide your `matching` function more data - noticeably slower aproach
1600
- - **function** - Creates two stage maching, if block passes `matching` function it is passed further to `useExtraInfo` with additional info
1599
+ - **boolean** - Provide your `matching` function more data - noticeably slower approach
1600
+ - **function** - Creates two stage matching, if block passes `matching` function it is passed further to `useExtraInfo` with additional info
1601
1601
  - `maxDistance` - The furthest distance for the search, defaults to 16.
1602
1602
  - `count` - Number of blocks to find before returning the search. Default to 1. Can return less if not enough blocks are found exploring the whole area.
1603
1603
 
@@ -1627,7 +1627,7 @@ with `metadata`.
1627
1627
 
1628
1628
  #### bot.recipesAll(itemType, metadata, craftingTable)
1629
1629
 
1630
- The same as bot.recipesFor except that it does not check wether the bot has enough materials for the recipe.
1630
+ The same as bot.recipesFor except that it does not check whether the bot has enough materials for the recipe.
1631
1631
 
1632
1632
  #### bot.nearestEntity(match = (entity) => { return true })
1633
1633
 
@@ -1635,7 +1635,7 @@ Return the nearest entity to the bot, matching the function (default to all enti
1635
1635
 
1636
1636
  Example:
1637
1637
  ```js
1638
- const cow = bot.nearestEntity(entity => entity.name.toLowerCase() === 'cow') // we use .toLowercase() because in 1.8 cow was capitalized, for newer versions that can be ommitted
1638
+ const cow = bot.nearestEntity(entity => entity.name.toLowerCase() === 'cow') // we use .toLowercase() because in 1.8 cow was capitalized, for newer versions that can be omitted
1639
1639
  ```
1640
1640
 
1641
1641
  ### Methods
@@ -1657,7 +1657,7 @@ Requests chat completion from the server.
1657
1657
  * `str` - String to complete.
1658
1658
  * `assumeCommand` - Field sent to server, defaults to false.
1659
1659
  * `sendBlockInSight` - Field sent to server, defaults to true. Set this option to false if you want more performance.
1660
- * `timeout` - Timeout in milliseconds, after which the function will return an ampty array, defaults to 5000.
1660
+ * `timeout` - Timeout in milliseconds, after which the function will return an empty array, defaults to 5000.
1661
1661
 
1662
1662
  #### bot.chat(message)
1663
1663
 
@@ -1683,11 +1683,11 @@ Adds a regex pattern to the bot's chat matching. Useful for bukkit servers where
1683
1683
  make an event that is called every time the pattern is matched to a message,
1684
1684
  the event will be called `"chat:name"`, with name being the name passed
1685
1685
  * `name` - the name used to listen for the event
1686
- * `pattern` - regular expression to match to messages recieved
1686
+ * `pattern` - regular expression to match to messages received
1687
1687
  * `chatPatternOptions` - object
1688
1688
  * `repeat` - defaults to true, whether to listen for this event after the first match
1689
1689
  * `parse` - instead of returning the actual message that was matched, return the capture groups from the regex
1690
- * `deprecated` - (**unstable**) used by bot.chatAddPattern to keep compatability, likely to be removed
1690
+ * `deprecated` - (**unstable**) used by bot.chatAddPattern to keep compatibility, likely to be removed
1691
1691
 
1692
1692
  returns a number which can be used with bot.removeChatPattern() to only delete this pattern
1693
1693
 
@@ -1695,10 +1695,10 @@ returns a number which can be used with bot.removeChatPattern() to only delete t
1695
1695
 
1696
1696
  #### bot.addChatPatternSet(name, patterns, chatPatternOptions)
1697
1697
 
1698
- make an event that is called every time all patterns havee been matched to messages,
1698
+ make an event that is called every time all patterns have been matched to messages,
1699
1699
  the event will be called `"chat:name"`, with name being the name passed
1700
1700
  * `name` - the name used to listen for the event
1701
- * `patterns` - array of regular expression to match to messages recieved
1701
+ * `patterns` - array of regular expression to match to messages received
1702
1702
  * `chatPatternOptions` - object
1703
1703
  * `repeat` - defaults to true, whether to listen for this event after the first match
1704
1704
  * `parse` - instead of returning the actual message that was matched, return the capture groups from the regex
@@ -1892,7 +1892,7 @@ dig any other blocks until the block has been broken, or you call
1892
1892
  `bot.stopDigging()`.
1893
1893
 
1894
1894
  * `block` - the block to start digging into
1895
- * `forceLook` - (optional) if true, look at the block and start mining instantly. If false, the bot will slowly turn to the block to mine. Additionally, this can be assigned to 'ignore' to prevent the bot from moving it's head at all. Also, this can be assigned to 'raycast' to raycast from the bots head to place where the bot is looking.
1895
+ * `forceLook` - (optional) if true, look at the block and start mining instantly. If false, the bot will slowly turn to the block to mine. Additionally, this can be assigned to 'ignore' to prevent the bot from moving its head at all. Also, this can be assigned to 'raycast' to raycast from the bots head to place where the bot is looking.
1896
1896
  * `digFace` - (optional) Default is 'auto' looks at the center of the block and mines the top face. Can also be a vec3 vector
1897
1897
  of the face the bot should be looking at when digging the block. For example: ```vec3(0, 1, 0)``` when mining the top. Can also be 'raycast' raycast checks if there is a face visible by the bot and mines that face. Useful for servers with anti cheat.
1898
1898
 
@@ -2249,4 +2249,4 @@ Note that while flying, `bot.entity.velocity` will not be accurate.
2249
2249
 
2250
2250
  #### bot.creative.stopFlying()
2251
2251
 
2252
- Restores `bot.physics.gravity` to it's original value.
2252
+ Restores `bot.physics.gravity` to its original value.
package/docs/history.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## 4.22.0
2
+ * [Remove debug logging (#3478)](https://github.com/PrismarineJS/mineflayer/commit/eb29d350ede0590fce17e04bf21071807a87e3a1) (thanks @extremeheat)
3
+
4
+ ## 4.21.0
5
+ * [1.20.6 (#3412)](https://github.com/PrismarineJS/mineflayer/commit/44fad41c18be5024564e24e1cdee624d35d4d387) (thanks @extremeheat)
6
+ * [Update README.md (#3420)](https://github.com/PrismarineJS/mineflayer/commit/1c2a5c0fa78f74a63fabd7edde85c4a74db32dee) (thanks @SilkePilon)
7
+ * [types: add pitchSpeed as an option in typings (#3446)](https://github.com/PrismarineJS/mineflayer/commit/05b48ad0dad4cf64a1c11660bac256d7b4015841) (thanks @GenerelSchwerz)
8
+ * [Fixed a bug with not closing the window when changing the subserver (#3424)](https://github.com/PrismarineJS/mineflayer/commit/d00c386cfe51cefc361c0ff4d30b100aee9f114a) (thanks @DenisKvak1)
9
+ * [Bump @types/node from 20.14.14 to 22.1.0 (#3431)](https://github.com/PrismarineJS/mineflayer/commit/1d461616b514969fdece38e49bfbec747ab8d76a) (thanks @dependabot[bot])
10
+ * [Fix updateSlot event type (#3425)](https://github.com/PrismarineJS/mineflayer/commit/5d39db26a6ab17baac38b68af8ccd3eeb4af3def) (thanks @DenisKvak1)
11
+ * [Player hitbox fixes (#3382)](https://github.com/PrismarineJS/mineflayer/commit/78b4eccb4572a821b11c3124b7a593f3b91f1180) (thanks @AreaDenial)
12
+ * [Typo fixes (#3418)](https://github.com/PrismarineJS/mineflayer/commit/ef042a242ca9f5fc5820fe4dc2e1d997ef1db202) (thanks @kgurchiek)
13
+ * [Added support for 1.18+ edit book packet #3204 (#3373)](https://github.com/PrismarineJS/mineflayer/commit/eb9982aa04973b0086aac68a2847005d77f01a3d) (thanks @unlimitedcoder2)
14
+ * [Fix typos (#3381)](https://github.com/PrismarineJS/mineflayer/commit/d581ea7cee4d5b7df9606f671656bb0be0fdbf91) (thanks @data-miner00)
15
+ * [Fix typescript types syntax for setCommandBlock (#3366)](https://github.com/PrismarineJS/mineflayer/commit/315cdfc4b1fc2760e4a8a36feb718626a66d5056) (thanks @undefined)
16
+ * [Remove invalid sign check (#3328)](https://github.com/PrismarineJS/mineflayer/commit/ec76468c8ac4c6232bad3c2b66d4160f95f58396) (thanks @zardoy)
17
+ * [refactor: simplifying the code of blockAtCursor (#3337)](https://github.com/PrismarineJS/mineflayer/commit/dc70f932ac9aaab6e6cdb15057b409b15c3232dd) (thanks @SnowRunescape)
18
+ * [Updated setCommandBlock's 3rd argument (#3356)](https://github.com/PrismarineJS/mineflayer/commit/04ad6db404f0da779004b3ddd0e049bf2c6be0a3) (thanks @FlooferLand)
19
+ * [Added the serverBrand property to index.d.ts (#3355)](https://github.com/PrismarineJS/mineflayer/commit/0bb2707d2f6d0d64a467d4e0d6ddc52adf526127) (thanks @Khaogamermain01)
20
+
1
21
  ## 4.20.1
2
22
  * [Add bossBarCreated event in index.d.ts (#3340)](https://github.com/PrismarineJS/mineflayer/commit/8299288526cd7ff24bcd87511814221f8ad62507) (thanks @gguio)
3
23
  * [Update scoreboard.js (#3318)](https://github.com/PrismarineJS/mineflayer/commit/195b3cbd70a110080af9b77a4659991c5d9e484a) (thanks @vicdum)
package/docs/tutorial.md CHANGED
@@ -453,7 +453,7 @@ The function could also be called when an error occurs.
453
453
 
454
454
  Below is an example of a bot that will craft oak logs into oak planks and then into sticks.
455
455
 
456
- Incorect approach ❌:
456
+ Incorrect approach ❌:
457
457
 
458
458
  ```js
459
459
  function craft (bot) {
@@ -1,4 +1,4 @@
1
- # Menual Chest Confirm
1
+ # Manual Chest Confirm
2
2
 
3
3
  This code snippet will tell the bot not to wait for chest confirmations that some spigot plugins will not send
4
4
 
@@ -10,7 +10,7 @@
10
10
  * may flood the chat, feel free to check them out for other purposes though.
11
11
  *
12
12
  * This bot also replies to some specific chat messages so you can ask him
13
- * a few informations while you are in game.
13
+ * a few information while you are in game.
14
14
  */
15
15
  const mineflayer = require('mineflayer')
16
16
  const { Vec3 } = require('vec3')
package/examples/echo.js CHANGED
@@ -9,7 +9,7 @@
9
9
  const mineflayer = require('mineflayer')
10
10
 
11
11
  if (process.argv.length < 4 || process.argv.length > 6) {
12
- console.log('Usage : node echo.js <host> <port> [<name>] [<password>]')
12
+ console.log('Usage : node echo.js <host> <port> [<name>] [online?]')
13
13
  process.exit(1)
14
14
  }
15
15
 
@@ -17,10 +17,14 @@ const bot = mineflayer.createBot({
17
17
  host: process.argv[2],
18
18
  port: parseInt(process.argv[3]),
19
19
  username: process.argv[4] ? process.argv[4] : 'echo',
20
- password: process.argv[5]
20
+ auth: process.argv[5] ? 'microsoft' : 'offline'
21
21
  })
22
22
 
23
23
  bot.on('chat', (username, message) => {
24
24
  if (username === bot.username) return
25
25
  bot.chat(message)
26
26
  })
27
+
28
+ bot.on('kicked', (reason) => {
29
+ console.log('I was kicked from the server: ' + reason)
30
+ })
@@ -1,5 +1,5 @@
1
1
  /*
2
- * This script will automaticly look at the closest entity.
2
+ * This script will automatically look at the closest entity.
3
3
  * It checks for a near entity every tick.
4
4
  */
5
5
  const mineflayer = require('mineflayer')
@@ -30,7 +30,7 @@ bot.on('spawn', function () {
30
30
 
31
31
  // Auto attack every 1,2 secs with bow
32
32
  // With crossbow attack when crossbow is charget (enchant 3 = 0.5s)
33
- // ['snowball', 'ender_pearl', 'egg', 'splash_potion'] auto attack every 0,1 sec, no recomended use autoAttack for these items, instead use "bot.hawkEye.oneShot(target, weapon)"
33
+ // ['snowball', 'ender_pearl', 'egg', 'splash_potion'] auto attack every 0,1 sec, no recommended use autoAttack for these items, instead use "bot.hawkEye.oneShot(target, weapon)"
34
34
 
35
35
  bot.hawkEye.autoAttack(target, weapon)
36
36
  // If you force stop attack use:
@@ -51,7 +51,7 @@ bot.on('chat', async (ign, msg) => {
51
51
  const { x: aboveX, y: aboveY, z: aboveZ } = block.position.offset(0, 1, 0)
52
52
  const blockBoundingBox = new AABB(aboveX, aboveY, aboveZ, aboveX + 1, aboveY + 2, aboveZ + 1)
53
53
  const entityAABBs = Object.values(bot.entities).map(entity => {
54
- // taken from taken from https://github.com/PrismarineJS/prismarine-physics/blob/d145e54a4bb8604300258badd7563f59f2101922/index.js#L92
54
+ // taken from https://github.com/PrismarineJS/prismarine-physics/blob/d145e54a4bb8604300258badd7563f59f2101922/index.js#L92
55
55
  const w = entity.height / 2
56
56
  const { x, y, z } = entity.position
57
57
  return new AABB(-w, 0, -w, w, entity.height, w).offset(x, y, z)
@@ -10,7 +10,7 @@
10
10
  # may flood the chat, feel free to check them out for other purposes though.
11
11
  #
12
12
  # This bot also replies to some specific chat messages so you can ask him
13
- # a few informations while you are in game.
13
+ # a few information while you are in game.
14
14
  # ===========================================================================
15
15
  import sys, re
16
16
  from javascript import require, On, Once, console
package/index.d.ts CHANGED
@@ -165,6 +165,13 @@ export interface BotEvents {
165
165
  particle: (particle: Particle) => Promise<void> | void
166
166
  }
167
167
 
168
+ export interface CommandBlockOptions {
169
+ mode: number,
170
+ trackOutput: boolean,
171
+ conditional: boolean,
172
+ alwaysActive: boolean
173
+ }
174
+
168
175
  export interface Bot extends TypedEmitter<BotEvents> {
169
176
  username: string
170
177
  protocolVersion: string
@@ -379,7 +386,9 @@ export interface Bot extends TypedEmitter<BotEvents> {
379
386
  times?: number
380
387
  ) => Promise<void>
381
388
 
382
- setCommandBlock: (pos: Vec3, command: string, trackOutput: boolean) => void
389
+
390
+
391
+ setCommandBlock: (pos: Vec3, command: string, options: CommandBlockOptions) => void
383
392
 
384
393
  clickWindow: (
385
394
  slot: number,
@@ -457,6 +466,7 @@ export interface GameState {
457
466
  dimension: Dimension
458
467
  difficulty: Difficulty
459
468
  maxPlayers: number
469
+ serverBrand: string
460
470
  }
461
471
 
462
472
  export type LevelType =
@@ -531,6 +541,7 @@ export interface PhysicsOptions {
531
541
  playerHeight: number
532
542
  jumpSpeed: number
533
543
  yawSpeed: number
544
+ pitchSpeed: number
534
545
  sprintSpeed: number
535
546
  maxGroundSpeedSoulSand: number
536
547
  maxGroundSpeedWater: number
@@ -639,7 +650,7 @@ export class Painting {
639
650
  interface StorageEvents {
640
651
  open: () => void
641
652
  close: () => void
642
- updateSlot: (oldItem: Item | null, newItem: Item | null) => void
653
+ updateSlot: (slot: number, oldItem: Item | null, newItem: Item | null) => void
643
654
  }
644
655
 
645
656
  interface FurnaceEvents extends StorageEvents {
package/lib/loader.js CHANGED
@@ -95,6 +95,7 @@ function createBot (options = {}) {
95
95
  }).map(key => options.plugins[key])
96
96
  bot.loadPlugins([...internalPlugins, ...externalPlugins])
97
97
 
98
+ options.validateChannelProtocol = false
98
99
  bot._client = bot._client ?? mc.createClient(options)
99
100
  bot._client.on('connect', () => {
100
101
  bot.emit('connect')
package/lib/particle.js CHANGED
@@ -5,8 +5,8 @@ module.exports = loader
5
5
  function loader (registry) {
6
6
  class Particle {
7
7
  constructor (id, position, offset, count = 1, movementSpeed = 0, longDistanceRender = false) {
8
- Object.assign(this, registry.particles[id])
9
8
  this.id = id
9
+ Object.assign(this, registry.particles[id] || registry.particlesByName[id])
10
10
  this.position = position
11
11
  this.offset = offset
12
12
  this.count = count
@@ -15,14 +15,26 @@ function loader (registry) {
15
15
  }
16
16
 
17
17
  static fromNetwork (packet) {
18
- return new Particle(
19
- packet.particleId,
20
- new Vec3(packet.x, packet.y, packet.z),
21
- new Vec3(packet.offsetX, packet.offsetY, packet.offsetZ),
22
- packet.particles,
23
- packet.particleData,
24
- packet.longDistance
25
- )
18
+ if (registry.supportFeature('updatedParticlesPacket')) {
19
+ // TODO: We add extra data that's inside packet.particle.data that varies by the particle's .type
20
+ return new Particle(
21
+ packet.particle.type,
22
+ new Vec3(packet.x, packet.y, packet.z),
23
+ new Vec3(packet.offsetX, packet.offsetY, packet.offsetZ),
24
+ packet.amount,
25
+ packet.velocityOffset,
26
+ packet.longDistance
27
+ )
28
+ } else {
29
+ return new Particle(
30
+ packet.particleId,
31
+ new Vec3(packet.x, packet.y, packet.z),
32
+ new Vec3(packet.offsetX, packet.offsetY, packet.offsetZ),
33
+ packet.particles,
34
+ packet.particleData,
35
+ packet.longDistance
36
+ )
37
+ }
26
38
  }
27
39
  }
28
40
 
@@ -221,7 +221,7 @@ function inject (bot, { version, storageBuilder, hideErrors }) {
221
221
  // if passed in block is within line of sight to the bot, returns true
222
222
  // also works on anything with a position value
223
223
  function canSeeBlock (block) {
224
- const headPos = bot.entity.position.offset(0, bot.entity.height, 0)
224
+ const headPos = bot.entity.position.offset(0, bot.entity.eyeHeight, 0)
225
225
  const range = headPos.distanceTo(block.position)
226
226
  const dir = block.position.offset(0.5, 0.5, 0.5).minus(headPos)
227
227
  const match = (inputBlock, iter) => {
@@ -455,9 +455,10 @@ function inject (bot, { version, storageBuilder, hideErrors }) {
455
455
  bot.emit('error', new Error('too many lines for sign text'))
456
456
  return
457
457
  }
458
+
458
459
  for (let i = 0; i < lines.length; ++i) {
459
- if (lines[i].length > 15) {
460
- bot.emit('error', new Error('signs have max line length 15'))
460
+ if (lines[i].length > 45) {
461
+ bot.emit('error', new Error('Signs have a maximum of 45 characters per line'))
461
462
  return
462
463
  }
463
464
  }
@@ -530,6 +531,9 @@ function inject (bot, { version, storageBuilder, hideErrors }) {
530
531
  if (bot.supportFeature('dimensionIsAnInt')) {
531
532
  dimension = packet.dimension
532
533
  worldName = dimensionToFolderName(dimension)
534
+ } else if (bot.supportFeature('spawnRespawnWorldDataField')) { // 1.20.5+
535
+ dimension = packet.worldState.dimension
536
+ worldName = packet.worldState.name
533
537
  } else {
534
538
  dimension = packet.dimension
535
539
  worldName = /^minecraft:.+/.test(packet.worldName) ? packet.worldName : `minecraft:${packet.worldName}`
@@ -541,6 +545,11 @@ function inject (bot, { version, storageBuilder, hideErrors }) {
541
545
  if (bot.supportFeature('dimensionIsAnInt')) { // <=1.15.2
542
546
  if (dimension === packet.dimension) return
543
547
  dimension = packet.dimension
548
+ } else if (bot.supportFeature('spawnRespawnWorldDataField')) { // 1.20.5+
549
+ if (dimension === packet.worldState.dimension) return
550
+ if (worldName === packet.worldState.name && packet.copyMetadata === true) return // don't unload chunks if in same world and metaData is true
551
+ dimension = packet.worldState.dimension
552
+ worldName = packet.worldState.name
544
553
  } else { // >= 1.15.2
545
554
  if (dimension === packet.dimension) return
546
555
  if (worldName === packet.worldName && packet.copyMetadata === true) return // don't unload chunks if in same world and metaData is true
@@ -10,17 +10,27 @@ function inject (bot) {
10
10
  if (bot.supportFeature('editBookIsPluginChannel')) {
11
11
  bot._client.registerChannel('MC|BEdit', 'slot')
12
12
  bot._client.registerChannel('MC|BSign', 'slot')
13
- editBook = (book, signing = false) => {
13
+ editBook = (book, pages, title, slot, signing = false) => {
14
14
  if (signing) bot._client.writeChannel('MC|BSign', Item.toNotch(book))
15
15
  else bot._client.writeChannel('MC|BEdit', Item.toNotch(book))
16
16
  }
17
17
  } else if (bot.supportFeature('hasEditBookPacket')) {
18
- editBook = (book, signing = false, hand = 0) => {
19
- bot._client.write('edit_book', {
20
- new_book: Item.toNotch(book),
21
- signing,
22
- hand
23
- })
18
+ if (bot.supportFeature('editBookPacketUsesNbt')) { // 1.13 - 1.17
19
+ editBook = (book, pages, title, slot, signing = false, hand = 0) => {
20
+ bot._client.write('edit_book', {
21
+ hand: slot,
22
+ pages,
23
+ title
24
+ })
25
+ }
26
+ } else { // 1.18+
27
+ editBook = (book, pages, title, slot, signing = false, hand = 0) => {
28
+ bot._client.write('edit_book', {
29
+ new_book: Item.toNotch(book),
30
+ signing,
31
+ hand
32
+ })
33
+ }
24
34
  }
25
35
  }
26
36
 
@@ -38,7 +48,7 @@ function inject (bot) {
38
48
  bot.setQuickBarSlot(moveToQuickBar ? 0 : slot - 36)
39
49
 
40
50
  const modifiedBook = await modifyBook(moveToQuickBar ? 36 : slot, pages, author, title, signing)
41
- editBook(modifiedBook, signing)
51
+ editBook(modifiedBook, pages, title, moveToQuickBar ? 0 : slot - 36, signing)
42
52
  await once(bot.inventory, `updateSlot:${moveToQuickBar ? 36 : slot}`)
43
53
 
44
54
  bot.setQuickBarSlot(quickBarSlot)
@@ -1,17 +1,19 @@
1
1
  module.exports = inject
2
2
 
3
3
  function inject (bot) {
4
+ if (bot.supportFeature('mcDataHasEntityMetadata')) {
5
+ // this is handled inside entities.js. We don't yet have entity metadataKeys for all versions but once we do
6
+ // we can delete the numerical checks here and in entities.js https://github.com/extremeheat/mineflayer/blob/eb9982aa04973b0086aac68a2847005d77f01a3d/lib/plugins/entities.js#L469
7
+ return
8
+ }
4
9
  bot._client.on('entity_metadata', (packet) => {
5
- if (!bot?.entity?.id === packet?.entityId) return
6
- if (packet?.metadata[1]?.key === 1) {
7
- if (!packet?.metadata[1]?.value) return
8
- bot.oxygenLevel = Math.round(packet.metadata[1].value / 15)
9
- bot.emit('breath')
10
- }
11
- if (packet?.metadata[0]?.key === 1) {
12
- if (!packet?.metadata[0]?.value) return
13
- bot.oxygenLevel = Math.round(packet.metadata[0].value / 15)
14
- bot.emit('breath')
10
+ if (!bot.entity) return
11
+ if (bot.entity.id !== packet.entityId) return
12
+ for (const metadata of packet.metadata) {
13
+ if (metadata.key === 1) {
14
+ bot.oxygenLevel = Math.round(packet.metadata[1].value / 15)
15
+ bot.emit('breath')
16
+ }
15
17
  }
16
18
  })
17
19
  }
@@ -196,7 +196,7 @@ function inject (bot, options) {
196
196
  bot.tabComplete = tabComplete
197
197
 
198
198
  function addDefaultPatterns () {
199
- // 1.19 changes the chat format to move <sender> prefix from message contents to a seperate field.
199
+ // 1.19 changes the chat format to move <sender> prefix from message contents to a separate field.
200
200
  // TODO: new chat lister to handle this
201
201
  if (!defaultChatPatterns) return
202
202
  bot.addChatPattern('whisper', new RegExp(`^${USERNAME_REGEX} whispers(?: to you)?:? (.*)$`), { deprecated: true })
@@ -35,7 +35,10 @@ function inject (bot) {
35
35
  item: Item.toNotch(item)
36
36
  })
37
37
 
38
- await onceWithCleanup(bot.inventory, `updateSlot:${slot}`, { checkCondition: (oldItem, newItem) => item === null ? newItem === null : newItem?.name === item.name && newItem?.count === item.count && newItem?.metadata === item.metadata })
38
+ await onceWithCleanup(bot.inventory, `updateSlot:${slot}`, {
39
+ timeout: 5000,
40
+ checkCondition: (oldItem, newItem) => item === null ? newItem === null : newItem?.name === item.name && newItem?.count === item.count && newItem?.metadata === item.metadata
41
+ })
39
42
  creativeSlotsUpdates[slot] = false
40
43
  }
41
44
 
@@ -42,10 +42,10 @@ function inject (bot) {
42
42
  )
43
43
  } else if (digFace === 'raycast') {
44
44
  // Check faces that could be seen from the current position. If the delta is smaller then 0.5 that means the
45
- // bot cam most likely not see the face as the block is 1 block thick
46
- // this could be false for blocks that have a smaller bounding box then 1x1x1
45
+ // bot can most likely not see the face as the block is 1 block thick
46
+ // this could be false for blocks that have a smaller bounding box than 1x1x1
47
47
  const dx = bot.entity.position.x - (block.position.x + 0.5)
48
- const dy = bot.entity.position.y + bot.entity.height - (block.position.y + 0.5)
48
+ const dy = bot.entity.position.y + bot.entity.eyeHeight - (block.position.y + 0.5)
49
49
  const dz = bot.entity.position.z - (block.position.z + 0.5)
50
50
  // Check y first then x and z
51
51
  const visibleFaces = {
@@ -63,7 +63,7 @@ function inject (bot) {
63
63
  0.5 + (i === 'y' ? visibleFaces[i] * 0.5 : 0),
64
64
  0.5 + (i === 'z' ? visibleFaces[i] * 0.5 : 0)
65
65
  )
66
- const startPos = bot.entity.position.offset(0, bot.entity.height, 0)
66
+ const startPos = bot.entity.position.offset(0, bot.entity.eyeHeight, 0)
67
67
  const rayBlock = bot.world.raycast(startPos, targetPos.clone().subtract(startPos).normalize(), 5)
68
68
  if (rayBlock) {
69
69
  if (startPos.distanceTo(rayBlock.intersect) < startPos.distanceTo(targetPos)) {
@@ -94,7 +94,7 @@ function inject (bot) {
94
94
  for (const i in validFaces) {
95
95
  const tPos = validFaces[i].targetPos
96
96
  const cDist = new Vec3(tPos.x, tPos.y, tPos.z).distanceSquared(
97
- bot.entity.position.offset(0, bot.entity.height, 0)
97
+ bot.entity.position.offset(0, bot.entity.eyeHeight, 0)
98
98
  )
99
99
  if (distSqrt > cDist) {
100
100
  closest = validFaces[i]
@@ -1,8 +1,12 @@
1
1
  const { Vec3 } = require('vec3')
2
2
  const conv = require('../conversions')
3
- const NAMED_ENTITY_HEIGHT = 1.62
4
- const NAMED_ENTITY_WIDTH = 0.6
5
- const CROUCH_HEIGHT = NAMED_ENTITY_HEIGHT - 0.08
3
+ // These values are only accurate for versions 1.14 and above (crouch hitbox changes)
4
+ // Todo: hitbox sizes for sleeping, swimming/crawling, and flying with elytra
5
+ const PLAYER_HEIGHT = 1.8
6
+ const CROUCH_HEIGHT = 1.5
7
+ const PLAYER_WIDTH = 0.6
8
+ const PLAYER_EYEHEIGHT = 1.62
9
+ const CROUCH_EYEHEIGHT = 1.27
6
10
 
7
11
  module.exports = inject
8
12
 
@@ -102,6 +106,9 @@ function inject (bot) {
102
106
  bot.entity.username = bot._client.username
103
107
  bot.entity.type = 'player'
104
108
  bot.entity.name = 'player'
109
+ bot.entity.height = PLAYER_HEIGHT
110
+ bot.entity.width = PLAYER_WIDTH
111
+ bot.entity.eyeHeight = PLAYER_EYEHEIGHT
105
112
  })
106
113
 
107
114
  bot._client.on('entity_equipment', (packet) => {
@@ -130,11 +137,13 @@ function inject (bot) {
130
137
  })
131
138
 
132
139
  bot.on('entityCrouch', (entity) => {
140
+ entity.eyeHeight = CROUCH_EYEHEIGHT
133
141
  entity.height = CROUCH_HEIGHT
134
142
  })
135
143
 
136
144
  bot.on('entityUncrouch', (entity) => {
137
- entity.height = NAMED_ENTITY_HEIGHT
145
+ entity.eyeHeight = PLAYER_EYEHEIGHT
146
+ entity.height = PLAYER_HEIGHT
138
147
  })
139
148
 
140
149
  bot._client.on('collect', (packet) => {
@@ -184,8 +193,9 @@ function inject (bot) {
184
193
  entity.username = bot.uuidToUsername[uuid]
185
194
  entity.uuid = uuid
186
195
  updateEntityPos(entity, pos)
187
- entity.height = NAMED_ENTITY_HEIGHT
188
- entity.width = NAMED_ENTITY_WIDTH
196
+ entity.eyeHeight = PLAYER_EYEHEIGHT
197
+ entity.height = PLAYER_HEIGHT
198
+ entity.width = PLAYER_WIDTH
189
199
  if (bot.players[entity.username] !== undefined && !bot.players[entity.username].entity) {
190
200
  bot.players[entity.username].entity = entity
191
201
  }
@@ -460,6 +470,12 @@ function inject (bot) {
460
470
  bot.emit('entityUncrouch', entity)
461
471
  }
462
472
  }
473
+
474
+ // Breathing (formerly in breath.js)
475
+ if (metas.air_supply != null) {
476
+ bot.oxygenLevel = Math.round(metas.air_supply / 15)
477
+ bot.emit('breath')
478
+ }
463
479
  } else {
464
480
  const typeSlot = (bot.supportFeature('itemsAreAlsoBlocks') ? 5 : 6) + (bot.supportFeature('entityMetadataHasLong') ? 1 : 0)
465
481
  const slot = packet.metadata.find(e => e.type === typeSlot)
@@ -25,8 +25,13 @@ function inject (bot) {
25
25
  if (!lastBobber || fishingTask.done) return
26
26
 
27
27
  const pos = lastBobber.position
28
- const parts = bot.registry.particlesByName
29
- if (packet.particleId === (parts?.fishing ?? parts.bubble).id && packet.particles === 6 && pos.distanceTo(new Vec3(packet.x, pos.y, packet.z)) <= 1.23) {
28
+
29
+ const bobberCondition = bot.registry.supportFeature('updatedParticlesPacket')
30
+ ? ((packet.particle.type === 'fishing' || packet.particle.type === 'bubble') && packet.amount === 6 && pos.distanceTo(new Vec3(packet.x, pos.y, packet.z)) <= 1.23)
31
+ // This "(particles.fishing ?? particles.bubble).id" condition doesn't make sense (these are both valid types)
32
+ : (packet.particleId === (bot.registry.particlesByName.fishing ?? bot.registry.particlesByName.bubble).id && packet.particles === 6 && pos.distanceTo(new Vec3(packet.x, pos.y, packet.z)) <= 1.23)
33
+
34
+ if (bobberCondition) {
30
35
  bot.activateItem()
31
36
  lastBobber = undefined
32
37
  fishingTask.finish()
@@ -25,8 +25,14 @@ function inject (bot, options) {
25
25
  function handleRespawnPacketData (packet) {
26
26
  bot.game.levelType = packet.levelType ?? (packet.isFlat ? 'flat' : 'default')
27
27
  bot.game.hardcore = packet.isHardcore ?? Boolean(packet.gameMode & 0b100)
28
- bot.game.gameMode = parseGameMode(packet.gameMode)
29
- if (bot.supportFeature('dimensionIsAnInt')) {
28
+ bot.game.gameMode = packet.gamemode || parseGameMode(packet.gameMode)
29
+ if (bot.supportFeature('segmentedRegistryCodecData')) { // 1.20.5
30
+ if (typeof packet.dimension === 'number') {
31
+ bot.game.dimension = bot.registry.dimensionsArray[packet.dimension]?.name?.replace('minecraft:', '')
32
+ } else if (typeof packet.dimension === 'string') { // iirc, in 1.21 it's back to a string
33
+ bot.game.dimension = packet.dimension.replace('minecraft:', '')
34
+ }
35
+ } else if (bot.supportFeature('dimensionIsAnInt')) {
30
36
  bot.game.dimension = dimensionNames[packet.dimension]
31
37
  } else if (bot.supportFeature('dimensionIsAString')) {
32
38
  bot.game.dimension = packet.dimension.replace('minecraft:', '')
@@ -40,25 +46,24 @@ function inject (bot, options) {
40
46
  bot.registry.loadDimensionCodec(packet.dimensionCodec)
41
47
  }
42
48
 
49
+ bot.game.minY = 0
50
+ bot.game.height = 256
51
+
43
52
  if (bot.supportFeature('dimensionDataInCodec')) { // 1.19+
44
- if (packet.worldType) { // login
53
+ // pre 1.20.5 before we consolidated login and respawn's SpawnInfo structure into one type,
54
+ // "dimension" was called "worldType" in login_packet's payload but not respawn.
55
+ if (packet.worldType && !bot.game.dimension) {
45
56
  bot.game.dimension = packet.worldType.replace('minecraft:', '')
46
- const { minY, height } = bot.registry.dimensionsByName[bot.game.dimension]
47
- bot.game.minY = minY
48
- bot.game.height = height
49
- } else if (packet.dimension) { // respawn
50
- bot.game.dimension = packet.dimension.replace('minecraft:', '')
51
- const { minY, height } = bot.registry.dimensionsByName[bot.game.dimension]
52
- bot.game.minY = minY
53
- bot.game.height = height
57
+ }
58
+ const dimData = bot.registry.dimensionsByName[bot.game.dimension]
59
+ if (dimData) {
60
+ bot.game.minY = dimData.minY
61
+ bot.game.height = dimData.height
54
62
  }
55
63
  } else if (bot.supportFeature('dimensionDataIsAvailable')) { // 1.18
56
64
  const dimensionData = nbt.simplify(packet.dimension)
57
65
  bot.game.minY = dimensionData.min_y
58
66
  bot.game.height = dimensionData.height
59
- } else {
60
- bot.game.minY = 0
61
- bot.game.height = 256
62
67
  }
63
68
 
64
69
  if (packet.difficulty) {
@@ -73,11 +78,11 @@ function inject (bot, options) {
73
78
 
74
79
  // 1.20.2
75
80
  bot._client.on('registry_data', (packet) => {
76
- bot.registry.loadDimensionCodec(packet.codec)
81
+ bot.registry.loadDimensionCodec(packet.codec || packet)
77
82
  })
78
83
 
79
84
  bot._client.on('login', (packet) => {
80
- handleRespawnPacketData(packet)
85
+ handleRespawnPacketData(packet.worldState || packet)
81
86
 
82
87
  bot.game.maxPlayers = packet.maxPlayers
83
88
  if (packet.enableRespawnScreen) {
@@ -95,7 +100,8 @@ function inject (bot, options) {
95
100
  })
96
101
 
97
102
  bot._client.on('respawn', (packet) => {
98
- handleRespawnPacketData(packet)
103
+ // in 1.20.5+ protocol we move the shared spawn data into one SpawnInfo type under .worldState
104
+ handleRespawnPacketData(packet.worldState || packet)
99
105
  bot.emit('game')
100
106
  })
101
107
 
@@ -691,6 +691,13 @@ function inject (bot, { hideErrors }) {
691
691
  bot.currentWindow = null
692
692
  bot.emit('windowClose', oldWindow)
693
693
  })
694
+ bot._client.on('login', () => {
695
+ // close window when switch subserver
696
+ const oldWindow = bot.currentWindow
697
+ if (!oldWindow) return
698
+ bot.currentWindow = null
699
+ bot.emit('windowClose', oldWindow)
700
+ })
694
701
  bot._client.on('set_slot', (packet) => {
695
702
  // set slot
696
703
  const window = packet.windowId === 0 ? bot.inventory : bot.currentWindow
@@ -323,7 +323,7 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
323
323
  }
324
324
 
325
325
  bot.lookAt = async (point, force) => {
326
- const delta = point.minus(bot.entity.position.offset(0, bot.entity.height, 0))
326
+ const delta = point.minus(bot.entity.position.offset(0, bot.entity.eyeHeight, 0))
327
327
  const yaw = Math.atan2(-delta.x, -delta.z)
328
328
  const groundDistance = Math.sqrt(delta.x * delta.x + delta.z * delta.z)
329
329
  const pitch = Math.atan2(delta.y, groundDistance)
@@ -332,7 +332,9 @@ function inject (bot, { physicsEnabled, maxCatchupTicks }) {
332
332
 
333
333
  // player position and look (clientbound)
334
334
  bot._client.on('position', (packet) => {
335
- bot.entity.height = 1.62
335
+ // Is this necessary? Feels like it might wrongly overwrite hitbox size sometimes
336
+ // e.g. when crouching/crawling/swimming. Can someone confirm?
337
+ bot.entity.height = 1.8
336
338
 
337
339
  // Velocity is only set to 0 if the flag is not set, otherwise keep current velocity
338
340
  const vel = bot.entity.velocity
@@ -17,12 +17,7 @@ module.exports = (bot) => {
17
17
  }
18
18
 
19
19
  bot.blockAtCursor = (maxDistance = 256, matcher = null) => {
20
- const { position, height, pitch, yaw } = bot.entity
21
-
22
- const eyePosition = position.offset(0, height, 0)
23
- const viewDirection = getViewDirection(pitch, yaw)
24
-
25
- return bot.world.raycast(eyePosition, viewDirection, maxDistance, matcher)
20
+ return bot.blockAtEntityCursor(bot.entity, maxDistance, matcher)
26
21
  }
27
22
 
28
23
  bot.entityAtCursor = (maxDistance = 3.5) => {
@@ -33,7 +28,7 @@ module.exports = (bot) => {
33
28
  .filter(entity => entity.type !== 'object' && entity.username !== bot.username && entity.position.distanceTo(bot.entity.position) <= maxDistance)
34
29
 
35
30
  const dir = new Vec3(-Math.sin(bot.entity.yaw) * Math.cos(bot.entity.pitch), Math.sin(bot.entity.pitch), -Math.cos(bot.entity.yaw) * Math.cos(bot.entity.pitch))
36
- const iterator = new RaycastIterator(bot.entity.position.offset(0, bot.entity.height, 0), dir.normalize(), maxDistance)
31
+ const iterator = new RaycastIterator(bot.entity.position.offset(0, bot.entity.eyeHeight, 0), dir.normalize(), maxDistance)
37
32
 
38
33
  let targetEntity = null
39
34
  let targetDist = maxDistance
@@ -42,7 +37,7 @@ module.exports = (bot) => {
42
37
  const entity = entities[i]
43
38
  const w = entity.width / 2
44
39
 
45
- const shapes = [[-w, 0, -w, w, entity.height + (entity.type === 'player' ? 0.18 : 0), w]]
40
+ const shapes = [[-w, 0, -w, w, entity.height, w]]
46
41
  const intersect = iterator.intersect(shapes, entity.position)
47
42
  if (intersect) {
48
43
  const entityDir = entity.position.minus(bot.entity.position) // Can be combined into 1 line
package/lib/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const testedVersions = ['1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20.1', '1.20.2', '1.20.4']
1
+ const testedVersions = ['1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20.1', '1.20.2', '1.20.4', '1.20.6']
2
2
  module.exports = {
3
3
 
4
4
  testedVersions,
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "mineflayer",
3
- "version": "4.20.1",
3
+ "version": "4.22.0",
4
4
  "description": "create minecraft bots with a stable, high level API",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
7
7
  "scripts": {
8
- "mocha_test": "mocha --reporter spec --exit",
8
+ "mocha_test": "mocha --reporter spec --exit --bail",
9
9
  "test": "npm run mocha_test",
10
10
  "pretest": "npm run lint",
11
11
  "lint": "standard && standard-markdown",
@@ -21,32 +21,33 @@
21
21
  },
22
22
  "license": "MIT",
23
23
  "dependencies": {
24
- "minecraft-data": "^3.56.0",
25
- "minecraft-protocol": "^1.47.0",
24
+ "minecraft-data": "^3.76.0",
25
+ "minecraft-protocol": "^1.49.0",
26
26
  "prismarine-biome": "^1.1.1",
27
27
  "prismarine-block": "^1.17.0",
28
28
  "prismarine-chat": "^1.7.1",
29
29
  "prismarine-chunk": "^1.34.0",
30
30
  "prismarine-entity": "^2.3.0",
31
- "prismarine-item": "^1.14.0",
31
+ "prismarine-item": "^1.15.0",
32
32
  "prismarine-nbt": "^2.0.0",
33
33
  "prismarine-physics": "^1.8.0",
34
34
  "prismarine-recipe": "^1.3.0",
35
- "prismarine-registry": "^1.5.0",
35
+ "prismarine-registry": "^1.8.0",
36
36
  "prismarine-windows": "^2.9.0",
37
37
  "prismarine-world": "^3.6.0",
38
- "protodef": "^1.14.0",
38
+ "protodef": "1.17.0",
39
39
  "typed-emitter": "^1.0.0",
40
40
  "vec3": "^0.1.7"
41
41
  },
42
42
  "devDependencies": {
43
- "@types/node": "^20.2.1",
43
+ "@types/node": "^22.1.0",
44
44
  "doctoc": "^2.0.1",
45
45
  "minecraft-wrap": "^1.3.0",
46
46
  "mineflayer": "file:.",
47
47
  "mocha": "^10.0.0",
48
+ "protodef-yaml": "^1.5.3",
48
49
  "standard": "^17.0.0",
49
50
  "standard-markdown": "^7.1.0",
50
- "typescript": "^5.0.3"
51
+ "typescript": "^5.4.5"
51
52
  }
52
53
  }