diff --git a/README.md b/README.md index 7ce5fcd..242f5a0 100644 --- a/README.md +++ b/README.md @@ -16,39 +16,75 @@ thirty has shipped a new version, and what changed in it. Foundry VTT v14 or later. GM only. +## Install + +In Foundry, open **Add-on Modules → Install Module**, paste this into the +**Manifest URL** box, and press Install: + +``` +https://github.com/vttforge/settings-vault/releases/latest/download/module.json +``` + +Then turn it on in a world: **Game Settings → Manage Modules**. + ## What it does Open **Settings → Configure Settings → Settings Vault**. +![The Settings Vault window. A list of packages with a tick box each, an export +section with a label field, and an import section with a file +picker.](docs/vault-window.png) + Tick the packages you want. Write a profile, and your browser saves a JSON file. In another world, choose the file and read it back. The window then lists every value it applied and every one it skipped, with the reason. A profile is plain JSON. Open it, read it, delete a line, send it to someone. -## The three scopes +## What a profile carries -Foundry stores a setting in one of three places, and each travels differently. +Foundry keeps a setting in one of three places, and each one travels +differently. -**World.** Same value for everyone in the world, held in the database. It -travels, and only a GM can write it. +**World.** One value for everybody in the world. It travels, and only a GM can +write it. **Client.** Held in the browser, one value per device. It travels, and lands on whichever device does the import. -**User.** Held in the database against one user. Foundry writes a user-scope -setting against whoever is logged in and gives no way to write another user's. -So a profile carries the values of the person who exported it, and the person -importing receives them as their own. You cannot move a player's settings for -them. +**User.** Held against one person. Foundry writes this kind against whoever is +logged in and gives no way to write someone else's. So a profile carries the +values of the person who exported it, and the person importing gets them as +their own. You cannot move a player's settings for them. + +The window counts each kind per package, so you know what a profile will carry +before you write it. + +## Passwords and keys + +Some modules keep an API token or a licence key in a setting. Values whose name +looks like one stay out of the file unless you tick the box. The test is the +name of the setting, so it catches the ordinary cases and nothing clever. + +Read a profile before you share it. + +## What happens when you import + +Each value goes in on its own. A value that no longer fits its setting is +skipped, and so is a setting whose module is not installed in this world. Both +are normal when a profile is older than the world it lands in. The rest of the +import carries on, and the report names what was left out. -The window shows the count per scope for each package, so you know what a -profile will carry before you write it. +Export this world first if you want a way back. ## Module updates Open **Settings → Configure Settings → Check for updates**. +![The Module Updates window. A Check now button, the time of the last check, and +one module listed with its installed version, the newer version, the release +notes and a link to the release page.](docs/updates-window.png) + The list shows every installed module, the version this world runs, and the newest release found. A module that is behind shows the new version, the release notes, and a link to the release page. Nothing here installs anything: use @@ -60,47 +96,30 @@ from, one sends no permission header, one answers 404, and `api.github.com` answers with the tag and the notes together. A module hosted anywhere else is listed as unchecked, with the reason. -### Why there is a button +### Why nothing checks on its own GitHub allows a browser sixty requests an hour without a token, and a conditional request that comes back "not modified" still spends one. Thirty modules would burn half of that on every check. -So nothing checks on its own. A result is kept for a day, opening the window -costs nothing, and the button asks for a fresh one. If a check runs out of -budget it stops there and says how many modules it left out. It does not keep -asking and it does not record a refusal as though those modules had been +So the check waits for you to ask. A result is kept for a day, opening the +window costs nothing, and the button asks for a fresh one. If a check runs out +of budget it stops there and says how many modules it left out. It does not keep +asking, and it does not record a refusal as though those modules had been checked. A personal access token would raise the ceiling. It would also mean this module -storing a credential, which is the thing the section below exists to keep out of +storing a credential, which is the thing the section above exists to keep out of a file. So there is no token setting. -### Tags that are not versions +### Releases it cannot read The newest release of a monorepo can be tagged `@scope/name@0.6.0`. That is a tag, not a version, and comparing it to an installed version produces an answer -with no meaning. A tag that does not start with a version is reported as unreadable -rather than shown as the version to upgrade to. - -## Credentials - -Some modules keep an API token or a licence key in a setting. Values whose key -names one stay out of the file unless you tick the box. The check reads the key -name, so it catches the common cases and nothing clever. - -Read a profile before you share it. - -## What happens on import - -Each value is applied on its own. A value that no longer fits its setting is -skipped, and so is a setting whose package is not installed in the target world. -Both are normal when a profile is older than the world it lands in. The rest of -the import continues, and the report names what was left out. - -Export the target world first if you want a way back. +with no meaning. A tag that does not start with a version is reported as +unreadable rather than shown as the version to upgrade to. -## From a macro +## The API, for macros and modules ```js const vault = game.modules.get('settings-vault').api; @@ -109,15 +128,15 @@ const vault = game.modules.get('settings-vault').api; const profile = vault.buildProfile({ namespaces: ['some-module'] }); // Write it back, and see what did not land. -const report = await vault.applyProfile(profile); -console.log(report.applied.length, report.skipped); +const imported = await vault.applyProfile(profile); +console.log(imported.applied.length, imported.skipped); // Open the profile window. vault.open(); // What the last update check found. Asks GitHub nothing. -const report = vault.lastReport(); -console.log(report.outdatedCount, report.checkedAt); +const updates = vault.lastReport(); +console.log(updates.outdatedCount, updates.checkedAt); // Ask GitHub. One request per module with a release page. GM only, because // the result is stored in the world. @@ -127,14 +146,6 @@ await vault.checkUpdates({ force: true }); vault.openUpdates(); ``` -## Install - -Paste this manifest URL into Foundry's module installer: - -``` -https://github.com/vttforge/settings-vault/releases/latest/download/module.json -``` - ## Build from source ```bash @@ -161,7 +172,12 @@ value and applies a profile over it, runs one real check against GitHub, seeds a higher version to draw the outdated row, and confirms both refusals: a player cannot check, and a spent request budget stops the loop instead of caching a refusal against every remaining module. It opens both windows and asserts the -console stayed clean. 26 checks. +console stayed clean. + +Then it does the part one world cannot show. It exports a profile, creates a +second world on the same system, launches it, and imports there. The new world +starts at its defaults, and after the import it holds the value from the first. +32 checks. Local only. Booting Foundry needs a licence and an account, so this cannot run in CI on a fresh clone. It needs `docker` on the PATH, plus diff --git a/docs/updates-window.png b/docs/updates-window.png new file mode 100644 index 0000000..cdc726f Binary files /dev/null and b/docs/updates-window.png differ diff --git a/docs/vault-window.png b/docs/vault-window.png new file mode 100644 index 0000000..7774360 Binary files /dev/null and b/docs/vault-window.png differ diff --git a/vite.config.mjs b/vite.config.mjs index 663e722..47250db 100644 --- a/vite.config.mjs +++ b/vite.config.mjs @@ -14,10 +14,12 @@ export default defineConfig({ id: 'settings-vault', kind: 'module', entry: 'scripts/main.ts', - // `readme`, `changelog` e `license` no manifesto sao caminhos dentro da - // pasta do modulo, entao os tres arquivos tem que entrar no zip. A lista - // padrao do plugin nao os inclui, e nomear a lista substitui o padrao. - staticAssets: ['lang', 'templates', 'packs', 'README.md', 'CHANGELOG.md', 'LICENSE'], + // `readme`, `changelog` and `license` in the manifest are paths inside the + // module folder, so all three files have to be in the zip. The plugin's + // default list leaves them out, and naming the list replaces the default. + // `docs` goes in with them: the README points at the images there, and + // Foundry reads that README from inside the module folder. + staticAssets: ['lang', 'templates', 'packs', 'docs', 'README.md', 'CHANGELOG.md', 'LICENSE'], }), ], });