incanto 0.61.0 → 0.62.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.
Files changed (46) hide show
  1. package/bin/incanto-verify.mjs +134 -55
  2. package/dist/2d.d.ts +42 -0
  3. package/dist/2d.js +3 -3
  4. package/dist/3d.js +5 -5
  5. package/dist/{create-game-DH7JI5xx.js → create-game-BpunnGPX.js} +8 -8
  6. package/dist/{create-game-IX5lEH0P.js → create-game-Caut3bqN.js} +6 -6
  7. package/dist/{duplicate-CGqAmK2h.js → duplicate-E4FUs5Bn.js} +1 -1
  8. package/dist/editor.js +39 -18
  9. package/dist/{environment-presets-CNxCuhZF.js → environment-presets-BAWeOeqf.js} +5 -3
  10. package/dist/{gameplay-Dtzd2itW.js → gameplay-CaHqDiQD.js} +126 -15
  11. package/dist/gameplay.js +1 -1
  12. package/dist/index.d.ts +92 -1
  13. package/dist/index.js +6 -6
  14. package/dist/{loader-D7jTvDQv.js → loader-DEe272nY.js} +103 -0
  15. package/dist/net.js +1 -1
  16. package/dist/{physics-2d-CBnor8Zf.js → physics-2d-BXmu2i7W.js} +11 -3
  17. package/dist/{physics-3d-BTUfUSWO.js → physics-3d-ClxP6Uv7.js} +121 -17
  18. package/dist/react.js +1 -1
  19. package/dist/{register-DL3izw8j.js → register-CNh4FlbD.js} +60 -9
  20. package/dist/{register-xuSRyD6b.js → register-D3yx8D4r.js} +158 -2
  21. package/dist/{replay-D7-yle3s.js → replay-DlgHItNv.js} +15 -2
  22. package/dist/{split-screen-5Ban4q4n.js → split-screen-CBM9wcMX.js} +2 -2
  23. package/dist/{src-DozXvyZS.js → src-CH00_JsR.js} +1 -1
  24. package/dist/{teardown-B6rwJOyS.js → teardown-C7qP-dcC.js} +1 -1
  25. package/dist/{test-Dch_7VQD.js → test-BMkg8zMV.js} +27 -15
  26. package/dist/test.d.ts +37 -0
  27. package/dist/test.js +2 -2
  28. package/dist/vite.js +2 -2
  29. package/editor/assets/{agent8-CF1JL2tR.js → agent8-m5mtAO_A.js} +1 -1
  30. package/editor/assets/{debug-3QzYhOPA.js → debug-CPhzCT8f.js} +1 -1
  31. package/editor/assets/{index-CAD2c5ug.js → index-D422P4kW.js} +91 -91
  32. package/editor/index.html +1 -1
  33. package/package.json +1 -1
  34. package/schemas/scene.schema.json +87 -0
  35. package/skills/incanto-behaviors-and-scripts.md +37 -0
  36. package/skills/incanto-building-2d-games.md +20 -1
  37. package/skills/incanto-building-3d-games.md +1 -1
  38. package/skills/incanto-gameplay-behaviors.md +15 -1
  39. package/skills/incanto-node-reference.md +11 -0
  40. package/skills/incanto-physics-and-input.md +23 -2
  41. package/skills/incanto-verifying-your-game.md +31 -1
  42. package/templates-app/beacon-isle-3d/package.json +1 -1
  43. package/templates-app/platformer-2d/package.json +1 -1
  44. package/templates-app/star-survivor/package.json +1 -1
  45. package/templates-app/tps-3d/package.json +1 -1
  46. package/templates-app/village-quest-3d/package.json +1 -1
package/editor/index.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Incanto Scene Editor</title>
7
7
  <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect width='16' height='16' rx='3' fill='%236ee7dc'/><text x='8' y='12' text-anchor='middle' font-size='11' font-family='monospace' fill='%230e1018'>i</text></svg>" />
8
- <script type="module" crossorigin src="./assets/index-CAD2c5ug.js"></script>
8
+ <script type="module" crossorigin src="./assets/index-D422P4kW.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="./assets/GameServer-C56iOUgF.js">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incanto",
3
- "version": "0.61.0",
3
+ "version": "0.62.0",
4
4
  "description": "Vibe-coding-first web game engine SDK — JSON-driven scenes on three.js",
5
5
  "keywords": [
6
6
  "game-engine",
@@ -295,6 +295,9 @@
295
295
  {
296
296
  "$ref": "#/$defs/Particles3D"
297
297
  },
298
+ {
299
+ "$ref": "#/$defs/Respawn"
300
+ },
298
301
  {
299
302
  "$ref": "#/$defs/RigidBody2D"
300
303
  },
@@ -5327,6 +5330,90 @@
5327
5330
  },
5328
5331
  "required": ["name", "type"]
5329
5332
  },
5333
+ "Respawn": {
5334
+ "type": "object",
5335
+ "x-signals": ["respawned"],
5336
+ "properties": {
5337
+ "name": {
5338
+ "type": "string"
5339
+ },
5340
+ "uid": {
5341
+ "type": "string"
5342
+ },
5343
+ "type": {
5344
+ "const": "Respawn"
5345
+ },
5346
+ "groups": {
5347
+ "type": "array",
5348
+ "items": {
5349
+ "type": "string"
5350
+ }
5351
+ },
5352
+ "tags": {
5353
+ "type": "object"
5354
+ },
5355
+ "props": {
5356
+ "type": "object",
5357
+ "properties": {
5358
+ "target": {
5359
+ "type": "string",
5360
+ "description": "A node path: '%UniqueName', '/Absolute/From/Root', or relative to this node ('../Skin').",
5361
+ "default": ".."
5362
+ },
5363
+ "below": {
5364
+ "default": null
5365
+ },
5366
+ "to": {
5367
+ "type": "array",
5368
+ "default": []
5369
+ },
5370
+ "resetVelocity": {
5371
+ "type": "boolean",
5372
+ "default": true
5373
+ }
5374
+ },
5375
+ "additionalProperties": false
5376
+ },
5377
+ "script": {
5378
+ "type": "object",
5379
+ "properties": {
5380
+ "name": {
5381
+ "type": "string"
5382
+ },
5383
+ "props": {
5384
+ "type": "object"
5385
+ }
5386
+ },
5387
+ "required": ["name"]
5388
+ },
5389
+ "network": {
5390
+ "type": "object",
5391
+ "properties": {
5392
+ "mode": {
5393
+ "enum": ["owner", "observer"]
5394
+ },
5395
+ "sync": {
5396
+ "type": "array",
5397
+ "items": {
5398
+ "type": "string",
5399
+ "minLength": 1
5400
+ }
5401
+ },
5402
+ "throttleMs": {
5403
+ "type": "number"
5404
+ }
5405
+ },
5406
+ "additionalProperties": false
5407
+ },
5408
+ "children": {
5409
+ "type": "array",
5410
+ "items": {
5411
+ "$ref": "#/$defs/node"
5412
+ }
5413
+ }
5414
+ },
5415
+ "required": ["name", "type"]
5416
+ },
5330
5417
  "RigidBody2D": {
5331
5418
  "type": "object",
5332
5419
  "x-signals": ["triggerEnter", "triggerExit"],
@@ -145,6 +145,43 @@ declared by their classes. Escape hatch for runtime one-offs: `node.declareSigna
145
145
  the emitting node. For per-emitter logic, attach a small behavior to the emitter itself
146
146
  (see the `Pickup` pattern: a coin's own `triggerEnter` → its own `onTaken` → `queueFree`).
147
147
 
148
+ ## Respawn (core node — catch a player who leaves the world)
149
+
150
+ Hang it off the thing it guards. That is the whole feature:
151
+
152
+ ```json
153
+ { "name": "Player", "type": "RigidBody3D", "props": { "…": "…" },
154
+ "children": [
155
+ { "name": "Controller", "type": "CharacterController3D" },
156
+ { "name": "Catch", "type": "Respawn" }
157
+ ] }
158
+ ```
159
+
160
+ With no props it guards its parent, catches it 50 m under the spawn (1000 px in
161
+ 2D — the same line `incanto-playtest` calls `fell`), puts it back where it
162
+ started and zeroes the velocity the fall built up. Works in both dimensions.
163
+
164
+ | Prop | Default | Meaning |
165
+ |---|---|---|
166
+ | `target` | `".."` | who is being caught — the parent, normally |
167
+ | `below` | `null` | the line, in the scene's own down. `null` = auto (see above) |
168
+ | `to` | `[]` | where to put it back. Empty = wherever it started |
169
+ | `resetVelocity` | `true` | drop the speed the fall built up — off and it falls straight back through |
170
+
171
+ Signal `respawned(target, y)`, and a `falls` counter. It deliberately does NOT
172
+ decide what falling COSTS: wire `respawned` to a `ScoreKeeper.loseLife` or a
173
+ `Health.damage` if it should hurt, and leave it alone if it should not.
174
+
175
+ **A node, not a behavior**, because a node holds one behavior and the player's
176
+ is already spoken for.
177
+
178
+ Nothing in the engine did this before 0.62, and five shipped examples proved
179
+ what that cost: with the `plays` rung finally counting defects,
180
+ `basic-3d-sideview` failed 8 seeded runs of 8, `water-lake-3d` 7,
181
+ `water-ocean-3d` 5, `water-pool-3d` 4, `water-river-3d` 1 — every one of them
182
+ the player walking off the terrain and falling forever. One `Respawn` node each,
183
+ no TypeScript, and all five are clean.
184
+
148
185
  ## Timer (core node — never setTimeout in game logic)
149
186
 
150
187
  ```json
@@ -170,7 +170,16 @@ ALWAYS confirm in the browser (`bun run dev`) that the feet touch the surface.
170
170
  | `zoom` | `1` | 2 = world pixels doubled |
171
171
  | `limits` | `[]` | `[minX,minY,maxX,maxY]` — view rect clamped inside |
172
172
  | `current` | `false` | mark exactly one |
173
- Camera `position` is the view CENTER. With no camera, the view shows (0,0)–(w,h).
173
+ Camera `position` is the view CENTER, and it composes with its ancestors like any
174
+ other node — so parenting the camera to the player (the Godot/Phaser idiom) works,
175
+ and so does putting it inside the container that holds the level. `follow` and the
176
+ `FollowCamera` behavior compare WORLD positions at both ends, so the target can live
177
+ under a different parent than the camera. With no camera, the view shows (0,0)–(w,h).
178
+
179
+ (Before 0.62 the view centre was the camera's LOCAL prop. A camera under a player at
180
+ `[1400, 900]` framed the world origin — and `framing`, which composes full world
181
+ matrices, reported `centred [1400, 900] … 1 in view`, certifying a frame the renderer
182
+ never drew.)
174
183
 
175
184
  ### `Label`
176
185
  `text`, `fontSize 16`, `color '#ffffff'`, `font 'monospace'`, `align 'left'|'center'|'right'`,
@@ -357,6 +366,16 @@ Mark unchanging subtrees (tile decor, backgrounds) `static: true` — the
357
366
  renderer syncs them once and skips them every frame. Flip to `false` to
358
367
  resume live syncing.
359
368
 
369
+ The freeze waits for the node to have something worth freezing: a `Sprite2D` or
370
+ `TileMap2D` whose texture has not decoded yet keeps syncing until it has, and a
371
+ texture that never arrives never freezes at all (`assetErrors()` is what reports
372
+ that one). Before 0.62 the latch fired on frame one regardless — and a
373
+ `TextureLoader` fills `.image` asynchronously while the renderer loads and syncs
374
+ in the same frame, so the state frozen was `visible: false`, permanently. A
375
+ static backdrop was invisible for the life of the game with nothing reporting
376
+ it: the texture loaded fine, `stats().errors` was 0, and `framing` reads props
377
+ rather than pixels and still called the node on-screen.
378
+
360
379
  ## Before you hand it back
361
380
 
362
381
  You cannot see this game. `incanto-verifying-your-game.md` is how you find out
@@ -69,7 +69,7 @@ All 3D nodes extend `Node3D` and therefore have the transform props:
69
69
  |---|---|---|
70
70
  | `position` | `[0,0,0]` | meters; +Y up |
71
71
  | `rotation` | `[0,0,0]` | **degrees**, Euler XYZ |
72
- | `scale` | `[1,1,1]` | |
72
+ | `scale` | `[1,1,1]` | resizes the DRAWN mesh. `Terrain3D`/`Water3D`/`River3D` reject a non-unit scale at load — they keep a CPU twin (`heightAt`, the heightfield collider, the water surface query) built from `size`, so scaling would move the picture and not the ground. Resize those with `size` |
73
73
  | `visible` | `true` | hides the whole subtree's rendering |
74
74
 
75
75
  ### `MeshInstance3D`
@@ -513,6 +513,11 @@ pickups, spinning/pulsing coins. Drives one `axis` of `position`, `rotation`
513
513
  "script": { "name": "Oscillate", "props": { "axis": "z", "amplitude": 180, "frequency": 0.5, "mode": "rotation" } } }
514
514
  ```
515
515
 
516
+ On a physics BODY, `mode: "rotation"` turns the collider too — a spinning blade
517
+ cuts, a swinging gate blocks. (Before 0.62 it turned only the mesh: a body's
518
+ angle was applied once at creation and never followed again, so every spinner
519
+ built this way was decoration.)
520
+
516
521
  ## Buoyancy
517
522
 
518
523
  Float on water. Hangs on a `RigidBody3D` and asks the water how high it is at
@@ -585,7 +590,7 @@ later clones get `EnemyTemplate2`, `EnemyTemplate3`, …)
585
590
  | `prefab` | `""` | node path of the template to clone (required) |
586
591
  | `interval` | `1` | seconds between spawns |
587
592
  | `max` | `0` | max LIVE instances (0 = unlimited) |
588
- | `at` | `[]` | offset added to the spawner position ([x,y(,z)]) |
593
+ | `at` | `[]` | where the clone appears, as an offset from the spawner ([x,y(,z)]) |
589
594
  | `autoStart` | `true` | begin ticking at ready |
590
595
  | `total` | `0` | total LIFETIME spawns (0 = infinite) |
591
596
 
@@ -634,6 +639,15 @@ MOVEMENT by design: **compose** it with `DamageOnContact` (deal damage), `Lifeti
634
639
  `direction` is a vector OR `'forward'` (from the node `rotation`); it's a
635
640
  union-typed prop so its default is `null` (= `'forward'`).
636
641
 
642
+ In 3D, `'forward'` is the engine's ONE facing convention — **+Z**, the same
643
+ `atan2(dx, dz)` basis `CharacterController3D` uses and `incanto-3d-character.md`
644
+ spells out under "the +Z-FORWARD rule". So aim the bullet (or its shooter) with
645
+ that formula and it flies at what you aimed at; do not add 180°. (Before 0.62
646
+ this returned three.js's −Z instead, so every `'forward'` shot left the muzzle
647
+ pointing at the shooter's back — at the right speed, with no error, which reads
648
+ as a gun that simply never hits anything.) In 2D `rotation` is a scalar and
649
+ `'forward'` is +x turned clockwise by it, unchanged.
650
+
637
651
  | Prop | Default | Meaning |
638
652
  |---|---|---|
639
653
  | `speed` | `300` | units per second along `direction` |
@@ -706,6 +706,17 @@ Signals: `finished`
706
706
 
707
707
  Signals: `finished`
708
708
 
709
+ ## `Respawn` — `incanto`
710
+
711
+ | Prop | Default | Kind |
712
+ |---|---|---|
713
+ | `target` | `".."` | node path |
714
+ | `below` | `null` | null |
715
+ | `to` | `[]` | array |
716
+ | `resetVelocity` | `true` | boolean |
717
+
718
+ Signals: `respawned`
719
+
709
720
  ## `RigidBody2D` — `incanto/2d`
710
721
 
711
722
  | Prop | Default | Kind |
@@ -50,6 +50,16 @@ the corners. Give the body its mesh as a CHILD and let the shape follow:
50
50
  "props": { "mesh": "box", "size": [14, 0.22, 2.2] } } ] }
51
51
  ```
52
52
 
53
+ The child's OWN `position`, `rotation` and `scale` are part of the shape: raise the plank on
54
+ the child instead of the body and the collider goes up with it; scale the crate 4× and it is
55
+ solid at 4×. (It was not before 0.62 — the child transform was dropped and the collider sat
56
+ at the body's origin at the authored size, so you walked through the deck you could see and
57
+ landed on an invisible slab at ground level. Measured: a plank drawn with its top at y=3.2
58
+ had its surface at y=0.2.) `collider.offset` stacks on top of that.
59
+
60
+ A ball, a capsule and a cylinder have ONE radius between them, so a child scaled unevenly in
61
+ x and z has no exact shape — the widest of the two is used and the engine says so by name.
62
+
53
63
  Bodies ignore an ANCESTOR's rotation, so a yawed structure puts each body at top level with
54
64
  its own `rotation` — the child mesh then turns with it, visual and collider together.
55
65
 
@@ -143,7 +153,13 @@ engine.input.isPressed('jump'); // held
143
153
  engine.input.justPressed('jump'); // one-frame edge (settled per tick)
144
154
  engine.input.getVector('move'); // normalized {x, y}, y-down (up = -y)
145
155
  ```
146
- Keys are `KeyboardEvent.code` strings (`KeyW`, `Space`, `ArrowLeft`). Unknown actions and
156
+ Keys are `KeyboardEvent.code` strings (`KeyW`, `Space`, `ArrowLeft`) — plus `Mouse0..4`
157
+ and `Pad0..16`, which live in the same space. **A value outside that set is a load
158
+ error** naming the one you probably meant (`'W' → "KeyW"`, `'space' → "Space"`,
159
+ `'Shift' → "ShiftLeft" or "ShiftRight"`): the map is looked up by exact string, so a
160
+ misspelled code binds to nothing and the player silently does not have that control.
161
+ An EMPTY key list is still fine — `{"keys": [], "touch": "button"}` is a touch-only
162
+ control. Unknown actions and
147
163
  wrong-kind queries (getVector on a button) are hard errors listing valid names.
148
164
  Action declarations RESET on every `setScene` (no keybind bleed between scenes).
149
165
 
@@ -239,7 +255,12 @@ Injected state combines with key state (vectors clamped to unit length).
239
255
  - Pickups: `Area2D` in a group + `triggerEnter` → check `other.isInGroup('player')` →
240
256
  `queueFree()` — wire it via JSON `connections` to a behavior method (the `Pickup`
241
257
  pattern in `incanto-behaviors-and-scripts.md`), or imperatively with `node.on(...)`.
242
- - Teleport: just write `node.position` — the body follows. Launch: write `linearVelocity`.
258
+ - Teleport: just write `node.position` — the body follows. **`node.rotation` follows
259
+ too**: turn a static or kinematic body at runtime and its COLLIDER turns with it, so
260
+ a spinning blade cuts and a swinging gate blocks. (It did not before 0.62 — the
261
+ angle was applied once at creation, the mesh turned and the collider stayed put.)
262
+ A free dynamic body's rotation belongs to the solver and is left alone.
263
+ Launch: write `linearVelocity`.
243
264
  - Reference: [examples/2d-phaser-sprite-character-gravity](https://github.com/rareboe/Incanto/tree/main/examples/2d-phaser-sprite-character-gravity) — gravity, jump, attack lockout, custom
244
265
  `Player` node type. Verified in Chromium end-to-end.
245
266
 
@@ -32,6 +32,7 @@ failures — but `framing` and `assetErrors()` you have to ASK for.
32
32
  $ bunx incanto verify # finds your scene AND your behaviours
33
33
  · behaviours: src/behaviors.ts (found, not named)
34
34
  ✓ loads — the scene is legal and its assets resolve
35
+ ! /Game/Chest: script Chest — target '%Ke' matches nothing
35
36
  ? plays — 8 runs played without reaching a win (4 lost, 4 ran out the clock)
36
37
  ✓ feels — 5 of 7 fired — silent: /Game/Boss/Roar, /Game/Boss/Boom
37
38
  · agrees — not run — this scene has no `multiplayer` header
@@ -67,11 +68,40 @@ encodes so you do not have to remember them:
67
68
 
68
69
  - a **failed** rung makes the ones above it meaningless, so only the first is
69
70
  worth reading — the rest are marked `·` skipped;
71
+ - a rung that PASSED can still have something to say. Anything its own tool
72
+ warned about is printed under it with a `!` — `loads` runs `incanto-check`,
73
+ and the ladder used to read only whether it passed and drop the warnings, so a
74
+ scene the check describes as "It will render black" came back as
75
+ `✓ loads — the scene is legal and its assets resolve`;
70
76
  - an **unmeasured** rung (`?`) is not a failure. "No dev server" means the
71
77
  question was never asked; treating that as a broken game sends you editing a
72
78
  scene that is fine;
73
79
  - a scene that declares **no win** is not a scene that cannot be won. A
74
- walkabout has no end, and that is reported as unmeasured, not failed.
80
+ walkabout has no end, and that is reported as unmeasured, not failed;
81
+ - but a **defect** fails the rung whatever the scene declares. `error` (a
82
+ behaviour threw) and `fell` (the player left the world) are counted BEFORE
83
+ anything else is decided:
84
+
85
+ ```
86
+ ✗ plays — 8 of 8 runs ended in a defect (8 fell out of the world)
87
+ next: the player left the world with nothing catching them — give the level a
88
+ floor, walls, or a respawn
89
+ ```
90
+
91
+ The routed answer to that last one is a `Respawn` node under the player
92
+ (`incanto-behaviors-and-scripts.md`) — one line of JSON, no per-frame `y`
93
+ check in your own code.
94
+
95
+ This used to be the sentence `8 runs played without error` — printed for a
96
+ scene with no declared win no matter what happened in it, including 8/8
97
+ `fell`, and exit 0. The child `incanto-playtest` had just printed
98
+ `✗ fell in 8/8`; the parent said the opposite of it.
99
+
100
+ `stuck` — the player went nowhere — is the third defect and the one that needs
101
+ a fair chance before it counts. It is only a failure when there WAS someone to
102
+ move and your behaviours were loaded. A scene with no character controller, no
103
+ `player` group and no node named `Player` reports `nothing here is drivable`,
104
+ and a run without `--behaviors` reports that instead: neither is a broken game.
75
105
 
76
106
  `feels` is the sound-and-effects rung: it lists what the scene DECLARES against
77
107
  what actually fired during the playtest. A game whose feedback is wired but
@@ -14,7 +14,7 @@
14
14
  "@dimforge/rapier2d-compat": "0.19.3",
15
15
  "@dimforge/rapier3d-compat": "0.19.3",
16
16
  "@pixiv/three-vrm": "^3.5.3",
17
- "incanto": "^0.61.0",
17
+ "incanto": "^0.62.0",
18
18
  "three": "^0.184.0"
19
19
  },
20
20
  "devDependencies": {
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "@dimforge/rapier2d-compat": "0.19.3",
14
- "incanto": "^0.61.0",
14
+ "incanto": "^0.62.0",
15
15
  "three": "^0.184.0"
16
16
  },
17
17
  "devDependencies": {
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "@dimforge/rapier2d-compat": "0.19.3",
14
- "incanto": "^0.61.0",
14
+ "incanto": "^0.62.0",
15
15
  "three": "^0.184.0"
16
16
  },
17
17
  "devDependencies": {
@@ -13,7 +13,7 @@
13
13
  "@dimforge/rapier2d-compat": "0.19.3",
14
14
  "@dimforge/rapier3d-compat": "0.19.3",
15
15
  "@pixiv/three-vrm": "^3.5.3",
16
- "incanto": "^0.61.0",
16
+ "incanto": "^0.62.0",
17
17
  "three": "^0.184.0"
18
18
  },
19
19
  "devDependencies": {
@@ -13,7 +13,7 @@
13
13
  "@dimforge/rapier2d-compat": "0.19.3",
14
14
  "@dimforge/rapier3d-compat": "0.19.3",
15
15
  "@pixiv/three-vrm": "^3.5.3",
16
- "incanto": "^0.61.0",
16
+ "incanto": "^0.62.0",
17
17
  "three": "^0.184.0"
18
18
  },
19
19
  "devDependencies": {