diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..80051eb
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,8 @@
+[*]
+end_of_line = lf
+insert_final_newline = true
+indent_style = space
+indent_size = 2
+
+[*.js]
+indent_size = 4
\ No newline at end of file
diff --git a/.github/workflows/test-pipeline.yml b/.github/workflows/test-pipeline.yml
new file mode 100644
index 0000000..9d1ce9e
--- /dev/null
+++ b/.github/workflows/test-pipeline.yml
@@ -0,0 +1,24 @@
+name: Tests CI Pipeline
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ node-version: [20.x]
+ steps:
+ - uses: actions/checkout@v4
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+ - name: Install dependencies
+ run: npm ci
+ - name: Run tests
+ run: npm test --silent
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..41f6c9a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+my-games/
+node_modules/
\ No newline at end of file
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index a26156a..e23e992 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -1,6 +1,9 @@
{
"recommendations": [
"tberman.json-schema-validator",
- "ms-vscode.live-server"
+ "ms-vscode.live-server",
+ "wayou.vscode-todo-highlight",
+ "esbenp.prettier-vscode",
+ "orta.vscode-jest"
]
}
\ No newline at end of file
diff --git a/README.md b/README.md
index 33df825..70ca1f7 100644
--- a/README.md
+++ b/README.md
@@ -1,71 +1,108 @@
# TextAdventureJS
-A text based adventure engine written in Javascript.
-Here's a very basic example, that is included in this Repositroy:
-https://dak0r.github.io/TextAdventureJS/example.html
+A text based adventure engine written in Javascript.
+This repo comes with a player that uses the engine as well as an editor to create games for it.
-A different project, that uses TextAdventureJS can be foundon my website:
-https://www.danielkorgel.com
+The library and the player are written in pure JavaScript.
+The editor uses jquery.
+
+## Player
+
+
+Try it here: https://dak0r.github.io/TextAdventureJS/player/
+
+## Editor
+
+This repo also provides a full editor including debugger functionality for creating your own games:
+
+
+
+Try it here: https://dak0r.github.io/TextAdventureJS/editor/
## Usage
-See `example.html` for a working minimal example.
+A barebones example that uses jquery:
+
```js
- // Defines where to write output
- function witeLine(outputLine){
- $("#outputArea").append(outputLine);
- }
- // Clears output area
- function clearArea(){
- $("#outputArea").val("");
- }
+// Defines where to write output
+function witeLine(outputLine) {
+ $("#gameLog").append(outputLine + " ");
+}
+// Clears written output from the area
+function clearArea() {
+ $("#gameLog").html("");
+}
+// Read user input
+function readInput() {
+ const inputText = $("#inputField").val().trim();
+ writeLine(inputText);
+ textAdv.input(inputText);
+ $("#inputField").val("");
+}
+
+// Add event handler for reading user input:
+$("#submit").click(function() { readInput(); });
+
+// Init textAdventureJS and load a game
+var textAdvEngine = new textAdventureEngine(witeLine, clearArea);
+textAdvEngine.loadDatabaseFromFile("game.json");
+```
+with this html elements:
- // Init engine and load a game
- var textAdv = new textAdventureEngine(witeLine, clearArea);
- textAdv.loadDatabaseFromFile("game.TADB.json");
+```html
+
+
+Submit
+```
- // Send Input to game
- textAdv.input("look at cookie");
+See [player/index.html](./player/index.html) for a more complex example.
-```
+## Game Database
-## Game Database
-A Text Adventure Database (TADB) is JSON file which describes games that can run in the TextAdventureJS Engine.
+A Text Adventure Game Database is JSON file which describes games that can run in the TextAdventureJS Engine.
-`.tadb.json` files can be validated using the JSON schema in this repo: `textAdventureDatabase.schema.json`.
-The schema contains also includes descriptions for the properties. I recommend using the [JSON Schema Validator](https://marketplace.visualstudio.com/items?itemName=tberman.json-schema-validator) for this task.
+These game files can be validated using the JSON schema in this repo: [textAdventureGameDatabase.schema.json](./textAdventureGameDatabase.schema.json).
+
+ I recommend using the [JSON Schema Validator](https://marketplace.visualstudio.com/items?itemName=tberman.json-schema-validator), if editing the json files manually.
## Concept
+
Each game exists of `objects` which are either in the players inventory or in `locations`. The player can interact with object using `verbs`.
### Locations
+
Locations are basically groups of `objects`.
The description text of a location soley exists of the objects which can be found in it. This means an empty location has no description. Thus a location should always have at least one object, at any given moment.
### Verbs
+
Verbs are commands that the user can type. \
- Each verb has a...
- - a name
- - list of synonyms (`words`)
- - a text that is shown, in case the verb can't be used with the object the user mentioned (`failure`) \
- E.G. if the user tries to 'open' an object, that can't be opened.
+ Each verb has a...
+
+- a name
+- list of synonyms (`words`)
+- a text that is shown, in case the verb can't be used with the object the user mentioned (`failure`) \
+ E.G. if the user tries to 'open' an object, that can't be opened.
### Objects
-Everything that player can see or interact with is an object.
+
+Everything that the player can see or interact with is an object.
Each object has...
+
- a unique name
- a list of `words` that the player can type to refer to this object
-- an optional text that is added to the location description if the object is in the players current location or in his inventory (`locationDescription`)
+- an optional text that is added to the location description if the object is in the players current location or in their inventory (`locationDescription`)
- a list of `actions` which describes the `verbs` that can be used with this object. \
- each of these actions has...
- - a `text` that will be shown if the verb is used with this object
- - zero, one or more functions listed under `action`, which can be used to change the current location and its objects (see Functions)
- - a list of 'usableObjects' *which is currently unused*. \
- It is designed to implement usage of object with other objects.
+ each of these actions has...
+ - a `text` that will be shown if the verb is used with this object
+ - zero, one or more functions listed under `commands`, which can be used to change the current location and its objects (see Commands)
+ - a list of 'usableObjects' _which is currently unused_. \
+ It is designed to implement usage of object with other objects.
#### Changing Objects
+
In almost every game there are scenarios where objects have to change their description texts or behaviors during gameplay. For example if you need a `chest` which the player can open only once, after that it will be open.
In this case you have to create a second object `chest_opened`, which has it's own description and verbs it can handle.
@@ -73,50 +110,138 @@ Now you can use the `objectReplaceInLocation` function in `chest`s `open` action
To close the chest again, you can use `objectReplaceInLocation` again in `chest_opened`s `close` action.
-### Functions
-Functions can be used in actions to modify the current location or the plazers inventory.
+#### Object specific failure texts
+
+If the player tries to do soemthing with an object and the action is not defined, the engine will output the default verb failure sentance. In some cases you might find it more immersive to have an object specfic failure text, though verbs have no object specifc failures, as they usually will vary by object.
+
+So In this case, you simply have to add the verb as an action to the object and add your failure message as text to the action.
+
+### Placeholders
+When adding a text to an action, you can use predefined placeholders which will be filled in automatically when the text is written. If the placeholder does not apply in the given context, the value is not replaced.
+
+Existing placeholders are:
+- `{verb}` is replaced with the word the player used to describe the verb / action
+- `{object}` is replaced with the word the player used to describe the object
+
+### Commands
+
+Commands must be used for any logic that goes beyond outputting text. You can change locations, add and remove objects from location or the players inventory and more.
+
+#### 'this' in command parameters
+
+If the command is supposed to affect the object that the action is defined on, you can refer to it using `this` instead of its unique nanme.
#### objectRemoveFromLocation
- ```
-objectRemoveFromLocation {objectId}
- ```
+
+```
+objectRemoveFromLocation {objectName}
+objectRemoveFromLocation this
+```
+
Removes the given object from the current location
#### objectAddToLocation
- ```
-objectAddToLocation {objectId}
- ```
+
+```
+objectAddToLocation {objectName}
+objectAddToLocation this
+```
+
Adds a given object to the current location
#### objectReplaceInLocation
- ```
-objectReplaceInLocation {objectIdToRemove} {objectIdToAdd}
- ```
-Removes `{objectIdToRemove}` and adds `{objectIdToAdd}`. Shorthand for sequentially calling `objectRemoveFromLocation` and `objectAddToLocation`.
+
+```
+objectReplaceInLocation {objectNameToRemove} {objectNameToAdd}
+objectReplaceInLocation this {objectNameToAdd}
+```
+
+Removes `{objectNameToRemove}` and adds `{objectNameToAdd}`. Shorthand for sequentially calling `objectRemoveFromLocation` and `objectAddToLocation`.
Useful if an object transitions into a different one like `chest_closed` to `chest_opened`.
#### gotoLocation
- ```
-gotoLocation {locationId}
- ```
+
+```
+gotoLocation {locationName}
+```
+
Changes the current location to a different one
#### showLocationDescription
- ```
+
+```
showLocationDescription
- ```
+```
+
Automatically shows the current location description, as if the user typed 'look'
#### inventoryAdd
- ```
-inventoryAdd {objectId}
- ```
-Adds the item to users inventory.
-The user can have multiple items in his inventory, the location descriptions will be listed below each other.
+
+```
+inventoryAdd {objectName}
+inventoryAdd this
+```
+
+Adds the item to users inventory.
+The player can have multiple items in their inventory, the location description will list them sequentially, after the objects in the location itself.
#### inventoryRemove
- ```
-inventoryRemove {objectId}
- ```
-Removes the item from users inventory
\ No newline at end of file
+
+```
+inventoryRemove {objectName}
+inventoryRemove this
+```
+
+Removes the item from users inventory
+
+#### restartGame
+
+```
+restartGame
+```
+
+Restarts the game. If a game save exits, it is deleted.
+Can be used to restart the game on demand or when the game was finished.
+
+### Analytics
+
+TextAdventureJS does not come with any analytics. Though it allows to provide a function which is then called for pre-defined analytics related events. The events are all related to the command parser, with the intention to improve the games based on player data.
+
+Defined events are:
+
+- `command`: a command was successfully parsed
+- `unknown_verb`: the user tried to use a verb that is not defined
+- `unknwon_object`: the user tried to use an object that is not present in the players current location or inventory.
+- `unkown_verb_for_object`: the user tried to do something with an object that is not defined
+- `unknown_command`: other parsing error
+
+Each event contains a body, that includes:
+
+- `input`: the full command the user entered
+- `currentLocation`: name of the current location
+- `location`: list of all object in the current location
+- `inventory`: list of all objects in the players inventory
+
+Example Analytics Function:
+
+```js
+function analyticsFunction(eventName, eventData) {
+ console.log("Analytics event: " + eventName);
+ console.log(eventData);
+}
+```
+
+## Testing
+
+## Running Editor and Player locally
+Running the editor and player html files locally requires a local webserver.
+I recommend the `ms-vscode.live-server` extension for vscode.
+
+### Unit Tests
+Quick steps to run tests locally:
+
+1. Install Node.js from https://nodejs.org/ if you don't already have it.
+2. From the project root run:
+ - `npm install` (installs dev dependencies like Jest)
+ - `npm test` (runs the test suite)
\ No newline at end of file
diff --git a/Templates/empty.tadb.json b/Templates/empty.tadb.json
index 97c9541..e49f5a8 100644
--- a/Templates/empty.tadb.json
+++ b/Templates/empty.tadb.json
@@ -1,22 +1,23 @@
{
- "$schema": "../textAdventureDatabase.schema.json",
+ "$schema": "../textAdventureGameDatabase.schema.json",
"general": {
"title": "TextAdventureJS Empty Database Template",
"author": "Your Name",
"version": "0.1",
- "request": [ "", "What do you do?" ],
+ "continue_enabled": true,
+ "request": ["", "What do you do?"],
"start": {
- "text": "Welcome to the Empty Template",
+ "text": ["Welcome to the Empty Template"],
"inventory": "",
- "action": "gotoLocation first_room"
- }
- },
- "ignored_words" :[ ],
- "verbs": {
- },
- "objects": {
+ "commands": []
+ },
+ "parser_ignored_words": [],
+ "parser_error_text": "Sorry, I didn't understand that.",
+ "parser_unknown_verb_text": "Sorry, I don't know how to do that."
},
+ "verbs": {},
+ "objects": {},
"locations": {
"first_room": {
"objects": []
diff --git a/Templates/miniExample.tadb.json b/Templates/miniExample.tadb.json
deleted file mode 100644
index 1c394fb..0000000
--- a/Templates/miniExample.tadb.json
+++ /dev/null
@@ -1,67 +0,0 @@
-{
- "$schema": "../textAdventureDatabase.schema.json",
- "general": {
- "title": "TextAdventureJS Database Mini Example",
- "author": "Daniel Korgel",
- "version": "0.1",
- "request": [ "", "What do you do?" ],
- "start": {
- "text": "Welcome to the textAdventureJS Mini Example",
- "action": "gotoLocation template_room"
- }
- },
- "ignored_words": [ "to", "through", "on", "off", "from", "around", "at" ],
- "verbs": {
- "pickup": {
- "failure": "Sorry you can't see that.",
- "words": [ "pick", "lift", "take", "get" ]
- }
- },
- "objects": {
- "template_object": {
- "words": [ "pillow", "pill" ],
- "locationDescription": "A pillow is lying on the floor",
- "actions": {
- "pickup": {
- "text": "okay got it",
- "action": [
- "objectRemoveFromLocation template_object",
- "objectState pickedUp",
- "inventoryAdd template_object"
- ]
- },
- "look": {
- "text": "soft and comfy",
- "action": ""
- }
- },
- "useableObjects": { }
- },
- "template_object_pickedUp": {
- "words": [ "pillow", "pill" ],
- "locationDescription": "You are holding pillow in your hand.",
- "actions": {
- "drop": {
- "text": "okay dropped it",
- "action": [
- "objectAddToLocation template_object",
- "objectState default",
- "inventoryRemove template_object"
- ]
- },
- "look": {
- "text": "soft and comfy",
- "action": ""
- }
- },
- "useableObjects": { }
- }
- },
- "locations": {
- "template_room": {
- "objects": [
- "template_object"
- ]
- }
- }
-}
\ No newline at end of file
diff --git a/Templates/new_project.tadb.json b/Templates/new_project.tadb.json
new file mode 100644
index 0000000..df69190
--- /dev/null
+++ b/Templates/new_project.tadb.json
@@ -0,0 +1,189 @@
+{
+ "$schema": "../textAdventureGameDatabase.schema.json",
+ "general": {
+ "title": "New TextAdventureJS Game",
+ "author": "Your Name",
+ "version": "0.1",
+ "continue_enabled": true,
+ "request": ["", "What do you do?"],
+ "start": {
+ "text": ["Welcome to the textAdventureJS Mini Example"],
+ "commands": ["gotoLocation first_room"]
+ },
+ "parser_ignored_words": [
+ "to",
+ "through",
+ "on",
+ "off",
+ "from",
+ "around",
+ "at"
+ ],
+ "parser_error_text": "Sorry, I didn't understand that.",
+ "parser_unknown_verb_text": "Sorry, I don't know how to do that."
+ },
+ "verbs": {
+ "look": {
+ "failure": "Sorry, you can't see that.",
+ "words": ["look", "view", "watch", "read", "search"],
+ "standalone_action": {
+ "text": [],
+ "commands": ["showLocationDescription"]
+ }
+ },
+ "restart": {
+ "failure": "Be careful! 'restart' will reset the game.",
+ "words": ["restart"],
+ "standalone_action": {
+ "text": [],
+ "commands": ["restartGame"]
+ }
+ },
+ "pickup": {
+ "failure": "Sorry, you can't see that.",
+ "words": ["pick", "pickup", "lift", "take", "get", "reach"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "drop": {
+ "failure": "How do you want to drop that?",
+ "words": ["drop", "let go"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "activate": {
+ "failure": "Sorry, you can't do that.",
+ "words": ["turn", "power", "switch"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "go": {
+ "failure": "Sorry, you can't go there.",
+ "words": ["go", "walk", "run", "goto"],
+ "standalone_action": {
+ "text": ["Where do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "open": {
+ "failure": "Sorry, you can't open that.",
+ "words": ["open", "break"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "close": {
+ "failure": "Sorry, you can't close that.",
+ "words": ["close", "shut"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "jump": {
+ "failure": "Sorry, this is not possible.",
+ "words": ["jump"],
+ "standalone_action": {
+ "text": ["You jump up and down."],
+ "commands": []
+ }
+ },
+ "use": {
+ "failure": "Well.. this does not work.",
+ "words": ["use", "combine", "throw", "attack"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "light": {
+ "failure": "Well.. this does not work.",
+ "words": ["light", "ignite", "burn", "fire"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "remove": {
+ "failure": "Well.. this does not work.",
+ "words": ["remove", "break", "split", "destroy"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ }
+ },
+ "objects": {
+ "template_object": {
+ "words": ["object", "obj"],
+ "locationDescription": "An object is lying on the floor.",
+ "actions": {
+ "pickup": {
+ "text": ["Okay got it."],
+ "commands": [
+ "objectRemoveFromLocation this",
+ "inventoryAdd template_object_pickedUp"
+ ]
+ },
+ "look": {
+ "text": ["A mysterious object."],
+ "commands": []
+ }
+ },
+ "useableObjects": {}
+ },
+ "template_object_pickedUp": {
+ "words": ["object", "obj"],
+ "locationDescription": "You are holding an object in your hands.",
+ "actions": {
+ "drop": {
+ "text": ["Okay dropped it."],
+ "commands": [
+ "objectAddToLocation template_object",
+ "inventoryRemove this"
+ ]
+ },
+ "look": {
+ "text": ["A mysterious object."],
+ "commands": []
+ }
+ },
+ "useableObjects": {}
+ },
+ "door_to_second_room": {
+ "words": ["door", "north", "up"],
+ "locationDescription": "A door is leading north.",
+ "actions": {
+ "go": {
+ "text": ["You walk through the door."],
+ "commands": ["gotoLocation second_room"]
+ }
+ }
+ },
+ "door_to_first_room": {
+ "words": ["door", "south", "down"],
+ "locationDescription": "A door is leading south.",
+ "actions": {
+ "go": {
+ "text": ["You walk through the door."],
+ "commands": ["gotoLocation first_room"]
+ }
+ }
+ }
+ },
+ "locations": {
+ "first_room": {
+ "objects": ["template_object", "door_to_second_room"]
+ },
+ "second_room": {
+ "objects": ["door_to_first_room"]
+ }
+ }
+}
diff --git a/__tests__/textAdventureEngine.test.js b/__tests__/textAdventureEngine.test.js
new file mode 100644
index 0000000..1a7a6c9
--- /dev/null
+++ b/__tests__/textAdventureEngine.test.js
@@ -0,0 +1,162 @@
+const fs = require("fs");
+const path = require("path");
+
+let sampleGame;
+let found = false;
+const p = path.join(__dirname, "..", "templates", "new_project.tadb.json");
+if (fs.existsSync(p)) {
+ sampleGame = JSON.parse(fs.readFileSync(p, "utf8"));
+ found = true;
+} else {
+ throw new Error("Could not find templates/new_project.tadb.json.");
+}
+const TextAdventureEngine = require("../textAdventure.js");
+
+describe("textAdventureEngine parser & action integration tests (using template)", () => {
+ let outputs = [];
+ let outputFn;
+ let clearFn;
+ let analyticsFn;
+ let engine;
+
+ beforeEach(() => {
+ outputs = [];
+ outputFn = jest.fn((line) => outputs.push(line));
+ clearFn = jest.fn(() => {
+ outputs = [];
+ });
+ analyticsFn = jest.fn();
+
+ // clear localStorage
+ if (
+ global.localStorage &&
+ typeof global.localStorage.clear === "function"
+ ) {
+ global.localStorage.clear();
+ }
+
+ engine = new TextAdventureEngine(outputFn, clearFn, analyticsFn);
+ engine.loadDatabaseFromObject(sampleGame);
+
+ // tests start with a clean output buffer
+ outputs.length = 0;
+ jest.clearAllMocks();
+ });
+
+ test("look object outputs object description", () => {
+ engine.input("look object");
+ const found = outputs.find((l) => l.includes("A mysterious object."));
+ expect(found).toBeDefined();
+ });
+
+ test("take object moves it to inventory and removes from location", () => {
+ engine.input("take object");
+ const gs = engine.devGetGameState();
+ expect(gs.inventory).toContain("template_object_pickedUp");
+ const objects = gs.locations[gs.currentLocation].objects;
+ expect(objects).not.toContain("template_object");
+ });
+
+ test("drop object returns it to location", () => {
+ engine.input("take object");
+ engine.input("drop object");
+ const gs = engine.devGetGameState();
+ expect(gs.inventory).not.toContain("template_object_pickedUp");
+ expect(gs.locations[gs.currentLocation].objects).toContain(
+ "template_object"
+ );
+ });
+
+ test("non-look actions save to localStorage when enabled", () => {
+ engine.input("take object");
+ const key = (sampleGame.author + "_" + sampleGame.general.title)
+ .replace(/\s+/g, "_")
+ .toLowerCase();
+ const stored = localStorage.getItem(key);
+ expect(stored).not.toBeNull();
+ });
+
+ test("go north moves player to second_room and shows descriptions", () => {
+ engine.input("go north");
+ const gs = engine.devGetGameState();
+ expect(gs.currentLocation).toBe("second_room");
+ const walked = outputs.find((l) =>
+ l.includes("You walk through the door.")
+ );
+ expect(walked).toBeDefined();
+ const desc = outputs.find((l) =>
+ l.includes("A door is leading south.")
+ );
+ expect(desc).toBeDefined();
+ });
+
+ test("go unknown location shows failure", () => {
+ engine.input("go nowhere");
+ const found = outputs.find((l) =>
+ l.includes(sampleGame.verbs.go.failure)
+ );
+ expect(found).toBeDefined();
+ });
+
+ // Additional parser edge-case tests
+ test('ignored words like "at" are stripped', () => {
+ engine.input("look at object");
+ const found = outputs.find((l) => l.includes("A mysterious object."));
+ expect(found).toBeDefined();
+ });
+
+ test("unknown verb with known object shows parser_unknown_verb_text", () => {
+ engine.input("foo object");
+ const found = outputs.find((l) =>
+ l.includes(sampleGame.general.parser_unknown_verb_text)
+ );
+ expect(found).toBeDefined();
+ });
+
+ test("verb with unknown object shows verb failure", () => {
+ engine.input("take nothing");
+ const failure = sampleGame.verbs.pickup.failure;
+ const found = outputs.find((l) => l.includes(failure));
+ expect(found).toBeDefined();
+ });
+
+ test("standalone verb prints standalone text", () => {
+ engine.input("jump");
+ const found = outputs.find((l) => l.includes("You jump up and down."));
+ expect(found).toBeDefined();
+ });
+
+ test("analytics function is called on command", () => {
+ engine.input("take object");
+ expect(analyticsFn).toHaveBeenCalledWith(
+ expect.any(String),
+ expect.objectContaining({
+ input: expect.any(String),
+ currentLocation: expect.any(String),
+ inventory: expect.any(Array),
+ })
+ );
+ });
+
+ test("game resumes from storage when continue_enabled true", () => {
+ // make a change and ensure it was saved
+ engine.input("take object");
+ const key = (sampleGame.author + "_" + sampleGame.general.title)
+ .replace(/\s+/g, "_")
+ .toLowerCase();
+ const stored = localStorage.getItem(key);
+ expect(stored).not.toBeNull();
+
+ // create a new engine and load the same database; it should resume from storage
+ const outputs2 = [];
+ const engine2 = new TextAdventureEngine(
+ (l) => outputs2.push(l),
+ () => (outputs2.length = 0),
+ () => {}
+ );
+ engine2.loadDatabaseFromObject(sampleGame);
+ expect(engine2.devGetGameState().inventory).toContain(
+ "template_object_pickedUp"
+ );
+ });
+});
diff --git a/css/channel-shift.css b/css/channel-shift.css
new file mode 100644
index 0000000..e864228
--- /dev/null
+++ b/css/channel-shift.css
@@ -0,0 +1,22 @@
+.channel-shift {
+ position: relative;
+ padding: 0 0 0 0;
+ &:before,
+ &:after {
+ top: 0;
+ position: absolute;
+ left: 0;
+ padding: 0 0 0 0;
+ display: block;
+ mix-blend-mode: screen;
+ content: attr(data-text);
+ }
+ &:before {
+ color: #ff3c74;
+ transform: translate(-1px, 1px);
+ }
+ &:after {
+ transform: translate(1px, -1px);
+ color: #62c4ff;
+ }
+}
diff --git a/docs/editor.jpg b/docs/editor.jpg
new file mode 100644
index 0000000..bedf7ea
Binary files /dev/null and b/docs/editor.jpg differ
diff --git a/docs/player.gif b/docs/player.gif
new file mode 100644
index 0000000..4e3958d
Binary files /dev/null and b/docs/player.gif differ
diff --git a/editor/editor-style.css b/editor/editor-style.css
new file mode 100644
index 0000000..62445f9
--- /dev/null
+++ b/editor/editor-style.css
@@ -0,0 +1,644 @@
+/* Sticky footer styles
+-------------------------------------------------- */
+html {
+ position: relative;
+ min-height: 100%;
+}
+
+.container {
+ width: auto;
+ padding: 0 15px;
+}
+
+.input-pair-container {
+ display: flex;
+ width: 100%;
+}
+.left-pair-element {
+ flex-grow: 1;
+ margin-right: 10px;
+}
+
+/* Form table styles: stronger separators, subtle zebra, no hover highlighting */
+table.w100 {
+ width: 100%;
+ border-collapse: separate;
+ border-spacing: 0;
+}
+table.w100 td {
+ padding: 8px 10px;
+ border: none; /* remove any visible white borders */
+ vertical-align: middle;
+}
+table.w100 tr {
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
+ background: transparent;
+}
+table.w100 tr.section-title td {
+ padding: 12px 8px 8px;
+ background: transparent;
+}
+
+/* Headline variants for headings inside tables */
+table.w100 tr.section-title td h1,
+table.w100 td h1 {
+ margin: 0;
+ display: inline-block;
+ background: rgba(98, 196, 255, 0.9);
+ color: var(--invert-font-color);
+ padding: 8px 14px;
+ border-radius: 10px;
+ box-shadow: 0 3px 8px rgba(0, 0, 0, 0.45);
+ font-size: 1.15em;
+ font-weight: 700;
+}
+
+table.w100 tr.section-title td h2,
+table.w100 td h2 {
+ margin: 0;
+ display: inline-block;
+ background: rgb(75, 151, 198);
+ color: var(--font-color);
+ padding: 4px 8px;
+ border-radius: 6px;
+ font-size: 1em;
+ font-weight: 600;
+}
+
+table.w100 tr.section-title td h3,
+table.w100 td h3 {
+ margin: 0;
+ display: inline-block;
+ background: rgb(171, 66, 115);
+ color: var(--font-color);
+ padding: 4px 8px;
+ border-radius: 6px;
+ font-size: 1em;
+ font-weight: 600;
+}
+
+table.w100 tr.section-title td h4,
+table.w100 td h4 {
+ margin: 0;
+ display: inline-block;
+ background: rgb(16, 94, 98);
+ color: var(--font-color);
+ padding: 4px 8px;
+ border-radius: 6px;
+ font-size: 1em;
+ font-weight: 600;
+}
+
+table.w100 tr.section-title td h5,
+table.w100 td h5 {
+ margin: 0;
+ display: inline-block;
+ background: linear-gradient(
+ 90deg,
+ var(--error-color),
+ rgba(255, 60, 116, 0.95)
+ );
+ color: var(--invert-font-color);
+ padding: 4px 8px;
+ border-radius: 6px;
+ font-size: 0.95em;
+ font-weight: 600;
+}
+table.w100 tr:nth-child(odd) {
+ background: rgba(255, 255, 255, 0.02);
+}
+
+.w100 {
+ width: 100%;
+}
+.w90 {
+ width: 90%;
+}
+.w80 {
+ width: 80%;
+}
+.w70 {
+ width: 70%;
+}
+.w60 {
+ width: 60%;
+}
+.w50 {
+ width: 50%;
+}
+.w40 {
+ width: 40%;
+}
+.w30 {
+ width: 30%;
+}
+.w20 {
+ width: 20%;
+}
+.w15 {
+ width: 15%;
+}
+.w10 {
+ width: 10%;
+}
+
+#editor {
+ display: grid;
+ grid-template-areas: "sidebar main";
+ grid-template-columns: 300px auto;
+ width: 100%;
+}
+
+/* Sidebar and main area should scroll and size with viewport */
+#elementSelection {
+ grid-area: sidebar;
+ min-height: calc(100vh - 220px);
+ overflow: auto;
+ background: rgba(255, 255, 255, 0.02);
+ padding: 8px;
+ border-right: 1px solid rgba(255, 255, 255, 0.04);
+}
+
+/* Drag & drop area */
+.file-drop-area {
+ margin: 18px 0;
+ padding: 36px 24px;
+ border-radius: 12px;
+ border: 2px dashed rgba(255, 255, 255, 0.06);
+ background: linear-gradient(
+ 180deg,
+ rgba(255, 255, 255, 0.01),
+ rgba(255, 255, 255, 0)
+ );
+ text-align: center;
+ transition: box-shadow 0.12s ease, transform 0.06s ease,
+ border-color 0.12s ease, background 0.12s ease;
+ cursor: pointer;
+}
+.file-drop-area h1 {
+ margin: 0 0 12px 0;
+ font-size: 1.05rem;
+ color: var(--primary-color);
+ letter-spacing: 1px;
+}
+.file-drop-area .muted {
+ color: var(--secondary-color);
+ font-weight: 600;
+ font-size: 0.95rem;
+}
+.file-drop-area p {
+ margin: 0;
+ color: var(--secondary-color);
+}
+.file-drop-area .btn {
+ margin-top: 12px;
+}
+
+.file-drop-area.drag-active {
+ box-shadow: 0 10px 40px rgba(98, 196, 255, 0.06);
+ border-color: rgba(98, 196, 255, 0.9);
+ background: linear-gradient(
+ 180deg,
+ rgba(98, 196, 255, 0.03),
+ rgba(98, 196, 255, 0.01)
+ );
+ transform: translateY(-2px);
+}
+.file-drop-area:hover {
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.06);
+ border-color: rgba(255, 255, 255, 0.08);
+ transform: translateY(-1px);
+}
+
+/* Small helper text for the drop area */
+.file-drop-area .hint {
+ display: block;
+ margin-top: 8px;
+ color: var(--secondary-color);
+ font-size: 0.95rem;
+}
+.file-drop-area:focus {
+ outline: none;
+ box-shadow: 0 10px 40px rgba(98, 196, 255, 0.06);
+}
+
+#elementEditor {
+ padding: 20px;
+ grid-area: main;
+ min-height: calc(100vh - 220px);
+ overflow: auto;
+ background: transparent;
+}
+
+/* Simple modal form styles */
+#editModalDialog input[type="text"],
+#editModalDialog textarea,
+#editModalDialog select {
+ width: 100%;
+ padding: 6px 8px;
+ margin: 6px 0 12px 0;
+ box-sizing: border-box;
+}
+
+/* Make selects readable on dark background */
+select,
+select option,
+select optgroup {
+ background: rgb(35, 35, 35);
+ color: var(--font-color);
+ border: 1px solid rgba(255, 255, 255, 0.04);
+ padding: 6px 8px;
+}
+
+/* Ensure selected option is clearly highlighted even when select is not focused */
+#elementSelection select option:checked,
+#elementSelection select option[selected] {
+ background: var(--primary-color);
+ color: #fff;
+}
+
+select::-ms-expand {
+ display: none;
+}
+
+.modal-action-row {
+ padding: 8px 0;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.03);
+}
+.modal-action-row:last-child {
+ border-bottom: none;
+}
+
+.list-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 8px 10px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.03);
+ background: transparent;
+ color: var(--font-color);
+ transition: background 0.15s ease;
+}
+.list-item .label {
+ flex: 1;
+}
+.list-item .actions {
+ margin-left: 8px;
+}
+.small-btn {
+ padding: 6px 8px;
+ font-size: 0.9em;
+ border-radius: 4px;
+ margin-left: 6px;
+}
+
+/* Inline group for per-object buttons */
+.object-list {
+ table-layout: fixed;
+}
+.object-list .btn-group-inline {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+}
+.object-list td {
+ vertical-align: middle;
+}
+.object-list td:first-child {
+ width: 25%;
+ max-width: 25%;
+ white-space: normal;
+ overflow-wrap: break-word;
+ word-break: break-word;
+}
+
+/* Functions list: render as a two-column table (function text + buttons) */
+.function-list-table {
+ width: 100%;
+ border-collapse: separate;
+ border-spacing: 0;
+}
+.function-list-table td {
+ padding: 8px 10px;
+ vertical-align: middle;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.03);
+}
+.function-list-table td:first-child {
+ width: 75%;
+ max-width: 75%;
+ white-space: normal;
+ overflow-wrap: break-word;
+ word-break: break-word;
+}
+.function-list-table .func-text {
+ display: block;
+}
+.function-list-table .btn-group-inline {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+ justify-content: flex-end;
+}
+.function-list-table tr:first-child td {
+ padding-top: 4px;
+}
+.function-list-table tr:last-child td {
+ padding-bottom: 4px;
+}
+/* Generic left column class for other tables */
+.left-col {
+ width: 25%;
+ max-width: 25%;
+ white-space: normal;
+ overflow-wrap: break-word;
+ word-break: break-word;
+}
+
+.list-item:hover {
+ background: rgba(255, 255, 255, 0.02);
+}
+
+/* Modal styles */
+.modal {
+ display: none;
+ position: fixed;
+ inset: 0;
+ align-items: center;
+ justify-content: center;
+ z-index: 9999;
+}
+.modal-backdrop {
+ position: absolute;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.6);
+}
+.modal-dialog {
+ position: relative;
+ background: var(--code-bg-color);
+ color: var(--font-color);
+ max-width: 900px;
+ width: min(900px, 95%);
+ padding: 18px;
+ border-radius: 8px;
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.6);
+ max-height: 80vh;
+ overflow: auto;
+}
+.modal-body {
+ margin-top: 8px;
+}
+.modal-actions {
+ margin-top: 12px;
+ text-align: right;
+}
+
+/* Buttons */
+.btn {
+ background: transparent;
+ color: var(--font-color);
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ padding: 8px 12px;
+ border-radius: 6px;
+ cursor: pointer;
+}
+.btn:hover {
+ opacity: 0.95;
+}
+.btn-primary {
+ background: var(--primary-color);
+ color: var(--font-color);
+ border: none;
+}
+.btn-error {
+ background: var(--error-color);
+ color: var(--font-color);
+ border: none;
+}
+.btn-default {
+ background: rgba(255, 255, 255, 0.03);
+}
+
+/* Prominent action buttons (Save / Close) */
+.large-action {
+ padding: 10px 16px;
+ font-weight: 700;
+ font-size: 1rem;
+ border-radius: 8px;
+ box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
+ transition: transform 0.08s ease, box-shadow 0.08s ease;
+}
+#btn-save.large-action {
+ background: var(--primary-color);
+ color: var(--font-color);
+ border: none;
+}
+#btn-close.large-action {
+ background: var(--error-color);
+ color: var(--font-color);
+ border: none;
+}
+#btn-save.large-action:hover,
+#btn-close.large-action:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 10px 24px rgba(0, 0, 0, 0.28);
+}
+/* Slight spacing so they don't blend into the menu */
+#btn-save,
+#btn-close {
+ margin-left: 8px;
+}
+
+/* Tabs */
+.editor-tab {
+ color: var(--secondary-color);
+ cursor: pointer;
+ text-decoration: none;
+ padding: 4px 8px;
+ border-radius: 6px;
+ transition: background 0.12s ease, color 0.12s ease;
+}
+.editor-tab:hover {
+ color: var(--font-color);
+ background: rgba(255, 255, 255, 0.03);
+}
+.editor-tab.active {
+ background: var(--primary-color);
+ color: var(--invert-font-color);
+ font-weight: 600;
+ text-decoration: none;
+}
+.editor-tab.active:hover {
+ background: var(--primary-color);
+ color: var(--invert-font-color);
+}
+.editor-tab + .editor-tab {
+ margin-left: 8px;
+}
+
+/* Inputs in modal—dark friendly */
+#editModalDialog input[type="text"],
+#editModalDialog textarea,
+#editModalDialog select {
+ width: 100%;
+ padding: 8px 10px;
+ margin: 6px 0 12px 0;
+ box-sizing: border-box;
+ background: rgba(255, 255, 255, 0.03);
+ color: var(--font-color);
+ border: 1px solid rgba(255, 255, 255, 0.04);
+}
+
+/* Modern textarea used in editor forms */
+.modern-textarea {
+ width: 100%;
+ min-height: 36px; /* single-line default */
+ max-height: 400px; /* sensible upper bound */
+ overflow: hidden; /* hide scrollbars, we'll auto expand */
+ resize: none;
+ padding: 8px 10px;
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ border-radius: 6px;
+ color: var(--font-color);
+ line-height: 1.4;
+ font-family: var(--mono-font-stack);
+ transition: box-shadow 0.12s ease, border-color 0.12s ease;
+}
+.modern-textarea:focus {
+ outline: none;
+ border-color: var(--primary-color);
+ box-shadow: 0 6px 18px rgba(98, 196, 255, 0.08);
+}
+
+/* Modern single-line inputs that match textarea styling */
+.modern-input {
+ width: 100%;
+ height: 36px;
+ padding: 8px 10px;
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ border-radius: 6px;
+ color: var(--font-color);
+ box-sizing: border-box;
+ line-height: 1.4;
+ font-family: var(--mono-font-stack);
+ transition: box-shadow 0.12s ease, border-color 0.12s ease;
+ -webkit-appearance: none;
+ appearance: none;
+}
+.modern-input:focus {
+ outline: none;
+ border-color: var(--primary-color);
+ box-shadow: 0 6px 18px rgba(98, 196, 255, 0.08);
+}
+
+/* Ensure selects with modern-input and textarea match and override terminal defaults */
+input.modern-input,
+select.modern-input,
+textarea.modern-textarea {
+ -webkit-appearance: none;
+ appearance: none;
+ -moz-appearance: none;
+ outline: none;
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+}
+
+/* Style single-line selects to show a custom dropdown arrow and padding */
+select:not([size]),
+select.modern-input {
+ padding-right: 40px; /* space for arrow */
+ background-image: url("data:image/svg+xml;utf8, ");
+ background-repeat: no-repeat;
+ background-position: calc(100% - 10px) center;
+ background-size: 12px 12px;
+ -webkit-appearance: none;
+ appearance: none;
+}
+
+/* Hide the native dropdown arrow in IE/Edge */
+select::-ms-expand {
+ display: none;
+}
+
+input.modern-input:focus,
+select.modern-input:focus,
+textarea.modern-textarea:focus,
+input.modern-input:focus-visible,
+select.modern-input:focus-visible,
+textarea.modern-textarea:focus-visible {
+ outline: none;
+ border-color: var(--primary-color) !important;
+ box-shadow: 0 6px 18px rgba(98, 196, 255, 0.08) !important;
+}
+
+/* Modern styled checkboxes that match the theme */
+input[type="checkbox"] {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 18px;
+ height: 18px;
+ border: 1px solid rgba(255,255,255,0.18);
+ border-radius: 6px;
+ background: linear-gradient(180deg, rgba(255,255,255,0.045), rgba(255,255,255,0.02));
+ display: inline-block;
+ vertical-align: middle;
+ position: relative;
+ transition: background-color 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease, transform 0.08s ease;
+ box-shadow: inset 0 -1px 0 rgba(0,0,0,0.25), 0 1px 0 rgba(0,0,0,0.06);
+}
+input[type="checkbox"]:hover {
+ border-color: rgba(255,255,255,0.28);
+ transform: translateY(-1px);
+}
+input[type="checkbox"]:focus {
+ outline: none;
+ box-shadow: 0 6px 18px rgba(98, 196, 255, 0.08);
+ border-color: var(--primary-color);
+}
+input[type="checkbox"]:checked {
+ background-color: var(--primary-color);
+ border-color: var(--primary-color);
+ background-image: url("data:image/svg+xml;utf8, ");
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: 12px 12px;
+ box-shadow: 0 1px 0 rgba(0,0,0,0.2);
+}
+input[type="checkbox"]:not(:checked) {
+ background: linear-gradient(180deg, rgba(255,255,255,0.1), rgba(255,255,255,0.06));
+ border-color: rgba(255,255,255,0.18);
+}
+
+/* Slight spacing for checkbox cells */
+td > input[type="checkbox"] {
+ margin-top: 4px;
+}
+
+
+#validationResults {
+ padding: 8px;
+ border-radius: 6px;
+ margin-top: 6px;
+}
+#validationResults ul {
+ margin: 6px 0 0 18px;
+}
+#validationResults div {
+ font-weight: bold;
+}
+
+/* responsive */
+@media (max-width: 700px) {
+ #editor {
+ grid-template-columns: 1fr;
+ grid-template-areas: "main";
+ }
+ #elementSelection {
+ order: 2;
+ min-height: 200px;
+ }
+ #elementEditor {
+ order: 1;
+ }
+}
diff --git a/editor/ext/terminal0.7.2_.min.css b/editor/ext/terminal0.7.2_.min.css
new file mode 100644
index 0000000..d92ba60
--- /dev/null
+++ b/editor/ext/terminal0.7.2_.min.css
@@ -0,0 +1 @@
+:root{--global-font-size:15px;--global-line-height:1.4em;--global-space:10px;--font-stack:Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;--mono-font-stack:Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;--background-color:#fff;--page-width:60em;--font-color:#151515;--invert-font-color:#fff;--primary-color:#1a95e0;--secondary-color:#727578;--error-color:#d20962;--progress-bar-background:#727578;--progress-bar-fill:#151515;--code-bg-color:#e8eff2;--input-style:solid;--display-h1-decoration:none}*{box-sizing:border-box;text-rendering:geometricPrecision}::-moz-selection{background:var(--primary-color);color:var(--invert-font-color)}::selection{background:var(--primary-color);color:var(--invert-font-color)}body{font-size:var(--global-font-size);color:var(--font-color);line-height:var(--global-line-height);margin:0;font-family:var(--font-stack);word-wrap:break-word;background-color:var(--background-color)}.logo,h1,h2,h3,h4,h5,h6{line-height:var(--global-line-height)}a{cursor:pointer;color:var(--primary-color);text-decoration:none}a:hover{background-color:var(--primary-color);color:var(--invert-font-color)}em{font-size:var(--global-font-size);font-style:italic;font-family:var(--font-stack);color:var(--font-color)}blockquote,code,em,strong{line-height:var(--global-line-height)}.logo,blockquote,code,footer,h1,h2,h3,h4,h5,h6,header,li,ol,p,section,ul{float:none;margin:0;padding:0}.logo,blockquote,h1,ol,p,ul{margin-top:calc(var(--global-space) * 2);margin-bottom:calc(var(--global-space) * 2)}.logo,h1{position:relative;display:inline-block;display:table-cell;padding:calc(var(--global-space) * 2) 0 calc(var(--global-space) * 2);margin:0;overflow:hidden;font-weight:600}h1::after{content:"====================================================================================================";position:absolute;bottom:5px;left:0;display:var(--display-h1-decoration)}.logo+*,h1+*{margin-top:0}h2,h3,h4,h5,h6{position:relative;margin-bottom:var(--global-line-height);font-weight:600}blockquote{position:relative;padding-left:calc(var(--global-space) * 2);padding-left:2ch;overflow:hidden}blockquote::after{content:">\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>\A>";white-space:pre;position:absolute;top:0;left:0;line-height:var(--global-line-height);color:#9ca2ab}code{font-weight:inherit;background-color:var(--code-bg-color);font-family:var(--mono-font-stack)}code::after,code::before{content:"`";display:inline}pre code::after,pre code::before{content:""}pre{display:block;word-break:break-all;word-wrap:break-word;color:var(--secondary-color);background-color:var(--background-color);border:1px solid var(--secondary-color);padding:var(--global-space);white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap}pre code{overflow-x:scroll;padding:0;margin:0;display:inline-block;min-width:100%;font-family:var(--mono-font-stack)}.terminal .logo,.terminal blockquote,.terminal code,.terminal h1,.terminal h2,.terminal h3,.terminal h4,.terminal h5,.terminal h6,.terminal strong{font-size:var(--global-font-size);font-style:normal;font-family:var(--font-stack);color:var(--font-color)}.terminal-prompt{position:relative;white-space:nowrap}.terminal-prompt::before{content:"> "}.terminal-prompt::after{content:"";-webkit-animation:cursor .8s infinite;animation:cursor .8s infinite;background:var(--primary-color);border-radius:0;display:inline-block;height:1em;margin-left:.2em;width:3px;bottom:-2px;position:relative}@-webkit-keyframes cursor{0%{opacity:0}50%{opacity:1}to{opacity:0}}@keyframes cursor{0%{opacity:0}50%{opacity:1}to{opacity:0}}li,li>ul>li{position:relative;display:block;padding-left:calc(var(--global-space) * 2)}nav>ul>li{padding-left:0}li::after{position:absolute;top:0;left:0}ul>li::after{content:"-"}nav ul>li::after{content:""}ol li::before{content:counters(item, ".") ". ";counter-increment:item}ol ol li::before{content:counters(item, ".") " ";counter-increment:item}.terminal-menu li::after,.terminal-menu li::before{display:none}ol{counter-reset:item}ol li:nth-child(n+10)::after{left:-7px}ol ol{margin-top:0;margin-bottom:0}.terminal-menu{width:100%}.terminal-nav{display:flex;flex-direction:column;align-items:flex-start}ul ul{margin-top:0;margin-bottom:0}.terminal-menu ul{list-style-type:none;padding:0!important;display:flex;flex-direction:column;width:100%;flex-grow:1;font-size:var(--global-font-size);margin-top:0}.terminal-menu li{display:flex;margin:0 0 .5em 0;padding:0}ol.terminal-toc li{border-bottom:1px dotted var(--secondary-color);padding:0;margin-bottom:15px}.terminal-menu li:last-child{margin-bottom:0}ol.terminal-toc li a{margin:4px 4px 4px 0;background:var(--background-color);position:relative;top:6px;text-align:left;padding-right:4px}.terminal-menu li a:not(.btn){text-decoration:none;display:block;width:100%;border:none;color:var(--secondary-color)}.terminal-menu li a.active{color:var(--font-color)}.terminal-menu li a:hover{background:0 0;color:inherit}ol.terminal-toc li::before{content:counters(item, ".") ". ";counter-increment:item;position:absolute;right:0;background:var(--background-color);padding:4px 0 4px 4px;bottom:-8px}ol.terminal-toc li a:hover{background:var(--primary-color);color:var(--invert-font-color)}hr{position:relative;overflow:hidden;margin:calc(var(--global-space) * 4) 0;border:0;border-bottom:1px dashed var(--secondary-color)}p{margin:0 0 var(--global-line-height);color:var(--global-font-color)}.container{max-width:var(--page-width)}.container,.container-fluid{margin:0 auto;padding:0 calc(var(--global-space) * 2)}img{width:100%}.progress-bar{height:8px;background-color:var(--progress-bar-background);margin:12px 0}.progress-bar.progress-bar-show-percent{margin-top:38px}.progress-bar-filled{background-color:var(--progress-bar-fill);height:100%;transition:width .3s ease;position:relative;width:0}.progress-bar-filled::before{content:"";border:6px solid transparent;border-top-color:var(--progress-bar-fill);position:absolute;top:-12px;right:-6px}.progress-bar-filled::after{color:var(--progress-bar-fill);content:attr(data-filled);display:block;font-size:12px;white-space:nowrap;position:absolute;border:6px solid transparent;top:-38px;right:0;transform:translateX(50%)}.progress-bar-no-arrow>.progress-bar-filled::after,.progress-bar-no-arrow>.progress-bar-filled::before{content:"";display:none;visibility:hidden;opacity:0}table{width:100%;border-collapse:collapse;margin:var(--global-line-height) 0;color:var(--font-color);font-size:var(--global-font-size)}table td,table th{vertical-align:top;border:1px solid var(--font-color);line-height:var(--global-line-height);padding:calc(var(--global-space)/ 2);font-size:1em}table thead th{font-size:1em}table tfoot tr th{font-weight:500}table caption{font-size:1em;margin:0 0 1em 0}table tbody td:first-child{font-weight:700;color:var(--secondary-color)}.form{width:100%}fieldset{border:1px solid var(--font-color);padding:1em}label{font-size:1em;color:var(--font-color)}input[type=email],input[type=number],input[type=password],input[type=search],input[type=text]{border:1px var(--input-style) var(--font-color);width:100%;padding:.7em .5em;font-size:1em;font-family:var(--font-stack);-webkit-appearance:none;border-radius:0}input[type=email]:active,input[type=email]:focus,input[type=number]:active,input[type=number]:focus,input[type=password]:active,input[type=password]:focus,input[type=search]:active,input[type=search]:focus,input[type=text]:active,input[type=text]:focus{outline:0;-webkit-appearance:none;border:1px solid var(--font-color)}input[type=email]:not(:placeholder-shown):invalid,input[type=number]:not(:placeholder-shown):invalid,input[type=password]:not(:placeholder-shown):invalid,input[type=search]:not(:placeholder-shown):invalid,input[type=text]:not(:placeholder-shown):invalid{border-color:var(--error-color)}input,textarea{color:var(--font-color);background-color:var(--background-color)}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:var(--secondary-color)!important;opacity:1}input::-moz-placeholder,textarea::-moz-placeholder{color:var(--secondary-color)!important;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:var(--secondary-color)!important;opacity:1}input::-ms-input-placeholder,textarea::-ms-input-placeholder{color:var(--secondary-color)!important;opacity:1}input::placeholder,textarea::placeholder{color:var(--secondary-color)!important;opacity:1}textarea{height:auto;width:100%;resize:none;border:1px var(--input-style) var(--font-color);padding:.5em;font-size:1em;font-family:var(--font-stack);-webkit-appearance:none;border-radius:0}textarea:focus{outline:0;-webkit-appearance:none;border:1px solid var(--font-color)}textarea:not(:placeholder-shown):invalid{border-color:var(--error-color)}input:-webkit-autofill,input:-webkit-autofill:focus textarea:-webkit-autofill,input:-webkit-autofill:hover,select:-webkit-autofill,select:-webkit-autofill:focus,select:-webkit-autofill:hover,textarea:-webkit-autofill:hover textarea:-webkit-autofill:focus{border:1px solid var(--font-color);-webkit-text-fill-color:var(--font-color);box-shadow:0 0 0 1000px var(--invert-font-color) inset;-webkit-box-shadow:0 0 0 1000px var(--invert-font-color) inset;transition:background-color 5000s ease-in-out 0s}.form-group{margin-bottom:var(--global-line-height);overflow:auto}.btn{border-style:solid;border-width:1px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;outline:0;padding:.65em 2em;font-size:1em;font-family:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:relative;z-index:1}.btn:active{box-shadow:none}.btn.btn-ghost{border-color:var(--font-color);color:var(--font-color);background-color:transparent}.btn.btn-ghost:focus,.btn.btn-ghost:hover{border-color:var(--tertiary-color);color:var(--tertiary-color);z-index:2}.btn.btn-ghost:hover{background-color:transparent}.btn-block{width:100%;display:flex}.btn-default{background-color:var(--font-color);border-color:var(--invert-font-color);color:var(--invert-font-color)}.btn-default:focus:not(.btn-ghost),.btn-default:hover{background-color:var(--secondary-color);color:var(--invert-font-color)}.btn-default.btn-ghost:focus,.btn-default.btn-ghost:hover{border-color:var(--secondary-color);color:var(--secondary-color);z-index:2}.btn-error{color:var(--invert-font-color);background-color:var(--error-color);border:1px solid var(--error-color)}.btn-error:focus:not(.btn-ghost),.btn-error:hover{background-color:var(--error-color);border-color:var(--error-color)}.btn-error.btn-ghost{border-color:var(--error-color);color:var(--error-color)}.btn-error.btn-ghost:focus,.btn-error.btn-ghost:hover{border-color:var(--error-color);color:var(--error-color);z-index:2}.btn-primary{color:var(--invert-font-color);background-color:var(--primary-color);border:1px solid var(--primary-color)}.btn-primary:focus:not(.btn-ghost),.btn-primary:hover{background-color:var(--primary-color);border-color:var(--primary-color)}.btn-primary.btn-ghost{border-color:var(--primary-color);color:var(--primary-color)}.btn-primary.btn-ghost:focus,.btn-primary.btn-ghost:hover{border-color:var(--primary-color);color:var(--primary-color);z-index:2}.btn-small{padding:.5em 1.3em!important;font-size:.9em!important}.btn-group{overflow:auto}.btn-group .btn{float:left}.btn-group .btn-ghost:not(:first-child){margin-left:-1px}.terminal-card{border:1px solid var(--secondary-color)}.terminal-card>header{color:var(--invert-font-color);text-align:center;background-color:var(--secondary-color);padding:.5em 0}.terminal-card>div:first-of-type{padding:var(--global-space)}.terminal-timeline{position:relative;padding-left:70px}.terminal-timeline::before{content:' ';background:var(--secondary-color);display:inline-block;position:absolute;left:35px;width:2px;height:100%;z-index:400}.terminal-timeline .terminal-card{margin-bottom:25px}.terminal-timeline .terminal-card::before{content:' ';background:var(--invert-font-color);border:2px solid var(--secondary-color);display:inline-block;position:absolute;margin-top:25px;left:26px;width:15px;height:15px;z-index:400}.terminal-alert{color:var(--font-color);padding:1em;border:1px solid var(--font-color);margin-bottom:var(--global-space)}.terminal-alert-error{color:var(--error-color);border-color:var(--error-color)}.terminal-alert-primary{color:var(--primary-color);border-color:var(--primary-color)}@media screen and (max-width:960px){label{display:block;width:100%}pre::-webkit-scrollbar{height:3px}}@media screen and (max-width:480px){form{width:100%}}@media only screen and (min-width:30em){.terminal-nav{flex-direction:row;align-items:center}.terminal-menu ul{flex-direction:row;justify-items:flex-end;align-items:center;justify-content:flex-end;margin-top:calc(var(--global-space) * 2)}.terminal-menu li{margin:0;margin-right:2em}.terminal-menu li:last-child{margin-right:0}}.terminal-media:not(:last-child){margin-bottom:1.25rem}.terminal-media-left{padding-right:var(--global-space)}.terminal-media-left,.terminal-media-right{display:table-cell;vertical-align:top}.terminal-media-right{padding-left:var(--global-space)}.terminal-media-body{display:table-cell;vertical-align:top}.terminal-media-heading{font-size:1em;font-weight:700}.terminal-media-content{margin-top:.3rem}.terminal-placeholder{background-color:var(--secondary-color);text-align:center;color:var(--font-color);font-size:1rem;border:1px solid var(--secondary-color)}figure>img{padding:0}.terminal-avatarholder{width:calc(var(--global-space) * 5);height:calc(var(--global-space) * 5)}.terminal-avatarholder img{padding:0}figure{margin:0}figure>figcaption{color:var(--secondary-color);text-align:center}.hljs{display:block;overflow-x:auto;padding:.5em;background:var(--block-background-color);color:var(--font-color)}.hljs-comment,.hljs-quote{color:var(--secondary-color)}.hljs-variable{color:var(--font-color)}.hljs-built_in,.hljs-keyword,.hljs-name,.hljs-selector-tag,.hljs-tag{color:var(--primary-color)}.hljs-addition,.hljs-attribute,.hljs-literal,.hljs-section,.hljs-string,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type{color:var(--secondary-color)}.hljs-string{color:var(--secondary-color)}.hljs-deletion,.hljs-meta,.hljs-selector-attr,.hljs-selector-pseudo{color:var(--primary-color)}.hljs-doctag{color:var(--secondary-color)}.hljs-attr{color:var(--primary-color)}.hljs-bullet,.hljs-link,.hljs-symbol{color:var(--primary-color)}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
\ No newline at end of file
diff --git a/editor/index.html b/editor/index.html
new file mode 100644
index 0000000..bae78e6
--- /dev/null
+++ b/editor/index.html
@@ -0,0 +1,126 @@
+
+
+
+
+ testAdventureJS Editor
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ textAdventureJS Game Database Editor
+
+
+
+
+
New Project
+
+
DRAG & DROP *.tadb.json HERE
+
or click to browse files
+
+
+
+
+
+
+
diff --git a/editor/tba_editor.js b/editor/tba_editor.js
new file mode 100644
index 0000000..4b7c13d
--- /dev/null
+++ b/editor/tba_editor.js
@@ -0,0 +1,1441 @@
+TBA_DATABASE = undefined;
+
+var filename = undefined;
+var fileHandle = undefined; // FileSystemFileHandle when available (File System Access API)
+
+var textAdv = undefined;
+
+var previewContainer = undefined;
+var previewLog = undefined;
+var previewInputText = undefined;
+var previewInputButton = undefined;
+var previewRestartButton = undefined;
+var previewReloadButton = undefined;
+
+const NEWLINE = "\n"; //"
";
+
+const FUNCTIONS = {
+ showLocationDescription: [],
+ gotoLocation: ["location"],
+ objectRemoveFromLocation: ["object"],
+ objectAddToLocation: ["object"],
+ objectReplaceInLocation: ["object", "object"],
+ inventoryAdd: ["object"],
+ inventoryRemove: ["object"],
+ restartGame: [],
+};
+
+var type;
+
+$(document).ready(function () {
+ // Create a hidden file input to support click-to-open
+ if ($("#fileInput").length === 0) {
+ const hiddenFileInput = $(
+ ' '
+ );
+ $("body").append(hiddenFileInput);
+ hiddenFileInput.on("change", function () {
+ const file = this.files && this.files[0];
+ if (file) {
+ // when the hidden input is used, we don't have a FileSystem handle
+ fileHandle = undefined;
+ importFile(file);
+ }
+ this.value = null;
+ });
+ }
+ $(".file-drop-area").on("dragenter", (e) => {
+ // highlight
+ e.preventDefault();
+ $(".file-drop-area").addClass("drag-active");
+ });
+
+ $(".file-drop-area").on("dragover", (e) => {
+ // Prevent navigation and keep highlight
+ e.preventDefault();
+ $(".file-drop-area").addClass("drag-active");
+ });
+
+ $(".file-drop-area").on("dragleave", (e) => {
+ // remove highlight
+ $(".file-drop-area").removeClass("drag-active");
+ });
+
+ $(".file-drop-area").on("drop", async (e) => {
+ e.preventDefault();
+ $(".file-drop-area").removeClass("drag-active");
+
+ var file = e.originalEvent.dataTransfer.files[0];
+ // drag/drop does not expose a FileSystem handle
+ fileHandle = undefined;
+ importFile(file);
+ });
+
+ // allow clicking the area to open a file picker (hidden input appended below)
+ $(".file-drop-area").on("click", async function (e) {
+ // prevent any default action (e.g., if this is inside a form or link)
+ if (e && typeof e.preventDefault === 'function') e.preventDefault();
+ if (e && typeof e.stopPropagation === 'function') e.stopPropagation();
+
+ // Prefer the File System Access API when available so we can keep a file handle
+ if (window.showOpenFilePicker) {
+ try {
+ const [handle] = await window.showOpenFilePicker({
+ multiple: false,
+ types: [
+ {
+ description: 'JSON',
+ accept: { 'application/json': ['.json', '.tadb.json'] },
+ },
+ ],
+ });
+ try {
+ const file = await handle.getFile();
+ filename = file.name;
+ fileHandle = handle;
+ importFile(file);
+ } catch (fileErr) {
+ console.error('Error reading file from handle:', fileErr);
+ alert('Failed to read selected file.');
+ }
+ } catch (err) {
+ // If the user cancelled the native picker (AbortError) or the action is not allowed,
+ // do not automatically fall back to the hidden input (this was causing the second
+ // picker to appear). Only fall back for unexpected errors.
+ if (err && (err.name === 'AbortError' || err.name === 'NotAllowedError' || err.name === 'SecurityError')) {
+ console.log('Open file picker cancelled by user.');
+ } else {
+ // Unexpected failure, fall back to the classic input element
+ $("#fileInput").click();
+ }
+ }
+ } else {
+ $("#fileInput").click();
+ }
+ });
+ // keyboard support: Enter or Space opens file picker when area is focused
+ $(".file-drop-area").on("keypress", function (e) {
+ if (
+ e.key === "Enter" ||
+ e.key === " " ||
+ e.keyCode === 13 ||
+ e.keyCode === 32
+ ) {
+ e.preventDefault();
+ if (window.showOpenFilePicker) {
+ // mimic click handler behaviour but keep it simple for keyboard
+ $(".file-drop-area").click();
+ } else {
+ $("#fileInput").click();
+ }
+ }
+ });
+ $("#btn-new").click(() => {
+ // Load the inlined default database JSON
+ getDefaultProjectJson().then(defaultJson => {
+ if (defaultJson) {
+ tba_init(defaultJson);
+ } else {
+ alert("Default project JSON not available.");
+ }
+ });
+ });
+ $("#btn-save").click(async () => {
+ await download(JSON.stringify(TBA_DATABASE, null, 2), filename, "application/json");
+ });
+ $("#btn-close").click(() => {
+ if (
+ confirm(
+ "Are you sure you want to close the current project? Unsaved changes will be lost."
+ )
+ ) {
+ TBA_DATABASE = undefined;
+ textAdv = undefined;
+ deleteDatabaseFromStorage();
+ updateEditorState();
+ }
+ });
+ // Make Save / Close buttons more prominent for quick access
+ $("#btn-save").addClass("btn-primary large-action");
+ $("#btn-close").addClass("btn-error large-action");
+
+ loadToDatabaseFromStorage();
+ updateEditorState();
+ if (TBA_DATABASE != undefined) {
+ onTypeChanged();
+ }
+});
+
+function importFile(file) {
+ // Clear any previously stored file handle when importing via drag or input
+ fileHandle = fileHandle && fileHandle.name === file.name ? fileHandle : undefined;
+
+ reader = new FileReader();
+
+ // Handle successful read and catch JSON/initialization errors
+ reader.onload = function (event) {
+ try {
+ console.log(event.target);
+ tba_init(event.target.result);
+ } catch (err) {
+ console.error("Error initializing database from file:", err);
+ alert(
+ "Error loading file '" +
+ (file && file.name ? file.name : "") +
+ "': " +
+ (err && err.message ? err.message : err)
+ );
+ }
+ };
+
+ // Inform the user if reading the file fails or is aborted
+ reader.onerror = function () {
+ console.error("FileReader error:", reader.error);
+ alert(
+ "Error reading file '" +
+ (file && file.name ? file.name : "") +
+ "': " +
+ (reader.error && reader.error.message
+ ? reader.error.message
+ : "Unknown error")
+ );
+ };
+ reader.onabort = function () {
+ alert("File read aborted.");
+ };
+
+ filename = file.name;
+ reader.readAsText(file);
+}
+
+async function download(content, fileName, contentType) {
+ const blob = new Blob([content], { type: contentType });
+
+ // If we have a FileSystemFileHandle (from showOpenFilePicker) or the browser supports
+ // showSaveFilePicker, prefer writing directly to disk.
+ if (window.showSaveFilePicker) {
+ try {
+ let handle = fileHandle;
+ if (!handle) {
+ handle = await window.showSaveFilePicker({
+ suggestedName:
+ fileName ||
+ (TBA_DATABASE && TBA_DATABASE.general
+ ? sanitizeFilename(TBA_DATABASE.general.title) + ".tadb.json"
+ : "game.tadb.json"),
+ types: [
+ {
+ description: "JSON",
+ accept: { "application/json": [".json", ".tadb.json"] },
+ },
+ ],
+ });
+ fileHandle = handle;
+ }
+ const writable = await handle.createWritable();
+ await writable.write(blob);
+ await writable.close();
+
+ // Update filename so subsequent downloads use the same name
+ //if (handle && handle.name) filename = handle.name;
+
+ // Ensure we still have write permission to the handle — request if necessary.
+ try {
+ if (handle && typeof handle.queryPermission === 'function') {
+ let perm = await handle.queryPermission({ mode: 'readwrite' });
+ if (perm !== 'granted') {
+ try {
+ perm = await handle.requestPermission({ mode: 'readwrite' });
+ } catch (permErr) {
+ console.warn('Permission request for file handle failed:', permErr);
+ }
+ }
+ if (perm !== 'granted') {
+ console.warn('File handle is not writable after save; user may need to re-open the file to continue seamless saving.');
+ }
+ }
+ } catch (permCheckErr) {
+ console.warn('Error while checking/requesting file handle permission:', permCheckErr);
+ }
+
+ console.log("File saved via File System Access API: " + filename);
+ return;
+ } catch (err) {
+ // If the user cancelled the save dialog, do not fall back to the classic download.
+ // showSaveFilePicker typically rejects with a DOMException with name 'AbortError'
+ if (err && (err.name === 'AbortError' || err.name === 'NotAllowedError' || err.name === 'SecurityError')) {
+ console.log('Save cancelled by user. No download started.');
+ return;
+ }
+ console.error("Save via File System Access API failed:", err);
+ // Fall back to classic download
+ }
+ }
+
+ // Fallback: create an anchor and trigger a download
+ const a = document.createElement("a");
+ const url = URL.createObjectURL(blob);
+ a.href = url;
+ a.download =
+ fileName ||
+ (TBA_DATABASE && TBA_DATABASE.general
+ ? sanitizeFilename(TBA_DATABASE.general.title) + ".tadb.json"
+ : "game.tadb.json");
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+}
+
+function sanitizeFilename(name) {
+ if (!name) return "game";
+ return name.replace(/[^a-z0-9_\-\.]/gi, "_").replace(/__+/g, "_");
+}
+
+function saveToDatabaseToStorage() {
+ localStorage.setItem("database", JSON.stringify(TBA_DATABASE));
+}
+function loadToDatabaseFromStorage() {
+ var stored = localStorage.getItem("database");
+ if (stored != undefined) {
+ TBA_DATABASE = JSON.parse(stored);
+ }
+}
+function deleteDatabaseFromStorage() {
+ localStorage.removeItem("database");
+}
+
+async function getDefaultProjectJson() {
+ try {
+ const response = await fetch("../templates/new_project.tadb.json");
+ const json = await response.json();
+ return JSON.stringify(json);
+ } catch (err) {
+ console.error("Failed to read default DB:", err);
+ return null;
+ }
+}
+
+function updateEditorState() {
+ if (TBA_DATABASE !== undefined) {
+ $("#drop-area").hide();
+ $("#editor-aera").show();
+ } else {
+ $("#drop-area").show();
+ $("#editor-aera").hide();
+ }
+}
+
+function tba_init(json) {
+ try {
+ TBA_DATABASE = JSON.parse(json);
+ updateEditorState();
+ onTypeChanged();
+ } catch (err) {
+ console.error("Failed to initialize database:", err);
+ alert(
+ "Error parsing database JSON: " +
+ (err && err.message ? err.message : err)
+ );
+ }
+}
+
+function onTypeChanged(typeParam) {
+ if (typeParam !== undefined) {
+ type = typeParam;
+ }
+ if (type === null || type === undefined) {
+ type = "general";
+ }
+
+ // Highlight the active tab
+ $(".editor-tab").removeClass("active");
+ const tabId = "tab" + type.charAt(0).toUpperCase() + type.slice(1);
+ $("#" + tabId).addClass("active");
+
+ $("#elementSelection").html("");
+ if (type == "general") {
+ $("#elementEditor").html(generateUiForGeneral(TBA_DATABASE.general));
+ return;
+ } else if (type == "preview") {
+ $("#elementEditor").html(generateUiForPreview());
+ return;
+ } else {
+ var newArea = $('
');
+ $("#elementSelection").append(newArea);
+ if (type === "verbs") {
+ newArea.append(
+ generateNewButton("Verb", function (name) {
+ if (TBA_DATABASE.verbs[name] !== undefined) {
+ alert("A verb with this name already exists.");
+ return;
+ }
+ const newVerb = getNewVerb(name);
+ TBA_DATABASE.verbs[name] = newVerb;
+ onTypeChanged();
+ })
+ );
+ } else if (type === "objects") {
+ newArea.append(
+ generateNewButton("Object", function (name) {
+ if (TBA_DATABASE.objects[name] !== undefined) {
+ alert("An object with this name already exists.");
+ return;
+ }
+ TBA_DATABASE.objects[name] = getNewObject(name);
+ onTypeChanged();
+ })
+ );
+ } else if (type === "locations") {
+ newArea.append(
+ generateNewButton("Location", function (name) {
+ if (TBA_DATABASE.locations[name] !== undefined) {
+ alert("A location with this name already exists.");
+ return;
+ }
+ TBA_DATABASE.locations[name] = getNewLocation();
+ onTypeChanged();
+ })
+ );
+ }
+
+ const elementSelector = $(
+ ' '
+ );
+ $.each(TBA_DATABASE[type], function (key, val) {
+ elementSelector.append(
+ $('' + key + " ")
+ );
+ });
+ $("#elementSelection").append(elementSelector);
+
+ $("#elementEditor").html("");
+ onElementChanged();
+ }
+}
+
+function onElementChanged() {
+ onDatabaseChanged();
+ let gui = undefined;
+ let element = $("#element").val();
+ if (element === null) {
+ element = $("#element option:first").val();
+ $("#element").val(element);
+ }
+
+ if (type == "verbs") {
+ gui = generateUiForVerbElement(element, TBA_DATABASE.verbs[element]);
+ } else if (type == "objects") {
+ gui = generateUiForObjectElement(
+ element,
+ TBA_DATABASE.objects[element]
+ );
+ } else if (type == "locations") {
+ gui = generateUiForLocationElement(
+ element,
+ TBA_DATABASE.locations[element]
+ );
+ }
+ if (gui !== undefined) {
+ $("#elementEditor").html(gui);
+ }
+}
+
+function onDatabaseChanged() {
+ saveToDatabaseToStorage();
+}
+
+function generateUiForPreview() {
+ if (previewContainer === undefined) {
+ previewContainer = $(
+ '
'
+ );
+ previewLog = $(
+ 'Loading
'
+ );
+
+ const inputDiv = $(
+ '
'
+ );
+ previewInputText = $(
+ ' '
+ );
+ previewInputButton = $(
+ '⏎ '
+ );
+
+ const buttonBar = $("
");
+ previewRestartButton = button("Reload Game");
+ previewReloadButton = button("Reset Current Room");
+ buttonBar.append(previewReloadButton);
+ buttonBar.append(" ");
+ buttonBar.append(previewRestartButton);
+
+ previewContainer.append(buttonBar);
+ previewContainer.append(previewLog);
+ inputDiv.append(previewInputText);
+ inputDiv.append(previewInputButton);
+ previewContainer.append(inputDiv);
+ }
+
+ previewInputButton.click(function () {
+ readUserInput();
+ });
+ previewRestartButton.click(function () {
+ textAdv = undefined;
+ startGame();
+ });
+ previewReloadButton.click(function () {
+ textAdv.devResetRoom();
+ });
+
+ previewInputText.keypress(function (event) {
+ var keycode = event.keyCode ? event.keyCode : event.which;
+ if (keycode == "13") {
+ // return
+ readUserInput();
+ }
+ });
+
+ if (textAdv === undefined) {
+ startGame();
+ }
+ updatePreviewSideBar();
+ previewLog.scrollTop(previewLog[0].scrollHeight);
+
+ setTimeout(function () {
+ previewInputText.focus();
+ }, 0);
+
+ return previewContainer;
+}
+
+function startGame() {
+ textAdv = new textAdventureEngine(writeLine, clearArea);
+ textAdv.loadDatabaseFromObject(TBA_DATABASE);
+}
+
+function writeLine(output) {
+ previewLog.delay(100).queue(function(next){
+ previewLog.append(output + " ");
+ previewLog.scrollTop(previewLog[0].scrollHeight);
+ previewInputButton.prop("disabled", false);
+ previewInputText.prop("readonly", false);
+ previewInputText.focus();
+ updatePreviewSideBar();
+ next();
+ });
+}
+
+function clearArea() {
+ previewLog.delay(100).queue(function(next){
+ previewLog.html("");
+ next();
+ });
+}
+
+function readUserInput() {
+ previewInputButton.prop("disabled", true);
+ previewInputText.prop("readonly", true);
+ const input = previewInputText.val();
+ writeLine("> " + input);
+ textAdv.input(input);
+ previewInputText.val("");
+}
+
+function updatePreviewSideBar() {
+ const gameState = textAdv.devGetGameState();
+ $("#elementSelection").html("");
+ const inventoryList = $("");
+ const inv = gameState.inventory;
+ if (
+ !inv ||
+ (Array.isArray(inv) ? inv.length === 0 : Object.keys(inv).length === 0)
+ ) {
+ inventoryList.append($("(empty) "));
+ } else {
+ $.each(inv, function (i, objectName) {
+ const invObj = $("" + objectName + " ");
+ const removeButton = button("✖", "btn-error btn-ghost small-btn");
+ removeButton.click(function () {
+ const index = gameState.inventory.indexOf(objectName);
+ gameState.inventory.splice(index, 1);
+ updatePreviewSideBar();
+ });
+ invObj.append(removeButton);
+ inventoryList.append(invObj);
+ });
+ }
+ const addToInv = generateNewObjectForLocationButton(
+ gameState.inventory,
+ function (obj) {
+ gameState.inventory.push(obj);
+ updatePreviewSideBar();
+ }
+ );
+ $("#elementSelection").append($("Inventory "));
+ $("#elementSelection").append(inventoryList);
+ $("#elementSelection").append(addToInv);
+ $("#elementSelection").append($(" "));
+
+ const locations = $("");
+ $.each(gameState.locations, function (locationName, locationObj) {
+ const location = $(" ");
+ if (locationName == gameState.currentLocation) {
+ location.append("" + locationName + " (current)");
+ } else {
+ const notCurrentLocation = $("" + locationName + " ");
+ const goButton = button("Go", "btn-default small-btn");
+ goButton.click(function () {
+ gameState.currentLocation = locationName;
+ updatePreviewSideBar();
+ });
+ notCurrentLocation.append(goButton);
+ location.append(notCurrentLocation);
+ }
+ const locationObjs = $("");
+ $.each(locationObj.objects, function (index, objectName) {
+ locationObjs.append($("" + objectName + " "));
+ });
+ location.append(locationObjs);
+ locations.append(location);
+ });
+ $("#elementSelection").append($("Location States "));
+
+ $("#elementSelection").append(locations);
+}
+
+function generateUiForGeneral(general) {
+ const editorGui = $("
");
+
+ const table = $('');
+ editorGui.append(table);
+ table.append(tableH3("Info"));
+ table.append(
+ generateInput("Title", general.title, function (value) {
+ general.title = value;
+ })
+ );
+ table.append(
+ generateInput("Author", general.author, function (value) {
+ general.author = value;
+ })
+ );
+ table.append(
+ generateInput("Version", general.version, function (value) {
+ general.version = value;
+ })
+ );
+ table.append(
+ generateCheckbox("Continue Enabled", general.continue_enabled, function (value) {
+ general.continue_enabled = value;
+ })
+ );
+ table.append(tableH3("Game Settings"));
+ table.append(
+ generateTextArea(
+ "Request",
+ general.request.join(NEWLINE),
+ function (value) {
+ general.request = value.split(NEWLINE);
+ }
+ )
+ );
+ table.append(
+ generateInput(
+ "Parser Ignored Words",
+ general.parser_ignored_words.join(", "),
+ function (value) {
+ general.parser_ignored_words = value
+ .split(",")
+ .map(function (item) {
+ return item.trim();
+ });
+ }
+ )
+ );
+ table.append(
+ generateInput(
+ "Parser Unknown Verb",
+ general.parser_unknown_verb_text,
+ function (value) {
+ general.parser_unknown_verb_text = value;
+ }
+ )
+ );
+ table.append(
+ generateInput(
+ "Parser Error Text",
+ general.parser_error_text,
+ function (value) {
+ general.parser_error_text = value;
+ }
+ )
+ );
+ table.append(tableH3("Game Start"));
+ table.append(
+ generateTextArea(
+ "Introduction",
+ general.start.text.join(NEWLINE),
+ function (value) {
+ general.start.text = value.split(NEWLINE);
+ }
+ )
+ );
+ table.append(generateFunctionsUI(general.start.commands));
+
+ return editorGui;
+}
+
+function generateUiForVerbElement(verbName, verb) {
+ const editorGui = $('');
+ const title = $("Verb ");
+
+ const nameOptions = $(" ");
+
+ const removeButton = button("Delete", "btn-error");
+ nameOptions.append("" + verbName + " ");
+ nameOptions.append(" ");
+ removeButton.click(function () {
+ if (
+ !TBA_DATABASE.verbs ||
+ Object.keys(TBA_DATABASE.verbs).length <= 1
+ ) {
+ alert("Cannot delete the only verb.");
+ return;
+ }
+ if (confirm('Delete verb "' + verbName + ' and also all usages"?')) {
+ delete TBA_DATABASE.verbs[verbName];
+ $.each(TBA_DATABASE.objects, function (objectName, object) {
+ $.each(object.actions, function (action_name, action) {
+ if (action_name === verbName) {
+ delete TBA_DATABASE.objects[objectName].actions[
+ action_name
+ ];
+ }
+ });
+ });
+ onTypeChanged();
+ }
+ });
+ nameOptions.append(removeButton);
+ nameOptions.append(" ");
+ const duplicateButton = button("Duplicate");
+ duplicateButton.click(function () {
+ const availableName = getAvailableVerbName(verbName);
+ const newName = prompt("Enter name for duplicated verb:", availableName);
+ if (newName === null || newName.trim() === "" || TBA_DATABASE.verbs[newName] !== undefined) {
+ alert("Invalid or duplicate verb name.");
+ return;
+ }
+ TBA_DATABASE.verbs[newName] = clone(verb);
+ onTypeChanged();
+ });
+ nameOptions.append(duplicateButton);
+
+ editorGui.append(tableRow2(title, nameOptions));
+ editorGui.append(
+ generateInput("Failure", verb.failure, function (value) {
+ verb.failure = value;
+ })
+ );
+ editorGui.append(
+ generateInput("Words", verb.words.join(", "), function (value) {
+ verb.words = value.split(",").map(function (item) {
+ return item.trim();
+ });
+ })
+ );
+
+ editorGui.append(
+ generateTextArea(
+ "Standalone Usage Text",
+ verb.standalone_action.text.join(NEWLINE),
+ function (value) {
+ verb.standalone_action.text = value.split(NEWLINE);
+ }
+ )
+ );
+ editorGui.append(generateFunctionsUI(verb.standalone_action.commands, "Standalone Usage Commands"));
+ return editorGui;
+}
+
+function generateUiForObjectElement(objectName, object) {
+ const editorGui = $('');
+ const title = $("Object ");
+
+ const nameOptions = $(" ");
+ const removeButton = button("Delete", "btn-error");
+ nameOptions.append("" + objectName + " ");
+ nameOptions.append(" ");
+ removeButton.click(function () {
+ if (
+ !TBA_DATABASE.objects ||
+ Object.keys(TBA_DATABASE.objects).length <= 1
+ ) {
+ alert("Cannot delete the only object.");
+ return;
+ }
+ if (
+ confirm('Delete object "' + objectName + ' and also all usages"?')
+ ) {
+ delete TBA_DATABASE.objects[objectName];
+ // delete object from all locations
+ $.each(TBA_DATABASE.locations, function (locationName, location) {
+ const index = location.objects.indexOf(objectName);
+ if (index !== -1) {
+ TBA_DATABASE.locations[locationName].objects.splice(
+ index,
+ 1
+ );
+ }
+ });
+ // delete all actions that reference this object
+ $.each(TBA_DATABASE.general.start.commands, function (index, call) {
+ if (call && call.includes(objectName)) {
+ TBA_DATABASE.general.start.commands.splice(index, 1);
+ }
+ });
+ $.each(TBA_DATABASE.objects, function (dbObjectName, object) {
+ $.each(object.actions, function (action_name, action_info) {
+ $.each(action_info.commands, function (index, call) {
+ if (call && call.includes(objectName)) {
+ TBA_DATABASE.objects[dbObjectName].actions[
+ action_name
+ ].commands.splice(index, 1);
+ }
+ });
+ });
+ });
+ onTypeChanged();
+ }
+ });
+ nameOptions.append(removeButton);
+ nameOptions.append(" ");
+ const duplicateButton = button("Duplicate");
+ duplicateButton.click(function () {
+ const availableName = getAvailableObjectName(objectName);
+ const newName = prompt("Enter name for duplicated object:", availableName);
+ if (newName === null || newName.trim() === "" || TBA_DATABASE.objects[newName] !== undefined) {
+ alert("Invalid or duplicate object name.");
+ return;
+ }
+ TBA_DATABASE.objects[newName] = clone(object);
+ onTypeChanged();
+ });
+ nameOptions.append(duplicateButton);
+
+ editorGui.append(tableRow2(title, nameOptions));
+
+ editorGui.append(
+ generateInput(
+ "Location Description",
+ object.locationDescription,
+ function (value) {
+ object.locationDescription = value;
+ }
+ )
+ );
+ editorGui.append(
+ generateInput("Words", object.words.join(", "), function (value) {
+ object.words = value.split(",").map(function (item) {
+ return item.trim();
+ });
+ })
+ );
+ editorGui.append(tableH3("Actions"));
+ $.each(object.actions, function (verb, action) {
+ const name = $("" + verb + " ");
+ const removeActionButton = button("Remove", "btn-error btn-ghost");
+ removeActionButton.click(function () {
+ delete object.actions[verb];
+ onElementChanged();
+ });
+ editorGui.append(tableRow2(name, removeActionButton));
+ editorGui.append(
+ generateTextArea(
+ "Text",
+ action.text.join(NEWLINE),
+ function (value) {
+ action.text = value.split(NEWLINE);
+ }
+ )
+ );
+ editorGui.append(generateFunctionsUI(action.commands));
+ });
+ editorGui.append(
+ tableRow2(
+ "Add Action ",
+ generateNewActionButton(object.actions, function (verb) {
+ if (object.actions[name] !== undefined) {
+ alert(
+ "An action with this verb already exists for this object."
+ );
+ return;
+ }
+ const newAction = getNewObjectAction();
+ object.actions[verb] = newAction;
+ onElementChanged();
+ })
+ )
+ );
+
+ return editorGui;
+}
+
+function generateUiForLocationElement(locationName, location) {
+ const editorGui = $('');
+ const title = $("Location ");
+
+ const nameOptions = $(" ");
+ const removeButton = button("Delete", "btn-error");
+ nameOptions.append("" + locationName + " ");
+ nameOptions.append(" ");
+ removeButton.click(function () {
+ if (
+ !TBA_DATABASE.locations ||
+ Object.keys(TBA_DATABASE.locations).length <= 1
+ ) {
+ alert("Cannot delete the only location.");
+ return;
+ }
+ if (
+ confirm(
+ 'Delete location "' + locationName + ' and also all usages"?'
+ )
+ ) {
+ delete TBA_DATABASE.locations[locationName];
+ // delete all actions that reference this location
+ $.each(TBA_DATABASE.general.start.commands, function (index, call) {
+ if (call && call.includes(locationName)) {
+ TBA_DATABASE.general.start.commands.splice(index, 1);
+ }
+ });
+ $.each(TBA_DATABASE.objects, function (objectName, object) {
+ $.each(object.actions, function (action_name, action_info) {
+ $.each(action_info.commands, function (index, call) {
+ if (call && call.includes(locationName)) {
+ TBA_DATABASE.objects[objectName].actions[
+ action_name
+ ].commands.splice(index, 1);
+ }
+ });
+ });
+ });
+ onTypeChanged();
+ }
+ });
+ nameOptions.append(removeButton);
+ nameOptions.append(" ");
+ const duplicateButton = button("Duplicate");
+ duplicateButton.click(function () {
+ const availableName = getAvailableLocationName(locationName);
+ const newName = prompt("Enter name for duplicated location:", availableName);
+ if (newName === null || newName.trim() === "" || TBA_DATABASE.locations[newName] !== undefined) {
+ alert("Invalid or duplicate location name.");
+ return;
+ }
+ TBA_DATABASE.locations[newName] = clone(location);
+ onTypeChanged();
+ });
+ nameOptions.append(duplicateButton);
+
+ editorGui.append(tableRow2(title, nameOptions));
+ editorGui.append(tableH3("Objects"));
+
+ const previewRow = $(" ");
+ const previewHeadline = $("Preview Location Description ");
+ const previewCell = $(" ");
+
+ editorGui.append(previewRow);
+ previewRow.append(previewHeadline);
+ previewRow.append(previewCell);
+
+ function updatePreviewLocationText() {
+ let text = "";
+ $.each(location.objects, function (index, objectName) {
+ if (text.length > 0) {
+ text += " ";
+ }
+ text += TBA_DATABASE.objects[objectName].locationDescription;
+ });
+ previewCell.text(text);
+ }
+ updatePreviewLocationText();
+
+ const rowSelector = $(" ");
+ const leftCellSelector = $(' ');
+
+ // Render a table where each object has its own Move Up / Move Down / Remove buttons
+ const objList = $('');
+ $.each(location.objects, function (index, objectName) {
+ const tr = $(" ");
+ const nameTd = $(" ").text(objectName);
+ tr.append(nameTd);
+ const btnsTd = $(" ");
+
+ const moveUpButton = button("▲", "btn-default small-btn");
+ moveUpButton.attr("title", "Move Up").attr("aria-label", "Move up");
+ moveUpButton.click(function () {
+ if (index > 0) {
+ moveArrayElement(location.objects, index, index - 1);
+ onElementChanged();
+ }
+ });
+ if (index === 0) {
+ moveUpButton.prop("disabled", true);
+ }
+
+ const moveDownButton = button("▼", "btn-default small-btn");
+ moveDownButton
+ .attr("title", "Move Down")
+ .attr("aria-label", "Move down");
+ moveDownButton.click(function () {
+ if (index < location.objects.length - 1) {
+ moveArrayElement(location.objects, index, index + 1);
+ onElementChanged();
+ }
+ });
+ if (index === location.objects.length - 1) {
+ moveDownButton.prop("disabled", true);
+ }
+
+ const editObjectButton = button("🖉", "btn-default small-btn");
+ editObjectButton
+ .attr("title", "Edit object")
+ .attr("aria-label", "Edit object");
+ editObjectButton.click(function () {
+ // Switch to Objects tab and select this object
+ onTypeChanged("objects");
+ // Ensure DOM is updated, then select the element, focus it, and scroll into view
+ setTimeout(function () {
+ const sel = $("#element");
+ sel.val(objectName);
+ onElementChanged();
+ // Bring the selected option into view and focus the select for accessibility
+ sel.focus();
+ const opt = sel.find('option[value="' + objectName + '"]')[0];
+ if (opt && sel[0]) {
+ sel[0].scrollTop = opt.offsetTop - sel.height() / 2;
+ }
+ }, 0);
+ });
+
+ const removeObjectButton = button(
+ "Remove",
+ "btn-error btn-ghost small-btn"
+ );
+ removeObjectButton.attr("title", "Remove").attr("aria-label", "Remove");
+ removeObjectButton.click(function () {
+ if (confirm('Remove "' + objectName + '"?')) {
+ location.objects.splice(index, 1);
+ onElementChanged();
+ }
+ });
+
+ // Put buttons inline in a small button group
+ const btnGroup = $('
');
+ btnGroup.append(moveUpButton);
+ btnGroup.append(moveDownButton);
+ btnGroup.append(removeObjectButton);
+ nameTd.append(" ");
+ nameTd.append(editObjectButton);
+ btnsTd.append(btnGroup);
+ tr.append(btnsTd);
+ objList.append(tr);
+ });
+
+ leftCellSelector.append(objList);
+ rowSelector.append(leftCellSelector);
+ editorGui.append(rowSelector);
+ const rowNewObject = $("Add Object ");
+ rowNewObject.append(
+ generateNewObjectForLocationButton(location.objects, function (obj) {
+ location.objects.push(obj);
+ onElementChanged();
+ })
+ );
+ editorGui.append(rowNewObject);
+
+ return editorGui;
+}
+
+function button(text, btnClasses = "btn-default") {
+ return $(
+ '' +
+ text +
+ " "
+ );
+}
+
+function paragraph(element) {
+ const p = $("
");
+ p.append(element);
+ return p;
+}
+function tableH3(title) {
+ return $('' + title + " ");
+}
+function tableRow(element) {
+ const e = $(" ");
+ e.append(element);
+ return e;
+}
+function tableRow2(left, right) {
+ const r = $(" ");
+ const el = $(' ');
+ el.append(left);
+ const er = $(" ");
+ er.append(right);
+ r.append(el);
+ r.append(er);
+ return r;
+}
+
+function moveArrayElement(arr, old_index, new_index) {
+ if (new_index >= arr.length) {
+ new_index = arr.length - 1;
+ } else if (new_index < 0) {
+ new_index = 0;
+ }
+ const element = arr.splice(old_index, 1)[0];
+ arr.splice(new_index, 0, element);
+}
+
+function clone(obj) {
+ const clonedObj = JSON.parse(JSON.stringify(obj));
+ return clonedObj;
+}
+
+function getAvailableLocationName(locationName) {
+ var index = 1;
+ var testName = locationName;
+ while (TBA_DATABASE.locations[testName] !== undefined) {
+ testName = locationName + " " + index;
+ index++;
+ }
+ return testName;
+}
+function getAvailableObjectName(objectName) {
+ var index = 1;
+ var testName = objectName;
+ while (TBA_DATABASE.objects[testName] !== undefined) {
+ testName = objectName + " " + index;
+ index++;
+ }
+ return testName;
+}
+function getAvailableVerbName(verbName) {
+ var index = 1;
+ var testName = verbName;
+ while (TBA_DATABASE.verbs[testName] !== undefined) {
+ testName = verbName + " " + index;
+ index++;
+ }
+ return testName;
+}
+
+function generateNewButton(name, onClick) {
+ const editorGui = $('
');
+ const elementNameInput = $(
+ ' '
+ );
+
+ const addButton = button("Add");
+ addButton.click(function () {
+ const name = elementNameInput.val();
+ if (name === undefined || name.length < 1) {
+ alert("Every element must have a unique name.");
+ return;
+ }
+ onClick(elementNameInput.val());
+ });
+ editorGui.append(elementNameInput);
+ editorGui.append(addButton);
+ return editorGui;
+}
+
+function generateNewActionButton(existingActions, onClick) {
+ const editorGui = $('
');
+ const selectNewAction = $(
+ ' '
+ );
+ $.each(TBA_DATABASE.verbs, function (verb) {
+ if (existingActions[verb] !== undefined) {
+ return;
+ }
+ selectNewAction.append($(" ").val(verb).html(verb));
+ });
+
+ const addButton = button("Add");
+ addButton.click(function () {
+ onClick(selectNewAction.val());
+ });
+ editorGui.append(selectNewAction);
+ editorGui.append(addButton);
+ return editorGui;
+}
+
+function generateNewObjectForLocationButton(existingObjects, onClick) {
+ const editorGui = $(" ");
+ const container = $('
');
+ const selectNewObject = $(
+ ' '
+ );
+ $.each(TBA_DATABASE.objects, function (obj) {
+ if (existingObjects[obj] !== undefined) {
+ return;
+ }
+ selectNewObject.append($(" ").val(obj).html(obj));
+ });
+
+ const addButton = button("Add");
+ addButton.click(function () {
+ onClick(selectNewObject.val());
+ });
+ container.append(selectNewObject);
+ container.append(addButton);
+ editorGui.append(container);
+ return editorGui;
+}
+
+function generateFunctionsUI(actions, title="Commands") {
+ const editorGui = $(" ");
+ const col1 = $("" + title + " ");
+ const col2 = $(" ");
+ editorGui.append(col1);
+ editorGui.append(col2);
+
+ const existingFunctionsTable = $(
+ ''
+ );
+ if (!actions || actions.length === 0) {
+ const tr = $(" ");
+ tr.append($('(no functions) '));
+ existingFunctionsTable.append(tr);
+ } else {
+ $.each(actions, function (index, actionString) {
+ const tr = $(" ");
+ const tdText = $(" ");
+ const span = $(' ').text(actionString);
+ tdText.append(span);
+ const tdBtns = $(" ");
+ const btnGroup = $('
');
+
+ const moveUpButton = button("▲", "btn-default small-btn");
+ moveUpButton.attr("title", "Move Up").attr("aria-label", "Move up");
+ moveUpButton.click(function () {
+ if (index > 0) {
+ moveArrayElement(actions, index, index - 1);
+ onElementChanged();
+ }
+ });
+ if (index === 0) {
+ moveUpButton.prop("disabled", true);
+ }
+
+ const moveDownButton = button("▼", "btn-default small-btn");
+ moveDownButton
+ .attr("title", "Move Down")
+ .attr("aria-label", "Move down");
+ moveDownButton.click(function () {
+ if (index < actions.length - 1) {
+ moveArrayElement(actions, index, index + 1);
+ onElementChanged();
+ }
+ });
+ if (index === actions.length - 1) {
+ moveDownButton.prop("disabled", true);
+ }
+
+ const removeButton = button("✖", "btn-error btn-ghost small-btn");
+ removeButton.attr("title", "Remove").attr("aria-label", "Remove");
+ removeButton.click(function () {
+ if (confirm('Remove "' + actionString + '"?')) {
+ actions.splice(index, 1);
+ onElementChanged();
+ }
+ });
+
+ btnGroup.append(moveUpButton);
+ btnGroup.append(moveDownButton);
+ btnGroup.append(removeButton);
+ tdBtns.append(btnGroup);
+
+ tr.append(tdText);
+ tr.append(tdBtns);
+ existingFunctionsTable.append(tr);
+ });
+ }
+ col2.append(existingFunctionsTable);
+
+ const container = $('
');
+ const paramsContainer = $('
');
+ const selectedFunction = $(
+ ' '
+ );
+ $.each(FUNCTIONS, function (funcName, params) {
+ selectedFunction.append($(" ").val(funcName).html(funcName));
+ });
+ selectedFunction.change(function () {
+ paramsContainer.empty();
+ const funcName = selectedFunction.val();
+ const params = FUNCTIONS[funcName];
+ $.each(params, function (index, paramName) {
+ const parameter = $(
+ ' '
+ );
+ if (paramName === "location") {
+ $.each(TBA_DATABASE.locations, function (locName, loc) {
+ parameter.append($(" ").val(locName).html(locName));
+ });
+ } else if (paramName === "object") {
+ parameter.append($(" ").val("this").html("this"));
+ $.each(TBA_DATABASE.objects, function (objName, obj) {
+ parameter.append($(" ").val(objName).html(objName));
+ });
+ }
+ paramsContainer.append(parameter);
+ });
+ });
+ container.append(selectedFunction);
+ container.append(paramsContainer);
+ const addButton = button("Add");
+ container.append(addButton);
+ col2.append(container);
+ addButton.click(function () {
+ const funcName = selectedFunction.val();
+ const params = FUNCTIONS[funcName];
+ let actionString = funcName;
+ $.each(params, function (index, paramName) {
+ const parameter = $("#parameter" + index);
+ actionString += " " + parameter.val();
+ });
+ actions.push(actionString);
+ onElementChanged();
+ });
+ return editorGui;
+}
+
+function generateInput(name, value, onChange) {
+ const inputFieldArea = $(" ");
+ inputFieldArea.append(
+ '' + name + " "
+ );
+ const inputField = $(
+ ' '
+ );
+ inputField.on("change", function () {
+ onChange(inputField.val());
+ onDatabaseChanged();
+ });
+ const valueTableField = $(" ");
+ valueTableField.append(inputField);
+ inputFieldArea.append(valueTableField);
+ return inputFieldArea;
+}
+
+function generateCheckbox(name, value, onChange) {
+ const inputFieldArea = $(" ");
+ inputFieldArea.append(
+ '' + name + " "
+ );
+ const inputToggle = $(
+ ' '
+ );
+ if (value) {
+ inputToggle.prop("checked", true);
+ }
+ inputToggle.on("change", function () {
+ onChange(inputToggle.prop("checked"));
+ onDatabaseChanged();
+ });
+ const valueTableField = $(" ");
+ valueTableField.append(inputToggle);
+ inputFieldArea.append(valueTableField);
+ return inputFieldArea;
+}
+
+function generateTextArea(name, value, onChange) {
+ const inputFieldArea = $(" ");
+ inputFieldArea.append(
+ '' + name + " "
+ );
+ const valueTableField = $(" ");
+ const inputField = $(
+ ''
+ );
+
+ // Auto-resize on input and persist changes
+ inputField.on("input", function () {
+ this.style.height = "auto";
+ this.style.height = this.scrollHeight + "px";
+ onChange(inputField.val());
+ onDatabaseChanged();
+ });
+
+ // Also handle change event (blur) for compatibility
+ inputField.on("change", function () {
+ onChange(inputField.val());
+ onDatabaseChanged();
+ });
+
+ valueTableField.append(inputField);
+ inputFieldArea.append(valueTableField);
+ inputField.val(value);
+
+ // Firefox may not compute scrollHeight correctly until the element is in the DOM,
+ // so run a delayed resize to ensure the textarea height matches its content.
+ setTimeout(function () {
+ inputField.each(function () {
+ this.style.height = "auto";
+ this.style.height = this.scrollHeight + "px";
+ });
+ }, 0);
+
+ return inputFieldArea;
+}
+
+function getNewVerb(name) {
+ var newVerb = {};
+ newVerb["failure"] = "That didnt work.";
+ newVerb["words"] = [name];
+ return newVerb;
+}
+
+function getNewObject(name) {
+ var newObject = {};
+ newObject["words"] = [name];
+ newObject["locationDescription"] = "";
+ newObject["actions"] = {};
+ return newObject;
+}
+
+function getNewObjectAction() {
+ var newAction = {};
+ newAction["text"] = ["That worked!"];
+ newAction["commands"] = [];
+ return newAction;
+}
+
+function getNewLocation() {
+ var newLocation = {};
+ newLocation["objects"] = [];
+ return newLocation;
+}
diff --git a/example.html b/example.html
deleted file mode 100644
index bd8123c..0000000
--- a/example.html
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
TextAdventureJS
-
-
-
-
-
\ No newline at end of file
diff --git a/lonesurvivor.tadb.json b/lonesurvivor.tadb.json
deleted file mode 100644
index 74acd9e..0000000
--- a/lonesurvivor.tadb.json
+++ /dev/null
@@ -1,381 +0,0 @@
-{
- "$schema": "./textAdventureDatabase.schema.json",
- "general": {
- "title": "Survivor Teaser",
- "author": "Daniel Korgel",
- "version": "0.1",
- "request": [ "", "What do you do?" ],
- "start": {
- "text": [ "", "", "", "You wake up with a headache..." ],
- "action": "gotoLocation start_chamber"
- }
- },
- "ignored_words": [ "to", "through", "on", "off", "from", "around", "at" ],
- "verbs": {
- "pickup": {
- "failure": "Sorry you can't see that.",
- "words": [ "pick", "pickup", "lift", "take", "get", "reach"
- ]
- },
- "look": {
- "failure": "Sorry you can't see that.",
- "words": [ "look", "view", "watch", "read", "search" ]
- },
- "activate": {
- "failure": "Sorry you can't do that.",
- "words": [ "turn", "power", "switch" ]
- },
- "go": {
- "failure": "Sorry you can't go there.",
- "words": [ "go", "walk", "run", "goto" ]
- },
- "open": {
- "failure": "Sorry you can't open that.",
- "words": [ "open", "break" ]
- },
- "close": {
- "failure": "Sorry you can't close that.",
- "words": [ "close", "shut" ]
- },
- "jump": {
- "failure": "Sorry this is not possible.",
- "words": [ "jump" ]
- },
- "drop": {
- "failure": "How do you want to drop that?",
- "words": [ "drop", "let go" ]
- },
- "use": {
- "failure": "Well.. this does not work.",
- "words": [ "use", "combine", "throw", "attack" ]
- },
- "light": {
- "failure": "Well.. this does not work.",
- "words": [ "light", "burn", "fire" ]
- },
- "remove": {
- "failure": "Well.. this does not work.",
- "words": [ "remove", "break", "split", "destroy" ]
- }
- },
- "objects": {
- "template_object": {
- "words": [
- "desk",
- "desktop",
- "workingplace"
- ],
- "locationDescription": "",
- "actions": {
- "pickup": {
- "text": "okay got it",
- "action": "inventoryAdd template_object"
- },
- "look": {
- "text": "soft and comfy",
- "action": ""
- }
- },
- "useableObjects": {
- "bedroom_desk": {
- "text": "soft and comfy",
- "action": ""
- },
- "test": {
- "text": "works",
- "action": ""
- }
- }
- },
- "plaster_wall_door_unidentified": {
- "words": [ "wall", "walls", "plaster", "metal" ],
- "locationDescription": "Plaster is crumbling from the WALLS.",
- "actions": {
- "look": {
- "text": "Plaster is crumbling from the walls. Something seems off, but it's too dark to have a closer look.",
- "action": ""
- },
- "touch": {
- "text": "The plaster feels wet and is crumbling in your hands as you touch it.",
- "action": ""
- },
- "break": {
- "text": "Why would you do that?",
- "action": ""
- }
- },
- "useableObjects": { }
- },
- "plaster_wall_door_door_visible": {
- "words": [ "wall", "walls" ],
- "locationDescription": "Plaster is crumbling from the WALLS.",
- "actions": {
- "look": {
- "text": "You notice some rusty metal emerging behind the crumbling PLASTER of one wall.",
- "action": ""
- },
- "remove": {
- "text": "You break the loose plaster of the wall. Piece by piece it reveals an old RUSTY METAL DOOR.",
- "action": [
- "objectReplaceInLocation plaster_wall_door_unidentified plaster_wall_door_door_found",
- "objectRemoveFromLocation metal_behind_plaster"
- ]
- }
- },
- "useableObjects": { }
- },
- "plaster_wall_door_door_found": {
- "words": [ "wall", "rusty", "door" ],
- "locationDescription": "On the other side for the room is a RUSTY METAL DOOR.",
- "actions": {
- "look": {
- "text": "A heavy metal door that was hidden under the plaster.",
- "action": ""
- },
- "go": {
- "text": "You open the rusty door and enter a hallway.",
- "action": "gotoLocation long_hallway"
- },
- "open": {
- "text": "You open the rusty door and enter a hallway.",
- "action": "gotoLocation long_hallway"
- }
- },
- "useableObjects": { }
- },
- "metal_behind_plaster": {
- "words": [ "rust", "rusty", "metal", "plaster" ],
- "locationDescription": "",
- "actions": {
- "look": {
- "text": "It looks like a rusty metal object is hidden behind the crumbling plaster.",
- "action": ""
- },
- "touch": {
- "text": "The plaster feels loose, you might be able to remove it with your hands.",
- "action": ""
- },
- "remove": {
- "text": "You break the loose plaster of the wall. Piece by piece it reveals an old RUSTY METAL DOOR.",
- "action": [
- "objectReplaceInLocation plaster_wall_door_door_visible plaster_wall_door_door_found",
- "objectRemoveFromLocation metal_behind_plaster"
- ]
- }
- },
- "useableObjects": { }
- },
- "room_start_chamber_dark": {
- "words": [ "room" ],
- "locationDescription": "You are in a dark ROOM.",
- "actions": {
- "enter": {
- "text": [
- "You wake up with a headache."
- ],
- "action": ""
- },
- "look": {
- "text": [
- "You look around..."
- ],
- "action": "showLocationDescription"
- },
- "light": {
- "text": "If only you'd had something to enlighten the room with...",
- "action": ""
- }
- },
- "useableObjects": { }
- },
- "room_start_chamber_enlighted": {
- "words": [ "room" ],
- "locationDescription": "The room is slightly lit.",
- "actions": {
- "look": {
- "text": [
- "You look at each wall..."
- ],
- "action": "showLocationDescription"
- },
- "light": {
- "text": "The room is already slightly illuminated.",
- "action": ""
- }
- },
- "useableObjects": { }
- },
- "box_matches_unidentified": {
- "words": [
- "box"
- ],
- "locationDescription": "You see a small BOX near you.",
- "actions": {
- "pickup": {
- "text": "You got it. It's a box of matches!",
- "action": [
- "objectRemoveFromLocation box_matches_unidentified",
- "inventoryAdd box_matches_pickedUp"
- ]
- },
- "look": {
- "text": "It's too dark to see the small box properly. You could probably reach it.",
- "action": ""
- }
- },
- "useableObjects": { }
- },
- "box_matches_pickedUp": {
- "words": [ "box", "matches", "matchbox", "match" ],
- "locationDescription": "You are holding the box of MATCHES.",
- "actions": {
- "use": {
- "text": "You light the last match. The room is now slightly illumanted.",
- "action": [
- "objectReplaceInLocation plaster_wall_door_unidentified plaster_wall_door_door_visible",
- "objectReplaceInLocation room_start_chamber_dark room_start_chamber_enlighted",
- "objectAddToLocation metal_behind_plaster",
- "inventoryRemove box_matches_pickedUp"
- ]
- },
- "light": {
- "text": "You light the last match. The room is now slightly illumanted.",
- "action": [
- "objectReplaceInLocation plaster_wall_door_unidentified plaster_wall_door_door_visible",
- "objectReplaceInLocation room_start_chamber_dark room_start_chamber_enlighted",
- "objectAddToLocation metal_behind_plaster",
- "inventoryRemove box_matches_pickedUp"
- ]
- },
- "drop": {
- "text": "You dropped the box of matches",
- "action": [
- "objectAddToLocation box_matches_dropped",
- "inventoryRemove box_matches_pickedUp"
- ]
- },
- "look": {
- "text": "An almost empty box of matches.",
- "action": ""
- }
- },
- "useableObjects": { }
- },
- "box_matches_dropped": {
- "words": [ "box", "matches", "matchbox" ],
- "locationDescription": "You dropped the the box of MATCHES here.",
- "actions": {
- "pickup": {
- "text": "You picked up the Box of Matches again!",
- "action": [
- "objectRemoveFromLocation box_matches_dropped",
- "inventoryAdd box_matches_pickedUp"
- ]
- },
- "look": {
- "text": "A small box of matches, that you dropped here...",
- "action": ""
- }
- },
- "useableObjects": { }
- },
- "room_hallway": {
- "words": [ "room", "hallway" ],
- "locationDescription": "The long corridor has cold stone walls.",
- "actions": {
- "look": {
- "text": "",
- "action": ""
- }
- },
- "useableObjects": { }
- },
- "exit_light": {
- "words": [ "light", "exit" ],
- "locationDescription": "You see LIGHT at the other end of the hallway.",
- "actions": {
- "look": {
- "text": "It seems to be sunlight that enlightens the end of the hallway.",
- "action": ""
- },
- "go": {
- "text": "You walk towards the light, but you don't seem to get any closer. A gust of wind blows the LEAFLET to you.",
- "action": ""
- }
- },
- "useableObjects": { }
- },
- "exit_light_without_leaflet": {
- "words": [ "light", "exit" ],
- "locationDescription": "You see LIGHT at the other end of the hallway.",
- "actions": {
- "look": {
- "text": "It seems to be sunlight that enlightens the end of the hallway.",
- "action": ""
- },
- "go": {
- "text": "You walk towards the light, but you don't seem to get any closer.",
- "action": ""
- }
- },
- "useableObjects": { }
- },
- "dev_leaflet_on_ground": {
- "words": [ "leaflet", "notice", "paper" ],
- "locationDescription": "A LEAFLET is lying on the ground.",
- "actions": {
- "look": {
- "text": "The text is too small to read the LEAFLET, while it's on the ground.",
- "action": ""
- },
- "pickup": {
- "text": "You take the leaflet.",
- "action": [
- "objectReplaceInLocation exit_light exit_light_without_leaflet",
- "objectRemoveFromLocation dev_leaflet_on_ground",
- "inventoryAdd dev_leaflet_pickedUp"
- ]
- }
- },
- "useableObjects": { }
- },
- "dev_leaflet_pickedUp": {
- "words": [ "leaflet", "notice", "paper" ],
- "locationDescription": "You are holding the LEAFLET in your hands.",
- "actions": {
- "look": {
- "text": [
- "\"Dear Player,",
- "Wow, you actually played this :-) I'm afraid your adventure ends here, already. This Text Based Adventure Engine was just a quick project on a rainy weekend. I hope you liked it, feel free to contact me if you did. -Daniel.\""
- ],
- "action": ""
- },
- "drop": {
- "text": "You dropped the leaflet.",
- "action": [
- "objectReplaceInLocation exit_light_without_leaflet exit_light",
- "objectAddToLocation dev_leaflet_on_ground",
- "inventoryRemove dev_leaflet_pickedUp"
- ]
- }
- },
- "useableObjects": { }
- }
- },
- "locations": {
- "start_chamber": {
- "objects": [
- "room_start_chamber_dark",
- "plaster_wall_door_unidentified",
- "box_matches_unidentified"
- ]
- },
- "long_hallway": {
- "objects": [
- "room_hallway",
- "exit_light",
- "dev_leaflet_on_ground"
- ]
- }
- }
-}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..2eef5c6
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,4465 @@
+{
+ "name": "textadventurejs",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "textadventurejs",
+ "version": "1.0.0",
+ "devDependencies": {
+ "jest": "^29.6.1",
+ "jest-environment-jsdom": "^29.7.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
+ "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
+ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helpers": "^7.28.4",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
+ "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
+ "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
+ "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
+ "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.5"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-async-generators": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+ "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-bigint": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
+ "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-class-properties": {
+ "version": "7.12.13",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
+ "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.12.13"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-class-static-block": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
+ "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-attributes": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz",
+ "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-meta": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
+ "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-json-strings": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+ "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-jsx": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz",
+ "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+ "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+ "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-numeric-separator": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+ "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-catch-binding": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+ "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-chaining": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+ "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-private-property-in-object": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
+ "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-top-level-await": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
+ "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-typescript": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz",
+ "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
+ "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.5",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
+ "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@bcoe/v8-coverage": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@istanbuljs/load-nyc-config": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+ "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "camelcase": "^5.3.1",
+ "find-up": "^4.1.0",
+ "get-package-type": "^0.1.0",
+ "js-yaml": "^3.13.1",
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/schema": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jest/console": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz",
+ "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/core": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz",
+ "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/console": "^29.7.0",
+ "@jest/reporters": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.9",
+ "jest-changed-files": "^29.7.0",
+ "jest-config": "^29.7.0",
+ "jest-haste-map": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-regex-util": "^29.6.3",
+ "jest-resolve": "^29.7.0",
+ "jest-resolve-dependencies": "^29.7.0",
+ "jest-runner": "^29.7.0",
+ "jest-runtime": "^29.7.0",
+ "jest-snapshot": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
+ "jest-watcher": "^29.7.0",
+ "micromatch": "^4.0.4",
+ "pretty-format": "^29.7.0",
+ "slash": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@jest/environment": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
+ "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/fake-timers": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "jest-mock": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/expect": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz",
+ "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "expect": "^29.7.0",
+ "jest-snapshot": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/expect-utils": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
+ "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jest-get-type": "^29.6.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/fake-timers": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
+ "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@sinonjs/fake-timers": "^10.0.2",
+ "@types/node": "*",
+ "jest-message-util": "^29.7.0",
+ "jest-mock": "^29.7.0",
+ "jest-util": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/globals": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
+ "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/environment": "^29.7.0",
+ "@jest/expect": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "jest-mock": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/reporters": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz",
+ "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@bcoe/v8-coverage": "^0.2.3",
+ "@jest/console": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@jridgewell/trace-mapping": "^0.3.18",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "collect-v8-coverage": "^1.0.0",
+ "exit": "^0.1.2",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.2.9",
+ "istanbul-lib-coverage": "^3.0.0",
+ "istanbul-lib-instrument": "^6.0.0",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-lib-source-maps": "^4.0.0",
+ "istanbul-reports": "^3.1.3",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-worker": "^29.7.0",
+ "slash": "^3.0.0",
+ "string-length": "^4.0.1",
+ "strip-ansi": "^6.0.0",
+ "v8-to-istanbul": "^9.0.1"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@jest/schemas": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
+ "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@sinclair/typebox": "^0.27.8"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/source-map": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz",
+ "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.18",
+ "callsites": "^3.0.0",
+ "graceful-fs": "^4.2.9"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/test-result": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz",
+ "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/console": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "collect-v8-coverage": "^1.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/test-sequencer": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz",
+ "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/test-result": "^29.7.0",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^29.7.0",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/transform": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz",
+ "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.11.6",
+ "@jest/types": "^29.6.3",
+ "@jridgewell/trace-mapping": "^0.3.18",
+ "babel-plugin-istanbul": "^6.1.1",
+ "chalk": "^4.0.0",
+ "convert-source-map": "^2.0.0",
+ "fast-json-stable-stringify": "^2.1.0",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^29.7.0",
+ "jest-regex-util": "^29.6.3",
+ "jest-util": "^29.7.0",
+ "micromatch": "^4.0.4",
+ "pirates": "^4.0.4",
+ "slash": "^3.0.0",
+ "write-file-atomic": "^4.0.2"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/types": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
+ "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/schemas": "^29.6.3",
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^3.0.0",
+ "@types/node": "*",
+ "@types/yargs": "^17.0.8",
+ "chalk": "^4.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@sinclair/typebox": {
+ "version": "0.27.8",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
+ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@sinonjs/commons": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
+ "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "type-detect": "4.0.8"
+ }
+ },
+ "node_modules/@sinonjs/fake-timers": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
+ "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@sinonjs/commons": "^3.0.0"
+ }
+ },
+ "node_modules/@tootallnate/once": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/graceful-fs": {
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
+ "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/istanbul-lib-coverage": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
+ "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/istanbul-lib-report": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz",
+ "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "*"
+ }
+ },
+ "node_modules/@types/istanbul-reports": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz",
+ "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/istanbul-lib-report": "*"
+ }
+ },
+ "node_modules/@types/jsdom": {
+ "version": "20.0.1",
+ "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz",
+ "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/tough-cookie": "*",
+ "parse5": "^7.0.0"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "25.0.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
+ "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/@types/stack-utils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
+ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/tough-cookie": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
+ "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/yargs": {
+ "version": "17.0.35",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz",
+ "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "node_modules/@types/yargs-parser": {
+ "version": "21.0.3",
+ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
+ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/abab": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
+ "deprecated": "Use your platform's native atob() and btoa() methods instead",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-globals": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz",
+ "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.1.0",
+ "acorn-walk": "^8.0.2"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.3.4",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
+ "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/babel-jest": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
+ "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/transform": "^29.7.0",
+ "@types/babel__core": "^7.1.14",
+ "babel-plugin-istanbul": "^6.1.1",
+ "babel-preset-jest": "^29.6.3",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.8.0"
+ }
+ },
+ "node_modules/babel-plugin-istanbul": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
+ "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@istanbuljs/load-nyc-config": "^1.0.0",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-instrument": "^5.0.4",
+ "test-exclude": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
+ "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/core": "^7.12.3",
+ "@babel/parser": "^7.14.7",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/babel-plugin-jest-hoist": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz",
+ "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.3.3",
+ "@babel/types": "^7.3.3",
+ "@types/babel__core": "^7.1.14",
+ "@types/babel__traverse": "^7.0.6"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/babel-preset-current-node-syntax": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz",
+ "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/plugin-syntax-async-generators": "^7.8.4",
+ "@babel/plugin-syntax-bigint": "^7.8.3",
+ "@babel/plugin-syntax-class-properties": "^7.12.13",
+ "@babel/plugin-syntax-class-static-block": "^7.14.5",
+ "@babel/plugin-syntax-import-attributes": "^7.24.7",
+ "@babel/plugin-syntax-import-meta": "^7.10.4",
+ "@babel/plugin-syntax-json-strings": "^7.8.3",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
+ "@babel/plugin-syntax-top-level-await": "^7.14.5"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0 || ^8.0.0-0"
+ }
+ },
+ "node_modules/babel-preset-jest": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz",
+ "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "babel-plugin-jest-hoist": "^29.6.3",
+ "babel-preset-current-node-syntax": "^1.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.11",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz",
+ "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/bser": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
+ "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "node-int64": "^0.4.0"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001762",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz",
+ "integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/char-regex": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
+ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cjs-module-lexer": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz",
+ "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">= 1.0.0",
+ "node": ">= 0.12.0"
+ }
+ },
+ "node_modules/collect-v8-coverage": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz",
+ "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/create-jest": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
+ "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "chalk": "^4.0.0",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.9",
+ "jest-config": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "prompts": "^2.0.1"
+ },
+ "bin": {
+ "create-jest": "bin/create-jest.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/cssom": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
+ "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cssstyle": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
+ "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssom": "~0.3.6"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cssstyle/node_modules/cssom": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/data-urls": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz",
+ "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "abab": "^2.0.6",
+ "whatwg-mimetype": "^3.0.0",
+ "whatwg-url": "^11.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decimal.js": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
+ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/dedent": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz",
+ "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "babel-plugin-macros": "^3.1.0"
+ },
+ "peerDependenciesMeta": {
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/detect-newline": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
+ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/diff-sequences": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
+ "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/domexception": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
+ "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
+ "deprecated": "Use your platform's native DOMException instead",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.267",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
+ "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/emittery": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
+ "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/emittery?sponsor=1"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
+ "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/escodegen": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/expect": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
+ "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/expect-utils": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "jest-matcher-utils": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fb-watchman": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
+ "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bser": "2.1.1"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-package-type": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/html-encoding-sniffer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
+ "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-encoding": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/import-local": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
+ "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pkg-dir": "^4.2.0",
+ "resolve-cwd": "^3.0.0"
+ },
+ "bin": {
+ "import-local-fixture": "fixtures/cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-generator-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
+ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/istanbul-lib-coverage": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
+ "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-instrument": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
+ "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/core": "^7.23.9",
+ "@babel/parser": "^7.23.9",
+ "@istanbuljs/schema": "^0.1.3",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-instrument/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-report": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+ "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^4.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-source-maps": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
+ "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^3.0.0",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-reports": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
+ "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jest": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
+ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/core": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "import-local": "^3.0.2",
+ "jest-cli": "^29.7.0"
+ },
+ "bin": {
+ "jest": "bin/jest.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-changed-files": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz",
+ "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "execa": "^5.0.0",
+ "jest-util": "^29.7.0",
+ "p-limit": "^3.1.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-circus": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz",
+ "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/environment": "^29.7.0",
+ "@jest/expect": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "co": "^4.6.0",
+ "dedent": "^1.0.0",
+ "is-generator-fn": "^2.0.0",
+ "jest-each": "^29.7.0",
+ "jest-matcher-utils": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-runtime": "^29.7.0",
+ "jest-snapshot": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "p-limit": "^3.1.0",
+ "pretty-format": "^29.7.0",
+ "pure-rand": "^6.0.0",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-cli": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz",
+ "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/core": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "chalk": "^4.0.0",
+ "create-jest": "^29.7.0",
+ "exit": "^0.1.2",
+ "import-local": "^3.0.2",
+ "jest-config": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
+ "yargs": "^17.3.1"
+ },
+ "bin": {
+ "jest": "bin/jest.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-config": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz",
+ "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.11.6",
+ "@jest/test-sequencer": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "babel-jest": "^29.7.0",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "deepmerge": "^4.2.2",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.2.9",
+ "jest-circus": "^29.7.0",
+ "jest-environment-node": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "jest-regex-util": "^29.6.3",
+ "jest-resolve": "^29.7.0",
+ "jest-runner": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
+ "micromatch": "^4.0.4",
+ "parse-json": "^5.2.0",
+ "pretty-format": "^29.7.0",
+ "slash": "^3.0.0",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "@types/node": "*",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-diff": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
+ "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "diff-sequences": "^29.6.3",
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-docblock": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz",
+ "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "detect-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-each": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz",
+ "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "chalk": "^4.0.0",
+ "jest-get-type": "^29.6.3",
+ "jest-util": "^29.7.0",
+ "pretty-format": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-environment-jsdom": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz",
+ "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/environment": "^29.7.0",
+ "@jest/fake-timers": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/jsdom": "^20.0.0",
+ "@types/node": "*",
+ "jest-mock": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jsdom": "^20.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "canvas": "^2.5.0"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-environment-node": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz",
+ "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/environment": "^29.7.0",
+ "@jest/fake-timers": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "jest-mock": "^29.7.0",
+ "jest-util": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-get-type": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz",
+ "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-haste-map": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz",
+ "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@types/graceful-fs": "^4.1.3",
+ "@types/node": "*",
+ "anymatch": "^3.0.3",
+ "fb-watchman": "^2.0.0",
+ "graceful-fs": "^4.2.9",
+ "jest-regex-util": "^29.6.3",
+ "jest-util": "^29.7.0",
+ "jest-worker": "^29.7.0",
+ "micromatch": "^4.0.4",
+ "walker": "^1.0.8"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "^2.3.2"
+ }
+ },
+ "node_modules/jest-leak-detector": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
+ "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-matcher-utils": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz",
+ "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "jest-diff": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-message-util": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz",
+ "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.12.13",
+ "@jest/types": "^29.6.3",
+ "@types/stack-utils": "^2.0.0",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "micromatch": "^4.0.4",
+ "pretty-format": "^29.7.0",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-mock": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz",
+ "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "jest-util": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-pnp-resolver": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
+ "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "peerDependencies": {
+ "jest-resolve": "*"
+ },
+ "peerDependenciesMeta": {
+ "jest-resolve": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-regex-util": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
+ "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-resolve": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz",
+ "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^29.7.0",
+ "jest-pnp-resolver": "^1.2.2",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
+ "resolve": "^1.20.0",
+ "resolve.exports": "^2.0.0",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-resolve-dependencies": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz",
+ "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jest-regex-util": "^29.6.3",
+ "jest-snapshot": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-runner": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz",
+ "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/console": "^29.7.0",
+ "@jest/environment": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "emittery": "^0.13.1",
+ "graceful-fs": "^4.2.9",
+ "jest-docblock": "^29.7.0",
+ "jest-environment-node": "^29.7.0",
+ "jest-haste-map": "^29.7.0",
+ "jest-leak-detector": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-resolve": "^29.7.0",
+ "jest-runtime": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-watcher": "^29.7.0",
+ "jest-worker": "^29.7.0",
+ "p-limit": "^3.1.0",
+ "source-map-support": "0.5.13"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-runtime": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz",
+ "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/environment": "^29.7.0",
+ "@jest/fake-timers": "^29.7.0",
+ "@jest/globals": "^29.7.0",
+ "@jest/source-map": "^29.6.3",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "cjs-module-lexer": "^1.0.0",
+ "collect-v8-coverage": "^1.0.0",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-mock": "^29.7.0",
+ "jest-regex-util": "^29.6.3",
+ "jest-resolve": "^29.7.0",
+ "jest-snapshot": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "slash": "^3.0.0",
+ "strip-bom": "^4.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-snapshot": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
+ "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.11.6",
+ "@babel/generator": "^7.7.2",
+ "@babel/plugin-syntax-jsx": "^7.7.2",
+ "@babel/plugin-syntax-typescript": "^7.7.2",
+ "@babel/types": "^7.3.3",
+ "@jest/expect-utils": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "babel-preset-current-node-syntax": "^1.0.0",
+ "chalk": "^4.0.0",
+ "expect": "^29.7.0",
+ "graceful-fs": "^4.2.9",
+ "jest-diff": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "jest-matcher-utils": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "natural-compare": "^1.4.0",
+ "pretty-format": "^29.7.0",
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-snapshot/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/jest-util": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
+ "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "graceful-fs": "^4.2.9",
+ "picomatch": "^2.2.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-validate": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz",
+ "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "camelcase": "^6.2.0",
+ "chalk": "^4.0.0",
+ "jest-get-type": "^29.6.3",
+ "leven": "^3.1.0",
+ "pretty-format": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-validate/node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/jest-watcher": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz",
+ "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/test-result": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.0.0",
+ "emittery": "^0.13.1",
+ "jest-util": "^29.7.0",
+ "string-length": "^4.0.1"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-worker": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
+ "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "jest-util": "^29.7.0",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-worker/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
+ "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsdom": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz",
+ "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "abab": "^2.0.6",
+ "acorn": "^8.8.1",
+ "acorn-globals": "^7.0.0",
+ "cssom": "^0.5.0",
+ "cssstyle": "^2.3.0",
+ "data-urls": "^3.0.2",
+ "decimal.js": "^10.4.2",
+ "domexception": "^4.0.0",
+ "escodegen": "^2.0.0",
+ "form-data": "^4.0.0",
+ "html-encoding-sniffer": "^3.0.0",
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.1",
+ "is-potential-custom-element-name": "^1.0.1",
+ "nwsapi": "^2.2.2",
+ "parse5": "^7.1.1",
+ "saxes": "^6.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^4.1.2",
+ "w3c-xmlserializer": "^4.0.0",
+ "webidl-conversions": "^7.0.0",
+ "whatwg-encoding": "^2.0.0",
+ "whatwg-mimetype": "^3.0.0",
+ "whatwg-url": "^11.0.0",
+ "ws": "^8.11.0",
+ "xml-name-validator": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "canvas": "^2.5.0"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/kleur": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/leven": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
+ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/make-dir": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
+ "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/make-dir/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/makeerror": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
+ "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tmpl": "1.0.5"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-int64": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nwsapi": {
+ "version": "2.2.23",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz",
+ "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-locate/node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
+ "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pretty-format": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
+ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/schemas": "^29.6.3",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^18.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/prompts": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+ "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "kleur": "^3.0.3",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/psl": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
+ "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^2.3.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/lupomontero"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/pure-rand": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
+ "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/dubzzz"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fast-check"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/resolve": {
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-cwd": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve.exports": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz",
+ "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=v12.22.7"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.13",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
+ "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/stack-utils": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
+ "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/string-length": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
+ "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "char-regex": "^1.0.2",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
+ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/test-exclude": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
+ "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^7.1.4",
+ "minimatch": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tmpl": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
+ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
+ "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.2.0",
+ "url-parse": "^1.5.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
+ "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/universalify": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "node_modules/v8-to-istanbul": {
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz",
+ "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.12",
+ "@types/istanbul-lib-coverage": "^2.0.1",
+ "convert-source-map": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.12.0"
+ }
+ },
+ "node_modules/w3c-xmlserializer": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
+ "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "xml-name-validator": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/walker": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
+ "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "makeerror": "1.0.12"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/whatwg-encoding": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
+ "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
+ "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "iconv-lite": "0.6.3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
+ "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
+ "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "^3.0.0",
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/write-file-atomic": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
+ "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^3.0.7"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.18.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
+ "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xml-name-validator": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
+ "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..0e4f0cb
--- /dev/null
+++ b/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "textadventurejs",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "test": "jest --detectOpenHandles --runInBand"
+ },
+ "jest": {
+ "testEnvironment": "jsdom"
+ },
+ "devDependencies": {
+ "jest": "^29.6.1",
+ "jest-environment-jsdom": "^29.7.0"
+ }
+}
diff --git a/player/index.html b/player/index.html
new file mode 100644
index 0000000..556eff4
--- /dev/null
+++ b/player/index.html
@@ -0,0 +1,73 @@
+
+
+
+ TextAdventureJS Player
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Load Game File
+
+
+
+
+ Loading
+
+
+
+ ⏎
+
+
+
+
+
diff --git a/player/textAdventurePlayer.css b/player/textAdventurePlayer.css
new file mode 100644
index 0000000..bdc4cdb
--- /dev/null
+++ b/player/textAdventurePlayer.css
@@ -0,0 +1,126 @@
+:root {
+ --mono-font-stack: Menlo, Monaco, Lucida Console, Liberation Mono,
+ DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;
+ --background-color: #222225;
+ --font-color: #e8e9ed;
+ --invert-font-color: #222225;
+ --secondary-color: #a3abba;
+ --tertiary-color: #a3abba;
+ --primary-color: #62c4ff;
+ --error-color: #ff3c74;
+}
+
+body {
+ background-color: #222225;
+ color: #ffffff;
+ font-family: Consolas, monaco, monospace;
+}
+
+a {
+ color: #62c4ff;
+}
+
+main {
+ justify-content: center;
+ width: 600px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+header {
+ display: flex;
+ justify-content: center;
+}
+
+#gameLog {
+ width: 600px;
+ height: 400px;
+ overflow-y: scroll;
+ overflow-x: wrap;
+ margin: auto;
+ text-align: left;
+ padding: 5px;
+}
+
+.modern-textarea {
+ min-height: 36px; /* single-line default */
+ max-height: 400px; /* sensible upper bound */
+ overflow: hidden; /* hide scrollbars, we'll auto expand */
+ resize: none;
+ padding: 8px 10px;
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ border-radius: 6px;
+ color: var(--font-color);
+ line-height: 1.4;
+ font-family: var(--mono-font-stack);
+ transition: box-shadow 0.12s ease, border-color 0.12s ease;
+}
+.modern-textarea:focus {
+ outline: none;
+ border-color: var(--primary-color);
+ box-shadow: 0 6px 18px rgba(98, 196, 255, 0.08);
+}
+
+.modern-input {
+ width: 100%;
+ height: 36px;
+ padding: 8px 10px;
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ border-radius: 6px;
+ color: var(--font-color);
+ box-sizing: border-box;
+ line-height: 1.4;
+ font-family: var(--mono-font-stack);
+ transition: box-shadow 0.12s ease, border-color 0.12s ease;
+ -webkit-appearance: none;
+ appearance: none;
+}
+.modern-input:focus {
+ outline: none;
+ border-color: var(--primary-color);
+ box-shadow: 0 6px 18px rgba(98, 196, 255, 0.08);
+}
+
+input.modern-input,
+select.modern-input,
+textarea.modern-textarea {
+ -webkit-appearance: none;
+ appearance: none;
+ -moz-appearance: none;
+ outline: none;
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+}
+
+input.modern-input:focus,
+select.modern-input:focus,
+textarea.modern-textarea:focus,
+input.modern-input:focus-visible,
+select.modern-input:focus-visible,
+textarea.modern-textarea:focus-visible {
+ outline: none;
+ border-color: var(--primary-color) !important;
+ box-shadow: 0 6px 18px rgba(98, 196, 255, 0.08) !important;
+}
+
+.button-in-input {
+ background: transparent;
+ border: 0px;
+ width: 30px;
+ position: relative;
+ top: -25px;
+ left: 560px;
+ color: #fff;
+ cursor: pointer;
+}
+
+.btn {
+ background: transparent;
+ color: var(--font-color);
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ padding: 8px 12px;
+ border-radius: 6px;
+ cursor: pointer;
+}
diff --git a/player/textAdventurePlayer.js b/player/textAdventurePlayer.js
new file mode 100644
index 0000000..031b92f
--- /dev/null
+++ b/player/textAdventurePlayer.js
@@ -0,0 +1,153 @@
+/**
+ * TextAdventureJS Player JavaScript Helper Functions
+ *
+ * The textAdventure Engine is not modifying your page directly, the integration has to provide functions for these tasks.
+ * This file provides default implementations which can be that can be used as a reference for building your own player.
+ */
+
+var textAdvEngine = undefined;
+var messageQueueCount = 0;
+
+/**
+ * Function that allows the text Adventure engine to output text to the player
+ * @param {string} output The output text
+ */
+function writeLine(output) {
+ const gameLog = document.getElementById("gameLog");
+ // delay messages a bit. With jQuery messageQueueCount is not needed, as you could use $(...).delay(100).queue(...)
+ messageQueueCount++;
+ setTimeout(function () {
+ gameLog.innerHTML += output + " ";
+ // Scroll to the bottom
+ gameLog.scrollTop = gameLog.scrollHeight;
+
+ // Enforce focus on the input field, so the user can continue typing
+ const playerInput = document.getElementById("playerInput");
+ playerInput.focus();
+ messageQueueCount--;
+ }, 100 * messageQueueCount);
+}
+
+/**
+ * Function that allows the text Adventure engine to clear the output area
+ */
+function clearArea() {
+ const gameLog = document.getElementById("gameLog");
+ messageQueueCount++;
+ setTimeout(function () {
+ gameLog.innerHTML = "";
+ gameLog.scrollTop = gameLog.scrollHeight;
+ playerInput.focus();
+ messageQueueCount--;
+ }, 100 * messageQueueCount);
+}
+
+/**
+ * Optional function that allows the text Adventure engine to send analytics events
+ * @param {string} eventName The name of the event
+ * @param {object} eventData Additional data for the event
+ */
+function analyticsFunction(eventName, eventData) {
+ console.log(
+ "Analytics event: " + eventName + " " + JSON.stringify(eventData)
+ );
+}
+
+/**
+ * Function to read player input from the input field and send it to the text adventure engine
+ */
+function readUserInput() {
+ const playerSubmitButton = document.getElementById("playerSubmitButton");
+ const playerInput = document.getElementById("playerInput");
+
+ const playerInputValue = playerInput.value.trim();
+ if (playerInputValue === "") {
+ // Ignore empty input
+ return;
+ }
+
+ // Disable the input field and submit button while processing the input
+ playerSubmitButton.disabled = true;
+ playerInput.readOnly = true;
+
+ // Output the user input to the output area
+ // this is not done by the engine automatically, to allow custom handling and formatting if desired
+ writeLine("> " + playerInputValue);
+
+ // Send the input to the text adventure engine for processing
+ textAdvEngine.input(playerInputValue);
+
+ // Clear the input field for the next input
+ playerInput.value = "";
+ playerSubmitButton.disabled = false;
+ playerInput.readOnly = false;
+}
+
+/**
+ * Function to set up the input field and submit button event listeners
+ */
+function setupInputFieldEventListeners() {
+ const playerSubmitButton = document.getElementById("playerSubmitButton");
+ const playerInput = document.getElementById("playerInput");
+
+ // Set up submit action for the button
+ playerSubmitButton.addEventListener("click", function () {
+ readUserInput();
+ });
+
+ // Set up submit action for the enter key, so that the button is not needed
+ playerInput.addEventListener("keydown", function (event) {
+ if (event.key === "Enter") {
+ readUserInput();
+ }
+ });
+
+ playerInput.focus();
+}
+
+/**
+ * Sets up Event Listener for opening any game file json from the UI
+ */
+function setupEventListenersForGameSelection() {
+ const fileInput = document.getElementById("fileInput");
+ fileInput.addEventListener("change", function () {
+ const file = this.files && this.files[0];
+ this.value = null;
+ if (!file) {
+ return;
+ }
+ reader = new FileReader();
+ // Handle successful read and catch JSON/initialization errors
+ reader.onload = function (event) {
+ try {
+ console.log(event.target);
+ const jsonObj = JSON.parse(event.target.result);
+ textAdvEngine.loadDatabaseFromObject(jsonObj);
+ } catch (err) {
+ console.error("Error initializing database from file:", err);
+ alert(
+ "Error loading file '" +
+ (file && file.name ? file.name : "") +
+ "': " +
+ (err && err.message ? err.message : err)
+ );
+ }
+ };
+ reader.readAsText(file);
+ });
+ const loadGameFileButton = document.getElementById("loadGameFile");
+ loadGameFileButton.addEventListener("click", function () {
+ fileInput.click();
+ });
+}
+
+/**
+ * Non jquery replacement of jquery's $.ready function
+ */
+function ready(fn) {
+ if (document.readyState !== "loading") {
+ fn();
+ } else {
+ document.addEventListener("DOMContentLoaded", fn);
+ }
+}
diff --git a/player/textadventurejs_teaser.tadb.json b/player/textadventurejs_teaser.tadb.json
new file mode 100644
index 0000000..24b11ba
--- /dev/null
+++ b/player/textadventurejs_teaser.tadb.json
@@ -0,0 +1,457 @@
+{
+ "$schema": "../textAdventureGameDatabase.schema.json",
+ "general": {
+ "title": "TextAdventureJS Teaser",
+ "author": "Daniel Korgel",
+ "version": "1.0",
+ "request": ["", "What do you do?"],
+ "continue_enabled": true,
+ "start": {
+ "text": ["", "", "", "You wake up with a headache..."],
+ "commands": ["gotoLocation start_chamber"]
+ },
+ "parser_ignored_words": [
+ "to",
+ "through",
+ "on",
+ "off",
+ "from",
+ "around",
+ "at"
+ ],
+ "parser_error_text": "Sorry, I didn't understand that.",
+ "parser_unknown_verb_text": "Sorry, I don't know how to do that."
+ },
+ "verbs": {
+ "look": {
+ "failure": "Sorry you can't see that.",
+ "words": ["look", "view", "watch", "read", "search"],
+ "standalone_action": {
+ "text": ["You look around."],
+ "commands": ["showLocationDescription"]
+ }
+ },
+ "restart": {
+ "failure": "Be careful! 'restart' will reset the game.",
+ "words": ["restart"],
+ "standalone_action": {
+ "text": [],
+ "commands": ["restartGame"]
+ }
+ },
+ "pickup": {
+ "failure": "Sorry you can't see that.",
+ "words": ["pick", "pickup", "lift", "take", "get", "reach"],
+ "standalone_action": {
+ "text": ["What do you want to pick up?"],
+ "commands": []
+ }
+ },
+ "drop": {
+ "failure": "How do you want to drop that?",
+ "words": ["drop", "let go"],
+ "standalone_action": {
+ "text": ["What do you want to drop?"],
+ "commands": []
+ }
+ },
+ "activate": {
+ "failure": "Sorry you can't do that.",
+ "words": ["turn", "power", "switch"],
+ "standalone_action": {
+ "text": ["What do you want to activate?"],
+ "commands": []
+ }
+ },
+ "go": {
+ "failure": "Sorry you can't go there.",
+ "words": ["go", "walk", "run", "goto"],
+ "standalone_action": {
+ "text": ["Where do you want to go?"],
+ "commands": []
+ }
+ },
+ "open": {
+ "failure": "Sorry you can't open that.",
+ "words": ["open", "break"],
+ "standalone_action": {
+ "text": ["What do you want to open?"],
+ "commands": []
+ }
+ },
+ "close": {
+ "failure": "Sorry you can't close that.",
+ "words": ["close", "shut"],
+ "standalone_action": {
+ "text": ["What do you want to close?"],
+ "commands": []
+ }
+ },
+ "jump": {
+ "failure": "Sorry this is not possible.",
+ "words": ["jump"],
+ "standalone_action": {
+ "text": ["You jump up and down."],
+ "commands": []
+ }
+ },
+ "use": {
+ "failure": "Well.. this does not work.",
+ "words": ["use", "combine", "throw", "attack"],
+ "standalone_action": {
+ "text": ["What do you want to use?"],
+ "commands": []
+ }
+ },
+ "light": {
+ "failure": "Well.. this does not work.",
+ "words": ["light", "ignite", "burn", "fire"],
+ "standalone_action": {
+ "text": ["What do you want to ignite?"],
+ "commands": []
+ }
+ },
+ "remove": {
+ "failure": "Well.. this does not work.",
+ "words": ["remove", "break", "split", "destroy"],
+ "standalone_action": {
+ "text": ["What do you want to remove?"],
+ "commands": []
+ }
+ }
+ },
+ "objects": {
+ "template_object": {
+ "words": ["desk", "desktop", "workingplace"],
+ "locationDescription": "",
+ "actions": {
+ "pickup": {
+ "text": ["okay got it"],
+ "commands": ["inventoryAdd template_object"]
+ },
+ "look": {
+ "text": ["soft and comfy"],
+ "commands": []
+ }
+ },
+ "useableObjects": {
+ "bedroom_desk": {
+ "text": ["soft and comfy"],
+ "commands": []
+ },
+ "test": {
+ "text": ["works"],
+ "commands": []
+ }
+ }
+ },
+ "plaster_wall_door_unidentified": {
+ "words": ["wall", "walls", "plaster", "metal"],
+ "locationDescription": "Plaster is crumbling from the WALLS.",
+ "actions": {
+ "look": {
+ "text": [
+ "Plaster is crumbling from the walls. Something seems off, but it's too dark to have a closer look."
+ ],
+ "commands": []
+ },
+ "touch": {
+ "text": [
+ "The plaster feels wet and is crumbling in your hands as you touch it."
+ ],
+ "commands": []
+ },
+ "break": {
+ "text": ["Why would you do that?"],
+ "commands": []
+ }
+ },
+ "useableObjects": {}
+ },
+ "plaster_wall_door_door_visible": {
+ "words": ["wall", "walls"],
+ "locationDescription": "Plaster is crumbling from the WALLS.",
+ "actions": {
+ "look": {
+ "text": [
+ "You notice some rusty metal emerging behind the crumbling PLASTER of one wall."
+ ],
+ "commands": []
+ },
+ "remove": {
+ "text": [
+ "You break the loose plaster of the wall. Piece by piece it reveals an old RUSTY METAL DOOR."
+ ],
+ "commands": [
+ "objectReplaceInLocation plaster_wall_door_unidentified plaster_wall_door_door_found",
+ "objectRemoveFromLocation metal_behind_plaster"
+ ]
+ }
+ },
+ "useableObjects": {}
+ },
+ "plaster_wall_door_door_found": {
+ "words": ["wall", "rusty", "door"],
+ "locationDescription": "On the other side for the room is a RUSTY METAL DOOR.",
+ "actions": {
+ "look": {
+ "text": ["A heavy metal door that was hidden under the plaster."],
+ "commands": []
+ },
+ "go": {
+ "text": ["You open the rusty door and enter a hallway."],
+ "commands": ["gotoLocation long_hallway"]
+ },
+ "open": {
+ "text": ["You open the rusty door and enter a hallway."],
+ "commands": ["gotoLocation long_hallway"]
+ }
+ },
+ "useableObjects": {}
+ },
+ "metal_behind_plaster": {
+ "words": ["rust", "rusty", "metal", "plaster"],
+ "locationDescription": "",
+ "actions": {
+ "look": {
+ "text": [
+ "It looks like a rusty metal object is hidden behind the crumbling plaster."
+ ],
+ "commands": []
+ },
+ "touch": {
+ "text": [
+ "The plaster feels loose, you might be able to remove it with your hands."
+ ],
+ "commands": []
+ },
+ "remove": {
+ "text": [
+ "You break the loose plaster of the wall. Piece by piece it reveals an old RUSTY METAL DOOR."
+ ],
+ "commands": [
+ "objectReplaceInLocation plaster_wall_door_door_visible plaster_wall_door_door_found",
+ "objectRemoveFromLocation metal_behind_plaster"
+ ]
+ }
+ },
+ "useableObjects": {}
+ },
+ "room_start_chamber_dark": {
+ "words": ["room"],
+ "locationDescription": "You are in a dark ROOM.",
+ "actions": {
+ "enter": {
+ "text": ["You wake up with a headache."],
+ "commands": []
+ },
+ "look": {
+ "text": ["You look around..."],
+ "commands": ["showLocationDescription"]
+ },
+ "light": {
+ "text": ["If only you'd had something to enlighten the room with..."],
+ "commands": []
+ }
+ },
+ "useableObjects": {}
+ },
+ "room_start_chamber_enlighted": {
+ "words": ["room"],
+ "locationDescription": "The room is slightly lit.",
+ "actions": {
+ "look": {
+ "text": ["You look at each wall..."],
+ "commands": ["showLocationDescription"]
+ },
+ "light": {
+ "text": ["The room is already slightly illuminated."],
+ "commands": []
+ }
+ },
+ "useableObjects": {}
+ },
+ "box_matches_unidentified": {
+ "words": ["box"],
+ "locationDescription": "You see a small BOX near you.",
+ "actions": {
+ "pickup": {
+ "text": ["You got it. It's a box of matches!"],
+ "commands": [
+ "objectRemoveFromLocation box_matches_unidentified",
+ "inventoryAdd box_matches_pickedUp"
+ ]
+ },
+ "look": {
+ "text": [
+ "It's too dark to see the small box properly. You could probably reach it."
+ ],
+ "commands": []
+ }
+ },
+ "useableObjects": {}
+ },
+ "box_matches_pickedUp": {
+ "words": ["box", "matches", "matchbox", "match"],
+ "locationDescription": "You are holding the box of MATCHES.",
+ "actions": {
+ "use": {
+ "text": [
+ "You light the last match. The room is now slightly illumanted."
+ ],
+ "commands": [
+ "objectReplaceInLocation plaster_wall_door_unidentified plaster_wall_door_door_visible",
+ "objectReplaceInLocation room_start_chamber_dark room_start_chamber_enlighted",
+ "objectAddToLocation metal_behind_plaster",
+ "inventoryRemove box_matches_pickedUp"
+ ]
+ },
+ "light": {
+ "text": [
+ "You light the last match. The room is now slightly illumanted."
+ ],
+ "commands": [
+ "objectReplaceInLocation plaster_wall_door_unidentified plaster_wall_door_door_visible",
+ "objectReplaceInLocation room_start_chamber_dark room_start_chamber_enlighted",
+ "objectAddToLocation metal_behind_plaster",
+ "inventoryRemove box_matches_pickedUp"
+ ]
+ },
+ "drop": {
+ "text": ["You dropped the box of matches"],
+ "commands": [
+ "objectAddToLocation box_matches_dropped",
+ "inventoryRemove box_matches_pickedUp"
+ ]
+ },
+ "look": {
+ "text": ["An almost empty box of matches."],
+ "commands": []
+ }
+ },
+ "useableObjects": {}
+ },
+ "box_matches_dropped": {
+ "words": ["box", "matches", "matchbox"],
+ "locationDescription": "You dropped the the box of MATCHES here.",
+ "actions": {
+ "pickup": {
+ "text": ["You picked up the Box of Matches again!"],
+ "commands": [
+ "objectRemoveFromLocation box_matches_dropped",
+ "inventoryAdd box_matches_pickedUp"
+ ]
+ },
+ "look": {
+ "text": ["A small box of matches, that you dropped here..."],
+ "commands": []
+ }
+ },
+ "useableObjects": {}
+ },
+ "room_hallway": {
+ "words": ["room", "hallway"],
+ "locationDescription": "The long corridor has cold stone walls.",
+ "actions": {
+ "look": {
+ "text": [""],
+ "commands": []
+ }
+ },
+ "useableObjects": {}
+ },
+ "exit_light": {
+ "words": ["light", "exit"],
+ "locationDescription": "You see LIGHT at the other end of the hallway.",
+ "actions": {
+ "look": {
+ "text": [
+ "It seems to be sunlight that enlightens the end of the hallway."
+ ],
+ "commands": []
+ },
+ "go": {
+ "text": [
+ "You walk towards the light, but you don't seem to get any closer. A gust of wind blows the LEAFLET to you."
+ ],
+ "commands": []
+ }
+ },
+ "useableObjects": {}
+ },
+ "exit_light_without_leaflet": {
+ "words": ["light", "exit"],
+ "locationDescription": "You see LIGHT at the other end of the hallway.",
+ "actions": {
+ "look": {
+ "text": [
+ "It seems to be sunlight that enlightens the end of the hallway."
+ ],
+ "commands": []
+ },
+ "go": {
+ "text": [
+ "You walk towards the light, but you don't seem to get any closer."
+ ],
+ "commands": []
+ }
+ },
+ "useableObjects": {}
+ },
+ "dev_leaflet_on_ground": {
+ "words": ["leaflet", "notice", "paper"],
+ "locationDescription": "A LEAFLET is lying on the ground.",
+ "actions": {
+ "look": {
+ "text": [
+ "The text is too small to read the LEAFLET, while it's on the ground."
+ ],
+ "commands": []
+ },
+ "pickup": {
+ "text": ["You take the leaflet."],
+ "commands": [
+ "objectReplaceInLocation exit_light exit_light_without_leaflet",
+ "objectRemoveFromLocation dev_leaflet_on_ground",
+ "inventoryAdd dev_leaflet_pickedUp"
+ ]
+ }
+ },
+ "useableObjects": {}
+ },
+ "dev_leaflet_pickedUp": {
+ "words": ["leaflet", "notice", "paper"],
+ "locationDescription": "You are holding the LEAFLET in your hands.",
+ "actions": {
+ "look": {
+ "text": [
+ "\"Dear Player,",
+ "Wow, congratulations on getting this far! I'm afraid your adventure ends here, already. This game is just a small teaser for the TextAdventureJS Engine. I hope you liked it, feel free to contact me if you did. -Daniel.\""
+ ],
+ "commands": []
+ },
+ "drop": {
+ "text": ["You dropped the leaflet."],
+ "commands": [
+ "objectReplaceInLocation exit_light_without_leaflet exit_light",
+ "objectAddToLocation dev_leaflet_on_ground",
+ "inventoryRemove dev_leaflet_pickedUp"
+ ]
+ }
+ },
+ "useableObjects": {}
+ }
+ },
+ "locations": {
+ "start_chamber": {
+ "objects": [
+ "room_start_chamber_dark",
+ "plaster_wall_door_unidentified",
+ "box_matches_unidentified"
+ ]
+ },
+ "long_hallway": {
+ "objects": ["room_hallway", "exit_light", "dev_leaflet_on_ground"]
+ }
+ }
+}
diff --git a/templates/empty.tadb.json b/templates/empty.tadb.json
new file mode 100644
index 0000000..e49f5a8
--- /dev/null
+++ b/templates/empty.tadb.json
@@ -0,0 +1,26 @@
+{
+ "$schema": "../textAdventureGameDatabase.schema.json",
+
+ "general": {
+ "title": "TextAdventureJS Empty Database Template",
+ "author": "Your Name",
+ "version": "0.1",
+ "continue_enabled": true,
+ "request": ["", "What do you do?"],
+ "start": {
+ "text": ["Welcome to the Empty Template"],
+ "inventory": "",
+ "commands": []
+ },
+ "parser_ignored_words": [],
+ "parser_error_text": "Sorry, I didn't understand that.",
+ "parser_unknown_verb_text": "Sorry, I don't know how to do that."
+ },
+ "verbs": {},
+ "objects": {},
+ "locations": {
+ "first_room": {
+ "objects": []
+ }
+ }
+}
diff --git a/templates/new_project.tadb.json b/templates/new_project.tadb.json
new file mode 100644
index 0000000..df69190
--- /dev/null
+++ b/templates/new_project.tadb.json
@@ -0,0 +1,189 @@
+{
+ "$schema": "../textAdventureGameDatabase.schema.json",
+ "general": {
+ "title": "New TextAdventureJS Game",
+ "author": "Your Name",
+ "version": "0.1",
+ "continue_enabled": true,
+ "request": ["", "What do you do?"],
+ "start": {
+ "text": ["Welcome to the textAdventureJS Mini Example"],
+ "commands": ["gotoLocation first_room"]
+ },
+ "parser_ignored_words": [
+ "to",
+ "through",
+ "on",
+ "off",
+ "from",
+ "around",
+ "at"
+ ],
+ "parser_error_text": "Sorry, I didn't understand that.",
+ "parser_unknown_verb_text": "Sorry, I don't know how to do that."
+ },
+ "verbs": {
+ "look": {
+ "failure": "Sorry, you can't see that.",
+ "words": ["look", "view", "watch", "read", "search"],
+ "standalone_action": {
+ "text": [],
+ "commands": ["showLocationDescription"]
+ }
+ },
+ "restart": {
+ "failure": "Be careful! 'restart' will reset the game.",
+ "words": ["restart"],
+ "standalone_action": {
+ "text": [],
+ "commands": ["restartGame"]
+ }
+ },
+ "pickup": {
+ "failure": "Sorry, you can't see that.",
+ "words": ["pick", "pickup", "lift", "take", "get", "reach"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "drop": {
+ "failure": "How do you want to drop that?",
+ "words": ["drop", "let go"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "activate": {
+ "failure": "Sorry, you can't do that.",
+ "words": ["turn", "power", "switch"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "go": {
+ "failure": "Sorry, you can't go there.",
+ "words": ["go", "walk", "run", "goto"],
+ "standalone_action": {
+ "text": ["Where do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "open": {
+ "failure": "Sorry, you can't open that.",
+ "words": ["open", "break"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "close": {
+ "failure": "Sorry, you can't close that.",
+ "words": ["close", "shut"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "jump": {
+ "failure": "Sorry, this is not possible.",
+ "words": ["jump"],
+ "standalone_action": {
+ "text": ["You jump up and down."],
+ "commands": []
+ }
+ },
+ "use": {
+ "failure": "Well.. this does not work.",
+ "words": ["use", "combine", "throw", "attack"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "light": {
+ "failure": "Well.. this does not work.",
+ "words": ["light", "ignite", "burn", "fire"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ },
+ "remove": {
+ "failure": "Well.. this does not work.",
+ "words": ["remove", "break", "split", "destroy"],
+ "standalone_action": {
+ "text": ["What do you want to {verb}?"],
+ "commands": []
+ }
+ }
+ },
+ "objects": {
+ "template_object": {
+ "words": ["object", "obj"],
+ "locationDescription": "An object is lying on the floor.",
+ "actions": {
+ "pickup": {
+ "text": ["Okay got it."],
+ "commands": [
+ "objectRemoveFromLocation this",
+ "inventoryAdd template_object_pickedUp"
+ ]
+ },
+ "look": {
+ "text": ["A mysterious object."],
+ "commands": []
+ }
+ },
+ "useableObjects": {}
+ },
+ "template_object_pickedUp": {
+ "words": ["object", "obj"],
+ "locationDescription": "You are holding an object in your hands.",
+ "actions": {
+ "drop": {
+ "text": ["Okay dropped it."],
+ "commands": [
+ "objectAddToLocation template_object",
+ "inventoryRemove this"
+ ]
+ },
+ "look": {
+ "text": ["A mysterious object."],
+ "commands": []
+ }
+ },
+ "useableObjects": {}
+ },
+ "door_to_second_room": {
+ "words": ["door", "north", "up"],
+ "locationDescription": "A door is leading north.",
+ "actions": {
+ "go": {
+ "text": ["You walk through the door."],
+ "commands": ["gotoLocation second_room"]
+ }
+ }
+ },
+ "door_to_first_room": {
+ "words": ["door", "south", "down"],
+ "locationDescription": "A door is leading south.",
+ "actions": {
+ "go": {
+ "text": ["You walk through the door."],
+ "commands": ["gotoLocation first_room"]
+ }
+ }
+ }
+ },
+ "locations": {
+ "first_room": {
+ "objects": ["template_object", "door_to_second_room"]
+ },
+ "second_room": {
+ "objects": ["door_to_first_room"]
+ }
+ }
+}
diff --git a/textAdventure.js b/textAdventure.js
index 88b0720..8e35f66 100644
--- a/textAdventure.js
+++ b/textAdventure.js
@@ -1,377 +1,484 @@
class textAdventureEngine {
-
- TBA_DEBUG = false;
-
- #database = undefined;
- #currentRoom = null;
- #inventory = {};
-
- constructor(outputFunction, clearOutputFunction) {
- this.outputAddLines = outputFunction;
- this.outputClear = clearOutputFunction;
- }
-
- loadDatabaseFromFile(gamedatabasePath, showGameName = true){
- this.outputClear();
- this.#writeOutputLines("Initializing Text Adventure Engine...");
- let base = this;
- $.getJSON( gamedatabasePath)
- .done(function( json ) {
- base.#initDatbase(json, showGameName);
- })
- .fail(function( jqxhr, textStatus, error ) {
- var err = textStatus + ", " + error;
- console.log( "Request Failed: " + err );
- base.outputAddLines("Failed to Load json");
-
- base.#showRequest();
- });
- }
-
- loadDatabaseFromObject(json, showGameName = true){
- this.outputClear();
- this.#writeOutputLines("Initializing Text Adventure Engine...");
- base.#initDatbase(json, showGameName);
- }
-
- input(cmd){
- this.#praseCommand(cmd);
- }
-
- #initDatbase(gameDatabaseObject, showGameName = true){
- this.#database = gameDatabaseObject;
-
- // Tell each object it's name
- $.each( this.#database.verbs, function( key, val ) {
- val["name"] = key;
- });
- $.each( this.#database.objects, function( key, val ) {
- val["name"] = key;
- });
- $.each( this.#database.locations, function( key, val ) {
- val["name"] = key;
- });
-
- if(this.TBA_DEBUG){
- var base = this;
- $.each( this.#database.verbs, function( i, item ) {
- base.outputAddLines("Loaded Action: "+item.name);
- });
- $.each( this.#database.objects, function( i, item ) {
- base.outputAddLines("Loaded Object: "+item.name);
- });
- $.each( this.#database.locations, function( i, item ) {
- base.outputAddLines("Loaded Location: "+item.title);
- });
-
- this.#writeOutputLines("Loading done.");
- }
- if(showGameName){
- this.outputClear();
- this.#writeOutputLines(["", "",
- "'"+this.#database.general.title+"' by "+this.#database.general.author,
- "Version: "+this.#database.general.version]);
- }else{
- this.outputClear();
- }
- this.#praseCommand("welcome");
- }
-
- #showRequest(){
- this.#writeOutputLines(this.#database.general.request);
- }
-
- #removeFromString(arr,str){
- let regex = new RegExp("\\b"+arr.join('|')+"\\b","gi");
- let removed = str.replace(regex, '');
- return removed.replace(/\s\s+/g, ' ');
- }
-
- #praseCommand(cmd){
- cmd = cmd.toLowerCase();
-
- // remove ignored words from command
- cmd = this.#removeFromString(this.#database.ignored_words, cmd);
- cmd = cmd.trim();
- console.log("Stripped command of parser: '"+cmd+"'");
-
- if(cmd=="welcome"){
- this.#inventory = {};
- if(this.#database.general.start.text.length > 0){
- this.#writeOutputLines(this.#database.general.start.text);
- }
- this.#parseActionString(undefined, this.#database.general.start.action);
- }else if(cmd == "debug"){
- if(this.TBA_DEBUG==true){
- this.TBA_DEBUG = true;
- }else{
- this.TBA_DEBUG = false;
- }
- }else if(cmd == "help" || cmd == "?" || cmd == "what" || cmd == "how" || cmd == "what do"){
-
- var allVerbs = "";
- $.each( this.#database.verbs, function( key, val ) {
- if(allVerbs != ""){
- allVerbs += ", "+val.name;
- }else{
- allVerbs += val.name;
- }
- });
- this.#writeOutputLines("Enter simple directions likelook at wall ");
- this.#writeOutputLines("Commonly used verbs are: "+allVerbs);
- } else {
- let words = cmd.split(" ");
-
- let locationState = this.#getLocationState(this.#currentRoom);
- let verb = this.#checkForVerb(words);
- let object = this.#checkForObject(words);
-
- //no action
- if(verb == undefined && object != undefined){
- this.#writeOutputLines("Unknown verb, please try to rephrase your command."); //TODO: Improve
- this.#showRequest();
- return;
- }
-
- // Look at room
- if(verb != undefined && verb.name == "look" && (words.length === 1)){
- locationState = this.#getLocationState(this.#currentRoom);
- this.#writeLocationDescription(locationState.objects);
- this.#showRequest();
- return;
- }
-
- //no object
- if(verb != undefined && (object == undefined)){
- console.log("object is undefined");
- this.#writeOutputLines(verb.failure);
- this.#showRequest();
- return;
- }
-
- //Do a regular Action (verb)
- if(verb!=undefined && object != undefined){
- console.log("Action: "+ verb.words);
- console.log("Object: "+ object.name);
- var result = object.actions[verb.name];
- if(this.TBA_DEBUG==true){
- console.log(result);
-
- this.#writeOutputLines("Action: "+ verb.name);
- this.#writeOutputLines("Object: "+ object.name);
- }
-
- if(result != undefined){
- //
- let objectStateVerbDefinition = object.actions[verb.name];
- this.#writeOutputLines(objectStateVerbDefinition.text); // This might be a string here
-
- if(objectStateVerbDefinition.action!=undefined){
- if($.isArray(objectStateVerbDefinition.action) && objectStateVerbDefinition.action.length > 0) {
- for(var i=0; i 0){
- if(fullLocationDescription.length !== 0){
- fullLocationDescription+= " ";
- }
- fullLocationDescription += thisObject.locationDescription;
- }
- }
- this.#writeOutputLines(fullLocationDescription);
-
- if(Object.keys(this.#inventory).length > 0){
- for(var index in this.#inventory) {
- let currentItemDescription = this.#inventory[index].locationDescription;
- if(currentItemDescription.length > 0){
- this.#writeOutputLines(currentItemDescription);
- }
- }
- }
- }
-
- #parseActionString(callingObject, actionString){
- var acts = actionString.split(" ");
- if(acts[0]=="objectState") {
- console.error("objectState was removed. Use objectReplaceInLocation instead!");
- }else if(acts[0]=="objectRemoveFromLocation"){
- console.log("removing Object from Location:"+acts[1]);
- var index = this.#getLocationState(this.#currentRoom).objects.indexOf(acts[1]);
- if (index > -1) {
- this.#getLocationState(this.#currentRoom).objects.splice(index, 1);
- console.log("Removed object with index: "+index);
- }else{
- console.log("Object not found in location: " + acts[1]);
- }
- }else if(acts[0]=="objectAddToLocation"){
- console.log("adding Object to Location:"+acts[1]);
- this.#getLocationState(this.#currentRoom).objects.push(acts[1]);
- }else if(acts[0]=="objectReplaceInLocation"){
- var index = this.#getLocationState(this.#currentRoom).objects.indexOf(acts[1]);
- if (index > -1) {
- this.#getLocationState(this.#currentRoom).objects.splice(index, 1);
- console.log("Removed object with index: "+index);
- }else{
- console.log("Object not found in location: " + acts[1]);
- }
- this.#getLocationState(this.#currentRoom).objects.push(acts[2]);
- }else if(acts[0]=="gotoLocation"){
- console.log("SIWTCHING LOCATION TO:"+acts[1]);
- this.#currentRoom = acts[1];
- var currentRoomState = this.#getLocationState(this.#currentRoom);
- this.#writeLocationDescription(currentRoomState.objects);
- }else if(acts[0]=="showLocationDescription"){
- var currentRoomState = this.#getLocationState(this.#currentRoom);
- this.#writeLocationDescription(currentRoomState.objects);
- }else if(acts[0]=="inventoryAdd"){
- console.log("Add or replace inventory item: " + acts[1]);
- this.#inventory[acts[1]] = this.#database.objects[acts[1]];
- }else if(acts[0]=="inventoryRemove"){
- console.log("Remove inventory object, if it exists "+acts[1]);
- delete this.#inventory[acts[1]];
- }
- }
-
- #getLocationState(location){
- return this.#database.locations[location];
- }
-
- #getObject(object){
- if (typeof object === 'string' || object instanceof String){
- return this.#database.objects[object];
- }else{
- return object;
- }
- }
-
- #checkForVerb(words){
- let value = undefined;
- for(var i=0; i= 0){
- value = val;
- return;
- }
- });
- }
- return value;
- }
-
- #checkForObject(words){
- var locationState = this.#getLocationState(this.#currentRoom);
- let value = undefined;
-
- // Check room Items
- for(var i=0; i 0){
- for(var index in this.#inventory) {
- let test = $.inArray(words[i], this.#getObject(this.#inventory[index]).words);
- if(test >= 0){
- value = this.#inventory[index];
- return value;
- }
- }
- }
- // check for objects in room
- var base = this;
- $.each(locationState.objects, function( index, val ) {
- let test = $.inArray(words[i], base.#getObject(val).words);
- if(test >= 0){
- value = base.#database.objects[val];
- return; // exit $.each loop
- }
- });
- if(value!=undefined){
- break;
- }
- }
- return value;
- }
-
- #writeOutputLines(lines){
- if (!Array.isArray(lines)){
- this.outputAddLines(lines);
- }else{
- for(var i=0; i 0){
- for(var index in this.#inventory) {
- let test = $.inArray(words[i], this.#getObject(this.#inventory[index]).words);
- if(test >= 0){
- value = this.#inventory[index];
- founds++;
- isInventoryItem = true;
- }
- }
- }
- if(!isInventoryItem){
- // check for objects in room
- var base = this;
- $.each(locationState.objects, function( index, val ) {
- let test = $.inArray(words[i], base.#database.objects[val].words);
- if(test >= 0){
- value = base.#database.objects[val];
- founds++;
- return;
- }
- });
- }
- if(founds>=2){
- break;
- }
- }
- return value;
- }
-
- #getObjectIndexByName(name){
- console.log("checking for object id");
- var value = undefined;
- $.each(this.#database.objects, function( index, val ) {
- if(val.name==name){
- value = index;
- return;
- }
- });
-
- return value;
- }
-}
\ No newline at end of file
+ #database = undefined;
+ #gameState = {
+ locations: {},
+ inventory: [],
+ currentLocation: null,
+ };
+
+ showGameInfo = true;
+
+ constructor(outputFunction, clearOutputFunction, analyticsFunction = null) {
+ this.outputAddLines = outputFunction;
+ this.outputClear = clearOutputFunction;
+ this.analyticsFunction = analyticsFunction;
+ }
+
+ async loadDatabaseFromFile(gamedatabasePath, showGameInfo = true) {
+ this.outputClear();
+ this.#writeOutputLines("Initializing Text Adventure Engine...");
+ this.outputClear();
+ const response = await fetch(gamedatabasePath);
+ const json = await response.json();
+ this.showGameInfo = showGameInfo;
+ this.#initDatbase(json);
+ }
+
+ loadDatabaseFromObject(json) {
+ this.outputClear();
+ this.#writeOutputLines("Initializing Text Adventure Engine...");
+ this.outputClear();
+ this.#initDatbase(json);
+ }
+
+ input(cmd) {
+ this.#praseCommand(cmd);
+ }
+
+ #initDatbase(gameDatabaseObject) {
+ this.#database = gameDatabaseObject;
+
+ if (
+ this.#database.general.continue_enabled === false ||
+ !this.#loadToGameStateFromStorage()
+ ) {
+ this.#resetGame();
+ } else {
+ this.#writeOutputLines(["Resuming from previous session...", " "]);
+ const currentRoomState = this.#getLocationState(
+ this.#gameState.currentLocation
+ );
+ this.#writeLocationDescription(currentRoomState.objects);
+ }
+ }
+
+ #showRequest() {
+ this.#writeOutputLines(this.#database.general.request);
+ }
+
+ #removeFromString(arr, str) {
+ const regex = new RegExp("\\b" + arr.join("|") + "\\b", "gi");
+ const removed = str.replace(regex, "");
+ return removed.replace(/\s\s+/g, " ");
+ }
+
+ #praseCommand(cmd) {
+ cmd = cmd.toLowerCase();
+
+ // remove ignored words from command
+ cmd = this.#removeFromString(
+ this.#database.general.parser_ignored_words,
+ cmd
+ );
+ cmd = cmd.trim();
+ console.log("Stripped command of parser: '" + cmd + "'");
+
+ if (
+ cmd == "help" ||
+ cmd == "?" ||
+ cmd == "what" ||
+ cmd == "how" ||
+ cmd == "what do"
+ ) {
+ let allVerbs = "";
+ const that = this;
+ Object.keys(this.#database.verbs).forEach(function (name, index) {
+ const val = that.#database.verbs[name];
+ if (allVerbs != "") {
+ allVerbs += ", " + name;
+ } else {
+ allVerbs += name;
+ }
+ });
+ this.#writeOutputLines([
+ "Enter simple directions like",
+ "look at wall ",
+ ]);
+ this.#writeOutputLines("Commonly used verbs are: " + allVerbs);
+ } else {
+ const words = cmd.split(" ");
+
+ // deconstruct tuple return from checkForVerb and checkForObject
+ const verbInfo = this.#checkForVerb(words);
+ const verb = this.#database.verbs[verbInfo.id];
+
+ const objectInfo = this.#checkForObject(words);
+
+ const object = this.#database.objects[objectInfo.id];
+
+ //no action
+ if (verb == undefined && object != undefined) {
+ this.#writeOutputLines(
+ this.#database.general.parser_unknown_verb_text
+ );
+ this.#analyticsEvent("unknown_verb", {
+ input: cmd,
+ });
+ return;
+ }
+
+ // verb as standalone action
+ if (verb != undefined && words.length === 1) {
+ console.log("Standalone Action: " + verbInfo.id);
+ this.#writeOutputLines(
+ this.#database.verbs[verbInfo.id].standalone_action.text,
+ { verb: verbInfo.word }
+ );
+ this.#runActions(
+ undefined,
+ this.#database.verbs[verbInfo.id].standalone_action.commands
+ );
+ this.#analyticsEvent("command", {
+ input: cmd,
+ });
+ this.#showRequest();
+ return;
+ }
+
+ //no object
+ if (verb != undefined && object == undefined) {
+ console.log("object is undefined");
+ this.#writeOutputLines(verb.failure, { verb: verbInfo.word });
+ this.#analyticsEvent("unknown_object", {
+ input: cmd,
+ });
+ this.#showRequest();
+ return;
+ }
+
+ //Do a regular Action (verb)
+ if (verb != undefined && object != undefined) {
+ console.log("Action: " + verb.words);
+ console.log("Object: " + objectInfo.id);
+ const result = object.actions[verbInfo.id];
+
+ if (result != undefined) {
+ const objectVerbAction = object.actions[verbInfo.id];
+ this.#writeOutputLines(objectVerbAction.text, {
+ verb: verbInfo.word,
+ object: objectInfo.word,
+ });
+ this.#runActions(objectInfo.id, objectVerbAction.commands);
+ this.#analyticsEvent("command", {
+ input: cmd,
+ });
+ if (verbInfo.id !== "look") {
+ // Auto Save
+ if (this.#database.general.continue_enabled == true) {
+ this.#saveGameStateToStorage();
+ }
+ }
+ } else {
+ this.#writeOutputLines(verb.failure, {
+ verb: verbInfo.word,
+ object: objectInfo.word,
+ });
+ this.#analyticsEvent("unknown_verb_for_object", {
+ input: cmd,
+ });
+ }
+ this.#showRequest();
+ return;
+ }
+ this.#writeOutputLines(this.#database.general.parser_error_text);
+ this.#analyticsEvent("unknown_command", {
+ input: cmd,
+ });
+ }
+ this.#showRequest();
+ }
+
+ #analyticsEvent(eventName, eventData = {}) {
+ if (this.analyticsFunction) {
+ const additionalData = {
+ currentLocation: this.#gameState.currentLocation,
+ location:
+ this.#gameState.locations[this.#gameState.currentLocation]
+ .objects,
+ inventory: this.#gameState.inventory,
+ };
+ eventData = { ...eventData, ...additionalData };
+ this.analyticsFunction(eventName, eventData);
+ }
+ }
+
+ #writeLocationDescription(objectsInLocation) {
+ let fullLocationDescription = "";
+ for (let i = 0; i < objectsInLocation.length; i++) {
+ const thisObject = this.#database.objects[objectsInLocation[i]];
+ if (thisObject.locationDescription.length > 0) {
+ if (fullLocationDescription.length !== 0) {
+ fullLocationDescription += " ";
+ }
+ fullLocationDescription += thisObject.locationDescription;
+ }
+ }
+ this.#writeOutputLines(fullLocationDescription);
+
+ if (this.#gameState.inventory.length > 0) {
+ for (let i = 0; i < this.#gameState.inventory.length; i++) {
+ const objectId = this.#gameState.inventory[i];
+ const currentItemDescription =
+ this.#getObject(objectId).locationDescription;
+ if (currentItemDescription.length > 0) {
+ this.#writeOutputLines(currentItemDescription);
+ }
+ }
+ }
+ }
+
+ #runActions(callingObjectName, actions) {
+ if (!actions) {
+ return;
+ }
+ if (Array.isArray(actions)) {
+ for (let i = 0; i < actions.length; i++) {
+ this.#parseActionString(callingObjectName, actions[i]);
+ }
+ } else {
+ this.#parseActionString(callingObjectName, actions);
+ }
+ }
+
+ #parseActionString(callingObjectName, actionString) {
+ if (!actionString) {
+ return;
+ }
+ const acts = actionString.split(" ");
+ for (let i = 1; i < acts.length; i++) {
+ if (acts[i].trim() == "this") {
+ if (callingObjectName != undefined) {
+ acts[i] = callingObjectName;
+ } else {
+ console.error(
+ "Action " +
+ acts[0] +
+ " had parameter 'this' but no calling object name was defined!"
+ );
+ return;
+ }
+ }
+ }
+ if (acts[0] == "objectRemoveFromLocation") {
+ console.log("removing Object from Location: " + acts[1]);
+ const index = this.#getLocationState(
+ this.#gameState.currentLocation
+ ).objects.indexOf(acts[1]);
+ if (index > -1) {
+ this.#getLocationState(
+ this.#gameState.currentLocation
+ ).objects.splice(index, 1);
+ console.log("Removed object with index: " + index);
+ } else {
+ console.log("Object not found in location: " + acts[1]);
+ }
+ } else if (acts[0] == "objectAddToLocation") {
+ console.log("adding Object to Location:" + acts[1]);
+ this.#getLocationState(
+ this.#gameState.currentLocation
+ ).objects.push(acts[1]);
+ } else if (acts[0] == "objectReplaceInLocation") {
+ const index = this.#getLocationState(
+ this.#gameState.currentLocation
+ ).objects.indexOf(acts[1]);
+ if (index > -1) {
+ this.#getLocationState(
+ this.#gameState.currentLocation
+ ).objects.splice(index, 1);
+ console.log("Removed object with index: " + index);
+ } else {
+ console.log("Object not found in location: " + acts[1]);
+ }
+ this.#getLocationState(
+ this.#gameState.currentLocation
+ ).objects.push(acts[2]);
+ } else if (acts[0] == "gotoLocation") {
+ console.log("SIWTCHING LOCATION TO:" + acts[1]);
+ this.#gameState.currentLocation = acts[1];
+ const currentRoomState = this.#getLocationState(
+ this.#gameState.currentLocation
+ );
+ this.#writeLocationDescription(currentRoomState.objects);
+ } else if (acts[0] == "showLocationDescription") {
+ const currentRoomState = this.#getLocationState(
+ this.#gameState.currentLocation
+ );
+ this.#writeLocationDescription(currentRoomState.objects);
+ } else if (acts[0] == "inventoryAdd") {
+ console.log("Add or replace inventory item: " + acts[1]);
+ this.#gameState.inventory.push(acts[1]);
+ } else if (acts[0] == "inventoryRemove") {
+ console.log("Remove inventory object, if it exists " + acts[1]);
+ const index = this.#gameState.inventory.indexOf(acts[1]);
+ if (index >= 0) {
+ this.#gameState.inventory.splice(index, 1);
+ }
+ } else if (acts[0] == "restartGame") {
+ console.log("Restarting game");
+ this.#resetGame();
+ }
+ }
+
+ #getLocationState(location) {
+ return this.#gameState.locations[location];
+ }
+
+ #getObject(object) {
+ if (typeof object === "string" || object instanceof String) {
+ return this.#database.objects[object];
+ } else {
+ return object;
+ }
+ }
+
+ #checkForVerb(words) {
+ let verbId = undefined;
+ let usedWord = undefined;
+ const that = this;
+ for (let i = 0; i < words.length && verbId === undefined; i++) {
+ Object.keys(this.#database.verbs).forEach(function (key, index) {
+ const test = that.#database.verbs[key].words.indexOf(words[i]);
+ if (test >= 0) {
+ verbId = key;
+ usedWord = words[i];
+ return;
+ }
+ });
+ }
+ return { id: verbId, word: usedWord };
+ }
+
+ #checkForObject(words) {
+ const locationState = this.#getLocationState(
+ this.#gameState.currentLocation
+ );
+ let objectId = undefined;
+ let usedWord = undefined;
+ // Check room Items
+ for (let i = 0; i < words.length; i++) {
+ // Check inventory item
+ if (this.#gameState.inventory.length > 0) {
+ for (
+ let invIndex = 0;
+ invIndex < this.#gameState.inventory.length;
+ invIndex++
+ ) {
+ const test = this.#getObject(
+ this.#gameState.inventory[invIndex]
+ ).words.indexOf(words[i]);
+ if (test >= 0) {
+ objectId = this.#gameState.inventory[invIndex];
+ usedWord = words[i];
+ return { id: objectId, word: usedWord };
+ }
+ }
+ }
+ // check for objects in room
+ const that = this;
+ locationState.objects.forEach(function (name, index) {
+ const test = that.#getObject(name).words.indexOf(words[i]);
+ if (test >= 0) {
+ objectId = name;
+ usedWord = words[i];
+ return; // break out of forEach
+ }
+ });
+ if (objectId != undefined) {
+ break;
+ }
+ }
+ return { id: objectId, word: usedWord };
+ }
+
+ #writeOutputLines(lines, placeholderValues = undefined) {
+ if (!lines) {
+ return;
+ }
+ if (!Array.isArray(lines)) {
+ lines = [lines];
+ }
+ for (let i = 0; i < lines.length; i++) {
+ let line = lines[i];
+ // replace placeholders
+ if (placeholderValues) {
+ Object.keys(placeholderValues).forEach(function (key, index) {
+ const regex = new RegExp("\\{" + key + "\\}", "gi");
+ line = line.replace(regex, placeholderValues[key]);
+ });
+ }
+ this.outputAddLines(line);
+ }
+ }
+
+ #getGameId() {
+ return (this.#database.author + "_" + this.#database.general.title)
+ .replace(/\s+/g, "_")
+ .toLowerCase();
+ }
+ #saveGameStateToStorage() {
+ localStorage.setItem(
+ this.#getGameId(),
+ JSON.stringify(this.#gameState)
+ );
+ console.log("Game state saved to local storage.");
+ }
+ #loadToGameStateFromStorage() {
+ const stored = localStorage.getItem(this.#getGameId());
+ if (stored == undefined) {
+ return false;
+ }
+ this.#gameState = JSON.parse(stored);
+ return true;
+ }
+ #deleteGameStateFromStorage() {
+ localStorage.removeItem(this.#getGameId());
+ }
+ #resetGame() {
+ this.#deleteGameStateFromStorage();
+ this.#gameState = {
+ locations: {},
+ inventory: [],
+ currentLocation: null,
+ };
+ const that = this;
+ Object.keys(this.#database.locations).forEach(function (key, index) {
+ const val = that.#database.locations[key];
+ that.#gameState.locations[key] = JSON.parse(JSON.stringify(val)); // deep copy
+ });
+
+ this.outputClear();
+ if (this.showGameInfo) {
+ this.#writeOutputLines([
+ "Game: " + this.#database.general.title,
+ "Version: " + this.#database.general.version,
+ "Author: " + this.#database.general.author,
+ ]);
+ if (this.#database.general.continue_enabled == true) {
+ this.#writeOutputLines("This game saves automatically.");
+ }
+ this.#writeOutputLines(" ");
+ }
+ this.#gameState.inventory = [];
+ if (this.#database.general.start.text.length > 0) {
+ this.#writeOutputLines(this.#database.general.start.text);
+ }
+ this.#runActions(undefined, this.#database.general.start.commands);
+ }
+
+ devGetGameState() {
+ return this.#gameState;
+ }
+ devResetRoom() {
+ this.#gameState.locations[this.#gameState.currentLocation] = JSON.parse(
+ JSON.stringify(
+ this.#database.locations[this.#gameState.currentLocation]
+ )
+ ); // deep copy
+ this.#praseCommand("look");
+ }
+}
+
+// Export for Node.js (required for tests)
+if (typeof module !== "undefined" && module.exports) {
+ module.exports = textAdventureEngine;
+}
diff --git a/textAdventureDatabase.schema.json b/textAdventureGameDatabase.schema.json
similarity index 66%
rename from textAdventureDatabase.schema.json
rename to textAdventureGameDatabase.schema.json
index 27d25a7..e81b84c 100644
--- a/textAdventureDatabase.schema.json
+++ b/textAdventureGameDatabase.schema.json
@@ -1,40 +1,25 @@
{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "http://example.com/product.schema.json",
+ "$schema": "https://json-schema.org/draft-07/schema#",
+ "$id": "https://dak0r.github.io/TextAdventureJS/textAdventureGameDatabase.schema.json",
"title": "textAdventure Game Database",
"description": "A game database for textAdventureJS",
"definitions": {
"action_definition": {
"description": "Defines what happens if a verb is used (with this object / in this location)",
"type": "object",
- "required": ["text", "action"],
+ "required": ["text", "commands"],
"properties": {
"text": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "array",
- "minItems": 1,
- "items": {
- "type": "string"
- }
- }
- ]
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
},
- "action": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- ]
+ "commands": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
}
}
}
@@ -54,6 +39,9 @@
"version": {
"type": "string"
},
+ "continue_enabled": {
+ "type": "boolean"
+ },
"request": {
"anyOf": [
{
@@ -72,23 +60,40 @@
"$ref": "#/definitions/action_definition"
}
},
- "required": ["title", "author", "version", "request", "start"]
- },
- "ignored_words" : {
- "description": "Words that will be ignored when parsing an user input. This prevent that they get interpreted as verbs or objects (Note: Thus objects and names can't have this as on of their 'words')",
- "type": "array",
- "items": {
+ "parser_parser_ignored_words": {
+ "description": "Words that will be ignored when parsing an user input. This prevent that they get interpreted as verbs or objects (Note: Thus objects and names can't have this as on of their 'words')",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "parser_error_text": {
"type": "string"
- }
+ },
+ "parser_unknown_verb_text": {
+ "type": "string"
+ },
+ "required": [
+ "title",
+ "author",
+ "version",
+ "continue_enabled",
+ "request",
+ "start",
+ "parser_ignored_words",
+ "parser_error_text",
+ "parser_unknown_verb_text"
+ ]
},
"verbs": {
"description": "List of verbs that are available throughout the game",
"type": "object",
"additionalProperties": {
"type": "object",
- "required": ["failure", "words"],
+ "required": ["failure", "words", "standalone_action"],
"properties": {
"failure": {
+ "description": "Shown if the verb is used with an unknown object or the object does not define this action.",
"type": "string"
},
"words": {
@@ -97,6 +102,10 @@
"items": { "type": "string" },
"minItems": 1,
"uniqueItems": true
+ },
+ "standalone_action": {
+ "description": "Run if the verb is used without an object.",
+ "$ref": "#/definitions/action_definition"
}
}
}
@@ -106,12 +115,7 @@
"type": "object",
"additionalProperties": {
"type": "object",
- "required": [
- "words",
- "locationDescription",
- "actions",
- "useableObjects"
- ],
+ "required": ["words", "locationDescription", "actions"],
"properties": {
"words": {
"description": "Alternative words that the player may use",
@@ -160,5 +164,5 @@
}
}
},
- "required": ["general", "ignored_words", "verbs", "objects", "locations"]
+ "required": ["general", "verbs", "objects", "locations"]
}