forked from mobxjs/mobx-state-tree
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
05248f7
commit 872a386
Showing
51 changed files
with
9,050 additions
and
1,758 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
*/node_modules | ||
*.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,3 +10,4 @@ package-lock.json | |
.vscode | ||
.editorconfig | ||
/test-results/**/*.xml | ||
/website/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
FROM node:8.11.4 | ||
|
||
WORKDIR /app/website | ||
|
||
EXPOSE 3000 35729 | ||
COPY ./docs /app/docs | ||
COPY ./website /app/website | ||
RUN yarn install | ||
|
||
CMD ["yarn", "start"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
version: "3" | ||
|
||
services: | ||
docusaurus: | ||
build: . | ||
ports: | ||
- 3000:3000 | ||
- 35729:35729 | ||
volumes: | ||
- ./docs:/app/docs | ||
- ./website/blog:/app/website/blog | ||
- ./website/core:/app/website/core | ||
- ./website/i18n:/app/website/i18n | ||
- ./website/pages:/app/website/pages | ||
- ./website/static:/app/website/static | ||
- ./website/sidebars.json:/app/website/sidebars.json | ||
- ./website/siteConfig.js:/app/website/siteConfig.js | ||
working_dir: /app/website |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
--- | ||
id: doc1 | ||
title: Latin-ish | ||
sidebar_label: Example Page | ||
--- | ||
|
||
Check the [documentation](https://docusaurus.io) for how to use Docusaurus. | ||
|
||
## Lorem | ||
|
||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum massa eget nulla aliquet sagittis. Proin odio tortor, vulputate ut odio in, ultrices ultricies augue. Cras ornare ultrices lorem malesuada iaculis. Etiam sit amet libero tempor, pulvinar mauris sed, sollicitudin sapien. | ||
|
||
## Mauris In Code | ||
|
||
``` | ||
Mauris vestibulum ullamcorper nibh, ut semper purus pulvinar ut. Donec volutpat orci sit amet mauris malesuada, non pulvinar augue aliquam. Vestibulum ultricies at urna ut suscipit. Morbi iaculis, erat at imperdiet semper, ipsum nulla sodales erat, eget tincidunt justo dui quis justo. Pellentesque dictum bibendum diam at aliquet. Sed pulvinar, dolor quis finibus ornare, eros odio facilisis erat, eu rhoncus nunc dui sed ex. Nunc gravida dui massa, sed ornare arcu tincidunt sit amet. Maecenas efficitur sapien neque, a laoreet libero feugiat ut. | ||
``` | ||
|
||
## Nulla | ||
|
||
Nulla facilisi. Maecenas sodales nec purus eget posuere. Sed sapien quam, pretium a risus in, porttitor dapibus erat. Sed sit amet fringilla ipsum, eget iaculis augue. Integer sollicitudin tortor quis ultricies aliquam. Suspendisse fringilla nunc in tellus cursus, at placerat tellus scelerisque. Sed tempus elit a sollicitudin rhoncus. Nulla facilisi. Morbi nec dolor dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras et aliquet lectus. Pellentesque sit amet eros nisi. Quisque ac sapien in sapien congue accumsan. Nullam in posuere ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacinia leo a nibh fringilla pharetra. | ||
|
||
## Orci | ||
|
||
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin venenatis lectus dui, vel ultrices ante bibendum hendrerit. Aenean egestas feugiat dui id hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur in tellus laoreet, eleifend nunc id, viverra leo. Proin vulputate non dolor vel vulputate. Curabitur pretium lobortis felis, sit amet finibus lorem suscipit ut. Sed non mollis risus. Duis sagittis, mi in euismod tincidunt, nunc mauris vestibulum urna, at euismod est elit quis erat. Phasellus accumsan vitae neque eu placerat. In elementum arcu nec tellus imperdiet, eget maximus nulla sodales. Curabitur eu sapien eget nisl sodales fermentum. | ||
|
||
## Phasellus | ||
|
||
Phasellus pulvinar ex id commodo imperdiet. Praesent odio nibh, sollicitudin sit amet faucibus id, placerat at metus. Donec vitae eros vitae tortor hendrerit finibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vitae purus dolor. Duis suscipit ac nulla et finibus. Phasellus ac sem sed dui dictum gravida. Phasellus eleifend vestibulum facilisis. Integer pharetra nec enim vitae mattis. Duis auctor, lectus quis condimentum bibendum, nunc dolor aliquam massa, id bibendum orci velit quis magna. Ut volutpat nulla nunc, sed interdum magna condimentum non. Sed urna metus, scelerisque vitae consectetur a, feugiat quis magna. Donec dignissim ornare nisl, eget tempor risus malesuada quis. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
id: doc2 | ||
title: document number 2 | ||
--- | ||
|
||
This is a link to [another document.](doc3.md) | ||
This is a link to an [external page.](http://www.example.com) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
--- | ||
id: doc3 | ||
title: This is document number 3 | ||
--- | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ac euismod odio, eu consequat dui. Nullam molestie consectetur risus id imperdiet. Proin sodales ornare turpis, non mollis massa ultricies id. Nam at nibh scelerisque, feugiat ante non, dapibus tortor. Vivamus volutpat diam quis tellus elementum bibendum. Praesent semper gravida velit quis aliquam. Etiam in cursus neque. Nam lectus ligula, malesuada et mauris a, bibendum faucibus mi. Phasellus ut interdum felis. Phasellus in odio pulvinar, porttitor urna eget, fringilla lectus. Aliquam sollicitudin est eros. Mauris consectetur quam vitae mauris interdum hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. | ||
|
||
Duis et egestas libero, imperdiet faucibus ipsum. Sed posuere eget urna vel feugiat. Vivamus a arcu sagittis, fermentum urna dapibus, congue lectus. Fusce vulputate porttitor nisl, ac cursus elit volutpat vitae. Nullam vitae ipsum egestas, convallis quam non, porta nibh. Morbi gravida erat nec neque bibendum, eu pellentesque velit posuere. Fusce aliquam erat eu massa eleifend tristique. | ||
|
||
Sed consequat sollicitudin ipsum eget tempus. Integer a aliquet velit. In justo nibh, pellentesque non suscipit eget, gravida vel lacus. Donec odio ante, malesuada in massa quis, pharetra tristique ligula. Donec eros est, tristique eget finibus quis, semper non nisl. Vivamus et elit nec enim ornare placerat. Sed posuere odio a elit cursus sagittis. | ||
|
||
Phasellus feugiat purus eu tortor ultrices finibus. Ut libero nibh, lobortis et libero nec, dapibus posuere eros. Sed sagittis euismod justo at consectetur. Nulla finibus libero placerat, cursus sapien at, eleifend ligula. Vivamus elit nisl, hendrerit ac nibh eu, ultrices tempus dui. Nam tellus neque, commodo non rhoncus eu, gravida in risus. Nullam id iaculis tortor. | ||
|
||
Nullam at odio in sem varius tempor sit amet vel lorem. Etiam eu hendrerit nisl. Fusce nibh mauris, vulputate sit amet ex vitae, congue rhoncus nisl. Sed eget tellus purus. Nullam tempus commodo erat ut tristique. Cras accumsan massa sit amet justo consequat eleifend. Integer scelerisque vitae tellus id consectetur. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
id: doc4 | ||
title: Other Document | ||
--- | ||
|
||
this is another document |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
id: doc5 | ||
title: Fifth Document | ||
--- | ||
|
||
Another one |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
### Trees, types and state | ||
|
||
Each **node** in the tree is described by two things: Its **type** (the shape of the thing) and its **data** (the state it is currently in). | ||
|
||
The simplest tree possible: | ||
|
||
```javascript | ||
import { types } from "mobx-state-tree" | ||
|
||
// declaring the shape of a node with the type `Todo` | ||
const Todo = types.model({ | ||
title: types.string | ||
}) | ||
|
||
// creating a tree based on the "Todo" type, with initial data: | ||
const coffeeTodo = Todo.create({ | ||
title: "Get coffee" | ||
}) | ||
``` | ||
|
||
The `types.model` type declaration is used to describe the shape of an object. | ||
Other built-in types include arrays, maps, primitives, etc. See the [types overview](#types-overview). | ||
The type information will be used for both. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
|
||
### Creating models | ||
|
||
<i><a style="color: white; background:cornflowerblue;padding:5px;margin:5px;border-radius:2px" href="https://egghead.io/lessons/react-describe-your-application-domain-using-mobx-state-tree-mst-models">egghead.io lesson 1: Describe Your Application Domain Using mobx-state-tree(MST) Models</a></i> | ||
|
||
The most important type in MST is `types.model`, which can be used to describe the shape of an object. | ||
An example: | ||
|
||
```javascript | ||
const TodoStore = types | ||
// 1 | ||
.model("TodoStore", { | ||
loaded: types.boolean, // 2 | ||
endpoint: "http://localhost", // 3 | ||
todos: types.array(Todo), // 4 | ||
selectedTodo: types.reference(Todo) // 5 | ||
}) | ||
.views(self => { | ||
return { | ||
// 6 | ||
get completedTodos() { | ||
return self.todos.filter(t => t.done) | ||
}, | ||
// 7 | ||
findTodosByUser(user) { | ||
return self.todos.filter(t => t.assignee === user) | ||
} | ||
} | ||
}) | ||
.actions(self => { | ||
return { | ||
addTodo(title) { | ||
self.todos.push({ | ||
id: Math.random(), | ||
title | ||
}) | ||
} | ||
} | ||
}) | ||
``` | ||
|
||
When defining a model, it is advised to give the model a name for debugging purposes (see `// 1`). | ||
A model takes additionally object argument defining the properties. | ||
|
||
The _properties_ argument is a key-value set where each key indicates the introduction of a property, and the value its type. The following types are acceptable: | ||
|
||
1. A type. This can be a simple primitive type like `types.boolean`, see `// 2`, or a complex, possibly pre-defined type (`// 4`) | ||
2. A primitive. Using a primitive as type is syntactic sugar for introducing a property with a default value. See `// 3`, `endpoint: "http://localhost"` is the same as `endpoint: types.optional(types.string, "http://localhost")`. The primitive type is inferred from the default value. Properties with a default value can be omitted in snapshots. | ||
3. A [computed property](https://mobx.js.org/refguide/computed-decorator.html), see `// 6`. Computed properties are tracked and memoized by MobX. Computed properties will not be stored in snapshots or emit patch events. It is possible to provide a setter for a computed property as well. A setter should always invoke an action. | ||
4. A view function (see `// 7`). A view function can, unlike computed properties, take arbitrary arguments. It won't be memoized, but its value can be tracked by MobX nonetheless. View functions are not allowed to change the model, but should rather be used to retrieve information from the model. | ||
|
||
_Tip: `(self) => ({ action1() { }, action2() { }})` is ES6 syntax for `function (self) { return { action1: function() { }, action2: function() { } }}`. In other words, it's short way of directly returning an object literal. | ||
For that reason a comma between each member of a model is mandatory, unlike classes which are syntactically a totally different concept._ | ||
|
||
`types.model` creates a chainable model type, where each chained method produces a new type: | ||
|
||
- `.named(name)` clones the current type, but gives it a new name | ||
- `.props(props)` produces a new type, based on the current one, and adds / overrides the specified properties | ||
- `.actions(self => object literal with actions)` produces a new type, based on the current one, and adds / overrides the specified actions | ||
- `.views(self => object literal with view functions)` produces a new type, based on the current one, and adds / overrides the specified view functions | ||
- `.preProcessSnapshot(snapshot => snapshot)` can be used to pre-process the raw JSON before instantiating a new model. See [Lifecycle hooks](#lifecycle-hooks-for-typesmodel) or alternatively `types.snapshotProcessor` | ||
- `.postProcessSnapshot(snapshot => snapshot)` can be used to post-process the raw JSON before getting a model snapshot. See [Lifecycle hooks](#lifecycle-hooks-for-typesmodel) or alternatively `types.snapshotProcessor` | ||
|
||
Note that `views` and `actions` don't define actions and views directly, but rather they should be given a function. | ||
The function will be invoked when a new model instance is created. The instance will be passed in as the first and only argument typically called `self`. | ||
This has two advantages: | ||
|
||
1. All methods will always be bound correctly, and won't suffer from an unbound `this` | ||
2. The closure can be used to store private state or methods of the instance. See also [actions](#actions) and [volatile state](#volatile-state). | ||
|
||
Quick example: | ||
|
||
```javascript | ||
const TodoStore = types | ||
.model("TodoStore", { | ||
/* props */ | ||
}) | ||
.actions(self => { | ||
const instantiationTime = Date.now() | ||
|
||
function addTodo(title) { | ||
console.log(`Adding Todo ${title} after ${(Date.now() - instantiationTime) / 1000}s.`) | ||
self.todos.push({ | ||
id: Math.random(), | ||
title | ||
}) | ||
} | ||
|
||
return { addTodo } | ||
}) | ||
``` | ||
|
||
It is perfectly fine to chain multiple `views`, `props` calls etc in arbitrary order. This can be a great way to structure complex types, mix-in utility functions, etc. Each call in the chain creates a new, immutable type which can itself be stored and reused as part of other types, etc. | ||
|
||
It is also possible to define lifecycle hooks in the _actions_ object. These are actions with a predefined name that are run at a specific moment. See [Lifecycle hooks](#lifecycle-hooks-for-typesmodel). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
|
||
### Tree semantics in detail | ||
|
||
MST trees have very specific semantics. These semantics purposefully constrain what you can do with MST. The reward for that is all kinds of generic features out of the box like snapshots, replayability, etc. If these constraints don't suit your app, you are probably better off using plain MobX with your own model classes, which is fine as well. | ||
|
||
1. Each object in an MST tree is considered a _node_. Each primitive (and frozen) value is considered a _leaf_. | ||
1. MST has only three types of nodes: _model_, _array_ and _map_. | ||
1. Every _node_ tree in an MST tree is a tree in itself. Any operation that can be invoked on the complete tree can also be applied to a subtree. | ||
1. A node can only exist exactly _once_ in a tree. This ensures it has a unique, identifiable position. | ||
1. It is however possible to refer to another object in the _same_ tree by using _references_ | ||
1. There is no limit to the number of MST trees that live in an application. However, each node can only live in exactly one tree. | ||
1. All _leaves_ in the tree must be serializable. It is not possible to store, for example, functions in a MST. | ||
1. The only free-form type in MST is frozen, with the requirement that frozen values are immutable and serializable so that the MST semantics can still be upheld. | ||
1. At any point in the tree it is possible to assign a snapshot to the tree instead of a concrete instance of the expected type. In that case an instance of the correct type, based on the snapshot, will be automatically created for you. | ||
1. Nodes in the MST tree will be reconciled (the exact same instance will be reused) when updating the tree by any means, based on their _identifier_ property. If there is no identifier property, instances won't be reconciled. | ||
1. If a node in the tree is replaced by another node, the original node will die and become unusable. This makes sure you are not accidentally holding on to stale objects anywhere in your application. | ||
1. If you want to create a new node based on an existing node in a tree, you can either `detach` that node, or `clone` it. | ||
|
||
These egghead.io lessons nicely leverage the specific semantics of MST trees: | ||
|
||
<i><a style="color: white; background:cornflowerblue;padding:5px;margin:5px;border-radius:2px" href="https://egghead.io/lessons/react-build-forms-with-react-to-edit-mobx-state-tree-models">egghead.io lesson 6: Build Forms with React to Edit mobx-state-tree Models</a></i><br> | ||
<i><a style="color: white; background:cornflowerblue;padding:5px;margin:5px;border-radius:2px" href="https://egghead.io/lessons/react-remove-model-instances-from-the-tree">egghead.io lesson 7: Remove Model Instances from the Tree</a></i><br> | ||
<i><a style="color: white; background:cornflowerblue;padding:5px;margin:5px;border-radius:2px" href="https://egghead.io/lessons/react-create-an-entry-form-to-add-models-to-the-state-tree">egghead.io lesson 8: Create an Entry Form to Add Models to the State Tree</a></i> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
|
||
|
||
### Composing trees | ||
|
||
In MST every node in the tree is a tree in itself. | ||
Trees can be composed by composing their types: | ||
|
||
```javascript | ||
const TodoStore = types.model({ | ||
todos: types.array(Todo) | ||
}) | ||
|
||
const storeInstance = TodoStore.create({ | ||
todos: [ | ||
{ | ||
title: "Get biscuit" | ||
} | ||
] | ||
}) | ||
``` | ||
|
||
The _snapshot_ passed to the `create` method of a type will recursively be turned in MST nodes. So, you can safely call: | ||
|
||
```javascript | ||
storeInstance.todos[0].setTitle("Chocolate instead plz") | ||
``` | ||
|
||
Because any node in a tree is a tree in itself, any built-in method in MST can be invoked on any node in the tree, not just the root. | ||
This makes it possible to get a patch stream of a certain subtree, or to apply middleware to a certain subtree only. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
|
||
|
||
### Actions | ||
|
||
<i><a style="color: white; background:cornflowerblue;padding:5px;margin:5px;border-radius:2px" href="https://egghead.io/lessons/react-attach-behavior-to-mobx-state-tree-models-using-actions">egghead.io lesson 2: Attach Behavior to mobx-state-tree Models Using Actions</a></i> | ||
|
||
By default, nodes can only be modified by one of their actions, or by actions higher up in the tree. | ||
Actions can be defined by returning an object from the action initializer function that was passed to `actions`. | ||
The initializer function is executed for each instance, so that `self` is always bound to the current instance. | ||
Also, the closure of that function can be used to store so called _volatile_ state for the instance or to create private functions that can only | ||
be invoked from the actions, but not from the outside. | ||
|
||
```javascript | ||
const Todo = types | ||
.model({ | ||
title: types.string | ||
}) | ||
.actions(self => { | ||
function setTitle(newTitle) { | ||
self.title = newTitle | ||
} | ||
|
||
return { | ||
setTitle | ||
} | ||
}) | ||
``` | ||
|
||
Shorter form if no local state or private functions are involved: | ||
|
||
```javascript | ||
const Todo = types | ||
.model({ | ||
title: types.string | ||
}) | ||
.actions(self => ({ | ||
// note the `({`, we are returning an object literal | ||
setTitle(newTitle) { | ||
self.title = newTitle | ||
} | ||
})) | ||
``` | ||
|
||
Actions are replayable and are therefore constrained in several ways: | ||
|
||
- Trying to modify a node without using an action will throw an exception. | ||
- It's recommended to make sure action arguments are serializable. Some arguments can be serialized automatically such as relative paths to other nodes | ||
- Actions can only modify models that belong to the (sub)tree on which they are invoked | ||
- You cannot use `this` inside actions. Instead, use `self`. This makes it safe to pass actions around without binding them or wrapping them in arrow functions. | ||
|
||
Useful methods: | ||
|
||
- [`onAction`](docs/API/README.md#onaction) listens to any action that is invoked on the model or any of its descendants. | ||
- [`addMiddleware`](docs/API/README.md#addmiddleware) adds an interceptor function to any action invoked on the subtree. | ||
- [`applyAction`](docs/API/README.md#applyaction) invokes an action on the model according to the given action description | ||
|
||
|
||
#### Action listeners versus middleware | ||
|
||
The difference between action listeners and middleware is: middleware can intercept the action that is about to be invoked, modify arguments, return types, etc. Action listeners cannot intercept and are only notified. Action listeners receive the action arguments in a serializable format, while middleware receives the raw arguments. (`onAction` is actually just a built-in middleware). | ||
|
||
For more details on creating middleware, see the [docs](docs/middleware.md). | ||
|
||
#### Disabling protected mode | ||
|
||
This may be desired if the default protection of `mobx-state-tree` doesn't fit your use case. For example, if you are not interested in replayable actions or hate the effort of writing actions to modify any field, `unprotect(tree)` will disable the protected mode of a tree allowing anyone to directly modify the tree. |
Oops, something went wrong.